Skip to content

Commit 51323a8

Browse files
bors[bot]matklad
andcommitted
Merge #1011
1011: cleanup r=matklad a=matklad Co-authored-by: Aleksey Kladov <[email protected]>
2 parents 48472f5 + 1b58e3e commit 51323a8

File tree

3 files changed

+298
-301
lines changed

3 files changed

+298
-301
lines changed

crates/ra_ide_api/src/diagnostics.rs

Lines changed: 298 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -1,65 +1,310 @@
1+
use itertools::Itertools;
12
use hir::{Problem, source_binder};
23
use ra_ide_api_light::Severity;
34
use ra_db::SourceDatabase;
5+
use ra_syntax::{
6+
Location, SourceFile, SyntaxKind, TextRange, SyntaxNode,
7+
ast::{self, AstNode},
48

5-
use crate::{Diagnostic, FileId, FileSystemEdit, SourceChange, db::RootDatabase};
9+
};
10+
use ra_text_edit::{TextEdit, TextEditBuilder};
11+
12+
use crate::{Diagnostic, FileId, FileSystemEdit, SourceChange, SourceFileEdit, db::RootDatabase};
613

714
pub(crate) fn diagnostics(db: &RootDatabase, file_id: FileId) -> Vec<Diagnostic> {
8-
let syntax = db.parse(file_id);
9-
10-
let mut res = ra_ide_api_light::diagnostics(&syntax)
11-
.into_iter()
12-
.map(|d| Diagnostic {
13-
range: d.range,
14-
message: d.msg,
15-
severity: d.severity,
16-
fix: d.fix.map(|fix| SourceChange::from_local_edit(file_id, fix)),
17-
})
18-
.collect::<Vec<_>>();
15+
let source_file = db.parse(file_id);
16+
let mut res = Vec::new();
17+
18+
syntax_errors(&mut res, &source_file);
19+
20+
for node in source_file.syntax().descendants() {
21+
check_unnecessary_braces_in_use_statement(&mut res, file_id, node);
22+
check_struct_shorthand_initialization(&mut res, file_id, node);
23+
}
24+
1925
if let Some(m) = source_binder::module_from_file_id(db, file_id) {
20-
for (name_node, problem) in m.problems(db) {
21-
let source_root = db.file_source_root(file_id);
22-
let diag = match problem {
23-
Problem::UnresolvedModule { candidate } => {
24-
let create_file =
25-
FileSystemEdit::CreateFile { source_root, path: candidate.clone() };
26-
let fix = SourceChange {
27-
label: "create module".to_string(),
28-
source_file_edits: Vec::new(),
29-
file_system_edits: vec![create_file],
26+
check_module(&mut res, db, file_id, m);
27+
};
28+
res
29+
}
30+
31+
fn syntax_errors(acc: &mut Vec<Diagnostic>, source_file: &SourceFile) {
32+
fn location_to_range(location: Location) -> TextRange {
33+
match location {
34+
Location::Offset(offset) => TextRange::offset_len(offset, 1.into()),
35+
Location::Range(range) => range,
36+
}
37+
}
38+
39+
acc.extend(source_file.errors().into_iter().map(|err| Diagnostic {
40+
range: location_to_range(err.location()),
41+
message: format!("Syntax Error: {}", err),
42+
severity: Severity::Error,
43+
fix: None,
44+
}));
45+
}
46+
47+
fn check_unnecessary_braces_in_use_statement(
48+
acc: &mut Vec<Diagnostic>,
49+
file_id: FileId,
50+
node: &SyntaxNode,
51+
) -> Option<()> {
52+
let use_tree_list = ast::UseTreeList::cast(node)?;
53+
if let Some((single_use_tree,)) = use_tree_list.use_trees().collect_tuple() {
54+
let range = use_tree_list.syntax().range();
55+
let edit =
56+
text_edit_for_remove_unnecessary_braces_with_self_in_use_statement(single_use_tree)
57+
.unwrap_or_else(|| {
58+
let to_replace = single_use_tree.syntax().text().to_string();
59+
let mut edit_builder = TextEditBuilder::default();
60+
edit_builder.delete(range);
61+
edit_builder.insert(range.start(), to_replace);
62+
edit_builder.finish()
63+
});
64+
65+
acc.push(Diagnostic {
66+
range,
67+
message: format!("Unnecessary braces in use statement"),
68+
severity: Severity::WeakWarning,
69+
fix: Some(SourceChange {
70+
label: "Remove unnecessary braces".to_string(),
71+
source_file_edits: vec![SourceFileEdit { file_id, edit }],
72+
file_system_edits: Vec::new(),
73+
cursor_position: None,
74+
}),
75+
});
76+
}
77+
78+
Some(())
79+
}
80+
81+
fn text_edit_for_remove_unnecessary_braces_with_self_in_use_statement(
82+
single_use_tree: &ast::UseTree,
83+
) -> Option<TextEdit> {
84+
let use_tree_list_node = single_use_tree.syntax().parent()?;
85+
if single_use_tree.path()?.segment()?.syntax().first_child()?.kind() == SyntaxKind::SELF_KW {
86+
let start = use_tree_list_node.prev_sibling()?.range().start();
87+
let end = use_tree_list_node.range().end();
88+
let range = TextRange::from_to(start, end);
89+
let mut edit_builder = TextEditBuilder::default();
90+
edit_builder.delete(range);
91+
return Some(edit_builder.finish());
92+
}
93+
None
94+
}
95+
96+
fn check_struct_shorthand_initialization(
97+
acc: &mut Vec<Diagnostic>,
98+
file_id: FileId,
99+
node: &SyntaxNode,
100+
) -> Option<()> {
101+
let struct_lit = ast::StructLit::cast(node)?;
102+
let named_field_list = struct_lit.named_field_list()?;
103+
for named_field in named_field_list.fields() {
104+
if let (Some(name_ref), Some(expr)) = (named_field.name_ref(), named_field.expr()) {
105+
let field_name = name_ref.syntax().text().to_string();
106+
let field_expr = expr.syntax().text().to_string();
107+
if field_name == field_expr {
108+
let mut edit_builder = TextEditBuilder::default();
109+
edit_builder.delete(named_field.syntax().range());
110+
edit_builder.insert(named_field.syntax().range().start(), field_name);
111+
let edit = edit_builder.finish();
112+
113+
acc.push(Diagnostic {
114+
range: named_field.syntax().range(),
115+
message: format!("Shorthand struct initialization"),
116+
severity: Severity::WeakWarning,
117+
fix: Some(SourceChange {
118+
label: "use struct shorthand initialization".to_string(),
119+
source_file_edits: vec![SourceFileEdit { file_id, edit }],
120+
file_system_edits: Vec::new(),
30121
cursor_position: None,
31-
};
32-
Diagnostic {
33-
range: name_node.range(),
34-
message: "unresolved module".to_string(),
35-
severity: Severity::Error,
36-
fix: Some(fix),
37-
}
122+
}),
123+
});
124+
}
125+
}
126+
}
127+
Some(())
128+
}
129+
130+
fn check_module(
131+
acc: &mut Vec<Diagnostic>,
132+
db: &RootDatabase,
133+
file_id: FileId,
134+
module: hir::Module,
135+
) {
136+
let source_root = db.file_source_root(file_id);
137+
for (name_node, problem) in module.problems(db) {
138+
let diag = match problem {
139+
Problem::UnresolvedModule { candidate } => {
140+
let create_file =
141+
FileSystemEdit::CreateFile { source_root, path: candidate.clone() };
142+
let fix = SourceChange {
143+
label: "create module".to_string(),
144+
source_file_edits: Vec::new(),
145+
file_system_edits: vec![create_file],
146+
cursor_position: None,
147+
};
148+
Diagnostic {
149+
range: name_node.range(),
150+
message: "unresolved module".to_string(),
151+
severity: Severity::Error,
152+
fix: Some(fix),
38153
}
39-
Problem::NotDirOwner { move_to, candidate } => {
40-
let move_file = FileSystemEdit::MoveFile {
41-
src: file_id,
42-
dst_source_root: source_root,
43-
dst_path: move_to.clone(),
44-
};
45-
let create_file =
46-
FileSystemEdit::CreateFile { source_root, path: move_to.join(candidate) };
47-
let fix = SourceChange {
48-
label: "move file and create module".to_string(),
49-
source_file_edits: Vec::new(),
50-
file_system_edits: vec![move_file, create_file],
51-
cursor_position: None,
52-
};
53-
Diagnostic {
54-
range: name_node.range(),
55-
message: "can't declare module at this location".to_string(),
56-
severity: Severity::Error,
57-
fix: Some(fix),
58-
}
154+
}
155+
Problem::NotDirOwner { move_to, candidate } => {
156+
let move_file = FileSystemEdit::MoveFile {
157+
src: file_id,
158+
dst_source_root: source_root,
159+
dst_path: move_to.clone(),
160+
};
161+
let create_file =
162+
FileSystemEdit::CreateFile { source_root, path: move_to.join(candidate) };
163+
let fix = SourceChange {
164+
label: "move file and create module".to_string(),
165+
source_file_edits: Vec::new(),
166+
file_system_edits: vec![move_file, create_file],
167+
cursor_position: None,
168+
};
169+
Diagnostic {
170+
range: name_node.range(),
171+
message: "can't declare module at this location".to_string(),
172+
severity: Severity::Error,
173+
fix: Some(fix),
59174
}
60-
};
61-
res.push(diag)
175+
}
176+
};
177+
acc.push(diag)
178+
}
179+
}
180+
181+
#[cfg(test)]
182+
mod tests {
183+
use test_utils::assert_eq_text;
184+
185+
use super::*;
186+
187+
type DiagnosticChecker = fn(&mut Vec<Diagnostic>, FileId, &SyntaxNode) -> Option<()>;
188+
189+
fn check_not_applicable(code: &str, func: DiagnosticChecker) {
190+
let file = SourceFile::parse(code);
191+
let mut diagnostics = Vec::new();
192+
for node in file.syntax().descendants() {
193+
func(&mut diagnostics, FileId(0), node);
62194
}
63-
};
64-
res
195+
assert!(diagnostics.is_empty());
196+
}
197+
198+
fn check_apply(before: &str, after: &str, func: DiagnosticChecker) {
199+
let file = SourceFile::parse(before);
200+
let mut diagnostics = Vec::new();
201+
for node in file.syntax().descendants() {
202+
func(&mut diagnostics, FileId(0), node);
203+
}
204+
let diagnostic =
205+
diagnostics.pop().unwrap_or_else(|| panic!("no diagnostics for:\n{}\n", before));
206+
let mut fix = diagnostic.fix.unwrap();
207+
let edit = fix.source_file_edits.pop().unwrap().edit;
208+
let actual = edit.apply(&before);
209+
assert_eq_text!(after, &actual);
210+
}
211+
212+
#[test]
213+
fn test_check_unnecessary_braces_in_use_statement() {
214+
check_not_applicable(
215+
"
216+
use a;
217+
use a::{c, d::e};
218+
",
219+
check_unnecessary_braces_in_use_statement,
220+
);
221+
check_apply("use {b};", "use b;", check_unnecessary_braces_in_use_statement);
222+
check_apply("use a::{c};", "use a::c;", check_unnecessary_braces_in_use_statement);
223+
check_apply("use a::{self};", "use a;", check_unnecessary_braces_in_use_statement);
224+
check_apply(
225+
"use a::{c, d::{e}};",
226+
"use a::{c, d::e};",
227+
check_unnecessary_braces_in_use_statement,
228+
);
229+
}
230+
231+
#[test]
232+
fn test_check_struct_shorthand_initialization() {
233+
check_not_applicable(
234+
r#"
235+
struct A {
236+
a: &'static str
237+
}
238+
239+
fn main() {
240+
A {
241+
a: "hello"
242+
}
243+
}
244+
"#,
245+
check_struct_shorthand_initialization,
246+
);
247+
248+
check_apply(
249+
r#"
250+
struct A {
251+
a: &'static str
252+
}
253+
254+
fn main() {
255+
let a = "haha";
256+
A {
257+
a: a
258+
}
259+
}
260+
"#,
261+
r#"
262+
struct A {
263+
a: &'static str
264+
}
265+
266+
fn main() {
267+
let a = "haha";
268+
A {
269+
a
270+
}
271+
}
272+
"#,
273+
check_struct_shorthand_initialization,
274+
);
275+
276+
check_apply(
277+
r#"
278+
struct A {
279+
a: &'static str,
280+
b: &'static str
281+
}
282+
283+
fn main() {
284+
let a = "haha";
285+
let b = "bb";
286+
A {
287+
a: a,
288+
b
289+
}
290+
}
291+
"#,
292+
r#"
293+
struct A {
294+
a: &'static str,
295+
b: &'static str
296+
}
297+
298+
fn main() {
299+
let a = "haha";
300+
let b = "bb";
301+
A {
302+
a,
303+
b
304+
}
305+
}
306+
"#,
307+
check_struct_shorthand_initialization,
308+
);
309+
}
65310
}

0 commit comments

Comments
 (0)