mirror of https://github.com/rust-lang/reference
Add more style checks.
This commit is contained in:
parent
56a13c082e
commit
f135b9a2be
|
@ -26,8 +26,8 @@ jobs:
|
||||||
mdbook --version
|
mdbook --version
|
||||||
- name: Run tests
|
- name: Run tests
|
||||||
run: mdbook test
|
run: mdbook test
|
||||||
- name: Check for unstable features
|
- name: Style checks
|
||||||
run: (cd stable-check && cargo run -- ../src)
|
run: (cd style-check && cargo run -- ../src)
|
||||||
- name: Check for broken links
|
- name: Check for broken links
|
||||||
run: |
|
run: |
|
||||||
curl -sSLo linkcheck.sh \
|
curl -sSLo linkcheck.sh \
|
||||||
|
|
4
STYLE.md
4
STYLE.md
|
@ -3,6 +3,10 @@
|
||||||
Some conventions and content guidelines are specified in the [introduction].
|
Some conventions and content guidelines are specified in the [introduction].
|
||||||
This document serves as a guide for editors and reviewers.
|
This document serves as a guide for editors and reviewers.
|
||||||
|
|
||||||
|
There is a [`style-check`](style-check/) tool which is run in CI to check some
|
||||||
|
of these. To use it locally, run
|
||||||
|
`cargo run --manifest-path=style-check/Cargo.toml src`.
|
||||||
|
|
||||||
## Markdown formatting
|
## Markdown formatting
|
||||||
|
|
||||||
* Use ATX-style heading with sentence case.
|
* Use ATX-style heading with sentence case.
|
||||||
|
|
|
@ -1,6 +0,0 @@
|
||||||
# This file is automatically @generated by Cargo.
|
|
||||||
# It is not intended for manual editing.
|
|
||||||
[[package]]
|
|
||||||
name = "stable-check"
|
|
||||||
version = "0.1.0"
|
|
||||||
|
|
|
@ -1,42 +0,0 @@
|
||||||
use std::error::Error;
|
|
||||||
use std::env;
|
|
||||||
use std::fs;
|
|
||||||
use std::fs::File;
|
|
||||||
use std::io::prelude::*;
|
|
||||||
use std::path::Path;
|
|
||||||
|
|
||||||
fn main() {
|
|
||||||
let arg = env::args().nth(1).unwrap_or_else(|| {
|
|
||||||
println!("Please pass a src directory as the first argument");
|
|
||||||
std::process::exit(1);
|
|
||||||
});
|
|
||||||
|
|
||||||
match check_directory(&Path::new(&arg)) {
|
|
||||||
Ok(()) => println!("passed!"),
|
|
||||||
Err(e) => {
|
|
||||||
println!("Error: {}", e);
|
|
||||||
std::process::exit(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn check_directory(dir: &Path) -> Result<(), Box<dyn Error>> {
|
|
||||||
for entry in fs::read_dir(dir)? {
|
|
||||||
let entry = entry?;
|
|
||||||
let path = entry.path();
|
|
||||||
|
|
||||||
if path.is_dir() {
|
|
||||||
return check_directory(&path);
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut file = File::open(&path)?;
|
|
||||||
let mut contents = String::new();
|
|
||||||
file.read_to_string(&mut contents)?;
|
|
||||||
|
|
||||||
if contents.contains("#![feature") {
|
|
||||||
return Err(From::from(format!("Feature flag found in {:?}", path)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
|
@ -0,0 +1,62 @@
|
||||||
|
# This file is automatically @generated by Cargo.
|
||||||
|
# It is not intended for manual editing.
|
||||||
|
[[package]]
|
||||||
|
name = "bitflags"
|
||||||
|
version = "1.2.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "getopts"
|
||||||
|
version = "0.2.21"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "14dbbfd5c71d70241ecf9e6f13737f7b5ce823821063188d7e46c41d371eebd5"
|
||||||
|
dependencies = [
|
||||||
|
"unicode-width",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "memchr"
|
||||||
|
version = "2.3.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3728d817d99e5ac407411fa471ff9800a778d88a24685968b36824eaf4bee400"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pulldown-cmark"
|
||||||
|
version = "0.8.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ffade02495f22453cd593159ea2f59827aae7f53fa8323f756799b670881dcf8"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags",
|
||||||
|
"getopts",
|
||||||
|
"memchr",
|
||||||
|
"unicase",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "style-check"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"pulldown-cmark",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unicase"
|
||||||
|
version = "2.6.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "50f37be617794602aabbeee0be4f259dc1778fabe05e2d67ee8f79326d5cb4f6"
|
||||||
|
dependencies = [
|
||||||
|
"version_check",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unicode-width"
|
||||||
|
version = "0.1.8"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9337591893a19b88d8d87f2cec1e73fad5cdfd10e5a6f349f498ad6ea2ffb1e3"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "version_check"
|
||||||
|
version = "0.9.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b5a972e5669d67ba988ce3dc826706fb0a8b01471c088cb0b6110b805cc36aed"
|
|
@ -1,5 +1,8 @@
|
||||||
[package]
|
[package]
|
||||||
name = "stable-check"
|
name = "style-check"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
authors = ["steveklabnik <steve@steveklabnik.com>"]
|
authors = ["steveklabnik <steve@steveklabnik.com>"]
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
pulldown-cmark = "0.8"
|
|
@ -0,0 +1,131 @@
|
||||||
|
use std::env;
|
||||||
|
use std::error::Error;
|
||||||
|
use std::fs;
|
||||||
|
use std::path::Path;
|
||||||
|
|
||||||
|
macro_rules! style_error {
|
||||||
|
($bad:expr, $path:expr, $($arg:tt)*) => {
|
||||||
|
*$bad = true;
|
||||||
|
eprint!("error in {}: ", $path.display());
|
||||||
|
eprintln!("{}", format_args!($($arg)*));
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let arg = env::args().nth(1).unwrap_or_else(|| {
|
||||||
|
eprintln!("Please pass a src directory as the first argument");
|
||||||
|
std::process::exit(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
let mut bad = false;
|
||||||
|
if let Err(e) = check_directory(&Path::new(&arg), &mut bad) {
|
||||||
|
eprintln!("error: {}", e);
|
||||||
|
std::process::exit(1);
|
||||||
|
}
|
||||||
|
if bad {
|
||||||
|
eprintln!("some style checks failed");
|
||||||
|
std::process::exit(1);
|
||||||
|
}
|
||||||
|
eprintln!("passed!");
|
||||||
|
}
|
||||||
|
|
||||||
|
fn check_directory(dir: &Path, bad: &mut bool) -> Result<(), Box<dyn Error>> {
|
||||||
|
for entry in fs::read_dir(dir)? {
|
||||||
|
let entry = entry?;
|
||||||
|
let path = entry.path();
|
||||||
|
|
||||||
|
if path.is_dir() {
|
||||||
|
check_directory(&path, bad)?;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if !matches!(
|
||||||
|
path.extension().and_then(|p| p.to_str()),
|
||||||
|
Some("md") | Some("html")
|
||||||
|
) {
|
||||||
|
// This may be extended in the future if other file types are needed.
|
||||||
|
style_error!(bad, path, "expected only md or html in src");
|
||||||
|
}
|
||||||
|
|
||||||
|
let contents = fs::read_to_string(&path)?;
|
||||||
|
if contents.contains("#![feature") {
|
||||||
|
style_error!(bad, path, "#![feature] attributes are not allowed");
|
||||||
|
}
|
||||||
|
if contents.contains('\r') {
|
||||||
|
style_error!(
|
||||||
|
bad,
|
||||||
|
path,
|
||||||
|
"CR characters not allowed, must use LF line endings"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if contents.contains('\t') {
|
||||||
|
style_error!(bad, path, "tab characters not allowed, use spaces");
|
||||||
|
}
|
||||||
|
if !contents.ends_with('\n') {
|
||||||
|
style_error!(bad, path, "file must end with a newline");
|
||||||
|
}
|
||||||
|
for line in contents.lines() {
|
||||||
|
if line.ends_with(' ') {
|
||||||
|
style_error!(bad, path, "lines must not end with spaces");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cmark_check(&path, bad, &contents)?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn cmark_check(path: &Path, bad: &mut bool, contents: &str) -> Result<(), Box<dyn Error>> {
|
||||||
|
use pulldown_cmark::{BrokenLink, CodeBlockKind, Event, Options, Parser, Tag};
|
||||||
|
|
||||||
|
macro_rules! cmark_error {
|
||||||
|
($bad:expr, $path:expr, $range:expr, $($arg:tt)*) => {
|
||||||
|
*$bad = true;
|
||||||
|
let lineno = contents[..$range.start].chars().filter(|&ch| ch == '\n').count() + 1;
|
||||||
|
eprint!("error in {} (line {}): ", $path.display(), lineno);
|
||||||
|
eprintln!("{}", format_args!($($arg)*));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let options = Options::all();
|
||||||
|
// Can't use `bad` because it would get captured in closure.
|
||||||
|
let mut link_err = false;
|
||||||
|
let mut cb = |link: BrokenLink<'_>| {
|
||||||
|
cmark_error!(
|
||||||
|
&mut link_err,
|
||||||
|
path,
|
||||||
|
link.span,
|
||||||
|
"broken {:?} link (reference `{}`)",
|
||||||
|
link.link_type,
|
||||||
|
link.reference
|
||||||
|
);
|
||||||
|
None
|
||||||
|
};
|
||||||
|
let parser = Parser::new_with_broken_link_callback(contents, options, Some(&mut cb));
|
||||||
|
|
||||||
|
for (event, range) in parser.into_offset_iter() {
|
||||||
|
match event {
|
||||||
|
Event::Start(Tag::CodeBlock(CodeBlockKind::Indented)) => {
|
||||||
|
cmark_error!(
|
||||||
|
bad,
|
||||||
|
path,
|
||||||
|
range,
|
||||||
|
"indented code blocks should use triple backtick-style \
|
||||||
|
with a language identifier"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
Event::Start(Tag::CodeBlock(CodeBlockKind::Fenced(languages))) => {
|
||||||
|
if languages.is_empty() {
|
||||||
|
cmark_error!(
|
||||||
|
bad,
|
||||||
|
path,
|
||||||
|
range,
|
||||||
|
"code block should include an explicit language",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*bad |= link_err;
|
||||||
|
Ok(())
|
||||||
|
}
|
Loading…
Reference in New Issue