aboutsummaryrefslogtreecommitdiff
path: root/crates
diff options
context:
space:
mode:
Diffstat (limited to 'crates')
-rw-r--r--crates/ra_assists/src/ast_editor.rs30
-rw-r--r--crates/ra_assists/src/lib.rs2
-rw-r--r--crates/ra_assists/src/move_bounds.rs135
-rw-r--r--crates/ra_hir/src/nameres.rs1
-rw-r--r--crates/ra_hir/src/nameres/collector.rs190
-rw-r--r--crates/ra_hir/src/nameres/mod_resolution.rs182
-rw-r--r--crates/ra_hir/src/nameres/tests.rs2
-rw-r--r--crates/ra_hir/src/nameres/tests/mod_resolution.rs (renamed from crates/ra_hir/src/nameres/tests/mods.rs)0
-rw-r--r--crates/ra_ide_api/src/lib.rs4
-rw-r--r--crates/ra_ide_api/src/references.rs154
-rw-r--r--crates/ra_ide_api/src/syntax_tree.rs714
-rw-r--r--crates/ra_lsp_server/src/main_loop/handlers.rs14
-rw-r--r--crates/ra_parser/src/grammar/items.rs2
-rw-r--r--crates/ra_parser/src/grammar/items/use_item.rs5
-rw-r--r--crates/ra_project_model/src/lib.rs2
-rw-r--r--crates/ra_syntax/test_data/parser/err/0002_duplicate_shebang.txt2
-rw-r--r--crates/ra_syntax/test_data/parser/err/0035_use_recover.rs5
-rw-r--r--crates/ra_syntax/test_data/parser/err/0035_use_recover.txt54
18 files changed, 873 insertions, 625 deletions
diff --git a/crates/ra_assists/src/ast_editor.rs b/crates/ra_assists/src/ast_editor.rs
index 048478662..a710edce8 100644
--- a/crates/ra_assists/src/ast_editor.rs
+++ b/crates/ra_assists/src/ast_editor.rs
@@ -297,6 +297,11 @@ impl AstBuilder<ast::Path> {
297 ast_node_from_file_text(text) 297 ast_node_from_file_text(text)
298 } 298 }
299 299
300 pub fn from_name(name: ast::Name) -> ast::Path {
301 let name = name.syntax().to_string();
302 Self::from_text(name.as_str())
303 }
304
300 pub fn from_pieces(enum_name: ast::Name, var_name: ast::Name) -> ast::Path { 305 pub fn from_pieces(enum_name: ast::Name, var_name: ast::Name) -> ast::Path {
301 Self::from_text(&format!("{}::{}", enum_name.syntax(), var_name.syntax())) 306 Self::from_text(&format!("{}::{}", enum_name.syntax(), var_name.syntax()))
302 } 307 }
@@ -380,6 +385,31 @@ impl AstBuilder<ast::MatchArmList> {
380 } 385 }
381} 386}
382 387
388impl AstBuilder<ast::WherePred> {
389 fn from_text(text: &str) -> ast::WherePred {
390 ast_node_from_file_text(&format!("fn f() where {} {{ }}", text))
391 }
392
393 pub fn from_pieces(
394 path: ast::Path,
395 bounds: impl Iterator<Item = ast::TypeBound>,
396 ) -> ast::WherePred {
397 let bounds = bounds.map(|b| b.syntax().to_string()).collect::<Vec<_>>().join(" + ");
398 Self::from_text(&format!("{}: {}", path.syntax(), bounds))
399 }
400}
401
402impl AstBuilder<ast::WhereClause> {
403 fn from_text(text: &str) -> ast::WhereClause {
404 ast_node_from_file_text(&format!("fn f() where {} {{ }}", text))
405 }
406
407 pub fn from_predicates(preds: impl Iterator<Item = ast::WherePred>) -> ast::WhereClause {
408 let preds = preds.map(|p| p.syntax().to_string()).collect::<Vec<_>>().join(", ");
409 Self::from_text(preds.as_str())
410 }
411}
412
383fn ast_node_from_file_text<N: AstNode>(text: &str) -> N { 413fn ast_node_from_file_text<N: AstNode>(text: &str) -> N {
384 let parse = SourceFile::parse(text); 414 let parse = SourceFile::parse(text);
385 let res = parse.tree().syntax().descendants().find_map(N::cast).unwrap(); 415 let res = parse.tree().syntax().descendants().find_map(N::cast).unwrap();
diff --git a/crates/ra_assists/src/lib.rs b/crates/ra_assists/src/lib.rs
index 03eec73ad..10ccc345c 100644
--- a/crates/ra_assists/src/lib.rs
+++ b/crates/ra_assists/src/lib.rs
@@ -102,6 +102,7 @@ mod remove_dbg;
102pub mod auto_import; 102pub mod auto_import;
103mod add_missing_impl_members; 103mod add_missing_impl_members;
104mod move_guard; 104mod move_guard;
105mod move_bounds;
105 106
106fn all_assists<DB: HirDatabase>() -> &'static [fn(AssistCtx<DB>) -> Option<Assist>] { 107fn all_assists<DB: HirDatabase>() -> &'static [fn(AssistCtx<DB>) -> Option<Assist>] {
107 &[ 108 &[
@@ -123,6 +124,7 @@ fn all_assists<DB: HirDatabase>() -> &'static [fn(AssistCtx<DB>) -> Option<Assis
123 inline_local_variable::inline_local_varialbe, 124 inline_local_variable::inline_local_varialbe,
124 move_guard::move_guard_to_arm_body, 125 move_guard::move_guard_to_arm_body,
125 move_guard::move_arm_cond_to_match_guard, 126 move_guard::move_arm_cond_to_match_guard,
127 move_bounds::move_bounds_to_where_clause,
126 ] 128 ]
127} 129}
128 130
diff --git a/crates/ra_assists/src/move_bounds.rs b/crates/ra_assists/src/move_bounds.rs
new file mode 100644
index 000000000..526de1d98
--- /dev/null
+++ b/crates/ra_assists/src/move_bounds.rs
@@ -0,0 +1,135 @@
1use hir::db::HirDatabase;
2use ra_syntax::{
3 ast::{self, AstNode, NameOwner, TypeBoundsOwner},
4 SyntaxElement,
5 SyntaxKind::*,
6 TextRange,
7};
8
9use crate::{ast_editor::AstBuilder, Assist, AssistCtx, AssistId};
10
11pub(crate) fn move_bounds_to_where_clause(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
12 let type_param_list = ctx.node_at_offset::<ast::TypeParamList>()?;
13
14 let mut type_params = type_param_list.type_params();
15 if type_params.all(|p| p.type_bound_list().is_none()) {
16 return None;
17 }
18
19 let parent = type_param_list.syntax().parent()?;
20 if parent.children_with_tokens().find(|it| it.kind() == WHERE_CLAUSE).is_some() {
21 return None;
22 }
23
24 let anchor: SyntaxElement = match parent.kind() {
25 FN_DEF => ast::FnDef::cast(parent)?.body()?.syntax().clone().into(),
26 TRAIT_DEF => ast::TraitDef::cast(parent)?.item_list()?.syntax().clone().into(),
27 IMPL_BLOCK => ast::ImplBlock::cast(parent)?.item_list()?.syntax().clone().into(),
28 ENUM_DEF => ast::EnumDef::cast(parent)?.variant_list()?.syntax().clone().into(),
29 STRUCT_DEF => parent
30 .children_with_tokens()
31 .find(|it| it.kind() == RECORD_FIELD_DEF_LIST || it.kind() == SEMI)?,
32 _ => return None,
33 };
34
35 ctx.add_action(
36 AssistId("move_bounds_to_where_clause"),
37 "move_bounds_to_where_clause",
38 |edit| {
39 let type_params = type_param_list.type_params().collect::<Vec<_>>();
40
41 for param in &type_params {
42 if let Some(bounds) = param.type_bound_list() {
43 let colon = param
44 .syntax()
45 .children_with_tokens()
46 .find(|it| it.kind() == COLON)
47 .unwrap();
48 let start = colon.text_range().start();
49 let end = bounds.syntax().text_range().end();
50 edit.delete(TextRange::from_to(start, end));
51 }
52 }
53
54 let predicates = type_params.iter().filter_map(build_predicate);
55 let where_clause = AstBuilder::<ast::WhereClause>::from_predicates(predicates);
56
57 let to_insert = match anchor.prev_sibling_or_token() {
58 Some(ref elem) if elem.kind() == WHITESPACE => {
59 format!("{} ", where_clause.syntax())
60 }
61 _ => format!(" {}", where_clause.syntax()),
62 };
63 edit.insert(anchor.text_range().start(), to_insert);
64 edit.target(type_param_list.syntax().text_range());
65 },
66 );
67
68 ctx.build()
69}
70
71fn build_predicate(param: &ast::TypeParam) -> Option<ast::WherePred> {
72 let path = AstBuilder::<ast::Path>::from_name(param.name()?);
73 let predicate =
74 AstBuilder::<ast::WherePred>::from_pieces(path, param.type_bound_list()?.bounds());
75 Some(predicate)
76}
77
78#[cfg(test)]
79mod tests {
80 use super::*;
81
82 use crate::helpers::check_assist;
83
84 #[test]
85 fn move_bounds_to_where_clause_fn() {
86 check_assist(
87 move_bounds_to_where_clause,
88 r#"
89 fn foo<T: u32, <|>F: FnOnce(T) -> T>() {}
90 "#,
91 r#"
92 fn foo<T, <|>F>() where T: u32, F: FnOnce(T) -> T {}
93 "#,
94 );
95 }
96
97 #[test]
98 fn move_bounds_to_where_clause_impl() {
99 check_assist(
100 move_bounds_to_where_clause,
101 r#"
102 impl<U: u32, <|>T> A<U, T> {}
103 "#,
104 r#"
105 impl<U, <|>T> A<U, T> where U: u32 {}
106 "#,
107 );
108 }
109
110 #[test]
111 fn move_bounds_to_where_clause_struct() {
112 check_assist(
113 move_bounds_to_where_clause,
114 r#"
115 struct A<<|>T: Iterator<Item = u32>> {}
116 "#,
117 r#"
118 struct A<<|>T> where T: Iterator<Item = u32> {}
119 "#,
120 );
121 }
122
123 #[test]
124 fn move_bounds_to_where_clause_tuple_struct() {
125 check_assist(
126 move_bounds_to_where_clause,
127 r#"
128 struct Pair<<|>T: u32>(T, T);
129 "#,
130 r#"
131 struct Pair<<|>T>(T, T) where T: u32;
132 "#,
133 );
134 }
135}
diff --git a/crates/ra_hir/src/nameres.rs b/crates/ra_hir/src/nameres.rs
index f69179bf6..dc2e2172b 100644
--- a/crates/ra_hir/src/nameres.rs
+++ b/crates/ra_hir/src/nameres.rs
@@ -50,6 +50,7 @@
50mod per_ns; 50mod per_ns;
51mod raw; 51mod raw;
52mod collector; 52mod collector;
53mod mod_resolution;
53#[cfg(test)] 54#[cfg(test)]
54mod tests; 55mod tests;
55 56
diff --git a/crates/ra_hir/src/nameres/collector.rs b/crates/ra_hir/src/nameres/collector.rs
index 698e40247..5af26f953 100644
--- a/crates/ra_hir/src/nameres/collector.rs
+++ b/crates/ra_hir/src/nameres/collector.rs
@@ -1,9 +1,5 @@
1use std::borrow::Cow; 1use ra_db::FileId;
2use std::sync::Arc; 2use ra_syntax::ast;
3
4use ra_db::{FileId, SourceRoot};
5use ra_syntax::{ast, SmolStr};
6use relative_path::RelativePathBuf;
7use rustc_hash::FxHashMap; 3use rustc_hash::FxHashMap;
8use test_utils::tested_by; 4use test_utils::tested_by;
9 5
@@ -12,8 +8,10 @@ use crate::{
12 ids::{AstItemDef, LocationCtx, MacroCallId, MacroCallLoc, MacroDefId, MacroFileKind}, 8 ids::{AstItemDef, LocationCtx, MacroCallId, MacroCallLoc, MacroDefId, MacroFileKind},
13 name::MACRO_RULES, 9 name::MACRO_RULES,
14 nameres::{ 10 nameres::{
15 diagnostics::DefDiagnostic, raw, CrateDefMap, CrateModuleId, ItemOrMacro, ModuleData, 11 diagnostics::DefDiagnostic,
16 ModuleDef, PerNs, ReachedFixedPoint, Resolution, ResolveMode, 12 mod_resolution::{resolve_submodule, ParentModule},
13 raw, CrateDefMap, CrateModuleId, ItemOrMacro, ModuleData, ModuleDef, PerNs,
14 ReachedFixedPoint, Resolution, ResolveMode,
17 }, 15 },
18 AstId, Const, DefDatabase, Enum, Function, HirFileId, MacroDef, Module, Name, Path, Static, 16 AstId, Const, DefDatabase, Enum, Function, HirFileId, MacroDef, Module, Name, Path, Static,
19 Struct, Trait, TypeAlias, Union, 17 Struct, Trait, TypeAlias, Union,
@@ -583,7 +581,7 @@ where
583 name, 581 name,
584 is_root, 582 is_root,
585 attr_path.as_ref(), 583 attr_path.as_ref(),
586 self.parent_module.as_ref(), 584 self.parent_module,
587 ) { 585 ) {
588 Ok(file_id) => { 586 Ok(file_id) => {
589 let module_id = self.push_child_module(name.clone(), ast_id, Some(file_id)); 587 let module_id = self.push_child_module(name.clone(), ast_id, Some(file_id));
@@ -694,180 +692,6 @@ fn is_macro_rules(path: &Path) -> bool {
694 path.as_ident() == Some(&MACRO_RULES) 692 path.as_ident() == Some(&MACRO_RULES)
695} 693}
696 694
697fn resolve_submodule(
698 db: &impl DefDatabase,
699 file_id: HirFileId,
700 name: &Name,
701 is_root: bool,
702 attr_path: Option<&SmolStr>,
703 parent_module: Option<&ParentModule>,
704) -> Result<FileId, RelativePathBuf> {
705 let file_id = file_id.original_file(db);
706 let source_root_id = db.file_source_root(file_id);
707 let path = db.file_relative_path(file_id);
708 let root = RelativePathBuf::default();
709 let dir_path = path.parent().unwrap_or(&root);
710 let mod_name = path.file_stem().unwrap_or("unknown");
711
712 let resolve_mode = match (attr_path.filter(|p| !p.is_empty()), parent_module) {
713 (Some(file_path), Some(parent_module)) => {
714 let file_path = normalize_attribute_path(file_path);
715 match parent_module.attribute_path() {
716 Some(parent_module_attr_path) => {
717 let path = dir_path
718 .join(format!(
719 "{}/{}",
720 normalize_attribute_path(parent_module_attr_path),
721 file_path
722 ))
723 .normalize();
724 ResolutionMode::InlineModuleWithAttributePath(
725 InsideInlineModuleMode::WithAttributePath(path),
726 )
727 }
728 None => {
729 let path =
730 dir_path.join(format!("{}/{}", parent_module.name, file_path)).normalize();
731 ResolutionMode::InsideInlineModule(InsideInlineModuleMode::WithAttributePath(
732 path,
733 ))
734 }
735 }
736 }
737 (None, Some(parent_module)) => match parent_module.attribute_path() {
738 Some(parent_module_attr_path) => {
739 let path = dir_path.join(format!(
740 "{}/{}.rs",
741 normalize_attribute_path(parent_module_attr_path),
742 name
743 ));
744 ResolutionMode::InlineModuleWithAttributePath(InsideInlineModuleMode::File(path))
745 }
746 None => {
747 let path = dir_path.join(format!("{}/{}.rs", parent_module.name, name));
748 ResolutionMode::InsideInlineModule(InsideInlineModuleMode::File(path))
749 }
750 },
751 (Some(file_path), None) => {
752 let file_path = normalize_attribute_path(file_path);
753 let path = dir_path.join(file_path.as_ref()).normalize();
754 ResolutionMode::OutOfLine(OutOfLineMode::WithAttributePath(path))
755 }
756 _ => {
757 let is_dir_owner = is_root || mod_name == "mod";
758 if is_dir_owner {
759 let file_mod = dir_path.join(format!("{}.rs", name));
760 let dir_mod = dir_path.join(format!("{}/mod.rs", name));
761 ResolutionMode::OutOfLine(OutOfLineMode::RootOrModRs {
762 file: file_mod,
763 directory: dir_mod,
764 })
765 } else {
766 let path = dir_path.join(format!("{}/{}.rs", mod_name, name));
767 ResolutionMode::OutOfLine(OutOfLineMode::FileInDirectory(path))
768 }
769 }
770 };
771
772 resolve_mode.resolve(db.source_root(source_root_id))
773}
774
775fn normalize_attribute_path(file_path: &SmolStr) -> Cow<str> {
776 let current_dir = "./";
777 let windows_path_separator = r#"\"#;
778 let current_dir_normalize = if file_path.starts_with(current_dir) {
779 &file_path[current_dir.len()..]
780 } else {
781 file_path.as_str()
782 };
783 if current_dir_normalize.contains(windows_path_separator) {
784 Cow::Owned(current_dir_normalize.replace(windows_path_separator, "/"))
785 } else {
786 Cow::Borrowed(current_dir_normalize)
787 }
788}
789
790enum OutOfLineMode {
791 RootOrModRs { file: RelativePathBuf, directory: RelativePathBuf },
792 FileInDirectory(RelativePathBuf),
793 WithAttributePath(RelativePathBuf),
794}
795
796impl OutOfLineMode {
797 pub fn resolve(&self, source_root: Arc<SourceRoot>) -> Result<FileId, RelativePathBuf> {
798 match self {
799 OutOfLineMode::RootOrModRs { file, directory } => match source_root.files.get(file) {
800 None => resolve_simple_path(source_root, directory).map_err(|_| file.clone()),
801 file_id => resolve_find_result(file_id, file),
802 },
803 OutOfLineMode::FileInDirectory(path) => resolve_simple_path(source_root, path),
804 OutOfLineMode::WithAttributePath(path) => resolve_simple_path(source_root, path),
805 }
806 }
807}
808
809enum InsideInlineModuleMode {
810 File(RelativePathBuf),
811 WithAttributePath(RelativePathBuf),
812}
813
814impl InsideInlineModuleMode {
815 pub fn resolve(&self, source_root: Arc<SourceRoot>) -> Result<FileId, RelativePathBuf> {
816 match self {
817 InsideInlineModuleMode::File(path) => resolve_simple_path(source_root, path),
818 InsideInlineModuleMode::WithAttributePath(path) => {
819 resolve_simple_path(source_root, path)
820 }
821 }
822 }
823}
824
825enum ResolutionMode {
826 OutOfLine(OutOfLineMode),
827 InsideInlineModule(InsideInlineModuleMode),
828 InlineModuleWithAttributePath(InsideInlineModuleMode),
829}
830
831impl ResolutionMode {
832 pub fn resolve(&self, source_root: Arc<SourceRoot>) -> Result<FileId, RelativePathBuf> {
833 use self::ResolutionMode::*;
834
835 match self {
836 OutOfLine(mode) => mode.resolve(source_root),
837 InsideInlineModule(mode) => mode.resolve(source_root),
838 InlineModuleWithAttributePath(mode) => mode.resolve(source_root),
839 }
840 }
841}
842
843fn resolve_simple_path(
844 source_root: Arc<SourceRoot>,
845 path: &RelativePathBuf,
846) -> Result<FileId, RelativePathBuf> {
847 resolve_find_result(source_root.files.get(path), path)
848}
849
850fn resolve_find_result(
851 file_id: Option<&FileId>,
852 path: &RelativePathBuf,
853) -> Result<FileId, RelativePathBuf> {
854 match file_id {
855 Some(file_id) => Ok(file_id.clone()),
856 None => Err(path.clone()),
857 }
858}
859
860struct ParentModule<'a> {
861 name: &'a Name,
862 attr_path: Option<&'a SmolStr>,
863}
864
865impl<'a> ParentModule<'a> {
866 pub fn attribute_path(&self) -> Option<&SmolStr> {
867 self.attr_path.filter(|p| !p.is_empty())
868 }
869}
870
871#[cfg(test)] 695#[cfg(test)]
872mod tests { 696mod tests {
873 use ra_db::SourceDatabase; 697 use ra_db::SourceDatabase;
diff --git a/crates/ra_hir/src/nameres/mod_resolution.rs b/crates/ra_hir/src/nameres/mod_resolution.rs
new file mode 100644
index 000000000..94c9946ff
--- /dev/null
+++ b/crates/ra_hir/src/nameres/mod_resolution.rs
@@ -0,0 +1,182 @@
1use std::{borrow::Cow, sync::Arc};
2
3use ra_db::{FileId, SourceRoot};
4use ra_syntax::SmolStr;
5use relative_path::RelativePathBuf;
6
7use crate::{DefDatabase, HirFileId, Name};
8
9#[derive(Clone, Copy)]
10pub(super) struct ParentModule<'a> {
11 pub(super) name: &'a Name,
12 pub(super) attr_path: Option<&'a SmolStr>,
13}
14
15impl<'a> ParentModule<'a> {
16 fn attribute_path(&self) -> Option<&SmolStr> {
17 self.attr_path.filter(|p| !p.is_empty())
18 }
19}
20
21pub(super) fn resolve_submodule(
22 db: &impl DefDatabase,
23 file_id: HirFileId,
24 name: &Name,
25 is_root: bool,
26 attr_path: Option<&SmolStr>,
27 parent_module: Option<ParentModule<'_>>,
28) -> Result<FileId, RelativePathBuf> {
29 let file_id = file_id.original_file(db);
30 let source_root_id = db.file_source_root(file_id);
31 let path = db.file_relative_path(file_id);
32 let root = RelativePathBuf::default();
33 let dir_path = path.parent().unwrap_or(&root);
34 let mod_name = path.file_stem().unwrap_or("unknown");
35
36 let resolve_mode = match (attr_path.filter(|p| !p.is_empty()), parent_module) {
37 (Some(file_path), Some(parent_module)) => {
38 let file_path = normalize_attribute_path(file_path);
39 match parent_module.attribute_path() {
40 Some(parent_module_attr_path) => {
41 let path = dir_path
42 .join(format!(
43 "{}/{}",
44 normalize_attribute_path(parent_module_attr_path),
45 file_path
46 ))
47 .normalize();
48 ResolutionMode::InlineModuleWithAttributePath(
49 InsideInlineModuleMode::WithAttributePath(path),
50 )
51 }
52 None => {
53 let path =
54 dir_path.join(format!("{}/{}", parent_module.name, file_path)).normalize();
55 ResolutionMode::InsideInlineModule(InsideInlineModuleMode::WithAttributePath(
56 path,
57 ))
58 }
59 }
60 }
61 (None, Some(parent_module)) => match parent_module.attribute_path() {
62 Some(parent_module_attr_path) => {
63 let path = dir_path.join(format!(
64 "{}/{}.rs",
65 normalize_attribute_path(parent_module_attr_path),
66 name
67 ));
68 ResolutionMode::InlineModuleWithAttributePath(InsideInlineModuleMode::File(path))
69 }
70 None => {
71 let path = dir_path.join(format!("{}/{}.rs", parent_module.name, name));
72 ResolutionMode::InsideInlineModule(InsideInlineModuleMode::File(path))
73 }
74 },
75 (Some(file_path), None) => {
76 let file_path = normalize_attribute_path(file_path);
77 let path = dir_path.join(file_path.as_ref()).normalize();
78 ResolutionMode::OutOfLine(OutOfLineMode::WithAttributePath(path))
79 }
80 _ => {
81 let is_dir_owner = is_root || mod_name == "mod";
82 if is_dir_owner {
83 let file_mod = dir_path.join(format!("{}.rs", name));
84 let dir_mod = dir_path.join(format!("{}/mod.rs", name));
85 ResolutionMode::OutOfLine(OutOfLineMode::RootOrModRs {
86 file: file_mod,
87 directory: dir_mod,
88 })
89 } else {
90 let path = dir_path.join(format!("{}/{}.rs", mod_name, name));
91 ResolutionMode::OutOfLine(OutOfLineMode::FileInDirectory(path))
92 }
93 }
94 };
95
96 resolve_mode.resolve(db.source_root(source_root_id))
97}
98
99fn normalize_attribute_path(file_path: &SmolStr) -> Cow<str> {
100 let current_dir = "./";
101 let windows_path_separator = r#"\"#;
102 let current_dir_normalize = if file_path.starts_with(current_dir) {
103 &file_path[current_dir.len()..]
104 } else {
105 file_path.as_str()
106 };
107 if current_dir_normalize.contains(windows_path_separator) {
108 Cow::Owned(current_dir_normalize.replace(windows_path_separator, "/"))
109 } else {
110 Cow::Borrowed(current_dir_normalize)
111 }
112}
113
114enum OutOfLineMode {
115 RootOrModRs { file: RelativePathBuf, directory: RelativePathBuf },
116 FileInDirectory(RelativePathBuf),
117 WithAttributePath(RelativePathBuf),
118}
119
120impl OutOfLineMode {
121 pub fn resolve(&self, source_root: Arc<SourceRoot>) -> Result<FileId, RelativePathBuf> {
122 match self {
123 OutOfLineMode::RootOrModRs { file, directory } => match source_root.files.get(file) {
124 None => resolve_simple_path(source_root, directory).map_err(|_| file.clone()),
125 file_id => resolve_find_result(file_id, file),
126 },
127 OutOfLineMode::FileInDirectory(path) => resolve_simple_path(source_root, path),
128 OutOfLineMode::WithAttributePath(path) => resolve_simple_path(source_root, path),
129 }
130 }
131}
132
133enum InsideInlineModuleMode {
134 File(RelativePathBuf),
135 WithAttributePath(RelativePathBuf),
136}
137
138impl InsideInlineModuleMode {
139 pub fn resolve(&self, source_root: Arc<SourceRoot>) -> Result<FileId, RelativePathBuf> {
140 match self {
141 InsideInlineModuleMode::File(path) => resolve_simple_path(source_root, path),
142 InsideInlineModuleMode::WithAttributePath(path) => {
143 resolve_simple_path(source_root, path)
144 }
145 }
146 }
147}
148
149enum ResolutionMode {
150 OutOfLine(OutOfLineMode),
151 InsideInlineModule(InsideInlineModuleMode),
152 InlineModuleWithAttributePath(InsideInlineModuleMode),
153}
154
155impl ResolutionMode {
156 pub fn resolve(&self, source_root: Arc<SourceRoot>) -> Result<FileId, RelativePathBuf> {
157 use self::ResolutionMode::*;
158
159 match self {
160 OutOfLine(mode) => mode.resolve(source_root),
161 InsideInlineModule(mode) => mode.resolve(source_root),
162 InlineModuleWithAttributePath(mode) => mode.resolve(source_root),
163 }
164 }
165}
166
167fn resolve_simple_path(
168 source_root: Arc<SourceRoot>,
169 path: &RelativePathBuf,
170) -> Result<FileId, RelativePathBuf> {
171 resolve_find_result(source_root.files.get(path), path)
172}
173
174fn resolve_find_result(
175 file_id: Option<&FileId>,
176 path: &RelativePathBuf,
177) -> Result<FileId, RelativePathBuf> {
178 match file_id {
179 Some(file_id) => Ok(file_id.clone()),
180 None => Err(path.clone()),
181 }
182}
diff --git a/crates/ra_hir/src/nameres/tests.rs b/crates/ra_hir/src/nameres/tests.rs
index c1dbad283..4ff897ca5 100644
--- a/crates/ra_hir/src/nameres/tests.rs
+++ b/crates/ra_hir/src/nameres/tests.rs
@@ -2,7 +2,7 @@ mod macros;
2mod globs; 2mod globs;
3mod incremental; 3mod incremental;
4mod primitives; 4mod primitives;
5mod mods; 5mod mod_resolution;
6 6
7use std::sync::Arc; 7use std::sync::Arc;
8 8
diff --git a/crates/ra_hir/src/nameres/tests/mods.rs b/crates/ra_hir/src/nameres/tests/mod_resolution.rs
index 4f8398460..4f8398460 100644
--- a/crates/ra_hir/src/nameres/tests/mods.rs
+++ b/crates/ra_hir/src/nameres/tests/mod_resolution.rs
diff --git a/crates/ra_ide_api/src/lib.rs b/crates/ra_ide_api/src/lib.rs
index 514dcaf96..e90fbd428 100644
--- a/crates/ra_ide_api/src/lib.rs
+++ b/crates/ra_ide_api/src/lib.rs
@@ -466,7 +466,7 @@ impl Analysis {
466 &self, 466 &self,
467 position: FilePosition, 467 position: FilePosition,
468 ) -> Cancelable<Option<ReferenceSearchResult>> { 468 ) -> Cancelable<Option<ReferenceSearchResult>> {
469 self.with_db(|db| references::find_all_refs(db, position)) 469 self.with_db(|db| references::find_all_refs(db, position).map(|it| it.info))
470 } 470 }
471 471
472 /// Returns a short text describing element at position. 472 /// Returns a short text describing element at position.
@@ -536,7 +536,7 @@ impl Analysis {
536 &self, 536 &self,
537 position: FilePosition, 537 position: FilePosition,
538 new_name: &str, 538 new_name: &str,
539 ) -> Cancelable<Option<SourceChange>> { 539 ) -> Cancelable<Option<RangeInfo<SourceChange>>> {
540 self.with_db(|db| references::rename(db, position, new_name)) 540 self.with_db(|db| references::rename(db, position, new_name))
541 } 541 }
542 542
diff --git a/crates/ra_ide_api/src/references.rs b/crates/ra_ide_api/src/references.rs
index d8a00067f..5f1f0efc3 100644
--- a/crates/ra_ide_api/src/references.rs
+++ b/crates/ra_ide_api/src/references.rs
@@ -4,7 +4,7 @@ use ra_syntax::{algo::find_node_at_offset, ast, AstNode, SourceFile, SyntaxNode}
4use relative_path::{RelativePath, RelativePathBuf}; 4use relative_path::{RelativePath, RelativePathBuf};
5 5
6use crate::{ 6use crate::{
7 db::RootDatabase, FileId, FilePosition, FileRange, FileSystemEdit, NavigationTarget, 7 db::RootDatabase, FileId, FilePosition, FileRange, FileSystemEdit, NavigationTarget, RangeInfo,
8 SourceChange, SourceFileEdit, TextRange, 8 SourceChange, SourceFileEdit, TextRange,
9}; 9};
10 10
@@ -48,9 +48,9 @@ impl IntoIterator for ReferenceSearchResult {
48pub(crate) fn find_all_refs( 48pub(crate) fn find_all_refs(
49 db: &RootDatabase, 49 db: &RootDatabase,
50 position: FilePosition, 50 position: FilePosition,
51) -> Option<ReferenceSearchResult> { 51) -> Option<RangeInfo<ReferenceSearchResult>> {
52 let parse = db.parse(position.file_id); 52 let parse = db.parse(position.file_id);
53 let (binding, analyzer) = find_binding(db, &parse.tree(), position)?; 53 let RangeInfo { range, info: (binding, analyzer) } = find_binding(db, &parse.tree(), position)?;
54 let declaration = NavigationTarget::from_bind_pat(position.file_id, &binding); 54 let declaration = NavigationTarget::from_bind_pat(position.file_id, &binding);
55 55
56 let references = analyzer 56 let references = analyzer
@@ -59,24 +59,26 @@ pub(crate) fn find_all_refs(
59 .map(move |ref_desc| FileRange { file_id: position.file_id, range: ref_desc.range }) 59 .map(move |ref_desc| FileRange { file_id: position.file_id, range: ref_desc.range })
60 .collect::<Vec<_>>(); 60 .collect::<Vec<_>>();
61 61
62 return Some(ReferenceSearchResult { declaration, references }); 62 return Some(RangeInfo::new(range, ReferenceSearchResult { declaration, references }));
63 63
64 fn find_binding<'a>( 64 fn find_binding<'a>(
65 db: &RootDatabase, 65 db: &RootDatabase,
66 source_file: &SourceFile, 66 source_file: &SourceFile,
67 position: FilePosition, 67 position: FilePosition,
68 ) -> Option<(ast::BindPat, hir::SourceAnalyzer)> { 68 ) -> Option<RangeInfo<(ast::BindPat, hir::SourceAnalyzer)>> {
69 let syntax = source_file.syntax(); 69 let syntax = source_file.syntax();
70 if let Some(binding) = find_node_at_offset::<ast::BindPat>(syntax, position.offset) { 70 if let Some(binding) = find_node_at_offset::<ast::BindPat>(syntax, position.offset) {
71 let range = binding.syntax().text_range();
71 let analyzer = hir::SourceAnalyzer::new(db, position.file_id, binding.syntax(), None); 72 let analyzer = hir::SourceAnalyzer::new(db, position.file_id, binding.syntax(), None);
72 return Some((binding, analyzer)); 73 return Some(RangeInfo::new(range, (binding, analyzer)));
73 }; 74 };
74 let name_ref = find_node_at_offset::<ast::NameRef>(syntax, position.offset)?; 75 let name_ref = find_node_at_offset::<ast::NameRef>(syntax, position.offset)?;
76 let range = name_ref.syntax().text_range();
75 let analyzer = hir::SourceAnalyzer::new(db, position.file_id, name_ref.syntax(), None); 77 let analyzer = hir::SourceAnalyzer::new(db, position.file_id, name_ref.syntax(), None);
76 let resolved = analyzer.resolve_local_name(&name_ref)?; 78 let resolved = analyzer.resolve_local_name(&name_ref)?;
77 if let Either::A(ptr) = resolved.ptr() { 79 if let Either::A(ptr) = resolved.ptr() {
78 if let ast::Pat::BindPat(binding) = ptr.to_node(source_file.syntax()) { 80 if let ast::Pat::BindPat(binding) = ptr.to_node(source_file.syntax()) {
79 return Some((binding, analyzer)); 81 return Some(RangeInfo::new(range, (binding, analyzer)));
80 } 82 }
81 } 83 }
82 None 84 None
@@ -87,12 +89,14 @@ pub(crate) fn rename(
87 db: &RootDatabase, 89 db: &RootDatabase,
88 position: FilePosition, 90 position: FilePosition,
89 new_name: &str, 91 new_name: &str,
90) -> Option<SourceChange> { 92) -> Option<RangeInfo<SourceChange>> {
91 let parse = db.parse(position.file_id); 93 let parse = db.parse(position.file_id);
92 if let Some((ast_name, ast_module)) = 94 if let Some((ast_name, ast_module)) =
93 find_name_and_module_at_offset(parse.tree().syntax(), position) 95 find_name_and_module_at_offset(parse.tree().syntax(), position)
94 { 96 {
97 let range = ast_name.syntax().text_range();
95 rename_mod(db, &ast_name, &ast_module, position, new_name) 98 rename_mod(db, &ast_name, &ast_module, position, new_name)
99 .map(|info| RangeInfo::new(range, info))
96 } else { 100 } else {
97 rename_reference(db, position, new_name) 101 rename_reference(db, position, new_name)
98 } 102 }
@@ -107,7 +111,7 @@ fn find_name_and_module_at_offset(
107 Some((ast_name, ast_module)) 111 Some((ast_name, ast_module))
108} 112}
109 113
110fn source_edit_from_fileid_range( 114fn source_edit_from_file_id_range(
111 file_id: FileId, 115 file_id: FileId,
112 range: TextRange, 116 range: TextRange,
113 new_name: &str, 117 new_name: &str,
@@ -179,19 +183,19 @@ fn rename_reference(
179 db: &RootDatabase, 183 db: &RootDatabase,
180 position: FilePosition, 184 position: FilePosition,
181 new_name: &str, 185 new_name: &str,
182) -> Option<SourceChange> { 186) -> Option<RangeInfo<SourceChange>> {
183 let refs = find_all_refs(db, position)?; 187 let RangeInfo { range, info: refs } = find_all_refs(db, position)?;
184 188
185 let edit = refs 189 let edit = refs
186 .into_iter() 190 .into_iter()
187 .map(|range| source_edit_from_fileid_range(range.file_id, range.range, new_name)) 191 .map(|range| source_edit_from_file_id_range(range.file_id, range.range, new_name))
188 .collect::<Vec<_>>(); 192 .collect::<Vec<_>>();
189 193
190 if edit.is_empty() { 194 if edit.is_empty() {
191 return None; 195 return None;
192 } 196 }
193 197
194 Some(SourceChange::source_file_edits("rename", edit)) 198 Some(RangeInfo::new(range, SourceChange::source_file_edits("rename", edit)))
195} 199}
196 200
197#[cfg(test)] 201#[cfg(test)]
@@ -342,38 +346,43 @@ mod tests {
342 let new_name = "foo2"; 346 let new_name = "foo2";
343 let source_change = analysis.rename(position, new_name).unwrap(); 347 let source_change = analysis.rename(position, new_name).unwrap();
344 assert_debug_snapshot!(&source_change, 348 assert_debug_snapshot!(&source_change,
345@r#"Some( 349@r###"
346 SourceChange { 350 Some(
347 label: "rename", 351 RangeInfo {
348 source_file_edits: [ 352 range: [4; 7),
349 SourceFileEdit { 353 info: SourceChange {
350 file_id: FileId( 354 label: "rename",
351 2, 355 source_file_edits: [
352 ), 356 SourceFileEdit {
353 edit: TextEdit { 357 file_id: FileId(
354 atoms: [ 358 2,
355 AtomTextEdit { 359 ),
356 delete: [4; 7), 360 edit: TextEdit {
357 insert: "foo2", 361 atoms: [
362 AtomTextEdit {
363 delete: [4; 7),
364 insert: "foo2",
365 },
366 ],
367 },
368 },
369 ],
370 file_system_edits: [
371 MoveFile {
372 src: FileId(
373 3,
374 ),
375 dst_source_root: SourceRootId(
376 0,
377 ),
378 dst_path: "bar/foo2.rs",
358 }, 379 },
359 ], 380 ],
381 cursor_position: None,
360 }, 382 },
361 }, 383 },
362 ], 384 )
363 file_system_edits: [ 385 "###);
364 MoveFile {
365 src: FileId(
366 3,
367 ),
368 dst_source_root: SourceRootId(
369 0,
370 ),
371 dst_path: "bar/foo2.rs",
372 },
373 ],
374 cursor_position: None,
375 },
376)"#);
377 } 386 }
378 387
379 #[test] 388 #[test]
@@ -389,38 +398,43 @@ mod tests {
389 let new_name = "foo2"; 398 let new_name = "foo2";
390 let source_change = analysis.rename(position, new_name).unwrap(); 399 let source_change = analysis.rename(position, new_name).unwrap();
391 assert_debug_snapshot!(&source_change, 400 assert_debug_snapshot!(&source_change,
392 @r###"Some( 401 @r###"
393 SourceChange { 402 Some(
394 label: "rename", 403 RangeInfo {
395 source_file_edits: [ 404 range: [4; 7),
396 SourceFileEdit { 405 info: SourceChange {
397 file_id: FileId( 406 label: "rename",
398 1, 407 source_file_edits: [
399 ), 408 SourceFileEdit {
400 edit: TextEdit { 409 file_id: FileId(
401 atoms: [ 410 1,
402 AtomTextEdit { 411 ),
403 delete: [4; 7), 412 edit: TextEdit {
404 insert: "foo2", 413 atoms: [
414 AtomTextEdit {
415 delete: [4; 7),
416 insert: "foo2",
417 },
418 ],
419 },
420 },
421 ],
422 file_system_edits: [
423 MoveFile {
424 src: FileId(
425 2,
426 ),
427 dst_source_root: SourceRootId(
428 0,
429 ),
430 dst_path: "foo2/mod.rs",
405 }, 431 },
406 ], 432 ],
433 cursor_position: None,
407 }, 434 },
408 }, 435 },
409 ], 436 )
410 file_system_edits: [ 437 "###
411 MoveFile {
412 src: FileId(
413 2,
414 ),
415 dst_source_root: SourceRootId(
416 0,
417 ),
418 dst_path: "foo2/mod.rs",
419 },
420 ],
421 cursor_position: None,
422 },
423)"###
424 ); 438 );
425 } 439 }
426 440
@@ -430,7 +444,7 @@ mod tests {
430 let mut text_edit_builder = ra_text_edit::TextEditBuilder::default(); 444 let mut text_edit_builder = ra_text_edit::TextEditBuilder::default();
431 let mut file_id: Option<FileId> = None; 445 let mut file_id: Option<FileId> = None;
432 if let Some(change) = source_change { 446 if let Some(change) = source_change {
433 for edit in change.source_file_edits { 447 for edit in change.info.source_file_edits {
434 file_id = Some(edit.file_id); 448 file_id = Some(edit.file_id);
435 for atom in edit.edit.as_atoms() { 449 for atom in edit.edit.as_atoms() {
436 text_edit_builder.replace(atom.delete, atom.insert.clone()); 450 text_edit_builder.replace(atom.delete, atom.insert.clone());
diff --git a/crates/ra_ide_api/src/syntax_tree.rs b/crates/ra_ide_api/src/syntax_tree.rs
index 914759709..e2bb120b4 100644
--- a/crates/ra_ide_api/src/syntax_tree.rs
+++ b/crates/ra_ide_api/src/syntax_tree.rs
@@ -1,357 +1,357 @@
1use crate::db::RootDatabase; 1use crate::db::RootDatabase;
2use ra_db::SourceDatabase; 2use ra_db::SourceDatabase;
3use ra_syntax::{ 3use ra_syntax::{
4 algo, AstNode, NodeOrToken, SourceFile, 4 algo, AstNode, NodeOrToken, SourceFile,
5 SyntaxKind::{RAW_STRING, STRING}, 5 SyntaxKind::{RAW_STRING, STRING},
6 SyntaxToken, TextRange, 6 SyntaxToken, TextRange,
7}; 7};
8 8
9pub use ra_db::FileId; 9pub use ra_db::FileId;
10 10
11pub(crate) fn syntax_tree( 11pub(crate) fn syntax_tree(
12 db: &RootDatabase, 12 db: &RootDatabase,
13 file_id: FileId, 13 file_id: FileId,
14 text_range: Option<TextRange>, 14 text_range: Option<TextRange>,
15) -> String { 15) -> String {
16 let parse = db.parse(file_id); 16 let parse = db.parse(file_id);
17 if let Some(text_range) = text_range { 17 if let Some(text_range) = text_range {
18 let node = match algo::find_covering_element(parse.tree().syntax(), text_range) { 18 let node = match algo::find_covering_element(parse.tree().syntax(), text_range) {
19 NodeOrToken::Node(node) => node, 19 NodeOrToken::Node(node) => node,
20 NodeOrToken::Token(token) => { 20 NodeOrToken::Token(token) => {
21 if let Some(tree) = syntax_tree_for_string(&token, text_range) { 21 if let Some(tree) = syntax_tree_for_string(&token, text_range) {
22 return tree; 22 return tree;
23 } 23 }
24 token.parent() 24 token.parent()
25 } 25 }
26 }; 26 };
27 27
28 format!("{:#?}", node) 28 format!("{:#?}", node)
29 } else { 29 } else {
30 format!("{:#?}", parse.tree().syntax()) 30 format!("{:#?}", parse.tree().syntax())
31 } 31 }
32} 32}
33 33
34/// Attempts parsing the selected contents of a string literal 34/// Attempts parsing the selected contents of a string literal
35/// as rust syntax and returns its syntax tree 35/// as rust syntax and returns its syntax tree
36fn syntax_tree_for_string(token: &SyntaxToken, text_range: TextRange) -> Option<String> { 36fn syntax_tree_for_string(token: &SyntaxToken, text_range: TextRange) -> Option<String> {
37 // When the range is inside a string 37 // When the range is inside a string
38 // we'll attempt parsing it as rust syntax 38 // we'll attempt parsing it as rust syntax
39 // to provide the syntax tree of the contents of the string 39 // to provide the syntax tree of the contents of the string
40 match token.kind() { 40 match token.kind() {
41 STRING | RAW_STRING => syntax_tree_for_token(token, text_range), 41 STRING | RAW_STRING => syntax_tree_for_token(token, text_range),
42 _ => None, 42 _ => None,
43 } 43 }
44} 44}
45 45
46fn syntax_tree_for_token(node: &SyntaxToken, text_range: TextRange) -> Option<String> { 46fn syntax_tree_for_token(node: &SyntaxToken, text_range: TextRange) -> Option<String> {
47 // Range of the full node 47 // Range of the full node
48 let node_range = node.text_range(); 48 let node_range = node.text_range();
49 let text = node.text().to_string(); 49 let text = node.text().to_string();
50 50
51 // We start at some point inside the node 51 // We start at some point inside the node
52 // Either we have selected the whole string 52 // Either we have selected the whole string
53 // or our selection is inside it 53 // or our selection is inside it
54 let start = text_range.start() - node_range.start(); 54 let start = text_range.start() - node_range.start();
55 55
56 // how many characters we have selected 56 // how many characters we have selected
57 let len = text_range.len().to_usize(); 57 let len = text_range.len().to_usize();
58 58
59 let node_len = node_range.len().to_usize(); 59 let node_len = node_range.len().to_usize();
60 60
61 let start = start.to_usize(); 61 let start = start.to_usize();
62 62
63 // We want to cap our length 63 // We want to cap our length
64 let len = len.min(node_len); 64 let len = len.min(node_len);
65 65
66 // Ensure our slice is inside the actual string 66 // Ensure our slice is inside the actual string
67 let end = if start + len < text.len() { start + len } else { text.len() - start }; 67 let end = if start + len < text.len() { start + len } else { text.len() - start };
68 68
69 let text = &text[start..end]; 69 let text = &text[start..end];
70 70
71 // Remove possible extra string quotes from the start 71 // Remove possible extra string quotes from the start
72 // and the end of the string 72 // and the end of the string
73 let text = text 73 let text = text
74 .trim_start_matches('r') 74 .trim_start_matches('r')
75 .trim_start_matches('#') 75 .trim_start_matches('#')
76 .trim_start_matches('"') 76 .trim_start_matches('"')
77 .trim_end_matches('#') 77 .trim_end_matches('#')
78 .trim_end_matches('"') 78 .trim_end_matches('"')
79 .trim() 79 .trim()
80 // Remove custom markers 80 // Remove custom markers
81 .replace("<|>", ""); 81 .replace("<|>", "");
82 82
83 let parsed = SourceFile::parse(&text); 83 let parsed = SourceFile::parse(&text);
84 84
85 // If the "file" parsed without errors, 85 // If the "file" parsed without errors,
86 // return its syntax 86 // return its syntax
87 if parsed.errors().is_empty() { 87 if parsed.errors().is_empty() {
88 return Some(format!("{:#?}", parsed.tree().syntax())); 88 return Some(format!("{:#?}", parsed.tree().syntax()));
89 } 89 }
90 90
91 None 91 None
92} 92}
93 93
94#[cfg(test)] 94#[cfg(test)]
95mod tests { 95mod tests {
96 use test_utils::assert_eq_text; 96 use test_utils::assert_eq_text;
97 97
98 use crate::mock_analysis::{single_file, single_file_with_range}; 98 use crate::mock_analysis::{single_file, single_file_with_range};
99 99
100 #[test] 100 #[test]
101 fn test_syntax_tree_without_range() { 101 fn test_syntax_tree_without_range() {
102 // Basic syntax 102 // Basic syntax
103 let (analysis, file_id) = single_file(r#"fn foo() {}"#); 103 let (analysis, file_id) = single_file(r#"fn foo() {}"#);
104 let syn = analysis.syntax_tree(file_id, None).unwrap(); 104 let syn = analysis.syntax_tree(file_id, None).unwrap();
105 105
106 assert_eq_text!( 106 assert_eq_text!(
107 syn.trim(), 107 syn.trim(),
108 r#" 108 r#"
109SOURCE_FILE@[0; 11) 109SOURCE_FILE@[0; 11)
110 FN_DEF@[0; 11) 110 FN_DEF@[0; 11)
111 FN_KW@[0; 2) "fn" 111 FN_KW@[0; 2) "fn"
112 WHITESPACE@[2; 3) " " 112 WHITESPACE@[2; 3) " "
113 NAME@[3; 6) 113 NAME@[3; 6)
114 IDENT@[3; 6) "foo" 114 IDENT@[3; 6) "foo"
115 PARAM_LIST@[6; 8) 115 PARAM_LIST@[6; 8)
116 L_PAREN@[6; 7) "(" 116 L_PAREN@[6; 7) "("
117 R_PAREN@[7; 8) ")" 117 R_PAREN@[7; 8) ")"
118 WHITESPACE@[8; 9) " " 118 WHITESPACE@[8; 9) " "
119 BLOCK_EXPR@[9; 11) 119 BLOCK_EXPR@[9; 11)
120 BLOCK@[9; 11) 120 BLOCK@[9; 11)
121 L_CURLY@[9; 10) "{" 121 L_CURLY@[9; 10) "{"
122 R_CURLY@[10; 11) "}" 122 R_CURLY@[10; 11) "}"
123"# 123"#
124 .trim() 124 .trim()
125 ); 125 );
126 126
127 let (analysis, file_id) = single_file( 127 let (analysis, file_id) = single_file(
128 r#" 128 r#"
129fn test() { 129fn test() {
130 assert!(" 130 assert!("
131 fn foo() { 131 fn foo() {
132 } 132 }
133 ", ""); 133 ", "");
134}"# 134}"#
135 .trim(), 135 .trim(),
136 ); 136 );
137 let syn = analysis.syntax_tree(file_id, None).unwrap(); 137 let syn = analysis.syntax_tree(file_id, None).unwrap();
138 138
139 assert_eq_text!( 139 assert_eq_text!(
140 syn.trim(), 140 syn.trim(),
141 r#" 141 r#"
142SOURCE_FILE@[0; 60) 142SOURCE_FILE@[0; 60)
143 FN_DEF@[0; 60) 143 FN_DEF@[0; 60)
144 FN_KW@[0; 2) "fn" 144 FN_KW@[0; 2) "fn"
145 WHITESPACE@[2; 3) " " 145 WHITESPACE@[2; 3) " "
146 NAME@[3; 7) 146 NAME@[3; 7)
147 IDENT@[3; 7) "test" 147 IDENT@[3; 7) "test"
148 PARAM_LIST@[7; 9) 148 PARAM_LIST@[7; 9)
149 L_PAREN@[7; 8) "(" 149 L_PAREN@[7; 8) "("
150 R_PAREN@[8; 9) ")" 150 R_PAREN@[8; 9) ")"
151 WHITESPACE@[9; 10) " " 151 WHITESPACE@[9; 10) " "
152 BLOCK_EXPR@[10; 60) 152 BLOCK_EXPR@[10; 60)
153 BLOCK@[10; 60) 153 BLOCK@[10; 60)
154 L_CURLY@[10; 11) "{" 154 L_CURLY@[10; 11) "{"
155 WHITESPACE@[11; 16) "\n " 155 WHITESPACE@[11; 16) "\n "
156 EXPR_STMT@[16; 58) 156 EXPR_STMT@[16; 58)
157 MACRO_CALL@[16; 57) 157 MACRO_CALL@[16; 57)
158 PATH@[16; 22) 158 PATH@[16; 22)
159 PATH_SEGMENT@[16; 22) 159 PATH_SEGMENT@[16; 22)
160 NAME_REF@[16; 22) 160 NAME_REF@[16; 22)
161 IDENT@[16; 22) "assert" 161 IDENT@[16; 22) "assert"
162 EXCL@[22; 23) "!" 162 EXCL@[22; 23) "!"
163 TOKEN_TREE@[23; 57) 163 TOKEN_TREE@[23; 57)
164 L_PAREN@[23; 24) "(" 164 L_PAREN@[23; 24) "("
165 STRING@[24; 52) "\"\n fn foo() {\n ..." 165 STRING@[24; 52) "\"\n fn foo() {\n ..."
166 COMMA@[52; 53) "," 166 COMMA@[52; 53) ","
167 WHITESPACE@[53; 54) " " 167 WHITESPACE@[53; 54) " "
168 STRING@[54; 56) "\"\"" 168 STRING@[54; 56) "\"\""
169 R_PAREN@[56; 57) ")" 169 R_PAREN@[56; 57) ")"
170 SEMI@[57; 58) ";" 170 SEMI@[57; 58) ";"
171 WHITESPACE@[58; 59) "\n" 171 WHITESPACE@[58; 59) "\n"
172 R_CURLY@[59; 60) "}" 172 R_CURLY@[59; 60) "}"
173"# 173"#
174 .trim() 174 .trim()
175 ); 175 );
176 } 176 }
177 177
178 #[test] 178 #[test]
179 fn test_syntax_tree_with_range() { 179 fn test_syntax_tree_with_range() {
180 let (analysis, range) = single_file_with_range(r#"<|>fn foo() {}<|>"#.trim()); 180 let (analysis, range) = single_file_with_range(r#"<|>fn foo() {}<|>"#.trim());
181 let syn = analysis.syntax_tree(range.file_id, Some(range.range)).unwrap(); 181 let syn = analysis.syntax_tree(range.file_id, Some(range.range)).unwrap();
182 182
183 assert_eq_text!( 183 assert_eq_text!(
184 syn.trim(), 184 syn.trim(),
185 r#" 185 r#"
186FN_DEF@[0; 11) 186FN_DEF@[0; 11)
187 FN_KW@[0; 2) "fn" 187 FN_KW@[0; 2) "fn"
188 WHITESPACE@[2; 3) " " 188 WHITESPACE@[2; 3) " "
189 NAME@[3; 6) 189 NAME@[3; 6)
190 IDENT@[3; 6) "foo" 190 IDENT@[3; 6) "foo"
191 PARAM_LIST@[6; 8) 191 PARAM_LIST@[6; 8)
192 L_PAREN@[6; 7) "(" 192 L_PAREN@[6; 7) "("
193 R_PAREN@[7; 8) ")" 193 R_PAREN@[7; 8) ")"
194 WHITESPACE@[8; 9) " " 194 WHITESPACE@[8; 9) " "
195 BLOCK_EXPR@[9; 11) 195 BLOCK_EXPR@[9; 11)
196 BLOCK@[9; 11) 196 BLOCK@[9; 11)
197 L_CURLY@[9; 10) "{" 197 L_CURLY@[9; 10) "{"
198 R_CURLY@[10; 11) "}" 198 R_CURLY@[10; 11) "}"
199"# 199"#
200 .trim() 200 .trim()
201 ); 201 );
202 202
203 let (analysis, range) = single_file_with_range( 203 let (analysis, range) = single_file_with_range(
204 r#"fn test() { 204 r#"fn test() {
205 <|>assert!(" 205 <|>assert!("
206 fn foo() { 206 fn foo() {
207 } 207 }
208 ", "");<|> 208 ", "");<|>
209}"# 209}"#
210 .trim(), 210 .trim(),
211 ); 211 );
212 let syn = analysis.syntax_tree(range.file_id, Some(range.range)).unwrap(); 212 let syn = analysis.syntax_tree(range.file_id, Some(range.range)).unwrap();
213 213
214 assert_eq_text!( 214 assert_eq_text!(
215 syn.trim(), 215 syn.trim(),
216 r#" 216 r#"
217EXPR_STMT@[16; 58) 217EXPR_STMT@[16; 58)
218 MACRO_CALL@[16; 57) 218 MACRO_CALL@[16; 57)
219 PATH@[16; 22) 219 PATH@[16; 22)
220 PATH_SEGMENT@[16; 22) 220 PATH_SEGMENT@[16; 22)
221 NAME_REF@[16; 22) 221 NAME_REF@[16; 22)
222 IDENT@[16; 22) "assert" 222 IDENT@[16; 22) "assert"
223 EXCL@[22; 23) "!" 223 EXCL@[22; 23) "!"
224 TOKEN_TREE@[23; 57) 224 TOKEN_TREE@[23; 57)
225 L_PAREN@[23; 24) "(" 225 L_PAREN@[23; 24) "("
226 STRING@[24; 52) "\"\n fn foo() {\n ..." 226 STRING@[24; 52) "\"\n fn foo() {\n ..."
227 COMMA@[52; 53) "," 227 COMMA@[52; 53) ","
228 WHITESPACE@[53; 54) " " 228 WHITESPACE@[53; 54) " "
229 STRING@[54; 56) "\"\"" 229 STRING@[54; 56) "\"\""
230 R_PAREN@[56; 57) ")" 230 R_PAREN@[56; 57) ")"
231 SEMI@[57; 58) ";" 231 SEMI@[57; 58) ";"
232"# 232"#
233 .trim() 233 .trim()
234 ); 234 );
235 } 235 }
236 236
237 #[test] 237 #[test]
238 fn test_syntax_tree_inside_string() { 238 fn test_syntax_tree_inside_string() {
239 let (analysis, range) = single_file_with_range( 239 let (analysis, range) = single_file_with_range(
240 r#"fn test() { 240 r#"fn test() {
241 assert!(" 241 assert!("
242<|>fn foo() { 242<|>fn foo() {
243}<|> 243}<|>
244fn bar() { 244fn bar() {
245} 245}
246 ", ""); 246 ", "");
247}"# 247}"#
248 .trim(), 248 .trim(),
249 ); 249 );
250 let syn = analysis.syntax_tree(range.file_id, Some(range.range)).unwrap(); 250 let syn = analysis.syntax_tree(range.file_id, Some(range.range)).unwrap();
251 assert_eq_text!( 251 assert_eq_text!(
252 syn.trim(), 252 syn.trim(),
253 r#" 253 r#"
254SOURCE_FILE@[0; 12) 254SOURCE_FILE@[0; 12)
255 FN_DEF@[0; 12) 255 FN_DEF@[0; 12)
256 FN_KW@[0; 2) "fn" 256 FN_KW@[0; 2) "fn"
257 WHITESPACE@[2; 3) " " 257 WHITESPACE@[2; 3) " "
258 NAME@[3; 6) 258 NAME@[3; 6)
259 IDENT@[3; 6) "foo" 259 IDENT@[3; 6) "foo"
260 PARAM_LIST@[6; 8) 260 PARAM_LIST@[6; 8)
261 L_PAREN@[6; 7) "(" 261 L_PAREN@[6; 7) "("
262 R_PAREN@[7; 8) ")" 262 R_PAREN@[7; 8) ")"
263 WHITESPACE@[8; 9) " " 263 WHITESPACE@[8; 9) " "
264 BLOCK_EXPR@[9; 12) 264 BLOCK_EXPR@[9; 12)
265 BLOCK@[9; 12) 265 BLOCK@[9; 12)
266 L_CURLY@[9; 10) "{" 266 L_CURLY@[9; 10) "{"
267 WHITESPACE@[10; 11) "\n" 267 WHITESPACE@[10; 11) "\n"
268 R_CURLY@[11; 12) "}" 268 R_CURLY@[11; 12) "}"
269"# 269"#
270 .trim() 270 .trim()
271 ); 271 );
272 272
273 // With a raw string 273 // With a raw string
274 let (analysis, range) = single_file_with_range( 274 let (analysis, range) = single_file_with_range(
275 r###"fn test() { 275 r###"fn test() {
276 assert!(r#" 276 assert!(r#"
277<|>fn foo() { 277<|>fn foo() {
278}<|> 278}<|>
279fn bar() { 279fn bar() {
280} 280}
281 "#, ""); 281 "#, "");
282}"### 282}"###
283 .trim(), 283 .trim(),
284 ); 284 );
285 let syn = analysis.syntax_tree(range.file_id, Some(range.range)).unwrap(); 285 let syn = analysis.syntax_tree(range.file_id, Some(range.range)).unwrap();
286 assert_eq_text!( 286 assert_eq_text!(
287 syn.trim(), 287 syn.trim(),
288 r#" 288 r#"
289SOURCE_FILE@[0; 12) 289SOURCE_FILE@[0; 12)
290 FN_DEF@[0; 12) 290 FN_DEF@[0; 12)
291 FN_KW@[0; 2) "fn" 291 FN_KW@[0; 2) "fn"
292 WHITESPACE@[2; 3) " " 292 WHITESPACE@[2; 3) " "
293 NAME@[3; 6) 293 NAME@[3; 6)
294 IDENT@[3; 6) "foo" 294 IDENT@[3; 6) "foo"
295 PARAM_LIST@[6; 8) 295 PARAM_LIST@[6; 8)
296 L_PAREN@[6; 7) "(" 296 L_PAREN@[6; 7) "("
297 R_PAREN@[7; 8) ")" 297 R_PAREN@[7; 8) ")"
298 WHITESPACE@[8; 9) " " 298 WHITESPACE@[8; 9) " "
299 BLOCK_EXPR@[9; 12) 299 BLOCK_EXPR@[9; 12)
300 BLOCK@[9; 12) 300 BLOCK@[9; 12)
301 L_CURLY@[9; 10) "{" 301 L_CURLY@[9; 10) "{"
302 WHITESPACE@[10; 11) "\n" 302 WHITESPACE@[10; 11) "\n"
303 R_CURLY@[11; 12) "}" 303 R_CURLY@[11; 12) "}"
304"# 304"#
305 .trim() 305 .trim()
306 ); 306 );
307 307
308 // With a raw string 308 // With a raw string
309 let (analysis, range) = single_file_with_range( 309 let (analysis, range) = single_file_with_range(
310 r###"fn test() { 310 r###"fn test() {
311 assert!(r<|>#" 311 assert!(r<|>#"
312fn foo() { 312fn foo() {
313} 313}
314fn bar() { 314fn bar() {
315}"<|>#, ""); 315}"<|>#, "");
316}"### 316}"###
317 .trim(), 317 .trim(),
318 ); 318 );
319 let syn = analysis.syntax_tree(range.file_id, Some(range.range)).unwrap(); 319 let syn = analysis.syntax_tree(range.file_id, Some(range.range)).unwrap();
320 assert_eq_text!( 320 assert_eq_text!(
321 syn.trim(), 321 syn.trim(),
322 r#" 322 r#"
323SOURCE_FILE@[0; 25) 323SOURCE_FILE@[0; 25)
324 FN_DEF@[0; 12) 324 FN_DEF@[0; 12)
325 FN_KW@[0; 2) "fn" 325 FN_KW@[0; 2) "fn"
326 WHITESPACE@[2; 3) " " 326 WHITESPACE@[2; 3) " "
327 NAME@[3; 6) 327 NAME@[3; 6)
328 IDENT@[3; 6) "foo" 328 IDENT@[3; 6) "foo"
329 PARAM_LIST@[6; 8) 329 PARAM_LIST@[6; 8)
330 L_PAREN@[6; 7) "(" 330 L_PAREN@[6; 7) "("
331 R_PAREN@[7; 8) ")" 331 R_PAREN@[7; 8) ")"
332 WHITESPACE@[8; 9) " " 332 WHITESPACE@[8; 9) " "
333 BLOCK_EXPR@[9; 12) 333 BLOCK_EXPR@[9; 12)
334 BLOCK@[9; 12) 334 BLOCK@[9; 12)
335 L_CURLY@[9; 10) "{" 335 L_CURLY@[9; 10) "{"
336 WHITESPACE@[10; 11) "\n" 336 WHITESPACE@[10; 11) "\n"
337 R_CURLY@[11; 12) "}" 337 R_CURLY@[11; 12) "}"
338 WHITESPACE@[12; 13) "\n" 338 WHITESPACE@[12; 13) "\n"
339 FN_DEF@[13; 25) 339 FN_DEF@[13; 25)
340 FN_KW@[13; 15) "fn" 340 FN_KW@[13; 15) "fn"
341 WHITESPACE@[15; 16) " " 341 WHITESPACE@[15; 16) " "
342 NAME@[16; 19) 342 NAME@[16; 19)
343 IDENT@[16; 19) "bar" 343 IDENT@[16; 19) "bar"
344 PARAM_LIST@[19; 21) 344 PARAM_LIST@[19; 21)
345 L_PAREN@[19; 20) "(" 345 L_PAREN@[19; 20) "("
346 R_PAREN@[20; 21) ")" 346 R_PAREN@[20; 21) ")"
347 WHITESPACE@[21; 22) " " 347 WHITESPACE@[21; 22) " "
348 BLOCK_EXPR@[22; 25) 348 BLOCK_EXPR@[22; 25)
349 BLOCK@[22; 25) 349 BLOCK@[22; 25)
350 L_CURLY@[22; 23) "{" 350 L_CURLY@[22; 23) "{"
351 WHITESPACE@[23; 24) "\n" 351 WHITESPACE@[23; 24) "\n"
352 R_CURLY@[24; 25) "}" 352 R_CURLY@[24; 25) "}"
353"# 353"#
354 .trim() 354 .trim()
355 ); 355 );
356 } 356 }
357} 357}
diff --git a/crates/ra_lsp_server/src/main_loop/handlers.rs b/crates/ra_lsp_server/src/main_loop/handlers.rs
index eb805a6d3..948d543ea 100644
--- a/crates/ra_lsp_server/src/main_loop/handlers.rs
+++ b/crates/ra_lsp_server/src/main_loop/handlers.rs
@@ -460,18 +460,16 @@ pub fn handle_prepare_rename(
460 460
461 // We support renaming references like handle_rename does. 461 // We support renaming references like handle_rename does.
462 // In the future we may want to reject the renaming of things like keywords here too. 462 // In the future we may want to reject the renaming of things like keywords here too.
463 let refs = match world.analysis().find_all_refs(position)? { 463 let optional_change = world.analysis().rename(position, "dummy")?;
464 let range = match optional_change {
464 None => return Ok(None), 465 None => return Ok(None),
465 Some(refs) => refs, 466 Some(it) => it.range,
466 }; 467 };
467 468
468 // Refs should always have a declaration
469 let r = refs.declaration();
470 let file_id = params.text_document.try_conv_with(&world)?; 469 let file_id = params.text_document.try_conv_with(&world)?;
471 let line_index = world.analysis().file_line_index(file_id)?; 470 let line_index = world.analysis().file_line_index(file_id)?;
472 let loc = to_location(r.file_id(), r.range(), &world, &line_index)?; 471 let range = range.conv_with(&line_index);
473 472 Ok(Some(PrepareRenameResponse::Range(range)))
474 Ok(Some(PrepareRenameResponse::Range(loc.range)))
475} 473}
476 474
477pub fn handle_rename(world: WorldSnapshot, params: RenameParams) -> Result<Option<WorkspaceEdit>> { 475pub fn handle_rename(world: WorldSnapshot, params: RenameParams) -> Result<Option<WorkspaceEdit>> {
@@ -488,7 +486,7 @@ pub fn handle_rename(world: WorldSnapshot, params: RenameParams) -> Result<Optio
488 let optional_change = world.analysis().rename(position, &*params.new_name)?; 486 let optional_change = world.analysis().rename(position, &*params.new_name)?;
489 let change = match optional_change { 487 let change = match optional_change {
490 None => return Ok(None), 488 None => return Ok(None),
491 Some(it) => it, 489 Some(it) => it.info,
492 }; 490 };
493 491
494 let source_change_req = change.try_conv_with(&world)?; 492 let source_change_req = change.try_conv_with(&world)?;
diff --git a/crates/ra_parser/src/grammar/items.rs b/crates/ra_parser/src/grammar/items.rs
index 6d426206e..b4327b78f 100644
--- a/crates/ra_parser/src/grammar/items.rs
+++ b/crates/ra_parser/src/grammar/items.rs
@@ -31,7 +31,7 @@ pub(super) enum ItemFlavor {
31 31
32pub(super) const ITEM_RECOVERY_SET: TokenSet = token_set![ 32pub(super) const ITEM_RECOVERY_SET: TokenSet = token_set![
33 FN_KW, STRUCT_KW, ENUM_KW, IMPL_KW, TRAIT_KW, CONST_KW, STATIC_KW, LET_KW, MOD_KW, PUB_KW, 33 FN_KW, STRUCT_KW, ENUM_KW, IMPL_KW, TRAIT_KW, CONST_KW, STATIC_KW, LET_KW, MOD_KW, PUB_KW,
34 CRATE_KW 34 CRATE_KW, USE_KW
35]; 35];
36 36
37pub(super) fn item_or_macro(p: &mut Parser, stop_on_r_curly: bool, flavor: ItemFlavor) { 37pub(super) fn item_or_macro(p: &mut Parser, stop_on_r_curly: bool, flavor: ItemFlavor) {
diff --git a/crates/ra_parser/src/grammar/items/use_item.rs b/crates/ra_parser/src/grammar/items/use_item.rs
index c0c7d0ec6..83a65e226 100644
--- a/crates/ra_parser/src/grammar/items/use_item.rs
+++ b/crates/ra_parser/src/grammar/items/use_item.rs
@@ -101,7 +101,10 @@ fn use_tree(p: &mut Parser) {
101 } 101 }
102 _ => { 102 _ => {
103 m.abandon(p); 103 m.abandon(p);
104 p.err_and_bump("expected one of `*`, `::`, `{`, `self`, `super` or an indentifier"); 104 p.err_recover(
105 "expected one of `*`, `::`, `{`, `self`, `super` or an identifier",
106 ITEM_RECOVERY_SET,
107 );
105 return; 108 return;
106 } 109 }
107 } 110 }
diff --git a/crates/ra_project_model/src/lib.rs b/crates/ra_project_model/src/lib.rs
index 676dc4941..9b2f534e7 100644
--- a/crates/ra_project_model/src/lib.rs
+++ b/crates/ra_project_model/src/lib.rs
@@ -153,7 +153,7 @@ impl ProjectWorkspace {
153 if let Some(file_id) = load(krate.root(&sysroot)) { 153 if let Some(file_id) = load(krate.root(&sysroot)) {
154 sysroot_crates.insert( 154 sysroot_crates.insert(
155 krate, 155 krate,
156 crate_graph.add_crate_root(file_id, Edition::Edition2015), 156 crate_graph.add_crate_root(file_id, Edition::Edition2018),
157 ); 157 );
158 } 158 }
159 } 159 }
diff --git a/crates/ra_syntax/test_data/parser/err/0002_duplicate_shebang.txt b/crates/ra_syntax/test_data/parser/err/0002_duplicate_shebang.txt
index 84867026f..bdb5fa6c5 100644
--- a/crates/ra_syntax/test_data/parser/err/0002_duplicate_shebang.txt
+++ b/crates/ra_syntax/test_data/parser/err/0002_duplicate_shebang.txt
@@ -28,7 +28,7 @@ SOURCE_FILE@[0; 42)
28 WHITESPACE@[41; 42) "\n" 28 WHITESPACE@[41; 42) "\n"
29error 23: expected `[` 29error 23: expected `[`
30error 23: expected an item 30error 23: expected an item
31error 27: expected one of `*`, `::`, `{`, `self`, `super` or an indentifier 31error 27: expected one of `*`, `::`, `{`, `self`, `super` or an identifier
32error 28: expected SEMI 32error 28: expected SEMI
33error 31: expected EXCL 33error 31: expected EXCL
34error 31: expected `{`, `[`, `(` 34error 31: expected `{`, `[`, `(`
diff --git a/crates/ra_syntax/test_data/parser/err/0035_use_recover.rs b/crates/ra_syntax/test_data/parser/err/0035_use_recover.rs
new file mode 100644
index 000000000..4a2668126
--- /dev/null
+++ b/crates/ra_syntax/test_data/parser/err/0035_use_recover.rs
@@ -0,0 +1,5 @@
1use foo::bar;
2use
3use crate::baz;
4use
5fn f() {}
diff --git a/crates/ra_syntax/test_data/parser/err/0035_use_recover.txt b/crates/ra_syntax/test_data/parser/err/0035_use_recover.txt
new file mode 100644
index 000000000..636840828
--- /dev/null
+++ b/crates/ra_syntax/test_data/parser/err/0035_use_recover.txt
@@ -0,0 +1,54 @@
1SOURCE_FILE@[0; 48)
2 USE_ITEM@[0; 13)
3 USE_KW@[0; 3) "use"
4 WHITESPACE@[3; 4) " "
5 USE_TREE@[4; 12)
6 PATH@[4; 12)
7 PATH@[4; 7)
8 PATH_SEGMENT@[4; 7)
9 NAME_REF@[4; 7)
10 IDENT@[4; 7) "foo"
11 COLONCOLON@[7; 9) "::"
12 PATH_SEGMENT@[9; 12)
13 NAME_REF@[9; 12)
14 IDENT@[9; 12) "bar"
15 SEMI@[12; 13) ";"
16 WHITESPACE@[13; 14) "\n"
17 USE_ITEM@[14; 17)
18 USE_KW@[14; 17) "use"
19 WHITESPACE@[17; 18) "\n"
20 USE_ITEM@[18; 33)
21 USE_KW@[18; 21) "use"
22 WHITESPACE@[21; 22) " "
23 USE_TREE@[22; 32)
24 PATH@[22; 32)
25 PATH@[22; 27)
26 PATH_SEGMENT@[22; 27)
27 CRATE_KW@[22; 27) "crate"
28 COLONCOLON@[27; 29) "::"
29 PATH_SEGMENT@[29; 32)
30 NAME_REF@[29; 32)
31 IDENT@[29; 32) "baz"
32 SEMI@[32; 33) ";"
33 WHITESPACE@[33; 34) "\n"
34 USE_ITEM@[34; 37)
35 USE_KW@[34; 37) "use"
36 WHITESPACE@[37; 38) "\n"
37 FN_DEF@[38; 47)
38 FN_KW@[38; 40) "fn"
39 WHITESPACE@[40; 41) " "
40 NAME@[41; 42)
41 IDENT@[41; 42) "f"
42 PARAM_LIST@[42; 44)
43 L_PAREN@[42; 43) "("
44 R_PAREN@[43; 44) ")"
45 WHITESPACE@[44; 45) " "
46 BLOCK_EXPR@[45; 47)
47 BLOCK@[45; 47)
48 L_CURLY@[45; 46) "{"
49 R_CURLY@[46; 47) "}"
50 WHITESPACE@[47; 48) "\n"
51error 17: expected one of `*`, `::`, `{`, `self`, `super` or an identifier
52error 17: expected SEMI
53error 37: expected one of `*`, `::`, `{`, `self`, `super` or an identifier
54error 37: expected SEMI