Skip to content

Commit 4c00bde

Browse files
bors[bot]popzxc
andauthored
Merge #6266
6266: Generate diagnostics docs r=matklad a=popzxc Resolves #6215 Co-authored-by: Igor Aleksanov <[email protected]>
2 parents 378dd90 + b8a74e0 commit 4c00bde

File tree

10 files changed

+156
-6
lines changed

10 files changed

+156
-6
lines changed

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,4 @@ crates/*/target
99
.vscode/settings.json
1010
generated_assists.adoc
1111
generated_features.adoc
12+
generated_diagnostic.adoc

crates/hir_def/src/diagnostics.rs

+9
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@ use syntax::{ast, AstPtr, SyntaxNodePtr};
77

88
use hir_expand::{HirFileId, InFile};
99

10+
// Diagnostic: unresolved-module
11+
//
12+
// This diagnostic is triggered if rust-analyzer is unable to discover referred module.
1013
#[derive(Debug)]
1114
pub struct UnresolvedModule {
1215
pub file: HirFileId,
@@ -29,6 +32,9 @@ impl Diagnostic for UnresolvedModule {
2932
}
3033
}
3134

35+
// Diagnostic: unresolved-extern-crate
36+
//
37+
// This diagnostic is triggered if rust-analyzer is unable to discover referred extern crate.
3238
#[derive(Debug)]
3339
pub struct UnresolvedExternCrate {
3440
pub file: HirFileId,
@@ -50,6 +56,9 @@ impl Diagnostic for UnresolvedExternCrate {
5056
}
5157
}
5258

59+
// Diagnostic: unresolved-import
60+
//
61+
// This diagnostic is triggered if rust-analyzer is unable to discover imported module.
5362
#[derive(Debug)]
5463
pub struct UnresolvedImport {
5564
pub file: HirFileId,

crates/hir_ty/src/diagnostics.rs

+55
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,9 @@ pub fn validate_body(db: &dyn HirDatabase, owner: DefWithBodyId, sink: &mut Diag
3636
validator.validate_body(db);
3737
}
3838

39+
// Diagnostic: no-such-field
40+
//
41+
// This diagnostic is triggered if created structure does not have field provided in record.
3942
#[derive(Debug)]
4043
pub struct NoSuchField {
4144
pub file: HirFileId,
@@ -60,6 +63,17 @@ impl Diagnostic for NoSuchField {
6063
}
6164
}
6265

66+
// Diagnostic: missing-structure-fields
67+
//
68+
// This diagnostic is triggered if record lacks some fields that exist in the corresponding structure.
69+
//
70+
// Example:
71+
//
72+
// ```rust
73+
// struct A { a: u8, b: u8 }
74+
//
75+
// let a = A { a: 10 };
76+
// ```
6377
#[derive(Debug)]
6478
pub struct MissingFields {
6579
pub file: HirFileId,
@@ -96,6 +110,21 @@ impl Diagnostic for MissingFields {
96110
}
97111
}
98112

113+
// Diagnostic: missing-pat-fields
114+
//
115+
// This diagnostic is triggered if pattern lacks some fields that exist in the corresponding structure.
116+
//
117+
// Example:
118+
//
119+
// ```rust
120+
// struct A { a: u8, b: u8 }
121+
//
122+
// let a = A { a: 10, b: 20 };
123+
//
124+
// if let A { a } = a {
125+
// // ...
126+
// }
127+
// ```
99128
#[derive(Debug)]
100129
pub struct MissingPatFields {
101130
pub file: HirFileId,
@@ -130,6 +159,9 @@ impl Diagnostic for MissingPatFields {
130159
}
131160
}
132161

162+
// Diagnostic: missing-match-arm
163+
//
164+
// This diagnostic is triggered if `match` block is missing one or more match arms.
133165
#[derive(Debug)]
134166
pub struct MissingMatchArms {
135167
pub file: HirFileId,
@@ -152,6 +184,17 @@ impl Diagnostic for MissingMatchArms {
152184
}
153185
}
154186

187+
// Diagnostic: missing-ok-in-tail-expr
188+
//
189+
// This diagnostic is triggered if block that should return `Result` returns a value not wrapped in `Ok`.
190+
//
191+
// Example:
192+
//
193+
// ```rust
194+
// fn foo() -> Result<u8, ()> {
195+
// 10
196+
// }
197+
// ```
155198
#[derive(Debug)]
156199
pub struct MissingOkInTailExpr {
157200
pub file: HirFileId,
@@ -173,6 +216,9 @@ impl Diagnostic for MissingOkInTailExpr {
173216
}
174217
}
175218

219+
// Diagnostic: break-outside-of-loop
220+
//
221+
// This diagnostic is triggered if `break` keyword is used outside of a loop.
176222
#[derive(Debug)]
177223
pub struct BreakOutsideOfLoop {
178224
pub file: HirFileId,
@@ -194,6 +240,9 @@ impl Diagnostic for BreakOutsideOfLoop {
194240
}
195241
}
196242

243+
// Diagnostic: missing-unsafe
244+
//
245+
// This diagnostic is triggered if operation marked as `unsafe` is used outside of `unsafe` function or block.
197246
#[derive(Debug)]
198247
pub struct MissingUnsafe {
199248
pub file: HirFileId,
@@ -215,6 +264,9 @@ impl Diagnostic for MissingUnsafe {
215264
}
216265
}
217266

267+
// Diagnostic: mismatched-arg-count
268+
//
269+
// This diagnostic is triggered if function is invoked with an incorrect amount of arguments.
218270
#[derive(Debug)]
219271
pub struct MismatchedArgCount {
220272
pub file: HirFileId,
@@ -264,6 +316,9 @@ impl fmt::Display for CaseType {
264316
}
265317
}
266318

319+
// Diagnostic: incorrect-ident-case
320+
//
321+
// This diagnostic is triggered if item name doesn't follow https://doc.rust-lang.org/1.0.0/style/style/naming/README.html[Rust naming convention].
267322
#[derive(Debug)]
268323
pub struct IncorrectCase {
269324
pub file: HirFileId,

xtask/src/codegen.rs

+3
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ mod gen_parser_tests;
1010
mod gen_assists_docs;
1111
mod gen_feature_docs;
1212
mod gen_features;
13+
mod gen_diagnostic_docs;
1314

1415
use std::{
1516
fmt, mem,
@@ -21,6 +22,7 @@ use crate::{ensure_rustfmt, project_root, Result};
2122

2223
pub use self::{
2324
gen_assists_docs::{generate_assists_docs, generate_assists_tests},
25+
gen_diagnostic_docs::generate_diagnostic_docs,
2426
gen_feature_docs::generate_feature_docs,
2527
gen_features::generate_features,
2628
gen_parser_tests::generate_parser_tests,
@@ -47,6 +49,7 @@ impl CodegenCmd {
4749
generate_assists_tests(Mode::Overwrite)?;
4850
generate_assists_docs(Mode::Overwrite)?;
4951
generate_feature_docs(Mode::Overwrite)?;
52+
generate_diagnostic_docs(Mode::Overwrite)?;
5053
Ok(())
5154
}
5255
}

xtask/src/codegen/gen_assists_docs.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
//! Generates `assists.md` documentation.
22
3-
use std::{fmt, fs, path::Path};
3+
use std::{fmt, path::Path};
44

55
use crate::{
66
codegen::{self, extract_comment_blocks_with_empty_lines, reformat, Location, Mode, PREAMBLE},
@@ -39,7 +39,7 @@ impl Assist {
3939
return Ok(res);
4040

4141
fn collect_file(acc: &mut Vec<Assist>, path: &Path) -> Result<()> {
42-
let text = fs::read_to_string(path)?;
42+
let text = xshell::read_file(path)?;
4343
let comment_blocks = extract_comment_blocks_with_empty_lines("Assist", &text);
4444

4545
for block in comment_blocks {
+74
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
//! Generates `assists.md` documentation.
2+
3+
use std::{fmt, path::PathBuf};
4+
5+
use crate::{
6+
codegen::{self, extract_comment_blocks_with_empty_lines, Location, Mode, PREAMBLE},
7+
project_root, rust_files, Result,
8+
};
9+
10+
pub fn generate_diagnostic_docs(mode: Mode) -> Result<()> {
11+
let diagnostics = Diagnostic::collect()?;
12+
let contents =
13+
diagnostics.into_iter().map(|it| it.to_string()).collect::<Vec<_>>().join("\n\n");
14+
let contents = format!("//{}\n{}\n", PREAMBLE, contents.trim());
15+
let dst = project_root().join("docs/user/generated_diagnostic.adoc");
16+
codegen::update(&dst, &contents, mode)?;
17+
Ok(())
18+
}
19+
20+
#[derive(Debug)]
21+
struct Diagnostic {
22+
id: String,
23+
location: Location,
24+
doc: String,
25+
}
26+
27+
impl Diagnostic {
28+
fn collect() -> Result<Vec<Diagnostic>> {
29+
let mut res = Vec::new();
30+
for path in rust_files(&project_root()) {
31+
collect_file(&mut res, path)?;
32+
}
33+
res.sort_by(|lhs, rhs| lhs.id.cmp(&rhs.id));
34+
return Ok(res);
35+
36+
fn collect_file(acc: &mut Vec<Diagnostic>, path: PathBuf) -> Result<()> {
37+
let text = xshell::read_file(&path)?;
38+
let comment_blocks = extract_comment_blocks_with_empty_lines("Diagnostic", &text);
39+
40+
for block in comment_blocks {
41+
let id = block.id;
42+
if let Err(msg) = is_valid_diagnostic_name(&id) {
43+
panic!("invalid diagnostic name: {:?}:\n {}", id, msg)
44+
}
45+
let doc = block.contents.join("\n");
46+
let location = Location::new(path.clone(), block.line);
47+
acc.push(Diagnostic { id, location, doc })
48+
}
49+
50+
Ok(())
51+
}
52+
}
53+
}
54+
55+
fn is_valid_diagnostic_name(diagnostic: &str) -> Result<(), String> {
56+
let diagnostic = diagnostic.trim();
57+
if diagnostic.find(char::is_whitespace).is_some() {
58+
return Err("Diagnostic names can't contain whitespace symbols".into());
59+
}
60+
if diagnostic.chars().any(|c| c.is_ascii_uppercase()) {
61+
return Err("Diagnostic names can't contain uppercase symbols".into());
62+
}
63+
if diagnostic.chars().any(|c| !c.is_ascii()) {
64+
return Err("Diagnostic can't contain non-ASCII symbols".into());
65+
}
66+
67+
Ok(())
68+
}
69+
70+
impl fmt::Display for Diagnostic {
71+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
72+
writeln!(f, "=== {}\n**Source:** {}\n{}", self.id, self.location, self.doc)
73+
}
74+
}

xtask/src/codegen/gen_feature_docs.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
//! Generates `assists.md` documentation.
22
3-
use std::{fmt, fs, path::PathBuf};
3+
use std::{fmt, path::PathBuf};
44

55
use crate::{
66
codegen::{self, extract_comment_blocks_with_empty_lines, Location, Mode, PREAMBLE},
@@ -33,7 +33,7 @@ impl Feature {
3333
return Ok(res);
3434

3535
fn collect_file(acc: &mut Vec<Feature>, path: PathBuf) -> Result<()> {
36-
let text = fs::read_to_string(&path)?;
36+
let text = xshell::read_file(&path)?;
3737
let comment_blocks = extract_comment_blocks_with_empty_lines("Feature", &text);
3838

3939
for block in comment_blocks {

xtask/src/codegen/gen_parser_tests.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,7 @@ fn existing_tests(dir: &Path, ok: bool) -> Result<HashMap<String, (PathBuf, Test
124124
let file_name = path.file_name().unwrap().to_str().unwrap();
125125
file_name[5..file_name.len() - 3].to_string()
126126
};
127-
let text = fs::read_to_string(&path)?;
127+
let text = xshell::read_file(&path)?;
128128
let test = Test { name: name.clone(), text, ok };
129129
if let Some(old) = res.insert(name, (path, test)) {
130130
println!("Duplicate test: {:?}", old);

xtask/src/release.rs

+8-1
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,14 @@ https://github.com/sponsors/rust-analyzer[GitHub Sponsors].
5252
let path = changelog_dir.join(format!("{}-changelog-{}.adoc", today, changelog_n));
5353
write_file(&path, &contents)?;
5454

55-
for &adoc in ["manual.adoc", "generated_features.adoc", "generated_assists.adoc"].iter() {
55+
for &adoc in [
56+
"manual.adoc",
57+
"generated_features.adoc",
58+
"generated_assists.adoc",
59+
"generated_diagnostic.adoc",
60+
]
61+
.iter()
62+
{
5663
let src = project_root().join("./docs/user/").join(adoc);
5764
let dst = website_root.join(adoc);
5865
cp(src, dst)?;

xtask/tests/tidy.rs

+1
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ fn smoke_test_docs_generation() {
4242
// We don't commit docs to the repo, so we can just overwrite in tests.
4343
codegen::generate_assists_docs(Mode::Overwrite).unwrap();
4444
codegen::generate_feature_docs(Mode::Overwrite).unwrap();
45+
codegen::generate_diagnostic_docs(Mode::Overwrite).unwrap();
4546
}
4647

4748
#[test]

0 commit comments

Comments
 (0)