From 7e8f17188efcecfdfd1afbbc894a53c65985f836 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Thu, 21 Mar 2019 22:13:11 +0300 Subject: diagnostics --- crates/ra_ide_api/src/diagnostics.rs | 30 +++++++++++++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) (limited to 'crates/ra_ide_api/src') diff --git a/crates/ra_ide_api/src/diagnostics.rs b/crates/ra_ide_api/src/diagnostics.rs index 156f28ca3..f662f7e2f 100644 --- a/crates/ra_ide_api/src/diagnostics.rs +++ b/crates/ra_ide_api/src/diagnostics.rs @@ -3,7 +3,7 @@ use hir::{Problem, source_binder}; use ra_db::SourceDatabase; use ra_syntax::{ Location, SourceFile, SyntaxKind, TextRange, SyntaxNode, - ast::{self, AstNode}, + ast::{self, AstNode, NameOwner}, }; use ra_text_edit::{TextEdit, TextEditBuilder}; @@ -134,6 +134,13 @@ fn check_module( file_id: FileId, module: hir::Module, ) { + for decl in module.declarations(db) { + match decl { + hir::ModuleDef::Function(f) => check_function(acc, db, f), + _ => (), + } + } + let source_root = db.file_source_root(file_id); for (name_node, problem) in module.problems(db) { let diag = match problem { @@ -153,6 +160,27 @@ fn check_module( } } +fn check_function(acc: &mut Vec, db: &RootDatabase, function: hir::Function) { + let (_file_id, fn_def) = function.source(db); + let source_file = fn_def.syntax().ancestors().find_map(ast::SourceFile::cast).unwrap(); + let source_map = function.body_source_map(db); + for d in function.diagnostics(db) { + match d { + hir::diagnostics::FunctionDiagnostic::NoSuchField { expr, field } => { + if let Some(field) = source_map.field_syntax(expr, field) { + let field = field.to_node(&source_file); + acc.push(Diagnostic { + message: "no such field".into(), + range: field.syntax().range(), + severity: Severity::Error, + fix: None, + }) + } + } + } + } +} + #[cfg(test)] mod tests { use test_utils::assert_eq_text; -- cgit v1.2.3 From fcca35969dd7c63a83ee34c4ce7d54cefdb72bbe Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Sat, 23 Mar 2019 16:28:47 +0300 Subject: allow dyn diagnostics --- crates/ra_ide_api/src/diagnostics.rs | 26 ++++++++------------------ 1 file changed, 8 insertions(+), 18 deletions(-) (limited to 'crates/ra_ide_api/src') diff --git a/crates/ra_ide_api/src/diagnostics.rs b/crates/ra_ide_api/src/diagnostics.rs index f662f7e2f..943fd2f53 100644 --- a/crates/ra_ide_api/src/diagnostics.rs +++ b/crates/ra_ide_api/src/diagnostics.rs @@ -3,7 +3,7 @@ use hir::{Problem, source_binder}; use ra_db::SourceDatabase; use ra_syntax::{ Location, SourceFile, SyntaxKind, TextRange, SyntaxNode, - ast::{self, AstNode, NameOwner}, + ast::{self, AstNode}, }; use ra_text_edit::{TextEdit, TextEditBuilder}; @@ -161,23 +161,13 @@ fn check_module( } fn check_function(acc: &mut Vec, db: &RootDatabase, function: hir::Function) { - let (_file_id, fn_def) = function.source(db); - let source_file = fn_def.syntax().ancestors().find_map(ast::SourceFile::cast).unwrap(); - let source_map = function.body_source_map(db); - for d in function.diagnostics(db) { - match d { - hir::diagnostics::FunctionDiagnostic::NoSuchField { expr, field } => { - if let Some(field) = source_map.field_syntax(expr, field) { - let field = field.to_node(&source_file); - acc.push(Diagnostic { - message: "no such field".into(), - range: field.syntax().range(), - severity: Severity::Error, - fix: None, - }) - } - } - } + for d in function.diagnostics(db).iter() { + acc.push(Diagnostic { + message: d.message(), + range: d.syntax_node().range(), + severity: Severity::Error, + fix: None, + }) } } -- cgit v1.2.3 From 3fb88e95aa5e122a521beec766d5b1264ca4de3b Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Sat, 23 Mar 2019 18:35:14 +0300 Subject: switch modules to new diagnostics --- crates/ra_ide_api/src/diagnostics.rs | 66 ++++++++++++++++-------------------- 1 file changed, 30 insertions(+), 36 deletions(-) (limited to 'crates/ra_ide_api/src') diff --git a/crates/ra_ide_api/src/diagnostics.rs b/crates/ra_ide_api/src/diagnostics.rs index 943fd2f53..254342c0a 100644 --- a/crates/ra_ide_api/src/diagnostics.rs +++ b/crates/ra_ide_api/src/diagnostics.rs @@ -1,5 +1,5 @@ use itertools::Itertools; -use hir::{Problem, source_binder}; +use hir::{source_binder, diagnostics::Diagnostic as _}; use ra_db::SourceDatabase; use ra_syntax::{ Location, SourceFile, SyntaxKind, TextRange, SyntaxNode, @@ -28,7 +28,7 @@ pub(crate) fn diagnostics(db: &RootDatabase, file_id: FileId) -> Vec } if let Some(m) = source_binder::module_from_file_id(db, file_id) { - check_module(&mut res, db, file_id, m); + check_module(&mut res, db, m); }; res } @@ -128,46 +128,40 @@ fn check_struct_shorthand_initialization( Some(()) } -fn check_module( - acc: &mut Vec, - db: &RootDatabase, - file_id: FileId, - module: hir::Module, -) { +fn check_module(acc: &mut Vec, db: &RootDatabase, module: hir::Module) { + let mut diagnostics = hir::diagnostics::Diagnostics::default(); + module.diagnostics(db, &mut diagnostics); for decl in module.declarations(db) { match decl { - hir::ModuleDef::Function(f) => check_function(acc, db, f), + hir::ModuleDef::Function(f) => f.diagnostics(db, &mut diagnostics), _ => (), } } - let source_root = db.file_source_root(file_id); - for (name_node, problem) in module.problems(db) { - let diag = match problem { - Problem::UnresolvedModule { candidate } => { - let create_file = - FileSystemEdit::CreateFile { source_root, path: candidate.clone() }; - let fix = SourceChange::file_system_edit("create module", create_file); - Diagnostic { - range: name_node.range(), - message: "unresolved module".to_string(), - severity: Severity::Error, - fix: Some(fix), - } - } - }; - acc.push(diag) - } -} - -fn check_function(acc: &mut Vec, db: &RootDatabase, function: hir::Function) { - for d in function.diagnostics(db).iter() { - acc.push(Diagnostic { - message: d.message(), - range: d.syntax_node().range(), - severity: Severity::Error, - fix: None, - }) + for d in diagnostics.iter() { + if let Some(d) = d.downcast_ref::() { + let source_root = db.file_source_root(d.file().original_file(db)); + let create_file = FileSystemEdit::CreateFile { source_root, path: d.candidate.clone() }; + let fix = SourceChange { + label: "create module".to_string(), + source_file_edits: Vec::new(), + file_system_edits: vec![create_file], + cursor_position: None, + }; + acc.push(Diagnostic { + range: d.syntax_node().range(), + message: d.message(), + severity: Severity::Error, + fix: Some(fix), + }) + } else { + acc.push(Diagnostic { + message: d.message(), + range: d.syntax_node().range(), + severity: Severity::Error, + fix: None, + }) + } } } -- cgit v1.2.3 From 79df62bc742afa33dcf5bedefd60860ca296b9da Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Sat, 23 Mar 2019 20:41:59 +0300 Subject: cleanup --- crates/ra_ide_api/src/diagnostics.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'crates/ra_ide_api/src') diff --git a/crates/ra_ide_api/src/diagnostics.rs b/crates/ra_ide_api/src/diagnostics.rs index 254342c0a..1395cede2 100644 --- a/crates/ra_ide_api/src/diagnostics.rs +++ b/crates/ra_ide_api/src/diagnostics.rs @@ -129,7 +129,7 @@ fn check_struct_shorthand_initialization( } fn check_module(acc: &mut Vec, db: &RootDatabase, module: hir::Module) { - let mut diagnostics = hir::diagnostics::Diagnostics::default(); + let mut diagnostics = hir::diagnostics::DiagnosticSink::default(); module.diagnostics(db, &mut diagnostics); for decl in module.declarations(db) { match decl { @@ -138,7 +138,7 @@ fn check_module(acc: &mut Vec, db: &RootDatabase, module: hir::Modul } } - for d in diagnostics.iter() { + for d in diagnostics.into_diagnostics().iter() { if let Some(d) = d.downcast_ref::() { let source_root = db.file_source_root(d.file().original_file(db)); let create_file = FileSystemEdit::CreateFile { source_root, path: d.candidate.clone() }; -- cgit v1.2.3 From 45fbab2b1ac02dab971d245c45c2404494cb3e03 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Sat, 23 Mar 2019 21:17:05 +0300 Subject: check impls as well --- crates/ra_ide_api/src/diagnostics.rs | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'crates/ra_ide_api/src') diff --git a/crates/ra_ide_api/src/diagnostics.rs b/crates/ra_ide_api/src/diagnostics.rs index 1395cede2..e03dcaa8f 100644 --- a/crates/ra_ide_api/src/diagnostics.rs +++ b/crates/ra_ide_api/src/diagnostics.rs @@ -138,6 +138,15 @@ fn check_module(acc: &mut Vec, db: &RootDatabase, module: hir::Modul } } + for impl_block in module.impl_blocks(db) { + for item in impl_block.items(db) { + match item { + hir::ImplItem::Method(f) => f.diagnostics(db, &mut diagnostics), + _ => (), + } + } + } + for d in diagnostics.into_diagnostics().iter() { if let Some(d) = d.downcast_ref::() { let source_root = db.file_source_root(d.file().original_file(db)); -- cgit v1.2.3 From 7ee2887d1ec129afed80845c2361ce35f1a0c013 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Sat, 23 Mar 2019 21:20:49 +0300 Subject: fixes --- crates/ra_ide_api/src/diagnostics.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'crates/ra_ide_api/src') diff --git a/crates/ra_ide_api/src/diagnostics.rs b/crates/ra_ide_api/src/diagnostics.rs index e03dcaa8f..5b1a90c82 100644 --- a/crates/ra_ide_api/src/diagnostics.rs +++ b/crates/ra_ide_api/src/diagnostics.rs @@ -1,10 +1,9 @@ use itertools::Itertools; -use hir::{source_binder, diagnostics::Diagnostic as _}; +use hir::{source_binder, diagnostics::{Diagnostic as _, DiagnosticSink}}; use ra_db::SourceDatabase; use ra_syntax::{ Location, SourceFile, SyntaxKind, TextRange, SyntaxNode, ast::{self, AstNode}, - }; use ra_text_edit::{TextEdit, TextEditBuilder}; @@ -129,7 +128,7 @@ fn check_struct_shorthand_initialization( } fn check_module(acc: &mut Vec, db: &RootDatabase, module: hir::Module) { - let mut diagnostics = hir::diagnostics::DiagnosticSink::default(); + let mut diagnostics = DiagnosticSink::default(); module.diagnostics(db, &mut diagnostics); for decl in module.declarations(db) { match decl { -- cgit v1.2.3 From c7ffd939f670a1cba5bf415759b43e63700761a7 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Sun, 24 Mar 2019 10:21:36 +0300 Subject: more enterprisey diagnostics setup --- crates/ra_ide_api/src/diagnostics.rs | 79 ++++++++++++++---------------------- 1 file changed, 30 insertions(+), 49 deletions(-) (limited to 'crates/ra_ide_api/src') diff --git a/crates/ra_ide_api/src/diagnostics.rs b/crates/ra_ide_api/src/diagnostics.rs index 5b1a90c82..bf77c9ab1 100644 --- a/crates/ra_ide_api/src/diagnostics.rs +++ b/crates/ra_ide_api/src/diagnostics.rs @@ -1,3 +1,5 @@ +use std::cell::RefCell; + use itertools::Itertools; use hir::{source_binder, diagnostics::{Diagnostic as _, DiagnosticSink}}; use ra_db::SourceDatabase; @@ -25,11 +27,36 @@ pub(crate) fn diagnostics(db: &RootDatabase, file_id: FileId) -> Vec check_unnecessary_braces_in_use_statement(&mut res, file_id, node); check_struct_shorthand_initialization(&mut res, file_id, node); } - + let res = RefCell::new(res); + let mut sink = DiagnosticSink::new(|d| { + res.borrow_mut().push(Diagnostic { + message: d.message(), + range: d.syntax_node().range(), + severity: Severity::Error, + fix: None, + }) + }) + .on::(|d| { + let source_root = db.file_source_root(d.file().original_file(db)); + let create_file = FileSystemEdit::CreateFile { source_root, path: d.candidate.clone() }; + let fix = SourceChange { + label: "create module".to_string(), + source_file_edits: Vec::new(), + file_system_edits: vec![create_file], + cursor_position: None, + }; + res.borrow_mut().push(Diagnostic { + range: d.syntax_node().range(), + message: d.message(), + severity: Severity::Error, + fix: Some(fix), + }) + }); if let Some(m) = source_binder::module_from_file_id(db, file_id) { - check_module(&mut res, db, m); + m.diagnostics(db, &mut sink); }; - res + drop(sink); + res.into_inner() } fn syntax_errors(acc: &mut Vec, source_file: &SourceFile) { @@ -127,52 +154,6 @@ fn check_struct_shorthand_initialization( Some(()) } -fn check_module(acc: &mut Vec, db: &RootDatabase, module: hir::Module) { - let mut diagnostics = DiagnosticSink::default(); - module.diagnostics(db, &mut diagnostics); - for decl in module.declarations(db) { - match decl { - hir::ModuleDef::Function(f) => f.diagnostics(db, &mut diagnostics), - _ => (), - } - } - - for impl_block in module.impl_blocks(db) { - for item in impl_block.items(db) { - match item { - hir::ImplItem::Method(f) => f.diagnostics(db, &mut diagnostics), - _ => (), - } - } - } - - for d in diagnostics.into_diagnostics().iter() { - if let Some(d) = d.downcast_ref::() { - let source_root = db.file_source_root(d.file().original_file(db)); - let create_file = FileSystemEdit::CreateFile { source_root, path: d.candidate.clone() }; - let fix = SourceChange { - label: "create module".to_string(), - source_file_edits: Vec::new(), - file_system_edits: vec![create_file], - cursor_position: None, - }; - acc.push(Diagnostic { - range: d.syntax_node().range(), - message: d.message(), - severity: Severity::Error, - fix: Some(fix), - }) - } else { - acc.push(Diagnostic { - message: d.message(), - range: d.syntax_node().range(), - severity: Severity::Error, - fix: None, - }) - } - } -} - #[cfg(test)] mod tests { use test_utils::assert_eq_text; -- cgit v1.2.3 From 5ce84f3cbc5d5a3106ea89460da4a3f473618f32 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Mon, 25 Mar 2019 09:40:49 +0300 Subject: tweak diagnostics API --- crates/ra_ide_api/src/diagnostics.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'crates/ra_ide_api/src') diff --git a/crates/ra_ide_api/src/diagnostics.rs b/crates/ra_ide_api/src/diagnostics.rs index bf77c9ab1..11391ef02 100644 --- a/crates/ra_ide_api/src/diagnostics.rs +++ b/crates/ra_ide_api/src/diagnostics.rs @@ -31,7 +31,7 @@ pub(crate) fn diagnostics(db: &RootDatabase, file_id: FileId) -> Vec let mut sink = DiagnosticSink::new(|d| { res.borrow_mut().push(Diagnostic { message: d.message(), - range: d.syntax_node().range(), + range: d.highlight_range(), severity: Severity::Error, fix: None, }) @@ -46,7 +46,7 @@ pub(crate) fn diagnostics(db: &RootDatabase, file_id: FileId) -> Vec cursor_position: None, }; res.borrow_mut().push(Diagnostic { - range: d.syntax_node().range(), + range: d.highlight_range(), message: d.message(), severity: Severity::Error, fix: Some(fix), -- cgit v1.2.3 From e9af69d9db8856d79f0da7ae7da66169cc225aac Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Mon, 25 Mar 2019 10:56:55 +0300 Subject: simplify --- crates/ra_ide_api/src/diagnostics.rs | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) (limited to 'crates/ra_ide_api/src') diff --git a/crates/ra_ide_api/src/diagnostics.rs b/crates/ra_ide_api/src/diagnostics.rs index 11391ef02..841103322 100644 --- a/crates/ra_ide_api/src/diagnostics.rs +++ b/crates/ra_ide_api/src/diagnostics.rs @@ -39,12 +39,7 @@ pub(crate) fn diagnostics(db: &RootDatabase, file_id: FileId) -> Vec .on::(|d| { let source_root = db.file_source_root(d.file().original_file(db)); let create_file = FileSystemEdit::CreateFile { source_root, path: d.candidate.clone() }; - let fix = SourceChange { - label: "create module".to_string(), - source_file_edits: Vec::new(), - file_system_edits: vec![create_file], - cursor_position: None, - }; + let fix = SourceChange::file_system_edit("create module", create_file); res.borrow_mut().push(Diagnostic { range: d.highlight_range(), message: d.message(), -- cgit v1.2.3 From 36cb58f76d702d87f445ee7eefe26a0bc1ee3811 Mon Sep 17 00:00:00 2001 From: Sergey Parilin Date: Fri, 22 Mar 2019 15:54:26 +0300 Subject: structure moved to ra_ide_api ra_ide_api_light removed completely --- crates/ra_ide_api/src/lib.rs | 11 +- .../src/snapshots/tests__file_structure.snap | 182 ++++++++++++++++++++ crates/ra_ide_api/src/structure.rs | 190 +++++++++++++++++++++ 3 files changed, 377 insertions(+), 6 deletions(-) create mode 100644 crates/ra_ide_api/src/snapshots/tests__file_structure.snap create mode 100644 crates/ra_ide_api/src/structure.rs (limited to 'crates/ra_ide_api/src') diff --git a/crates/ra_ide_api/src/lib.rs b/crates/ra_ide_api/src/lib.rs index 8aa3eb088..9063f78a9 100644 --- a/crates/ra_ide_api/src/lib.rs +++ b/crates/ra_ide_api/src/lib.rs @@ -6,9 +6,6 @@ //! database, and the `ra_hir` crate, where majority of the analysis happens. //! However, IDE specific bits of the analysis (most notably completion) happen //! in this crate. -//! -//! The sibling `ra_ide_api_light` handles those bits of IDE functionality -//! which are restricted to a single file and need only syntax. // For proving that RootDatabase is RefUnwindSafe. #![recursion_limit = "128"] @@ -33,10 +30,11 @@ mod impls; mod assists; mod diagnostics; mod syntax_tree; -mod line_index; mod folding_ranges; +mod line_index; mod line_index_utils; mod join_lines; +mod structure; mod typing; mod matching_brace; @@ -72,9 +70,10 @@ pub use crate::{ line_index_utils::translate_offset_with_edit, folding_ranges::{Fold, FoldKind}, syntax_highlighting::HighlightedRange, + structure::{StructureNode, file_structure}, diagnostics::Severity, }; -pub use ra_ide_api_light::StructureNode; + pub use ra_db::{ Canceled, CrateGraph, CrateId, FileId, FilePosition, FileRange, SourceRootId, Edition @@ -388,7 +387,7 @@ impl Analysis { /// file outline. pub fn file_structure(&self, file_id: FileId) -> Vec { let file = self.db.parse(file_id); - ra_ide_api_light::file_structure(&file) + structure::file_structure(&file) } /// Returns the set of folding ranges. diff --git a/crates/ra_ide_api/src/snapshots/tests__file_structure.snap b/crates/ra_ide_api/src/snapshots/tests__file_structure.snap new file mode 100644 index 000000000..2efa8e22c --- /dev/null +++ b/crates/ra_ide_api/src/snapshots/tests__file_structure.snap @@ -0,0 +1,182 @@ +--- +created: "2019-02-05T22:03:50.763530100Z" +creator: insta@0.6.1 +source: crates/ra_ide_api/src/structure.rs +expression: structure +--- +[ + StructureNode { + parent: None, + label: "Foo", + navigation_range: [8; 11), + node_range: [1; 26), + kind: STRUCT_DEF, + detail: None, + deprecated: false + }, + StructureNode { + parent: Some( + 0 + ), + label: "x", + navigation_range: [18; 19), + node_range: [18; 24), + kind: NAMED_FIELD_DEF, + detail: Some( + "i32" + ), + deprecated: false + }, + StructureNode { + parent: None, + label: "m", + navigation_range: [32; 33), + node_range: [28; 158), + kind: MODULE, + detail: None, + deprecated: false + }, + StructureNode { + parent: Some( + 2 + ), + label: "bar1", + navigation_range: [43; 47), + node_range: [40; 52), + kind: FN_DEF, + detail: Some( + "fn()" + ), + deprecated: false + }, + StructureNode { + parent: Some( + 2 + ), + label: "bar2", + navigation_range: [60; 64), + node_range: [57; 81), + kind: FN_DEF, + detail: Some( + "fn(t: T) -> T" + ), + deprecated: false + }, + StructureNode { + parent: Some( + 2 + ), + label: "bar3", + navigation_range: [89; 93), + node_range: [86; 156), + kind: FN_DEF, + detail: Some( + "fn(a: A, b: B) -> Vec< u32 >" + ), + deprecated: false + }, + StructureNode { + parent: None, + label: "E", + navigation_range: [165; 166), + node_range: [160; 180), + kind: ENUM_DEF, + detail: None, + deprecated: false + }, + StructureNode { + parent: Some( + 6 + ), + label: "X", + navigation_range: [169; 170), + node_range: [169; 170), + kind: ENUM_VARIANT, + detail: None, + deprecated: false + }, + StructureNode { + parent: Some( + 6 + ), + label: "Y", + navigation_range: [172; 173), + node_range: [172; 178), + kind: ENUM_VARIANT, + detail: None, + deprecated: false + }, + StructureNode { + parent: None, + label: "T", + navigation_range: [186; 187), + node_range: [181; 193), + kind: TYPE_ALIAS_DEF, + detail: Some( + "()" + ), + deprecated: false + }, + StructureNode { + parent: None, + label: "S", + navigation_range: [201; 202), + node_range: [194; 213), + kind: STATIC_DEF, + detail: Some( + "i32" + ), + deprecated: false + }, + StructureNode { + parent: None, + label: "C", + navigation_range: [220; 221), + node_range: [214; 232), + kind: CONST_DEF, + detail: Some( + "i32" + ), + deprecated: false + }, + StructureNode { + parent: None, + label: "impl E", + navigation_range: [239; 240), + node_range: [234; 243), + kind: IMPL_BLOCK, + detail: None, + deprecated: false + }, + StructureNode { + parent: None, + label: "impl fmt::Debug for E", + navigation_range: [265; 266), + node_range: [245; 269), + kind: IMPL_BLOCK, + detail: None, + deprecated: false + }, + StructureNode { + parent: None, + label: "obsolete", + navigation_range: [288; 296), + node_range: [271; 301), + kind: FN_DEF, + detail: Some( + "fn()" + ), + deprecated: true + }, + StructureNode { + parent: None, + label: "very_obsolete", + navigation_range: [341; 354), + node_range: [303; 359), + kind: FN_DEF, + detail: Some( + "fn()" + ), + deprecated: true + } +] diff --git a/crates/ra_ide_api/src/structure.rs b/crates/ra_ide_api/src/structure.rs new file mode 100644 index 000000000..ec2c9bbc6 --- /dev/null +++ b/crates/ra_ide_api/src/structure.rs @@ -0,0 +1,190 @@ +use crate::TextRange; + +use ra_syntax::{ + algo::visit::{visitor, Visitor}, + ast::{self, AttrsOwner, NameOwner, TypeParamsOwner, TypeAscriptionOwner}, + AstNode, SourceFile, SyntaxKind, SyntaxNode, WalkEvent, +}; + +#[derive(Debug, Clone)] +pub struct StructureNode { + pub parent: Option, + pub label: String, + pub navigation_range: TextRange, + pub node_range: TextRange, + pub kind: SyntaxKind, + pub detail: Option, + pub deprecated: bool, +} + +pub fn file_structure(file: &SourceFile) -> Vec { + let mut res = Vec::new(); + let mut stack = Vec::new(); + + for event in file.syntax().preorder() { + match event { + WalkEvent::Enter(node) => { + if let Some(mut symbol) = structure_node(node) { + symbol.parent = stack.last().map(|&n| n); + stack.push(res.len()); + res.push(symbol); + } + } + WalkEvent::Leave(node) => { + if structure_node(node).is_some() { + stack.pop().unwrap(); + } + } + } + } + res +} + +fn structure_node(node: &SyntaxNode) -> Option { + fn decl(node: &N) -> Option { + decl_with_detail(node, None) + } + + fn decl_with_ascription( + node: &N, + ) -> Option { + decl_with_type_ref(node, node.ascribed_type()) + } + + fn decl_with_type_ref( + node: &N, + type_ref: Option<&ast::TypeRef>, + ) -> Option { + let detail = type_ref.map(|type_ref| { + let mut detail = String::new(); + collapse_ws(type_ref.syntax(), &mut detail); + detail + }); + decl_with_detail(node, detail) + } + + fn decl_with_detail( + node: &N, + detail: Option, + ) -> Option { + let name = node.name()?; + + Some(StructureNode { + parent: None, + label: name.text().to_string(), + navigation_range: name.syntax().range(), + node_range: node.syntax().range(), + kind: node.syntax().kind(), + detail, + deprecated: node.attrs().filter_map(|x| x.as_named()).any(|x| x == "deprecated"), + }) + } + + fn collapse_ws(node: &SyntaxNode, output: &mut String) { + let mut can_insert_ws = false; + for line in node.text().chunks().flat_map(|chunk| chunk.lines()) { + let line = line.trim(); + if line.is_empty() { + if can_insert_ws { + output.push_str(" "); + can_insert_ws = false; + } + } else { + output.push_str(line); + can_insert_ws = true; + } + } + } + + visitor() + .visit(|fn_def: &ast::FnDef| { + let mut detail = String::from("fn"); + if let Some(type_param_list) = fn_def.type_param_list() { + collapse_ws(type_param_list.syntax(), &mut detail); + } + if let Some(param_list) = fn_def.param_list() { + collapse_ws(param_list.syntax(), &mut detail); + } + if let Some(ret_type) = fn_def.ret_type() { + detail.push_str(" "); + collapse_ws(ret_type.syntax(), &mut detail); + } + + decl_with_detail(fn_def, Some(detail)) + }) + .visit(decl::) + .visit(decl::) + .visit(decl::) + .visit(decl::) + .visit(decl::) + .visit(|td: &ast::TypeAliasDef| decl_with_type_ref(td, td.type_ref())) + .visit(decl_with_ascription::) + .visit(decl_with_ascription::) + .visit(decl_with_ascription::) + .visit(|im: &ast::ImplBlock| { + let target_type = im.target_type()?; + let target_trait = im.target_trait(); + let label = match target_trait { + None => format!("impl {}", target_type.syntax().text()), + Some(t) => { + format!("impl {} for {}", t.syntax().text(), target_type.syntax().text(),) + } + }; + + let node = StructureNode { + parent: None, + label, + navigation_range: target_type.syntax().range(), + node_range: im.syntax().range(), + kind: im.syntax().kind(), + detail: None, + deprecated: false, + }; + Some(node) + }) + .accept(node)? +} + +#[cfg(test)] +mod tests { + use super::*; + use insta::assert_debug_snapshot_matches; + + #[test] + fn test_file_structure() { + let file = SourceFile::parse( + r#" +struct Foo { + x: i32 +} + +mod m { + fn bar1() {} + fn bar2(t: T) -> T {} + fn bar3(a: A, + b: B) -> Vec< + u32 + > {} +} + +enum E { X, Y(i32) } +type T = (); +static S: i32 = 92; +const C: i32 = 92; + +impl E {} + +impl fmt::Debug for E {} + +#[deprecated] +fn obsolete() {} + +#[deprecated(note = "for awhile")] +fn very_obsolete() {} +"#, + ); + let structure = file_structure(&file); + assert_debug_snapshot_matches!("file_structure", structure); + } +} -- cgit v1.2.3 From 309716cffe93d065bcad0344b0f332425576c1e5 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Mon, 25 Mar 2019 14:28:04 +0300 Subject: move tests to where they belong --- crates/ra_ide_api/src/diagnostics.rs | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) (limited to 'crates/ra_ide_api/src') diff --git a/crates/ra_ide_api/src/diagnostics.rs b/crates/ra_ide_api/src/diagnostics.rs index 841103322..5a78e94d8 100644 --- a/crates/ra_ide_api/src/diagnostics.rs +++ b/crates/ra_ide_api/src/diagnostics.rs @@ -152,6 +152,9 @@ fn check_struct_shorthand_initialization( #[cfg(test)] mod tests { use test_utils::assert_eq_text; + use insta::assert_debug_snapshot_matches; + + use crate::mock_analysis::single_file; use super::*; @@ -180,6 +183,34 @@ mod tests { assert_eq_text!(after, &actual); } + #[test] + fn test_unresolved_module_diagnostic() { + let (analysis, file_id) = single_file("mod foo;"); + let diagnostics = analysis.diagnostics(file_id).unwrap(); + assert_debug_snapshot_matches!(diagnostics, @r####"[ + Diagnostic { + message: "unresolved module", + range: [0; 8), + fix: Some( + SourceChange { + label: "create module", + source_file_edits: [], + file_system_edits: [ + CreateFile { + source_root: SourceRootId( + 0 + ), + path: "foo.rs" + } + ], + cursor_position: None + } + ), + severity: Error + } +]"####); + } + #[test] fn test_check_unnecessary_braces_in_use_statement() { check_not_applicable( -- cgit v1.2.3 From e03189c1109573bec9600eb20efd9291d69f1d5c Mon Sep 17 00:00:00 2001 From: Wilco Kusee Date: Mon, 25 Mar 2019 21:03:32 +0100 Subject: Move ra_ide_api unit tests --- crates/ra_ide_api/src/parent_module.rs | 30 +++- crates/ra_ide_api/src/references.rs | 48 +++++- crates/ra_ide_api/src/symbol_index.rs | 58 ++++++++ crates/ra_ide_api/src/syntax_tree.rs | 257 +++++++++++++++++++++++++++++++++ 4 files changed, 391 insertions(+), 2 deletions(-) (limited to 'crates/ra_ide_api/src') diff --git a/crates/ra_ide_api/src/parent_module.rs b/crates/ra_ide_api/src/parent_module.rs index 603c3db6a..27788c984 100644 --- a/crates/ra_ide_api/src/parent_module.rs +++ b/crates/ra_ide_api/src/parent_module.rs @@ -28,7 +28,11 @@ pub(crate) fn crate_for(db: &RootDatabase, file_id: FileId) -> Vec { #[cfg(test)] mod tests { - use crate::mock_analysis::analysis_and_position; + use crate::{ + AnalysisChange, CrateGraph, + mock_analysis::{analysis_and_position, MockAnalysis}, + Edition::Edition2018, +}; #[test] fn test_resolve_parent_module() { @@ -59,4 +63,28 @@ mod tests { let nav = analysis.parent_module(pos).unwrap().pop().unwrap(); nav.assert_match("baz MODULE FileId(1) [32; 44)"); } + + #[test] + fn test_resolve_crate_root() { + let mock = MockAnalysis::with_files( + " + //- /bar.rs + mod foo; + //- /foo.rs + // empty <|> + ", + ); + let root_file = mock.id_of("/bar.rs"); + let mod_file = mock.id_of("/foo.rs"); + let mut host = mock.analysis_host(); + assert!(host.analysis().crate_for(mod_file).unwrap().is_empty()); + + let mut crate_graph = CrateGraph::default(); + let crate_id = crate_graph.add_crate_root(root_file, Edition2018); + let mut change = AnalysisChange::new(); + change.set_crate_graph(crate_graph); + host.apply_change(change); + + assert_eq!(host.analysis().crate_for(mod_file).unwrap(), vec![crate_id]); + } } diff --git a/crates/ra_ide_api/src/references.rs b/crates/ra_ide_api/src/references.rs index 22741445a..20bbf11a3 100644 --- a/crates/ra_ide_api/src/references.rs +++ b/crates/ra_ide_api/src/references.rs @@ -216,9 +216,55 @@ mod tests { use crate::{ mock_analysis::single_file_with_position, mock_analysis::analysis_and_position, - FileId + FileId, ReferenceSearchResult }; + #[test] + fn test_find_all_refs_for_local() { + let code = r#" + fn main() { + let mut i = 1; + let j = 1; + i = i<|> + j; + + { + i = 0; + } + + i = 5; + }"#; + + let refs = get_all_refs(code); + assert_eq!(refs.len(), 5); + } + + #[test] + fn test_find_all_refs_for_param_inside() { + let code = r#" + fn foo(i : u32) -> u32 { + i<|> + }"#; + + let refs = get_all_refs(code); + assert_eq!(refs.len(), 2); + } + + #[test] + fn test_find_all_refs_for_fn_param() { + let code = r#" + fn foo(i<|> : u32) -> u32 { + i + }"#; + + let refs = get_all_refs(code); + assert_eq!(refs.len(), 2); + } + + fn get_all_refs(text: &str) -> ReferenceSearchResult { + let (analysis, position) = single_file_with_position(text); + analysis.find_all_refs(position).unwrap().unwrap() + } + #[test] fn test_rename_for_local() { test_rename( diff --git a/crates/ra_ide_api/src/symbol_index.rs b/crates/ra_ide_api/src/symbol_index.rs index 0978d164a..0eadc4e71 100644 --- a/crates/ra_ide_api/src/symbol_index.rs +++ b/crates/ra_ide_api/src/symbol_index.rs @@ -270,3 +270,61 @@ fn to_file_symbol(node: &SyntaxNode, file_id: FileId) -> Option { container_name: None, }) } + +#[cfg(test)] +mod tests { + use ra_syntax::SmolStr; + use crate::{ + navigation_target::NavigationTarget, + mock_analysis::single_file, + Query, +}; + + #[test] + fn test_world_symbols_with_no_container() { + let code = r#" + enum FooInner { } + "#; + + let mut symbols = get_symbols_matching(code, "FooInner"); + + let s = symbols.pop().unwrap(); + + assert_eq!(s.name(), "FooInner"); + assert!(s.container_name().is_none()); + } + + #[test] + fn test_world_symbols_include_container_name() { + let code = r#" +fn foo() { + enum FooInner { } +} + "#; + + let mut symbols = get_symbols_matching(code, "FooInner"); + + let s = symbols.pop().unwrap(); + + assert_eq!(s.name(), "FooInner"); + assert_eq!(s.container_name(), Some(&SmolStr::new("foo"))); + + let code = r#" +mod foo { + struct FooInner; +} + "#; + + let mut symbols = get_symbols_matching(code, "FooInner"); + + let s = symbols.pop().unwrap(); + + assert_eq!(s.name(), "FooInner"); + assert_eq!(s.container_name(), Some(&SmolStr::new("foo"))); + } + + fn get_symbols_matching(text: &str, query: &str) -> Vec { + let (analysis, _) = single_file(text); + analysis.symbol_search(Query::new(query.into())).unwrap() + } +} diff --git a/crates/ra_ide_api/src/syntax_tree.rs b/crates/ra_ide_api/src/syntax_tree.rs index bbe9222b4..276f8a8c8 100644 --- a/crates/ra_ide_api/src/syntax_tree.rs +++ b/crates/ra_ide_api/src/syntax_tree.rs @@ -85,3 +85,260 @@ fn syntax_tree_for_token(node: &T, text_range: TextRange) -> Option None } + +#[cfg(test)] +mod tests { + use crate::mock_analysis::{single_file, single_file_with_range}; + + #[test] + fn test_syntax_tree_without_range() { + // Basic syntax + let (analysis, file_id) = single_file(r#"fn foo() {}"#); + let syn = analysis.syntax_tree(file_id, None); + + assert_eq!( + syn.trim(), + r#" +SOURCE_FILE@[0; 11) + FN_DEF@[0; 11) + FN_KW@[0; 2) + WHITESPACE@[2; 3) + NAME@[3; 6) + IDENT@[3; 6) "foo" + PARAM_LIST@[6; 8) + L_PAREN@[6; 7) + R_PAREN@[7; 8) + WHITESPACE@[8; 9) + BLOCK@[9; 11) + L_CURLY@[9; 10) + R_CURLY@[10; 11) + "# + .trim() + ); + + let (analysis, file_id) = single_file( + r#" +fn test() { + assert!(" + fn foo() { + } + ", ""); +}"# + .trim(), + ); + let syn = analysis.syntax_tree(file_id, None); + + assert_eq!( + syn.trim(), + r#" +SOURCE_FILE@[0; 60) + FN_DEF@[0; 60) + FN_KW@[0; 2) + WHITESPACE@[2; 3) + NAME@[3; 7) + IDENT@[3; 7) "test" + PARAM_LIST@[7; 9) + L_PAREN@[7; 8) + R_PAREN@[8; 9) + WHITESPACE@[9; 10) + BLOCK@[10; 60) + L_CURLY@[10; 11) + WHITESPACE@[11; 16) + EXPR_STMT@[16; 58) + MACRO_CALL@[16; 57) + PATH@[16; 22) + PATH_SEGMENT@[16; 22) + NAME_REF@[16; 22) + IDENT@[16; 22) "assert" + EXCL@[22; 23) + TOKEN_TREE@[23; 57) + L_PAREN@[23; 24) + STRING@[24; 52) + COMMA@[52; 53) + WHITESPACE@[53; 54) + STRING@[54; 56) + R_PAREN@[56; 57) + SEMI@[57; 58) + WHITESPACE@[58; 59) + R_CURLY@[59; 60) + "# + .trim() + ); + } + + #[test] + fn test_syntax_tree_with_range() { + let (analysis, range) = single_file_with_range(r#"<|>fn foo() {}<|>"#.trim()); + let syn = analysis.syntax_tree(range.file_id, Some(range.range)); + + assert_eq!( + syn.trim(), + r#" +FN_DEF@[0; 11) + FN_KW@[0; 2) + WHITESPACE@[2; 3) + NAME@[3; 6) + IDENT@[3; 6) "foo" + PARAM_LIST@[6; 8) + L_PAREN@[6; 7) + R_PAREN@[7; 8) + WHITESPACE@[8; 9) + BLOCK@[9; 11) + L_CURLY@[9; 10) + R_CURLY@[10; 11) + "# + .trim() + ); + + let (analysis, range) = single_file_with_range( + r#"fn test() { + <|>assert!(" + fn foo() { + } + ", "");<|> +}"# + .trim(), + ); + let syn = analysis.syntax_tree(range.file_id, Some(range.range)); + + assert_eq!( + syn.trim(), + r#" +EXPR_STMT@[16; 58) + MACRO_CALL@[16; 57) + PATH@[16; 22) + PATH_SEGMENT@[16; 22) + NAME_REF@[16; 22) + IDENT@[16; 22) "assert" + EXCL@[22; 23) + TOKEN_TREE@[23; 57) + L_PAREN@[23; 24) + STRING@[24; 52) + COMMA@[52; 53) + WHITESPACE@[53; 54) + STRING@[54; 56) + R_PAREN@[56; 57) + SEMI@[57; 58) + "# + .trim() + ); + } + + #[test] + fn test_syntax_tree_inside_string() { + let (analysis, range) = single_file_with_range( + r#"fn test() { + assert!(" +<|>fn foo() { +}<|> +fn bar() { +} + ", ""); +}"# + .trim(), + ); + let syn = analysis.syntax_tree(range.file_id, Some(range.range)); + assert_eq!( + syn.trim(), + r#" +SOURCE_FILE@[0; 12) + FN_DEF@[0; 12) + FN_KW@[0; 2) + WHITESPACE@[2; 3) + NAME@[3; 6) + IDENT@[3; 6) "foo" + PARAM_LIST@[6; 8) + L_PAREN@[6; 7) + R_PAREN@[7; 8) + WHITESPACE@[8; 9) + BLOCK@[9; 12) + L_CURLY@[9; 10) + WHITESPACE@[10; 11) + R_CURLY@[11; 12) +"# + .trim() + ); + + // With a raw string + let (analysis, range) = single_file_with_range( + r###"fn test() { + assert!(r#" +<|>fn foo() { +}<|> +fn bar() { +} + "#, ""); +}"### + .trim(), + ); + let syn = analysis.syntax_tree(range.file_id, Some(range.range)); + assert_eq!( + syn.trim(), + r#" +SOURCE_FILE@[0; 12) + FN_DEF@[0; 12) + FN_KW@[0; 2) + WHITESPACE@[2; 3) + NAME@[3; 6) + IDENT@[3; 6) "foo" + PARAM_LIST@[6; 8) + L_PAREN@[6; 7) + R_PAREN@[7; 8) + WHITESPACE@[8; 9) + BLOCK@[9; 12) + L_CURLY@[9; 10) + WHITESPACE@[10; 11) + R_CURLY@[11; 12) +"# + .trim() + ); + + // With a raw string + let (analysis, range) = single_file_with_range( + r###"fn test() { + assert!(r<|>#" +fn foo() { +} +fn bar() { +}"<|>#, ""); +}"### + .trim(), + ); + let syn = analysis.syntax_tree(range.file_id, Some(range.range)); + assert_eq!( + syn.trim(), + r#" +SOURCE_FILE@[0; 25) + FN_DEF@[0; 12) + FN_KW@[0; 2) + WHITESPACE@[2; 3) + NAME@[3; 6) + IDENT@[3; 6) "foo" + PARAM_LIST@[6; 8) + L_PAREN@[6; 7) + R_PAREN@[7; 8) + WHITESPACE@[8; 9) + BLOCK@[9; 12) + L_CURLY@[9; 10) + WHITESPACE@[10; 11) + R_CURLY@[11; 12) + WHITESPACE@[12; 13) + FN_DEF@[13; 25) + FN_KW@[13; 15) + WHITESPACE@[15; 16) + NAME@[16; 19) + IDENT@[16; 19) "bar" + PARAM_LIST@[19; 21) + L_PAREN@[19; 20) + R_PAREN@[20; 21) + WHITESPACE@[21; 22) + BLOCK@[22; 25) + L_CURLY@[22; 23) + WHITESPACE@[23; 24) + R_CURLY@[24; 25) + +"# + .trim() + ); + } +} -- cgit v1.2.3 From e28db444dfdc797002bd3561940cde2659b831de Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Tue, 26 Mar 2019 19:00:11 +0300 Subject: rename --- crates/ra_ide_api/src/change.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'crates/ra_ide_api/src') diff --git a/crates/ra_ide_api/src/change.rs b/crates/ra_ide_api/src/change.rs index 26fde91bc..a4a086931 100644 --- a/crates/ra_ide_api/src/change.rs +++ b/crates/ra_ide_api/src/change.rs @@ -220,8 +220,8 @@ impl RootDatabase { self.query(ra_db::ParseQuery).sweep(sweep); self.query(hir::db::HirParseQuery).sweep(sweep); - self.query(hir::db::FileItemsQuery).sweep(sweep); - self.query(hir::db::FileItemQuery).sweep(sweep); + self.query(hir::db::AstIdMapQuery).sweep(sweep); + self.query(hir::db::AstIdToNodeQuery).sweep(sweep); self.query(hir::db::RawItemsWithSourceMapQuery).sweep(sweep); self.query(hir::db::BodyWithSourceMapQuery).sweep(sweep); -- cgit v1.2.3