aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.travis.yml1
-rw-r--r--Cargo.toml4
-rw-r--r--crates/ra_hir/src/code_model.rs55
-rw-r--r--crates/ra_hir/src/expr/scope.rs2
-rw-r--r--crates/ra_hir/src/from_source.rs30
-rw-r--r--crates/ra_hir/src/lib.rs8
-rw-r--r--crates/ra_hir/src/source_binder.rs20
-rw-r--r--crates/ra_hir/src/ty/tests.rs19
-rw-r--r--crates/ra_hir_def/src/builtin_type.rs32
-rw-r--r--crates/ra_hir_def/src/nameres/collector.rs25
-rw-r--r--crates/ra_hir_def/src/nameres/raw.rs7
-rw-r--r--crates/ra_hir_expand/src/builtin_macro.rs80
-rw-r--r--crates/ra_hir_expand/src/db.rs66
-rw-r--r--crates/ra_hir_expand/src/hygiene.rs7
-rw-r--r--crates/ra_hir_expand/src/lib.rs12
-rw-r--r--crates/ra_hir_expand/src/name.rs3
-rw-r--r--crates/ra_hir_expand/src/quote.rs261
-rw-r--r--crates/ra_ide_api/src/display.rs2
-rw-r--r--crates/ra_ide_api/src/display/navigation_target.rs398
-rw-r--r--crates/ra_ide_api/src/goto_definition.rs19
-rw-r--r--crates/ra_ide_api/src/goto_type_definition.rs4
-rw-r--r--crates/ra_ide_api/src/hover.rs48
-rw-r--r--crates/ra_ide_api/src/impls.rs11
-rw-r--r--crates/ra_ide_api/src/lib.rs4
-rw-r--r--crates/ra_ide_api/src/references.rs15
-rw-r--r--crates/ra_ide_api/src/references/classify.rs22
-rw-r--r--crates/ra_ide_api/src/references/name_definition.rs38
-rw-r--r--crates/ra_ide_api/src/references/search_scope.rs6
-rw-r--r--crates/ra_ide_api/src/snapshots/rainbow_highlighting.html12
-rw-r--r--crates/ra_ide_api/src/syntax_highlighting.rs168
-rw-r--r--crates/ra_syntax/src/syntax_error.rs4
-rw-r--r--crates/ra_syntax/src/validation.rs23
-rw-r--r--crates/ra_syntax/test_data/parser/err/0037_visibility_in_traits.rs6
-rw-r--r--crates/ra_syntax/test_data/parser/err/0037_visibility_in_traits.txt99
34 files changed, 1072 insertions, 439 deletions
diff --git a/.travis.yml b/.travis.yml
index 7d66f72d8..af71a9cce 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -14,7 +14,6 @@ matrix:
14 script: 14 script:
15 - rustup component add rustfmt 15 - rustup component add rustfmt
16 - rustup component add rust-src 16 - rustup component add rust-src
17 - sed -i "s/debug = 1/debug = false/g" Cargo.toml
18 - cargo test --no-run # let's measure compile time separately 17 - cargo test --no-run # let's measure compile time separately
19 - cargo test 18 - cargo test
20 env: 19 env:
diff --git a/Cargo.toml b/Cargo.toml
index 97c02b40f..5c57020f7 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -2,7 +2,9 @@
2members = [ "crates/*", "xtask/" ] 2members = [ "crates/*", "xtask/" ]
3 3
4[profile.dev] 4[profile.dev]
5debug = 1 # only line info 5# disabling debug info speeds up builds a bunch,
6# and we don't rely on it for debugging that much.
7debug = 0
6 8
7[profile.release] 9[profile.release]
8incremental = true 10incremental = true
diff --git a/crates/ra_hir/src/code_model.rs b/crates/ra_hir/src/code_model.rs
index e5bfad3ca..09c4e97fa 100644
--- a/crates/ra_hir/src/code_model.rs
+++ b/crates/ra_hir/src/code_model.rs
@@ -22,7 +22,7 @@ use ra_syntax::ast::{self, NameOwner, TypeAscriptionOwner};
22use crate::{ 22use crate::{
23 adt::VariantDef, 23 adt::VariantDef,
24 db::{AstDatabase, DefDatabase, HirDatabase}, 24 db::{AstDatabase, DefDatabase, HirDatabase},
25 expr::{validation::ExprValidator, Body, BodySourceMap}, 25 expr::{validation::ExprValidator, BindingAnnotation, Body, BodySourceMap, Pat, PatId},
26 generics::HasGenericParams, 26 generics::HasGenericParams,
27 ids::{ 27 ids::{
28 AstItemDef, ConstId, EnumId, FunctionId, MacroDefId, StaticId, StructId, TraitId, 28 AstItemDef, ConstId, EnumId, FunctionId, MacroDefId, StaticId, StructId, TraitId,
@@ -32,7 +32,7 @@ use crate::{
32 resolve::{Resolver, Scope, TypeNs}, 32 resolve::{Resolver, Scope, TypeNs},
33 traits::TraitData, 33 traits::TraitData,
34 ty::{InferenceResult, Namespace, TraitRef}, 34 ty::{InferenceResult, Namespace, TraitRef},
35 Either, HasSource, ImportId, Name, ScopeDef, Ty, 35 Either, HasSource, ImportId, Name, ScopeDef, Source, Ty,
36}; 36};
37 37
38/// hir::Crate describes a single crate. It's the main interface with which 38/// hir::Crate describes a single crate. It's the main interface with which
@@ -1070,3 +1070,54 @@ impl AssocItem {
1070 .expect("AssocItem without container") 1070 .expect("AssocItem without container")
1071 } 1071 }
1072} 1072}
1073
1074#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
1075pub struct Local {
1076 pub(crate) parent: DefWithBody,
1077 pub(crate) pat_id: PatId,
1078}
1079
1080impl Local {
1081 pub fn name(self, db: &impl HirDatabase) -> Option<Name> {
1082 let body = db.body_hir(self.parent);
1083 match &body[self.pat_id] {
1084 Pat::Bind { name, .. } => Some(name.clone()),
1085 _ => None,
1086 }
1087 }
1088
1089 pub fn is_self(self, db: &impl HirDatabase) -> bool {
1090 self.name(db) == Some(name::SELF_PARAM)
1091 }
1092
1093 pub fn is_mut(self, db: &impl HirDatabase) -> bool {
1094 let body = db.body_hir(self.parent);
1095 match &body[self.pat_id] {
1096 Pat::Bind { mode, .. } => match mode {
1097 BindingAnnotation::Mutable | BindingAnnotation::RefMut => true,
1098 _ => false,
1099 },
1100 _ => false,
1101 }
1102 }
1103
1104 pub fn parent(self, _db: &impl HirDatabase) -> DefWithBody {
1105 self.parent
1106 }
1107
1108 pub fn module(self, db: &impl HirDatabase) -> Module {
1109 self.parent.module(db)
1110 }
1111
1112 pub fn ty(self, db: &impl HirDatabase) -> Ty {
1113 let infer = db.infer(self.parent);
1114 infer[self.pat_id].clone()
1115 }
1116
1117 pub fn source(self, db: &impl HirDatabase) -> Source<Either<ast::BindPat, ast::SelfParam>> {
1118 let (_body, source_map) = db.body_with_source_map(self.parent);
1119 let src = source_map.pat_syntax(self.pat_id).unwrap(); // Hmm...
1120 let root = src.file_syntax(db);
1121 src.map(|ast| ast.map(|it| it.cast().unwrap().to_node(&root), |it| it.to_node(&root)))
1122 }
1123}
diff --git a/crates/ra_hir/src/expr/scope.rs b/crates/ra_hir/src/expr/scope.rs
index 5a1eade2c..daf8d8d07 100644
--- a/crates/ra_hir/src/expr/scope.rs
+++ b/crates/ra_hir/src/expr/scope.rs
@@ -17,7 +17,7 @@ impl_arena_id!(ScopeId);
17 17
18#[derive(Debug, PartialEq, Eq)] 18#[derive(Debug, PartialEq, Eq)]
19pub struct ExprScopes { 19pub struct ExprScopes {
20 body: Arc<Body>, 20 pub(crate) body: Arc<Body>,
21 scopes: Arena<ScopeId, ScopeData>, 21 scopes: Arena<ScopeId, ScopeData>,
22 scope_by_expr: FxHashMap<ExprId, ScopeId>, 22 scope_by_expr: FxHashMap<ExprId, ScopeId>,
23} 23}
diff --git a/crates/ra_hir/src/from_source.rs b/crates/ra_hir/src/from_source.rs
index c95d2cdd0..2c441b0f4 100644
--- a/crates/ra_hir/src/from_source.rs
+++ b/crates/ra_hir/src/from_source.rs
@@ -2,13 +2,17 @@
2 2
3use hir_def::{StructId, StructOrUnionId, UnionId}; 3use hir_def::{StructId, StructOrUnionId, UnionId};
4use hir_expand::name::AsName; 4use hir_expand::name::AsName;
5use ra_syntax::ast::{self, AstNode, NameOwner}; 5use ra_syntax::{
6 ast::{self, AstNode, NameOwner},
7 match_ast,
8};
6 9
7use crate::{ 10use crate::{
8 db::{AstDatabase, DefDatabase, HirDatabase}, 11 db::{AstDatabase, DefDatabase, HirDatabase},
9 ids::{AstItemDef, LocationCtx}, 12 ids::{AstItemDef, LocationCtx},
10 AstId, Const, Crate, Enum, EnumVariant, FieldSource, Function, HasSource, ImplBlock, Module, 13 AstId, Const, Crate, DefWithBody, Enum, EnumVariant, FieldSource, Function, HasSource,
11 ModuleSource, Source, Static, Struct, StructField, Trait, TypeAlias, Union, VariantDef, 14 ImplBlock, Local, Module, ModuleSource, Source, Static, Struct, StructField, Trait, TypeAlias,
15 Union, VariantDef,
12}; 16};
13 17
14pub trait FromSource: Sized { 18pub trait FromSource: Sized {
@@ -126,6 +130,26 @@ impl FromSource for StructField {
126 } 130 }
127} 131}
128 132
133impl Local {
134 pub fn from_source(db: &impl HirDatabase, src: Source<ast::BindPat>) -> Option<Self> {
135 let file_id = src.file_id;
136 let parent: DefWithBody = src.ast.syntax().ancestors().find_map(|it| {
137 let res = match_ast! {
138 match it {
139 ast::ConstDef(ast) => { Const::from_source(db, Source { ast, file_id})?.into() },
140 ast::StaticDef(ast) => { Static::from_source(db, Source { ast, file_id})?.into() },
141 ast::FnDef(ast) => { Function::from_source(db, Source { ast, file_id})?.into() },
142 _ => return None,
143 }
144 };
145 Some(res)
146 })?;
147 let (_body, source_map) = db.body_with_source_map(parent);
148 let pat_id = source_map.node_pat(&src.ast.into())?;
149 Some(Local { parent, pat_id })
150 }
151}
152
129impl Module { 153impl Module {
130 pub fn from_declaration(db: &impl HirDatabase, src: Source<ast::Module>) -> Option<Self> { 154 pub fn from_declaration(db: &impl HirDatabase, src: Source<ast::Module>) -> Option<Self> {
131 let src_parent = Source { 155 let src_parent = Source {
diff --git a/crates/ra_hir/src/lib.rs b/crates/ra_hir/src/lib.rs
index 131f6c797..806f1daed 100644
--- a/crates/ra_hir/src/lib.rs
+++ b/crates/ra_hir/src/lib.rs
@@ -65,7 +65,7 @@ pub use crate::{
65 docs::{DocDef, Docs, Documentation}, 65 docs::{DocDef, Docs, Documentation},
66 src::{HasBodySource, HasSource}, 66 src::{HasBodySource, HasSource},
67 Adt, AssocItem, Const, ConstData, Container, Crate, CrateDependency, DefWithBody, Enum, 67 Adt, AssocItem, Const, ConstData, Container, Crate, CrateDependency, DefWithBody, Enum,
68 EnumVariant, FieldSource, FnData, Function, HasBody, MacroDef, Module, ModuleDef, 68 EnumVariant, FieldSource, FnData, Function, HasBody, Local, MacroDef, Module, ModuleDef,
69 ModuleSource, Static, Struct, StructField, Trait, TypeAlias, Union, 69 ModuleSource, Static, Struct, StructField, Trait, TypeAlias, Union,
70 }, 70 },
71 expr::ExprScopes, 71 expr::ExprScopes,
@@ -76,7 +76,11 @@ pub use crate::{
76 resolve::ScopeDef, 76 resolve::ScopeDef,
77 source_binder::{PathResolution, ScopeEntryWithSyntax, SourceAnalyzer}, 77 source_binder::{PathResolution, ScopeEntryWithSyntax, SourceAnalyzer},
78 ty::{ 78 ty::{
79 display::HirDisplay, ApplicationTy, CallableDef, Substs, TraitRef, Ty, TypeCtor, TypeWalk, 79 display::HirDisplay,
80 primitive::{
81 FloatBitness, FloatTy, IntBitness, IntTy, Signedness, UncertainFloatTy, UncertainIntTy,
82 },
83 ApplicationTy, CallableDef, Substs, TraitRef, Ty, TypeCtor, TypeWalk,
80 }, 84 },
81}; 85};
82 86
diff --git a/crates/ra_hir/src/source_binder.rs b/crates/ra_hir/src/source_binder.rs
index 66cb4b357..c5fdf3bab 100644
--- a/crates/ra_hir/src/source_binder.rs
+++ b/crates/ra_hir/src/source_binder.rs
@@ -28,7 +28,7 @@ use crate::{
28 ids::LocationCtx, 28 ids::LocationCtx,
29 resolve::{ScopeDef, TypeNs, ValueNs}, 29 resolve::{ScopeDef, TypeNs, ValueNs},
30 ty::method_resolution::{self, implements_trait}, 30 ty::method_resolution::{self, implements_trait},
31 AssocItem, Const, DefWithBody, Either, Enum, FromSource, Function, HasBody, HirFileId, 31 AssocItem, Const, DefWithBody, Either, Enum, FromSource, Function, HasBody, HirFileId, Local,
32 MacroDef, Module, Name, Path, Resolver, Static, Struct, Ty, 32 MacroDef, Module, Name, Path, Resolver, Static, Struct, Ty,
33}; 33};
34 34
@@ -94,6 +94,7 @@ fn def_with_body_from_child_node(
94#[derive(Debug)] 94#[derive(Debug)]
95pub struct SourceAnalyzer { 95pub struct SourceAnalyzer {
96 resolver: Resolver, 96 resolver: Resolver,
97 body_owner: Option<DefWithBody>,
97 body_source_map: Option<Arc<BodySourceMap>>, 98 body_source_map: Option<Arc<BodySourceMap>>,
98 infer: Option<Arc<crate::ty::InferenceResult>>, 99 infer: Option<Arc<crate::ty::InferenceResult>>,
99 scopes: Option<Arc<crate::expr::ExprScopes>>, 100 scopes: Option<Arc<crate::expr::ExprScopes>>,
@@ -104,7 +105,7 @@ pub enum PathResolution {
104 /// An item 105 /// An item
105 Def(crate::ModuleDef), 106 Def(crate::ModuleDef),
106 /// A local binding (only value namespace) 107 /// A local binding (only value namespace)
107 LocalBinding(Either<AstPtr<ast::BindPat>, AstPtr<ast::SelfParam>>), 108 Local(Local),
108 /// A generic parameter 109 /// A generic parameter
109 GenericParam(u32), 110 GenericParam(u32),
110 SelfType(crate::ImplBlock), 111 SelfType(crate::ImplBlock),
@@ -152,6 +153,7 @@ impl SourceAnalyzer {
152 let resolver = expr::resolver_for_scope(def.body(db), db, scope); 153 let resolver = expr::resolver_for_scope(def.body(db), db, scope);
153 SourceAnalyzer { 154 SourceAnalyzer {
154 resolver, 155 resolver,
156 body_owner: Some(def),
155 body_source_map: Some(source_map), 157 body_source_map: Some(source_map),
156 infer: Some(def.infer(db)), 158 infer: Some(def.infer(db)),
157 scopes: Some(scopes), 159 scopes: Some(scopes),
@@ -162,6 +164,7 @@ impl SourceAnalyzer {
162 .ancestors() 164 .ancestors()
163 .find_map(|node| try_get_resolver_for_node(db, file_id, &node)) 165 .find_map(|node| try_get_resolver_for_node(db, file_id, &node))
164 .unwrap_or_default(), 166 .unwrap_or_default(),
167 body_owner: None,
165 body_source_map: None, 168 body_source_map: None,
166 infer: None, 169 infer: None,
167 scopes: None, 170 scopes: None,
@@ -233,16 +236,9 @@ impl SourceAnalyzer {
233 }); 236 });
234 let values = self.resolver.resolve_path_in_value_ns_fully(db, &path).and_then(|val| { 237 let values = self.resolver.resolve_path_in_value_ns_fully(db, &path).and_then(|val| {
235 let res = match val { 238 let res = match val {
236 ValueNs::LocalBinding(it) => { 239 ValueNs::LocalBinding(pat_id) => {
237 // We get a `PatId` from resolver, but it actually can only 240 let var = Local { parent: self.body_owner?, pat_id };
238 // point at `BindPat`, and not at the arbitrary pattern. 241 PathResolution::Local(var)
239 let pat_ptr = self
240 .body_source_map
241 .as_ref()?
242 .pat_syntax(it)?
243 .ast // FIXME: ignoring file_id here is definitelly wrong
244 .map_a(|ptr| ptr.cast::<ast::BindPat>().unwrap());
245 PathResolution::LocalBinding(pat_ptr)
246 } 242 }
247 ValueNs::Function(it) => PathResolution::Def(it.into()), 243 ValueNs::Function(it) => PathResolution::Def(it.into()),
248 ValueNs::Const(it) => PathResolution::Def(it.into()), 244 ValueNs::Const(it) => PathResolution::Def(it.into()),
diff --git a/crates/ra_hir/src/ty/tests.rs b/crates/ra_hir/src/ty/tests.rs
index e56b9356e..896bf2924 100644
--- a/crates/ra_hir/src/ty/tests.rs
+++ b/crates/ra_hir/src/ty/tests.rs
@@ -4810,3 +4810,22 @@ fn no_such_field_diagnostics() {
4810 "### 4810 "###
4811 ); 4811 );
4812} 4812}
4813
4814#[test]
4815fn infer_builtin_macros_line() {
4816 assert_snapshot!(
4817 infer(r#"
4818#[rustc_builtin_macro]
4819macro_rules! line {() => {}}
4820
4821fn main() {
4822 let x = line!();
4823}
4824"#),
4825 @r###"
4826 ![0; 1) '6': i32
4827 [64; 88) '{ ...!(); }': ()
4828 [74; 75) 'x': i32
4829 "###
4830 );
4831}
diff --git a/crates/ra_hir_def/src/builtin_type.rs b/crates/ra_hir_def/src/builtin_type.rs
index 12929caa9..2ec0c83fe 100644
--- a/crates/ra_hir_def/src/builtin_type.rs
+++ b/crates/ra_hir_def/src/builtin_type.rs
@@ -3,6 +3,8 @@
3//! A peculiarity of built-in types is that they are always available and are 3//! A peculiarity of built-in types is that they are always available and are
4//! not associated with any particular crate. 4//! not associated with any particular crate.
5 5
6use std::fmt;
7
6use hir_expand::name::{self, Name}; 8use hir_expand::name::{self, Name};
7 9
8#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] 10#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
@@ -61,3 +63,33 @@ impl BuiltinType {
61 (name::F64, BuiltinType::Float { bitness: FloatBitness::X64 }), 63 (name::F64, BuiltinType::Float { bitness: FloatBitness::X64 }),
62 ]; 64 ];
63} 65}
66
67impl fmt::Display for BuiltinType {
68 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
69 let type_name = match self {
70 BuiltinType::Char => "char",
71 BuiltinType::Bool => "bool",
72 BuiltinType::Str => "str",
73 BuiltinType::Int { signedness, bitness } => match (signedness, bitness) {
74 (Signedness::Signed, IntBitness::Xsize) => "isize",
75 (Signedness::Signed, IntBitness::X8) => "i8",
76 (Signedness::Signed, IntBitness::X16) => "i16",
77 (Signedness::Signed, IntBitness::X32) => "i32",
78 (Signedness::Signed, IntBitness::X64) => "i64",
79 (Signedness::Signed, IntBitness::X128) => "i128",
80
81 (Signedness::Unsigned, IntBitness::Xsize) => "usize",
82 (Signedness::Unsigned, IntBitness::X8) => "u8",
83 (Signedness::Unsigned, IntBitness::X16) => "u16",
84 (Signedness::Unsigned, IntBitness::X32) => "u32",
85 (Signedness::Unsigned, IntBitness::X64) => "u64",
86 (Signedness::Unsigned, IntBitness::X128) => "u128",
87 },
88 BuiltinType::Float { bitness } => match bitness {
89 FloatBitness::X32 => "f32",
90 FloatBitness::X64 => "f64",
91 },
92 };
93 f.write_str(type_name)
94 }
95}
diff --git a/crates/ra_hir_def/src/nameres/collector.rs b/crates/ra_hir_def/src/nameres/collector.rs
index 6db9937a4..37d0f3093 100644
--- a/crates/ra_hir_def/src/nameres/collector.rs
+++ b/crates/ra_hir_def/src/nameres/collector.rs
@@ -1,8 +1,9 @@
1//! FIXME: write short doc here 1//! FIXME: write short doc here
2 2
3use hir_expand::{ 3use hir_expand::{
4 builtin_macro::find_builtin_macro,
4 name::{self, AsName, Name}, 5 name::{self, AsName, Name},
5 HirFileId, MacroCallId, MacroCallLoc, MacroDefId, MacroFileKind, 6 HirFileId, MacroCallId, MacroCallLoc, MacroDefId, MacroDefKind, MacroFileKind,
6}; 7};
7use ra_cfg::CfgOptions; 8use ra_cfg::CfgOptions;
8use ra_db::{CrateId, FileId}; 9use ra_db::{CrateId, FileId};
@@ -692,10 +693,30 @@ where
692 fn collect_macro(&mut self, mac: &raw::MacroData) { 693 fn collect_macro(&mut self, mac: &raw::MacroData) {
693 let ast_id = AstId::new(self.file_id, mac.ast_id); 694 let ast_id = AstId::new(self.file_id, mac.ast_id);
694 695
696 // Case 0: builtin macros
697 if mac.builtin {
698 if let Some(name) = &mac.name {
699 let krate = self.def_collector.def_map.krate;
700 if let Some(macro_id) = find_builtin_macro(name, krate, ast_id) {
701 self.def_collector.define_macro(
702 self.module_id,
703 name.clone(),
704 macro_id,
705 mac.export,
706 );
707 return;
708 }
709 }
710 }
711
695 // Case 1: macro rules, define a macro in crate-global mutable scope 712 // Case 1: macro rules, define a macro in crate-global mutable scope
696 if is_macro_rules(&mac.path) { 713 if is_macro_rules(&mac.path) {
697 if let Some(name) = &mac.name { 714 if let Some(name) = &mac.name {
698 let macro_id = MacroDefId { ast_id, krate: self.def_collector.def_map.krate }; 715 let macro_id = MacroDefId {
716 ast_id,
717 krate: self.def_collector.def_map.krate,
718 kind: MacroDefKind::Declarative,
719 };
699 self.def_collector.define_macro(self.module_id, name.clone(), macro_id, mac.export); 720 self.def_collector.define_macro(self.module_id, name.clone(), macro_id, mac.export);
700 } 721 }
701 return; 722 return;
diff --git a/crates/ra_hir_def/src/nameres/raw.rs b/crates/ra_hir_def/src/nameres/raw.rs
index 369376f30..f52002bc0 100644
--- a/crates/ra_hir_def/src/nameres/raw.rs
+++ b/crates/ra_hir_def/src/nameres/raw.rs
@@ -200,6 +200,7 @@ pub(super) struct MacroData {
200 pub(super) path: Path, 200 pub(super) path: Path,
201 pub(super) name: Option<Name>, 201 pub(super) name: Option<Name>,
202 pub(super) export: bool, 202 pub(super) export: bool,
203 pub(super) builtin: bool,
203} 204}
204 205
205struct RawItemsCollector { 206struct RawItemsCollector {
@@ -367,7 +368,11 @@ impl RawItemsCollector {
367 // FIXME: cfg_attr 368 // FIXME: cfg_attr
368 let export = m.attrs().filter_map(|x| x.simple_name()).any(|name| name == "macro_export"); 369 let export = m.attrs().filter_map(|x| x.simple_name()).any(|name| name == "macro_export");
369 370
370 let m = self.raw_items.macros.alloc(MacroData { ast_id, path, name, export }); 371 // FIXME: cfg_attr
372 let builtin =
373 m.attrs().filter_map(|x| x.simple_name()).any(|name| name == "rustc_builtin_macro");
374
375 let m = self.raw_items.macros.alloc(MacroData { ast_id, path, name, export, builtin });
371 self.push_item(current_module, attrs, RawItemKind::Macro(m)); 376 self.push_item(current_module, attrs, RawItemKind::Macro(m));
372 } 377 }
373 378
diff --git a/crates/ra_hir_expand/src/builtin_macro.rs b/crates/ra_hir_expand/src/builtin_macro.rs
new file mode 100644
index 000000000..97fb0cb55
--- /dev/null
+++ b/crates/ra_hir_expand/src/builtin_macro.rs
@@ -0,0 +1,80 @@
1//! Builtin macro
2use crate::db::AstDatabase;
3use crate::{
4 ast::{self, AstNode},
5 name, AstId, CrateId, HirFileId, MacroCallId, MacroDefId, MacroDefKind, MacroFileKind,
6 TextUnit,
7};
8
9use crate::quote;
10
11#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
12pub enum BuiltinExpander {
13 Line,
14}
15
16impl BuiltinExpander {
17 pub fn expand(
18 &self,
19 db: &dyn AstDatabase,
20 id: MacroCallId,
21 tt: &tt::Subtree,
22 ) -> Result<tt::Subtree, mbe::ExpandError> {
23 match self {
24 BuiltinExpander::Line => line_expand(db, id, tt),
25 }
26 }
27}
28
29pub fn find_builtin_macro(
30 ident: &name::Name,
31 krate: CrateId,
32 ast_id: AstId<ast::MacroCall>,
33) -> Option<MacroDefId> {
34 // FIXME: Better registering method
35 if ident == &name::LINE_MACRO {
36 Some(MacroDefId { krate, ast_id, kind: MacroDefKind::BuiltIn(BuiltinExpander::Line) })
37 } else {
38 None
39 }
40}
41
42fn to_line_number(db: &dyn AstDatabase, file: HirFileId, pos: TextUnit) -> usize {
43 // FIXME: Use expansion info
44 let file_id = file.original_file(db);
45 let text = db.file_text(file_id);
46 let mut line_num = 1;
47
48 // Count line end
49 for (i, c) in text.chars().enumerate() {
50 if i == pos.to_usize() {
51 break;
52 }
53 if c == '\n' {
54 line_num += 1;
55 }
56 }
57
58 line_num
59}
60
61fn line_expand(
62 db: &dyn AstDatabase,
63 id: MacroCallId,
64 _tt: &tt::Subtree,
65) -> Result<tt::Subtree, mbe::ExpandError> {
66 let loc = db.lookup_intern_macro(id);
67 let macro_call = loc.ast_id.to_node(db);
68
69 let arg = macro_call.token_tree().ok_or_else(|| mbe::ExpandError::UnexpectedToken)?;
70 let arg_start = arg.syntax().text_range().start();
71
72 let file = id.as_file(MacroFileKind::Expr);
73 let line_num = to_line_number(db, file, arg_start);
74
75 let expanded = quote! {
76 #line_num
77 };
78
79 Ok(expanded)
80}
diff --git a/crates/ra_hir_expand/src/db.rs b/crates/ra_hir_expand/src/db.rs
index b4dafe1d8..5eadee9c2 100644
--- a/crates/ra_hir_expand/src/db.rs
+++ b/crates/ra_hir_expand/src/db.rs
@@ -9,10 +9,37 @@ use ra_prof::profile;
9use ra_syntax::{AstNode, Parse, SyntaxNode}; 9use ra_syntax::{AstNode, Parse, SyntaxNode};
10 10
11use crate::{ 11use crate::{
12 ast_id_map::AstIdMap, HirFileId, HirFileIdRepr, MacroCallId, MacroCallLoc, MacroDefId, 12 ast_id_map::AstIdMap, BuiltinExpander, HirFileId, HirFileIdRepr, MacroCallId, MacroCallLoc,
13 MacroFile, MacroFileKind, 13 MacroDefId, MacroDefKind, MacroFile, MacroFileKind,
14}; 14};
15 15
16#[derive(Debug, Clone, Eq, PartialEq)]
17pub enum TokenExpander {
18 MacroRules(mbe::MacroRules),
19 Builtin(BuiltinExpander),
20}
21
22impl TokenExpander {
23 pub fn expand(
24 &self,
25 db: &dyn AstDatabase,
26 id: MacroCallId,
27 tt: &tt::Subtree,
28 ) -> Result<tt::Subtree, mbe::ExpandError> {
29 match self {
30 TokenExpander::MacroRules(it) => it.expand(tt),
31 TokenExpander::Builtin(it) => it.expand(db, id, tt),
32 }
33 }
34
35 pub fn shift(&self) -> u32 {
36 match self {
37 TokenExpander::MacroRules(it) => it.shift(),
38 TokenExpander::Builtin(_) => 0,
39 }
40 }
41}
42
16// FIXME: rename to ExpandDatabase 43// FIXME: rename to ExpandDatabase
17#[salsa::query_group(AstDatabaseStorage)] 44#[salsa::query_group(AstDatabaseStorage)]
18pub trait AstDatabase: SourceDatabase { 45pub trait AstDatabase: SourceDatabase {
@@ -24,7 +51,7 @@ pub trait AstDatabase: SourceDatabase {
24 #[salsa::interned] 51 #[salsa::interned]
25 fn intern_macro(&self, macro_call: MacroCallLoc) -> MacroCallId; 52 fn intern_macro(&self, macro_call: MacroCallLoc) -> MacroCallId;
26 fn macro_arg(&self, id: MacroCallId) -> Option<Arc<(tt::Subtree, mbe::TokenMap)>>; 53 fn macro_arg(&self, id: MacroCallId) -> Option<Arc<(tt::Subtree, mbe::TokenMap)>>;
27 fn macro_def(&self, id: MacroDefId) -> Option<Arc<(mbe::MacroRules, mbe::TokenMap)>>; 54 fn macro_def(&self, id: MacroDefId) -> Option<Arc<(TokenExpander, mbe::TokenMap)>>;
28 fn parse_macro( 55 fn parse_macro(
29 &self, 56 &self,
30 macro_file: MacroFile, 57 macro_file: MacroFile,
@@ -41,18 +68,25 @@ pub(crate) fn ast_id_map(db: &dyn AstDatabase, file_id: HirFileId) -> Arc<AstIdM
41pub(crate) fn macro_def( 68pub(crate) fn macro_def(
42 db: &dyn AstDatabase, 69 db: &dyn AstDatabase,
43 id: MacroDefId, 70 id: MacroDefId,
44) -> Option<Arc<(mbe::MacroRules, mbe::TokenMap)>> { 71) -> Option<Arc<(TokenExpander, mbe::TokenMap)>> {
45 let macro_call = id.ast_id.to_node(db); 72 match id.kind {
46 let arg = macro_call.token_tree()?; 73 MacroDefKind::Declarative => {
47 let (tt, tmap) = mbe::ast_to_token_tree(&arg).or_else(|| { 74 let macro_call = id.ast_id.to_node(db);
48 log::warn!("fail on macro_def to token tree: {:#?}", arg); 75 let arg = macro_call.token_tree()?;
49 None 76 let (tt, tmap) = mbe::ast_to_token_tree(&arg).or_else(|| {
50 })?; 77 log::warn!("fail on macro_def to token tree: {:#?}", arg);
51 let rules = MacroRules::parse(&tt).ok().or_else(|| { 78 None
52 log::warn!("fail on macro_def parse: {:#?}", tt); 79 })?;
53 None 80 let rules = MacroRules::parse(&tt).ok().or_else(|| {
54 })?; 81 log::warn!("fail on macro_def parse: {:#?}", tt);
55 Some(Arc::new((rules, tmap))) 82 None
83 })?;
84 Some(Arc::new((TokenExpander::MacroRules(rules), tmap)))
85 }
86 MacroDefKind::BuiltIn(expander) => {
87 Some(Arc::new((TokenExpander::Builtin(expander.clone()), mbe::TokenMap::default())))
88 }
89 }
56} 90}
57 91
58pub(crate) fn macro_arg( 92pub(crate) fn macro_arg(
@@ -74,7 +108,7 @@ pub(crate) fn macro_expand(
74 let macro_arg = db.macro_arg(id).ok_or("Fail to args in to tt::TokenTree")?; 108 let macro_arg = db.macro_arg(id).ok_or("Fail to args in to tt::TokenTree")?;
75 109
76 let macro_rules = db.macro_def(loc.def).ok_or("Fail to find macro definition")?; 110 let macro_rules = db.macro_def(loc.def).ok_or("Fail to find macro definition")?;
77 let tt = macro_rules.0.expand(&macro_arg.0).map_err(|err| format!("{:?}", err))?; 111 let tt = macro_rules.0.expand(db, id, &macro_arg.0).map_err(|err| format!("{:?}", err))?;
78 // Set a hard limit for the expanded tt 112 // Set a hard limit for the expanded tt
79 let count = tt.count(); 113 let count = tt.count();
80 if count > 65536 { 114 if count > 65536 {
diff --git a/crates/ra_hir_expand/src/hygiene.rs b/crates/ra_hir_expand/src/hygiene.rs
index 77428ec99..379562a2c 100644
--- a/crates/ra_hir_expand/src/hygiene.rs
+++ b/crates/ra_hir_expand/src/hygiene.rs
@@ -9,7 +9,7 @@ use crate::{
9 db::AstDatabase, 9 db::AstDatabase,
10 either::Either, 10 either::Either,
11 name::{AsName, Name}, 11 name::{AsName, Name},
12 HirFileId, HirFileIdRepr, 12 HirFileId, HirFileIdRepr, MacroDefKind,
13}; 13};
14 14
15#[derive(Debug)] 15#[derive(Debug)]
@@ -24,7 +24,10 @@ impl Hygiene {
24 HirFileIdRepr::FileId(_) => None, 24 HirFileIdRepr::FileId(_) => None,
25 HirFileIdRepr::MacroFile(macro_file) => { 25 HirFileIdRepr::MacroFile(macro_file) => {
26 let loc = db.lookup_intern_macro(macro_file.macro_call_id); 26 let loc = db.lookup_intern_macro(macro_file.macro_call_id);
27 Some(loc.def.krate) 27 match loc.def.kind {
28 MacroDefKind::Declarative => Some(loc.def.krate),
29 MacroDefKind::BuiltIn(_) => None,
30 }
28 } 31 }
29 }; 32 };
30 Hygiene { def_crate } 33 Hygiene { def_crate }
diff --git a/crates/ra_hir_expand/src/lib.rs b/crates/ra_hir_expand/src/lib.rs
index 151d1d785..c6ffa2c6f 100644
--- a/crates/ra_hir_expand/src/lib.rs
+++ b/crates/ra_hir_expand/src/lib.rs
@@ -10,6 +10,8 @@ pub mod either;
10pub mod name; 10pub mod name;
11pub mod hygiene; 11pub mod hygiene;
12pub mod diagnostics; 12pub mod diagnostics;
13pub mod builtin_macro;
14pub mod quote;
13 15
14use std::hash::{Hash, Hasher}; 16use std::hash::{Hash, Hasher};
15use std::sync::Arc; 17use std::sync::Arc;
@@ -21,6 +23,7 @@ use ra_syntax::{
21}; 23};
22 24
23use crate::ast_id_map::FileAstId; 25use crate::ast_id_map::FileAstId;
26use crate::builtin_macro::BuiltinExpander;
24 27
25/// Input to the analyzer is a set of files, where each file is identified by 28/// Input to the analyzer is a set of files, where each file is identified by
26/// `FileId` and contains source code. However, another source of source code in 29/// `FileId` and contains source code. However, another source of source code in
@@ -122,6 +125,13 @@ impl salsa::InternKey for MacroCallId {
122pub struct MacroDefId { 125pub struct MacroDefId {
123 pub krate: CrateId, 126 pub krate: CrateId,
124 pub ast_id: AstId<ast::MacroCall>, 127 pub ast_id: AstId<ast::MacroCall>,
128 pub kind: MacroDefKind,
129}
130
131#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
132pub enum MacroDefKind {
133 Declarative,
134 BuiltIn(BuiltinExpander),
125} 135}
126 136
127#[derive(Debug, Clone, PartialEq, Eq, Hash)] 137#[derive(Debug, Clone, PartialEq, Eq, Hash)]
@@ -144,7 +154,7 @@ pub struct ExpansionInfo {
144 pub(crate) def_start: (HirFileId, TextUnit), 154 pub(crate) def_start: (HirFileId, TextUnit),
145 pub(crate) shift: u32, 155 pub(crate) shift: u32,
146 156
147 pub(crate) macro_def: Arc<(mbe::MacroRules, mbe::TokenMap)>, 157 pub(crate) macro_def: Arc<(db::TokenExpander, mbe::TokenMap)>,
148 pub(crate) macro_arg: Arc<(tt::Subtree, mbe::TokenMap)>, 158 pub(crate) macro_arg: Arc<(tt::Subtree, mbe::TokenMap)>,
149 pub(crate) exp_map: Arc<mbe::RevTokenMap>, 159 pub(crate) exp_map: Arc<mbe::RevTokenMap>,
150} 160}
diff --git a/crates/ra_hir_expand/src/name.rs b/crates/ra_hir_expand/src/name.rs
index 720896ee8..1bf17d12b 100644
--- a/crates/ra_hir_expand/src/name.rs
+++ b/crates/ra_hir_expand/src/name.rs
@@ -140,3 +140,6 @@ pub const RESULT_TYPE: Name = Name::new_inline_ascii(6, b"Result");
140pub const OUTPUT_TYPE: Name = Name::new_inline_ascii(6, b"Output"); 140pub const OUTPUT_TYPE: Name = Name::new_inline_ascii(6, b"Output");
141pub const TARGET_TYPE: Name = Name::new_inline_ascii(6, b"Target"); 141pub const TARGET_TYPE: Name = Name::new_inline_ascii(6, b"Target");
142pub const BOX_TYPE: Name = Name::new_inline_ascii(3, b"Box"); 142pub const BOX_TYPE: Name = Name::new_inline_ascii(3, b"Box");
143
144// Builtin Macros
145pub const LINE_MACRO: Name = Name::new_inline_ascii(4, b"line");
diff --git a/crates/ra_hir_expand/src/quote.rs b/crates/ra_hir_expand/src/quote.rs
new file mode 100644
index 000000000..9cd17f0e3
--- /dev/null
+++ b/crates/ra_hir_expand/src/quote.rs
@@ -0,0 +1,261 @@
1//! A simplified version of quote-crate like quasi quote macro
2
3// A helper macro quote macro
4// FIXME:
5// 1. Not all puncts are handled
6// 2. #()* pattern repetition not supported now
7// * But we can do it manually, see `test_quote_derive_copy_hack`
8#[doc(hidden)]
9#[macro_export]
10macro_rules! __quote {
11 () => {
12 Vec::<tt::TokenTree>::new()
13 };
14
15 ( @SUBTREE $delim:ident $($tt:tt)* ) => {
16 {
17 let children = $crate::__quote!($($tt)*);
18 let subtree = tt::Subtree {
19 delimiter: tt::Delimiter::$delim,
20 token_trees: $crate::quote::IntoTt::to_tokens(children),
21 };
22 subtree
23 }
24 };
25
26 ( @PUNCT $first:literal ) => {
27 {
28 vec![
29 tt::Leaf::Punct(tt::Punct {
30 char: $first,
31 spacing: tt::Spacing::Alone,
32 }).into()
33 ]
34 }
35 };
36
37 ( @PUNCT $first:literal, $sec:literal ) => {
38 {
39 vec![
40 tt::Leaf::Punct(tt::Punct {
41 char: $first,
42 spacing: tt::Spacing::Joint,
43 }).into(),
44 tt::Leaf::Punct(tt::Punct {
45 char: $sec,
46 spacing: tt::Spacing::Alone,
47 }).into()
48 ]
49 }
50 };
51
52 // hash variable
53 ( # $first:ident $($tail:tt)* ) => {
54 {
55 let token = $crate::quote::ToTokenTree::to_token($first);
56 let mut tokens = vec![token.into()];
57 let mut tail_tokens = $crate::quote::IntoTt::to_tokens($crate::__quote!($($tail)*));
58 tokens.append(&mut tail_tokens);
59 tokens
60 }
61 };
62
63 // Brace
64 ( { $($tt:tt)* } ) => { $crate::__quote!(@SUBTREE Brace $($tt)*) };
65 // Bracket
66 ( [ $($tt:tt)* ] ) => { $crate::__quote!(@SUBTREE Bracket $($tt)*) };
67 // Parenthesis
68 ( ( $($tt:tt)* ) ) => { $crate::__quote!(@SUBTREE Parenthesis $($tt)*) };
69
70 // Literal
71 ( $tt:literal ) => { vec![$crate::quote::ToTokenTree::to_token($tt).into()] };
72 // Ident
73 ( $tt:ident ) => {
74 vec![ {
75 tt::Leaf::Ident(tt::Ident {
76 text: stringify!($tt).into(),
77 id: tt::TokenId::unspecified(),
78 }).into()
79 }]
80 };
81
82 // Puncts
83 // FIXME: Not all puncts are handled
84 ( -> ) => {$crate::__quote!(@PUNCT '-', '>')};
85 ( & ) => {$crate::__quote!(@PUNCT '&')};
86 ( , ) => {$crate::__quote!(@PUNCT ',')};
87 ( : ) => {$crate::__quote!(@PUNCT ':')};
88 ( . ) => {$crate::__quote!(@PUNCT '.')};
89
90 ( $first:tt $($tail:tt)+ ) => {
91 {
92 let mut tokens = $crate::quote::IntoTt::to_tokens($crate::__quote!($first));
93 let mut tail_tokens = $crate::quote::IntoTt::to_tokens($crate::__quote!($($tail)*));
94
95 tokens.append(&mut tail_tokens);
96 tokens
97 }
98 };
99}
100
101/// FIXME:
102/// It probably should implement in proc-macro
103#[macro_export]
104macro_rules! quote {
105 ( $($tt:tt)* ) => {
106 $crate::quote::IntoTt::to_subtree($crate::__quote!($($tt)*))
107 }
108}
109
110pub(crate) trait IntoTt {
111 fn to_subtree(self) -> tt::Subtree;
112 fn to_tokens(self) -> Vec<tt::TokenTree>;
113}
114
115impl IntoTt for Vec<tt::TokenTree> {
116 fn to_subtree(self) -> tt::Subtree {
117 tt::Subtree { delimiter: tt::Delimiter::None, token_trees: self }
118 }
119
120 fn to_tokens(self) -> Vec<tt::TokenTree> {
121 self
122 }
123}
124
125impl IntoTt for tt::Subtree {
126 fn to_subtree(self) -> tt::Subtree {
127 self
128 }
129
130 fn to_tokens(self) -> Vec<tt::TokenTree> {
131 vec![tt::TokenTree::Subtree(self)]
132 }
133}
134
135pub(crate) trait ToTokenTree {
136 fn to_token(self) -> tt::TokenTree;
137}
138
139impl ToTokenTree for tt::TokenTree {
140 fn to_token(self) -> tt::TokenTree {
141 self
142 }
143}
144
145impl ToTokenTree for tt::Subtree {
146 fn to_token(self) -> tt::TokenTree {
147 self.into()
148 }
149}
150
151macro_rules! impl_to_to_tokentrees {
152 ($($ty:ty => $this:ident $im:block);*) => {
153 $(
154 impl ToTokenTree for $ty {
155 fn to_token($this) -> tt::TokenTree {
156 let leaf: tt::Leaf = $im.into();
157 leaf.into()
158 }
159 }
160
161 impl ToTokenTree for &$ty {
162 fn to_token($this) -> tt::TokenTree {
163 let leaf: tt::Leaf = $im.clone().into();
164 leaf.into()
165 }
166 }
167 )*
168 }
169}
170
171impl_to_to_tokentrees! {
172 u32 => self { tt::Literal{text: self.to_string().into()} };
173 usize => self { tt::Literal{text: self.to_string().into()}};
174 i32 => self { tt::Literal{text: self.to_string().into()}};
175 &str => self { tt::Literal{text: self.to_string().into()}};
176 String => self { tt::Literal{text: self.into()}};
177 tt::Leaf => self { self };
178 tt::Literal => self { self };
179 tt::Ident => self { self };
180 tt::Punct => self { self }
181}
182
183#[cfg(test)]
184mod tests {
185 #[test]
186 fn test_quote_delimiters() {
187 assert_eq!(quote!({}).to_string(), "{}");
188 assert_eq!(quote!(()).to_string(), "()");
189 assert_eq!(quote!([]).to_string(), "[]");
190 }
191
192 #[test]
193 fn test_quote_idents() {
194 assert_eq!(quote!(32).to_string(), "32");
195 assert_eq!(quote!(struct).to_string(), "struct");
196 }
197
198 #[test]
199 fn test_quote_hash_simple_literal() {
200 let a = 20;
201 assert_eq!(quote!(#a).to_string(), "20");
202 let s: String = "hello".into();
203 assert_eq!(quote!(#s).to_string(), "hello");
204 }
205
206 fn mk_ident(name: &str) -> tt::Ident {
207 tt::Ident { text: name.into(), id: tt::TokenId::unspecified() }
208 }
209
210 #[test]
211 fn test_quote_hash_token_tree() {
212 let a = mk_ident("hello");
213
214 let quoted = quote!(#a);
215 assert_eq!(quoted.to_string(), "hello");
216 let t = format!("{:?}", quoted);
217 assert_eq!(t, "Subtree { delimiter: None, token_trees: [Leaf(Ident(Ident { text: \"hello\", id: TokenId(4294967295) }))] }");
218 }
219
220 #[test]
221 fn test_quote_simple_derive_copy() {
222 let name = mk_ident("Foo");
223
224 let quoted = quote! {
225 impl Clone for #name {
226 fn clone(&self) -> Self {
227 Self {}
228 }
229 }
230 };
231
232 assert_eq!(quoted.to_string(), "impl Clone for Foo {fn clone (& self) -> Self {Self {}}}");
233 }
234
235 #[test]
236 fn test_quote_derive_copy_hack() {
237 // Assume the given struct is:
238 // struct Foo {
239 // name: String,
240 // id: u32,
241 // }
242 let struct_name = mk_ident("Foo");
243 let fields = [mk_ident("name"), mk_ident("id")];
244 let fields = fields
245 .into_iter()
246 .map(|it| quote!(#it: self.#it.clone(), ).token_trees.clone())
247 .flatten();
248
249 let list = tt::Subtree { delimiter: tt::Delimiter::Brace, token_trees: fields.collect() };
250
251 let quoted = quote! {
252 impl Clone for #struct_name {
253 fn clone(&self) -> Self {
254 Self #list
255 }
256 }
257 };
258
259 assert_eq!(quoted.to_string(), "impl Clone for Foo {fn clone (& self) -> Self {Self {name : self . name . clone () , id : self . id . clone () ,}}}");
260 }
261}
diff --git a/crates/ra_ide_api/src/display.rs b/crates/ra_ide_api/src/display.rs
index a980c56bc..30617412a 100644
--- a/crates/ra_ide_api/src/display.rs
+++ b/crates/ra_ide_api/src/display.rs
@@ -15,7 +15,7 @@ pub use function_signature::FunctionSignature;
15pub use navigation_target::NavigationTarget; 15pub use navigation_target::NavigationTarget;
16pub use structure::{file_structure, StructureNode}; 16pub use structure::{file_structure, StructureNode};
17 17
18pub(crate) use navigation_target::{description_from_symbol, docs_from_symbol}; 18pub(crate) use navigation_target::{description_from_symbol, docs_from_symbol, ToNav};
19pub(crate) use short_label::ShortLabel; 19pub(crate) use short_label::ShortLabel;
20 20
21pub(crate) fn function_label(node: &ast::FnDef) -> String { 21pub(crate) fn function_label(node: &ast::FnDef) -> String {
diff --git a/crates/ra_ide_api/src/display/navigation_target.rs b/crates/ra_ide_api/src/display/navigation_target.rs
index 1bf81e7d5..f7ad08515 100644
--- a/crates/ra_ide_api/src/display/navigation_target.rs
+++ b/crates/ra_ide_api/src/display/navigation_target.rs
@@ -1,11 +1,11 @@
1//! FIXME: write short doc here 1//! FIXME: write short doc here
2 2
3use hir::{AssocItem, FieldSource, HasSource, ModuleSource}; 3use hir::{AssocItem, Either, FieldSource, HasSource, ModuleSource};
4use ra_db::{FileId, SourceDatabase}; 4use ra_db::{FileId, SourceDatabase};
5use ra_syntax::{ 5use ra_syntax::{
6 ast::{self, DocCommentsOwner}, 6 ast::{self, DocCommentsOwner, NameOwner},
7 match_ast, AstNode, AstPtr, SmolStr, 7 match_ast, AstNode, SmolStr,
8 SyntaxKind::{self, NAME}, 8 SyntaxKind::{self, BIND_PAT},
9 SyntaxNode, TextRange, 9 SyntaxNode, TextRange,
10}; 10};
11 11
@@ -29,19 +29,8 @@ pub struct NavigationTarget {
29 docs: Option<String>, 29 docs: Option<String>,
30} 30}
31 31
32fn find_range_from_node( 32pub(crate) trait ToNav {
33 db: &RootDatabase, 33 fn to_nav(&self, db: &RootDatabase) -> NavigationTarget;
34 src: hir::HirFileId,
35 node: &SyntaxNode,
36) -> (FileId, TextRange) {
37 let text_range = node.text_range();
38 let (file_id, text_range) = src
39 .expansion_info(db)
40 .and_then(|expansion_info| expansion_info.find_range(text_range))
41 .unwrap_or((src, text_range));
42
43 // FIXME: handle recursive macro generated macro
44 (file_id.original_file(db), text_range)
45} 34}
46 35
47impl NavigationTarget { 36impl NavigationTarget {
@@ -87,88 +76,6 @@ impl NavigationTarget {
87 self.focus_range 76 self.focus_range
88 } 77 }
89 78
90 pub(crate) fn from_bind_pat(
91 db: &RootDatabase,
92 file_id: FileId,
93 pat: &ast::BindPat,
94 ) -> NavigationTarget {
95 NavigationTarget::from_named(db, file_id.into(), pat, None, None)
96 }
97
98 pub(crate) fn from_symbol(db: &RootDatabase, symbol: FileSymbol) -> NavigationTarget {
99 NavigationTarget {
100 file_id: symbol.file_id,
101 name: symbol.name.clone(),
102 kind: symbol.ptr.kind(),
103 full_range: symbol.ptr.range(),
104 focus_range: symbol.name_range,
105 container_name: symbol.container_name.clone(),
106 description: description_from_symbol(db, &symbol),
107 docs: docs_from_symbol(db, &symbol),
108 }
109 }
110
111 pub(crate) fn from_pat(
112 db: &RootDatabase,
113 file_id: FileId,
114 pat: AstPtr<ast::BindPat>,
115 ) -> NavigationTarget {
116 let parse = db.parse(file_id);
117 let pat = pat.to_node(parse.tree().syntax());
118 NavigationTarget::from_bind_pat(db, file_id, &pat)
119 }
120
121 pub(crate) fn from_self_param(
122 file_id: FileId,
123 par: AstPtr<ast::SelfParam>,
124 ) -> NavigationTarget {
125 let (name, full_range) = ("self".into(), par.syntax_node_ptr().range());
126
127 NavigationTarget {
128 file_id,
129 name,
130 full_range,
131 focus_range: None,
132 kind: NAME,
133 container_name: None,
134 description: None, //< No document node for SelfParam
135 docs: None, //< No document node for SelfParam
136 }
137 }
138
139 pub(crate) fn from_module(db: &RootDatabase, module: hir::Module) -> NavigationTarget {
140 let src = module.definition_source(db);
141 let name = module.name(db).map(|it| it.to_string().into()).unwrap_or_default();
142 match src.ast {
143 ModuleSource::SourceFile(node) => {
144 let (file_id, text_range) = find_range_from_node(db, src.file_id, node.syntax());
145
146 NavigationTarget::from_syntax(
147 file_id,
148 name,
149 None,
150 text_range,
151 node.syntax(),
152 None,
153 None,
154 )
155 }
156 ModuleSource::Module(node) => {
157 let (file_id, text_range) = find_range_from_node(db, src.file_id, node.syntax());
158
159 NavigationTarget::from_syntax(
160 file_id,
161 name,
162 None,
163 text_range,
164 node.syntax(),
165 node.doc_comment_text(),
166 node.short_label(),
167 )
168 }
169 }
170 }
171
172 pub(crate) fn from_module_to_decl(db: &RootDatabase, module: hir::Module) -> NavigationTarget { 79 pub(crate) fn from_module_to_decl(db: &RootDatabase, module: hir::Module) -> NavigationTarget {
173 let name = module.name(db).map(|it| it.to_string().into()).unwrap_or_default(); 80 let name = module.name(db).map(|it| it.to_string().into()).unwrap_or_default();
174 if let Some(src) = module.declaration_source(db) { 81 if let Some(src) = module.declaration_source(db) {
@@ -183,55 +90,7 @@ impl NavigationTarget {
183 src.ast.short_label(), 90 src.ast.short_label(),
184 ); 91 );
185 } 92 }
186 NavigationTarget::from_module(db, module) 93 module.to_nav(db)
187 }
188
189 pub(crate) fn from_field(db: &RootDatabase, field: hir::StructField) -> NavigationTarget {
190 let src = field.source(db);
191 match src.ast {
192 FieldSource::Named(it) => NavigationTarget::from_named(
193 db,
194 src.file_id,
195 &it,
196 it.doc_comment_text(),
197 it.short_label(),
198 ),
199 FieldSource::Pos(it) => {
200 let (file_id, text_range) = find_range_from_node(db, src.file_id, it.syntax());
201 NavigationTarget::from_syntax(
202 file_id,
203 "".into(),
204 None,
205 text_range,
206 it.syntax(),
207 None,
208 None,
209 )
210 }
211 }
212 }
213
214 pub(crate) fn from_def_source<A, D>(db: &RootDatabase, def: D) -> NavigationTarget
215 where
216 D: HasSource<Ast = A>,
217 A: ast::DocCommentsOwner + ast::NameOwner + ShortLabel,
218 {
219 let src = def.source(db);
220 NavigationTarget::from_named(
221 db,
222 src.file_id,
223 &src.ast,
224 src.ast.doc_comment_text(),
225 src.ast.short_label(),
226 )
227 }
228
229 pub(crate) fn from_adt_def(db: &RootDatabase, adt_def: hir::Adt) -> NavigationTarget {
230 match adt_def {
231 hir::Adt::Struct(it) => NavigationTarget::from_def_source(db, it),
232 hir::Adt::Union(it) => NavigationTarget::from_def_source(db, it),
233 hir::Adt::Enum(it) => NavigationTarget::from_def_source(db, it),
234 }
235 } 94 }
236 95
237 pub(crate) fn from_def( 96 pub(crate) fn from_def(
@@ -239,14 +98,14 @@ impl NavigationTarget {
239 module_def: hir::ModuleDef, 98 module_def: hir::ModuleDef,
240 ) -> Option<NavigationTarget> { 99 ) -> Option<NavigationTarget> {
241 let nav = match module_def { 100 let nav = match module_def {
242 hir::ModuleDef::Module(module) => NavigationTarget::from_module(db, module), 101 hir::ModuleDef::Module(module) => module.to_nav(db),
243 hir::ModuleDef::Function(func) => NavigationTarget::from_def_source(db, func), 102 hir::ModuleDef::Function(it) => it.to_nav(db),
244 hir::ModuleDef::Adt(it) => NavigationTarget::from_adt_def(db, it), 103 hir::ModuleDef::Adt(it) => it.to_nav(db),
245 hir::ModuleDef::Const(it) => NavigationTarget::from_def_source(db, it), 104 hir::ModuleDef::Const(it) => it.to_nav(db),
246 hir::ModuleDef::Static(it) => NavigationTarget::from_def_source(db, it), 105 hir::ModuleDef::Static(it) => it.to_nav(db),
247 hir::ModuleDef::EnumVariant(it) => NavigationTarget::from_def_source(db, it), 106 hir::ModuleDef::EnumVariant(it) => it.to_nav(db),
248 hir::ModuleDef::Trait(it) => NavigationTarget::from_def_source(db, it), 107 hir::ModuleDef::Trait(it) => it.to_nav(db),
249 hir::ModuleDef::TypeAlias(it) => NavigationTarget::from_def_source(db, it), 108 hir::ModuleDef::TypeAlias(it) => it.to_nav(db),
250 hir::ModuleDef::BuiltinType(..) => { 109 hir::ModuleDef::BuiltinType(..) => {
251 return None; 110 return None;
252 } 111 }
@@ -254,41 +113,6 @@ impl NavigationTarget {
254 Some(nav) 113 Some(nav)
255 } 114 }
256 115
257 pub(crate) fn from_impl_block(
258 db: &RootDatabase,
259 impl_block: hir::ImplBlock,
260 ) -> NavigationTarget {
261 let src = impl_block.source(db);
262 let (file_id, text_range) = find_range_from_node(db, src.file_id, src.ast.syntax());
263
264 NavigationTarget::from_syntax(
265 file_id,
266 "impl".into(),
267 None,
268 text_range,
269 src.ast.syntax(),
270 None,
271 None,
272 )
273 }
274
275 pub(crate) fn from_assoc_item(
276 db: &RootDatabase,
277 assoc_item: hir::AssocItem,
278 ) -> NavigationTarget {
279 match assoc_item {
280 AssocItem::Function(it) => NavigationTarget::from_def_source(db, it),
281 AssocItem::Const(it) => NavigationTarget::from_def_source(db, it),
282 AssocItem::TypeAlias(it) => NavigationTarget::from_def_source(db, it),
283 }
284 }
285
286 pub(crate) fn from_macro_def(db: &RootDatabase, macro_call: hir::MacroDef) -> NavigationTarget {
287 let src = macro_call.source(db);
288 log::debug!("nav target {:#?}", src.ast.syntax());
289 NavigationTarget::from_named(db, src.file_id, &src.ast, src.ast.doc_comment_text(), None)
290 }
291
292 #[cfg(test)] 116 #[cfg(test)]
293 pub(crate) fn assert_match(&self, expected: &str) { 117 pub(crate) fn assert_match(&self, expected: &str) {
294 let actual = self.debug_render(); 118 let actual = self.debug_render();
@@ -359,6 +183,198 @@ impl NavigationTarget {
359 } 183 }
360} 184}
361 185
186impl ToNav for FileSymbol {
187 fn to_nav(&self, db: &RootDatabase) -> NavigationTarget {
188 NavigationTarget {
189 file_id: self.file_id,
190 name: self.name.clone(),
191 kind: self.ptr.kind(),
192 full_range: self.ptr.range(),
193 focus_range: self.name_range,
194 container_name: self.container_name.clone(),
195 description: description_from_symbol(db, self),
196 docs: docs_from_symbol(db, self),
197 }
198 }
199}
200
201pub(crate) trait ToNavFromAst {}
202impl ToNavFromAst for hir::Function {}
203impl ToNavFromAst for hir::Const {}
204impl ToNavFromAst for hir::Static {}
205impl ToNavFromAst for hir::Struct {}
206impl ToNavFromAst for hir::Enum {}
207impl ToNavFromAst for hir::EnumVariant {}
208impl ToNavFromAst for hir::Union {}
209impl ToNavFromAst for hir::TypeAlias {}
210impl ToNavFromAst for hir::Trait {}
211
212impl<D> ToNav for D
213where
214 D: HasSource + ToNavFromAst + Copy,
215 D::Ast: ast::DocCommentsOwner + ast::NameOwner + ShortLabel,
216{
217 fn to_nav(&self, db: &RootDatabase) -> NavigationTarget {
218 let src = self.source(db);
219 NavigationTarget::from_named(
220 db,
221 src.file_id,
222 &src.ast,
223 src.ast.doc_comment_text(),
224 src.ast.short_label(),
225 )
226 }
227}
228
229impl ToNav for hir::Module {
230 fn to_nav(&self, db: &RootDatabase) -> NavigationTarget {
231 let src = self.definition_source(db);
232 let name = self.name(db).map(|it| it.to_string().into()).unwrap_or_default();
233 match src.ast {
234 ModuleSource::SourceFile(node) => {
235 let (file_id, text_range) = find_range_from_node(db, src.file_id, node.syntax());
236
237 NavigationTarget::from_syntax(
238 file_id,
239 name,
240 None,
241 text_range,
242 node.syntax(),
243 None,
244 None,
245 )
246 }
247 ModuleSource::Module(node) => {
248 let (file_id, text_range) = find_range_from_node(db, src.file_id, node.syntax());
249
250 NavigationTarget::from_syntax(
251 file_id,
252 name,
253 None,
254 text_range,
255 node.syntax(),
256 node.doc_comment_text(),
257 node.short_label(),
258 )
259 }
260 }
261 }
262}
263
264impl ToNav for hir::ImplBlock {
265 fn to_nav(&self, db: &RootDatabase) -> NavigationTarget {
266 let src = self.source(db);
267 let (file_id, text_range) = find_range_from_node(db, src.file_id, src.ast.syntax());
268
269 NavigationTarget::from_syntax(
270 file_id,
271 "impl".into(),
272 None,
273 text_range,
274 src.ast.syntax(),
275 None,
276 None,
277 )
278 }
279}
280
281impl ToNav for hir::StructField {
282 fn to_nav(&self, db: &RootDatabase) -> NavigationTarget {
283 let src = self.source(db);
284
285 match src.ast {
286 FieldSource::Named(it) => NavigationTarget::from_named(
287 db,
288 src.file_id,
289 &it,
290 it.doc_comment_text(),
291 it.short_label(),
292 ),
293 FieldSource::Pos(it) => {
294 let (file_id, text_range) = find_range_from_node(db, src.file_id, it.syntax());
295 NavigationTarget::from_syntax(
296 file_id,
297 "".into(),
298 None,
299 text_range,
300 it.syntax(),
301 None,
302 None,
303 )
304 }
305 }
306 }
307}
308
309impl ToNav for hir::MacroDef {
310 fn to_nav(&self, db: &RootDatabase) -> NavigationTarget {
311 let src = self.source(db);
312 log::debug!("nav target {:#?}", src.ast.syntax());
313 NavigationTarget::from_named(db, src.file_id, &src.ast, src.ast.doc_comment_text(), None)
314 }
315}
316
317impl ToNav for hir::Adt {
318 fn to_nav(&self, db: &RootDatabase) -> NavigationTarget {
319 match self {
320 hir::Adt::Struct(it) => it.to_nav(db),
321 hir::Adt::Union(it) => it.to_nav(db),
322 hir::Adt::Enum(it) => it.to_nav(db),
323 }
324 }
325}
326
327impl ToNav for hir::AssocItem {
328 fn to_nav(&self, db: &RootDatabase) -> NavigationTarget {
329 match self {
330 AssocItem::Function(it) => it.to_nav(db),
331 AssocItem::Const(it) => it.to_nav(db),
332 AssocItem::TypeAlias(it) => it.to_nav(db),
333 }
334 }
335}
336
337impl ToNav for hir::Local {
338 fn to_nav(&self, db: &RootDatabase) -> NavigationTarget {
339 let src = self.source(db);
340 let (full_range, focus_range) = match src.ast {
341 Either::A(it) => {
342 (it.syntax().text_range(), it.name().map(|it| it.syntax().text_range()))
343 }
344 Either::B(it) => (it.syntax().text_range(), Some(it.self_kw_token().text_range())),
345 };
346 let name = match self.name(db) {
347 Some(it) => it.to_string().into(),
348 None => "".into(),
349 };
350 NavigationTarget {
351 file_id: src.file_id.original_file(db),
352 name,
353 kind: BIND_PAT,
354 full_range,
355 focus_range,
356 container_name: None,
357 description: None,
358 docs: None,
359 }
360 }
361}
362
363fn find_range_from_node(
364 db: &RootDatabase,
365 src: hir::HirFileId,
366 node: &SyntaxNode,
367) -> (FileId, TextRange) {
368 let text_range = node.text_range();
369 let (file_id, text_range) = src
370 .expansion_info(db)
371 .and_then(|expansion_info| expansion_info.find_range(text_range))
372 .unwrap_or((src, text_range));
373
374 // FIXME: handle recursive macro generated macro
375 (file_id.original_file(db), text_range)
376}
377
362pub(crate) fn docs_from_symbol(db: &RootDatabase, symbol: &FileSymbol) -> Option<String> { 378pub(crate) fn docs_from_symbol(db: &RootDatabase, symbol: &FileSymbol) -> Option<String> {
363 let parse = db.parse(symbol.file_id); 379 let parse = db.parse(symbol.file_id);
364 let node = symbol.ptr.to_node(parse.tree().syntax()); 380 let node = symbol.ptr.to_node(parse.tree().syntax());
diff --git a/crates/ra_ide_api/src/goto_definition.rs b/crates/ra_ide_api/src/goto_definition.rs
index afa59cbe3..6c8387f6c 100644
--- a/crates/ra_ide_api/src/goto_definition.rs
+++ b/crates/ra_ide_api/src/goto_definition.rs
@@ -9,7 +9,7 @@ use ra_syntax::{
9 9
10use crate::{ 10use crate::{
11 db::RootDatabase, 11 db::RootDatabase,
12 display::ShortLabel, 12 display::{ShortLabel, ToNav},
13 references::{classify_name_ref, NameKind::*}, 13 references::{classify_name_ref, NameKind::*},
14 FilePosition, NavigationTarget, RangeInfo, 14 FilePosition, NavigationTarget, RangeInfo,
15}; 15};
@@ -56,20 +56,19 @@ pub(crate) fn reference_definition(
56 56
57 let name_kind = classify_name_ref(db, file_id, &name_ref).map(|d| d.kind); 57 let name_kind = classify_name_ref(db, file_id, &name_ref).map(|d| d.kind);
58 match name_kind { 58 match name_kind {
59 Some(Macro(mac)) => return Exact(NavigationTarget::from_macro_def(db, mac)), 59 Some(Macro(mac)) => return Exact(mac.to_nav(db)),
60 Some(Field(field)) => return Exact(NavigationTarget::from_field(db, field)), 60 Some(Field(field)) => return Exact(field.to_nav(db)),
61 Some(AssocItem(assoc)) => return Exact(NavigationTarget::from_assoc_item(db, assoc)), 61 Some(AssocItem(assoc)) => return Exact(assoc.to_nav(db)),
62 Some(Def(def)) => match NavigationTarget::from_def(db, def) { 62 Some(Def(def)) => match NavigationTarget::from_def(db, def) {
63 Some(nav) => return Exact(nav), 63 Some(nav) => return Exact(nav),
64 None => return Approximate(vec![]), 64 None => return Approximate(vec![]),
65 }, 65 },
66 Some(SelfType(ty)) => { 66 Some(SelfType(ty)) => {
67 if let Some((def_id, _)) = ty.as_adt() { 67 if let Some((adt, _)) = ty.as_adt() {
68 return Exact(NavigationTarget::from_adt_def(db, def_id)); 68 return Exact(adt.to_nav(db));
69 } 69 }
70 } 70 }
71 Some(Pat((_, pat))) => return Exact(NavigationTarget::from_pat(db, file_id, pat)), 71 Some(Local(local)) => return Exact(local.to_nav(db)),
72 Some(SelfParam(par)) => return Exact(NavigationTarget::from_self_param(file_id, par)),
73 Some(GenericParam(_)) => { 72 Some(GenericParam(_)) => {
74 // FIXME: go to the generic param def 73 // FIXME: go to the generic param def
75 } 74 }
@@ -79,7 +78,7 @@ pub(crate) fn reference_definition(
79 // Fallback index based approach: 78 // Fallback index based approach:
80 let navs = crate::symbol_index::index_resolve(db, name_ref) 79 let navs = crate::symbol_index::index_resolve(db, name_ref)
81 .into_iter() 80 .into_iter()
82 .map(|s| NavigationTarget::from_symbol(db, s)) 81 .map(|s| s.to_nav(db))
83 .collect(); 82 .collect();
84 Approximate(navs) 83 Approximate(navs)
85} 84}
@@ -95,7 +94,7 @@ pub(crate) fn name_definition(
95 if module.has_semi() { 94 if module.has_semi() {
96 let src = hir::Source { file_id: file_id.into(), ast: module }; 95 let src = hir::Source { file_id: file_id.into(), ast: module };
97 if let Some(child_module) = hir::Module::from_declaration(db, src) { 96 if let Some(child_module) = hir::Module::from_declaration(db, src) {
98 let nav = NavigationTarget::from_module(db, child_module); 97 let nav = child_module.to_nav(db);
99 return Some(vec![nav]); 98 return Some(vec![nav]);
100 } 99 }
101 } 100 }
diff --git a/crates/ra_ide_api/src/goto_type_definition.rs b/crates/ra_ide_api/src/goto_type_definition.rs
index 059d80524..71146591d 100644
--- a/crates/ra_ide_api/src/goto_type_definition.rs
+++ b/crates/ra_ide_api/src/goto_type_definition.rs
@@ -3,7 +3,7 @@
3use ra_db::SourceDatabase; 3use ra_db::SourceDatabase;
4use ra_syntax::{ast, AstNode}; 4use ra_syntax::{ast, AstNode};
5 5
6use crate::{db::RootDatabase, FilePosition, NavigationTarget, RangeInfo}; 6use crate::{db::RootDatabase, display::ToNav, FilePosition, NavigationTarget, RangeInfo};
7 7
8pub(crate) fn goto_type_definition( 8pub(crate) fn goto_type_definition(
9 db: &RootDatabase, 9 db: &RootDatabase,
@@ -33,7 +33,7 @@ pub(crate) fn goto_type_definition(
33 33
34 let adt_def = analyzer.autoderef(db, ty).find_map(|ty| ty.as_adt().map(|adt| adt.0))?; 34 let adt_def = analyzer.autoderef(db, ty).find_map(|ty| ty.as_adt().map(|adt| adt.0))?;
35 35
36 let nav = NavigationTarget::from_adt_def(db, adt_def); 36 let nav = adt_def.to_nav(db);
37 Some(RangeInfo::new(node.text_range(), vec![nav])) 37 Some(RangeInfo::new(node.text_range(), vec![nav]))
38} 38}
39 39
diff --git a/crates/ra_ide_api/src/hover.rs b/crates/ra_ide_api/src/hover.rs
index ba328efa1..086e6dec3 100644
--- a/crates/ra_ide_api/src/hover.rs
+++ b/crates/ra_ide_api/src/hover.rs
@@ -117,27 +117,23 @@ pub(crate) fn hover(db: &RootDatabase, position: FilePosition) -> Option<RangeIn
117 hir::AssocItem::Const(it) => from_def_source(db, it), 117 hir::AssocItem::Const(it) => from_def_source(db, it),
118 hir::AssocItem::TypeAlias(it) => from_def_source(db, it), 118 hir::AssocItem::TypeAlias(it) => from_def_source(db, it),
119 }), 119 }),
120 Some(Def(it)) => { 120 Some(Def(it)) => match it {
121 match it { 121 hir::ModuleDef::Module(it) => {
122 hir::ModuleDef::Module(it) => { 122 if let hir::ModuleSource::Module(it) = it.definition_source(db).ast {
123 if let hir::ModuleSource::Module(it) = it.definition_source(db).ast { 123 res.extend(hover_text(it.doc_comment_text(), it.short_label()))
124 res.extend(hover_text(it.doc_comment_text(), it.short_label()))
125 }
126 }
127 hir::ModuleDef::Function(it) => res.extend(from_def_source(db, it)),
128 hir::ModuleDef::Adt(Adt::Struct(it)) => res.extend(from_def_source(db, it)),
129 hir::ModuleDef::Adt(Adt::Union(it)) => res.extend(from_def_source(db, it)),
130 hir::ModuleDef::Adt(Adt::Enum(it)) => res.extend(from_def_source(db, it)),
131 hir::ModuleDef::EnumVariant(it) => res.extend(from_def_source(db, it)),
132 hir::ModuleDef::Const(it) => res.extend(from_def_source(db, it)),
133 hir::ModuleDef::Static(it) => res.extend(from_def_source(db, it)),
134 hir::ModuleDef::Trait(it) => res.extend(from_def_source(db, it)),
135 hir::ModuleDef::TypeAlias(it) => res.extend(from_def_source(db, it)),
136 hir::ModuleDef::BuiltinType(_) => {
137 // FIXME: hover for builtin Type ?
138 } 124 }
139 } 125 }
140 } 126 hir::ModuleDef::Function(it) => res.extend(from_def_source(db, it)),
127 hir::ModuleDef::Adt(Adt::Struct(it)) => res.extend(from_def_source(db, it)),
128 hir::ModuleDef::Adt(Adt::Union(it)) => res.extend(from_def_source(db, it)),
129 hir::ModuleDef::Adt(Adt::Enum(it)) => res.extend(from_def_source(db, it)),
130 hir::ModuleDef::EnumVariant(it) => res.extend(from_def_source(db, it)),
131 hir::ModuleDef::Const(it) => res.extend(from_def_source(db, it)),
132 hir::ModuleDef::Static(it) => res.extend(from_def_source(db, it)),
133 hir::ModuleDef::Trait(it) => res.extend(from_def_source(db, it)),
134 hir::ModuleDef::TypeAlias(it) => res.extend(from_def_source(db, it)),
135 hir::ModuleDef::BuiltinType(it) => res.extend(Some(it.to_string())),
136 },
141 Some(SelfType(ty)) => { 137 Some(SelfType(ty)) => {
142 if let Some((adt_def, _)) = ty.as_adt() { 138 if let Some((adt_def, _)) = ty.as_adt() {
143 res.extend(match adt_def { 139 res.extend(match adt_def {
@@ -147,7 +143,7 @@ pub(crate) fn hover(db: &RootDatabase, position: FilePosition) -> Option<RangeIn
147 }) 143 })
148 } 144 }
149 } 145 }
150 Some(Pat(_)) | Some(SelfParam(_)) => { 146 Some(Local(_)) => {
151 // Hover for these shows type names 147 // Hover for these shows type names
152 no_fallback = true; 148 no_fallback = true;
153 } 149 }
@@ -722,4 +718,16 @@ fn func(foo: i32) { if true { <|>foo; }; }
722 assert_eq!(trim_markup_opt(hover.info.first()), Some("macro_rules! foo")); 718 assert_eq!(trim_markup_opt(hover.info.first()), Some("macro_rules! foo"));
723 assert_eq!(hover.info.is_exact(), true); 719 assert_eq!(hover.info.is_exact(), true);
724 } 720 }
721
722 #[test]
723 fn test_hover_tuple_field() {
724 let (analysis, position) = single_file_with_position(
725 "
726 struct TS(String, i32<|>);
727 ",
728 );
729 let hover = analysis.hover(position).unwrap().unwrap();
730 assert_eq!(trim_markup_opt(hover.info.first()), Some("i32"));
731 assert_eq!(hover.info.is_exact(), true);
732 }
725} 733}
diff --git a/crates/ra_ide_api/src/impls.rs b/crates/ra_ide_api/src/impls.rs
index b899ed3a5..bc9b66550 100644
--- a/crates/ra_ide_api/src/impls.rs
+++ b/crates/ra_ide_api/src/impls.rs
@@ -4,7 +4,7 @@ use hir::{db::HirDatabase, ApplicationTy, FromSource, Ty, TypeCtor};
4use ra_db::SourceDatabase; 4use ra_db::SourceDatabase;
5use ra_syntax::{algo::find_node_at_offset, ast, AstNode}; 5use ra_syntax::{algo::find_node_at_offset, ast, AstNode};
6 6
7use crate::{db::RootDatabase, FilePosition, NavigationTarget, RangeInfo}; 7use crate::{db::RootDatabase, display::ToNav, FilePosition, NavigationTarget, RangeInfo};
8 8
9pub(crate) fn goto_implementation( 9pub(crate) fn goto_implementation(
10 db: &RootDatabase, 10 db: &RootDatabase,
@@ -58,7 +58,7 @@ fn impls_for_def(
58 impls 58 impls
59 .all_impls() 59 .all_impls()
60 .filter(|impl_block| is_equal_for_find_impls(&ty, &impl_block.target_ty(db))) 60 .filter(|impl_block| is_equal_for_find_impls(&ty, &impl_block.target_ty(db)))
61 .map(|imp| NavigationTarget::from_impl_block(db, imp)) 61 .map(|imp| imp.to_nav(db))
62 .collect(), 62 .collect(),
63 ) 63 )
64} 64}
@@ -75,12 +75,7 @@ fn impls_for_trait(
75 let krate = module.krate(); 75 let krate = module.krate();
76 let impls = db.impls_in_crate(krate); 76 let impls = db.impls_in_crate(krate);
77 77
78 Some( 78 Some(impls.lookup_impl_blocks_for_trait(tr).map(|imp| imp.to_nav(db)).collect())
79 impls
80 .lookup_impl_blocks_for_trait(tr)
81 .map(|imp| NavigationTarget::from_impl_block(db, imp))
82 .collect(),
83 )
84} 79}
85 80
86fn is_equal_for_find_impls(original_ty: &Ty, impl_ty: &Ty) -> bool { 81fn is_equal_for_find_impls(original_ty: &Ty, impl_ty: &Ty) -> bool {
diff --git a/crates/ra_ide_api/src/lib.rs b/crates/ra_ide_api/src/lib.rs
index d0188da44..484fbcc82 100644
--- a/crates/ra_ide_api/src/lib.rs
+++ b/crates/ra_ide_api/src/lib.rs
@@ -56,7 +56,7 @@ use ra_db::{
56}; 56};
57use ra_syntax::{SourceFile, TextRange, TextUnit}; 57use ra_syntax::{SourceFile, TextRange, TextUnit};
58 58
59use crate::{db::LineIndexDatabase, symbol_index::FileSymbol}; 59use crate::{db::LineIndexDatabase, display::ToNav, symbol_index::FileSymbol};
60 60
61pub use crate::{ 61pub use crate::{
62 assists::{Assist, AssistId}, 62 assists::{Assist, AssistId},
@@ -351,7 +351,7 @@ impl Analysis {
351 self.with_db(|db| { 351 self.with_db(|db| {
352 symbol_index::world_symbols(db, query) 352 symbol_index::world_symbols(db, query)
353 .into_iter() 353 .into_iter()
354 .map(|s| NavigationTarget::from_symbol(db, s)) 354 .map(|s| s.to_nav(db))
355 .collect::<Vec<_>>() 355 .collect::<Vec<_>>()
356 }) 356 })
357 } 357 }
diff --git a/crates/ra_ide_api/src/references.rs b/crates/ra_ide_api/src/references.rs
index b5b1c9a16..9cb9433e7 100644
--- a/crates/ra_ide_api/src/references.rs
+++ b/crates/ra_ide_api/src/references.rs
@@ -19,7 +19,9 @@ use ra_db::{SourceDatabase, SourceDatabaseExt};
19use ra_prof::profile; 19use ra_prof::profile;
20use ra_syntax::{algo::find_node_at_offset, ast, AstNode, SourceFile, SyntaxNode, TextUnit}; 20use ra_syntax::{algo::find_node_at_offset, ast, AstNode, SourceFile, SyntaxNode, TextUnit};
21 21
22use crate::{db::RootDatabase, FilePosition, FileRange, NavigationTarget, RangeInfo}; 22use crate::{
23 db::RootDatabase, display::ToNav, FilePosition, FileRange, NavigationTarget, RangeInfo,
24};
23 25
24pub(crate) use self::{ 26pub(crate) use self::{
25 classify::{classify_name, classify_name_ref}, 27 classify::{classify_name, classify_name_ref},
@@ -76,16 +78,15 @@ pub(crate) fn find_all_refs(
76 let RangeInfo { range, info: (name, def) } = find_name(db, &syntax, position)?; 78 let RangeInfo { range, info: (name, def) } = find_name(db, &syntax, position)?;
77 79
78 let declaration = match def.kind { 80 let declaration = match def.kind {
79 NameKind::Macro(mac) => NavigationTarget::from_macro_def(db, mac), 81 NameKind::Macro(mac) => mac.to_nav(db),
80 NameKind::Field(field) => NavigationTarget::from_field(db, field), 82 NameKind::Field(field) => field.to_nav(db),
81 NameKind::AssocItem(assoc) => NavigationTarget::from_assoc_item(db, assoc), 83 NameKind::AssocItem(assoc) => assoc.to_nav(db),
82 NameKind::Def(def) => NavigationTarget::from_def(db, def)?, 84 NameKind::Def(def) => NavigationTarget::from_def(db, def)?,
83 NameKind::SelfType(ref ty) => match ty.as_adt() { 85 NameKind::SelfType(ref ty) => match ty.as_adt() {
84 Some((def_id, _)) => NavigationTarget::from_adt_def(db, def_id), 86 Some((adt, _)) => adt.to_nav(db),
85 None => return None, 87 None => return None,
86 }, 88 },
87 NameKind::Pat((_, pat)) => NavigationTarget::from_pat(db, position.file_id, pat), 89 NameKind::Local(local) => local.to_nav(db),
88 NameKind::SelfParam(par) => NavigationTarget::from_self_param(position.file_id, par),
89 NameKind::GenericParam(_) => return None, 90 NameKind::GenericParam(_) => return None,
90 }; 91 };
91 92
diff --git a/crates/ra_ide_api/src/references/classify.rs b/crates/ra_ide_api/src/references/classify.rs
index 153082d5b..217f9951e 100644
--- a/crates/ra_ide_api/src/references/classify.rs
+++ b/crates/ra_ide_api/src/references/classify.rs
@@ -1,13 +1,13 @@
1//! Functions that are used to classify an element from its definition or reference. 1//! Functions that are used to classify an element from its definition or reference.
2 2
3use hir::{Either, FromSource, Module, ModuleSource, Path, PathResolution, Source, SourceAnalyzer}; 3use hir::{FromSource, Module, ModuleSource, Path, PathResolution, Source, SourceAnalyzer};
4use ra_db::FileId; 4use ra_db::FileId;
5use ra_prof::profile; 5use ra_prof::profile;
6use ra_syntax::{ast, match_ast, AstNode, AstPtr}; 6use ra_syntax::{ast, match_ast, AstNode};
7use test_utils::tested_by; 7use test_utils::tested_by;
8 8
9use super::{ 9use super::{
10 name_definition::{from_assoc_item, from_module_def, from_pat, from_struct_field}, 10 name_definition::{from_assoc_item, from_module_def, from_struct_field},
11 NameDefinition, NameKind, 11 NameDefinition, NameKind,
12}; 12};
13use crate::db::RootDatabase; 13use crate::db::RootDatabase;
@@ -25,7 +25,13 @@ pub(crate) fn classify_name(
25 match_ast! { 25 match_ast! {
26 match parent { 26 match parent {
27 ast::BindPat(it) => { 27 ast::BindPat(it) => {
28 from_pat(db, file_id, AstPtr::new(&it)) 28 let src = hir::Source { file_id, ast: it };
29 let local = hir::Local::from_source(db, src)?;
30 Some(NameDefinition {
31 visibility: None,
32 container: local.module(db),
33 kind: NameKind::Local(local),
34 })
29 }, 35 },
30 ast::RecordFieldDef(it) => { 36 ast::RecordFieldDef(it) => {
31 let ast = hir::FieldSource::Named(it); 37 let ast = hir::FieldSource::Named(it);
@@ -159,10 +165,10 @@ pub(crate) fn classify_name_ref(
159 match resolved { 165 match resolved {
160 Def(def) => Some(from_module_def(db, def, Some(container))), 166 Def(def) => Some(from_module_def(db, def, Some(container))),
161 AssocItem(item) => Some(from_assoc_item(db, item)), 167 AssocItem(item) => Some(from_assoc_item(db, item)),
162 LocalBinding(Either::A(pat)) => from_pat(db, file_id, pat), 168 Local(local) => {
163 LocalBinding(Either::B(par)) => { 169 let container = local.module(db);
164 let kind = NameKind::SelfParam(par); 170 let kind = NameKind::Local(local);
165 Some(NameDefinition { kind, container, visibility }) 171 Some(NameDefinition { kind, container, visibility: None })
166 } 172 }
167 GenericParam(par) => { 173 GenericParam(par) => {
168 // FIXME: get generic param def 174 // FIXME: get generic param def
diff --git a/crates/ra_ide_api/src/references/name_definition.rs b/crates/ra_ide_api/src/references/name_definition.rs
index 4580bc789..450f7ea9b 100644
--- a/crates/ra_ide_api/src/references/name_definition.rs
+++ b/crates/ra_ide_api/src/references/name_definition.rs
@@ -4,10 +4,9 @@
4//! Note that the reference search is possible for not all of the classified items. 4//! Note that the reference search is possible for not all of the classified items.
5 5
6use hir::{ 6use hir::{
7 db::AstDatabase, Adt, AssocItem, DefWithBody, FromSource, HasSource, HirFileId, MacroDef, 7 Adt, AssocItem, HasSource, Local, MacroDef, Module, ModuleDef, StructField, Ty, VariantDef,
8 Module, ModuleDef, StructField, Ty, VariantDef,
9}; 8};
10use ra_syntax::{ast, ast::VisibilityOwner, match_ast, AstNode, AstPtr}; 9use ra_syntax::{ast, ast::VisibilityOwner};
11 10
12use crate::db::RootDatabase; 11use crate::db::RootDatabase;
13 12
@@ -18,8 +17,7 @@ pub enum NameKind {
18 AssocItem(AssocItem), 17 AssocItem(AssocItem),
19 Def(ModuleDef), 18 Def(ModuleDef),
20 SelfType(Ty), 19 SelfType(Ty),
21 Pat((DefWithBody, AstPtr<ast::BindPat>)), 20 Local(Local),
22 SelfParam(AstPtr<ast::SelfParam>),
23 GenericParam(u32), 21 GenericParam(u32),
24} 22}
25 23
@@ -30,36 +28,6 @@ pub(crate) struct NameDefinition {
30 pub kind: NameKind, 28 pub kind: NameKind,
31} 29}
32 30
33pub(super) fn from_pat(
34 db: &RootDatabase,
35 file_id: HirFileId,
36 pat: AstPtr<ast::BindPat>,
37) -> Option<NameDefinition> {
38 let root = db.parse_or_expand(file_id)?;
39 let def = pat.to_node(&root).syntax().ancestors().find_map(|node| {
40 match_ast! {
41 match node {
42 ast::FnDef(it) => {
43 let src = hir::Source { file_id, ast: it };
44 Some(hir::Function::from_source(db, src)?.into())
45 },
46 ast::ConstDef(it) => {
47 let src = hir::Source { file_id, ast: it };
48 Some(hir::Const::from_source(db, src)?.into())
49 },
50 ast::StaticDef(it) => {
51 let src = hir::Source { file_id, ast: it };
52 Some(hir::Static::from_source(db, src)?.into())
53 },
54 _ => None,
55 }
56 }
57 })?;
58 let kind = NameKind::Pat((def, pat));
59 let container = def.module(db);
60 Some(NameDefinition { kind, container, visibility: None })
61}
62
63pub(super) fn from_assoc_item(db: &RootDatabase, item: AssocItem) -> NameDefinition { 31pub(super) fn from_assoc_item(db: &RootDatabase, item: AssocItem) -> NameDefinition {
64 let container = item.module(db); 32 let container = item.module(db);
65 let visibility = match item { 33 let visibility = match item {
diff --git a/crates/ra_ide_api/src/references/search_scope.rs b/crates/ra_ide_api/src/references/search_scope.rs
index f2789e0b2..2907787c2 100644
--- a/crates/ra_ide_api/src/references/search_scope.rs
+++ b/crates/ra_ide_api/src/references/search_scope.rs
@@ -71,13 +71,13 @@ impl NameDefinition {
71 let module_src = self.container.definition_source(db); 71 let module_src = self.container.definition_source(db);
72 let file_id = module_src.file_id.original_file(db); 72 let file_id = module_src.file_id.original_file(db);
73 73
74 if let NameKind::Pat((def, _)) = self.kind { 74 if let NameKind::Local(var) = self.kind {
75 let mut res = FxHashMap::default(); 75 let range = match var.parent(db) {
76 let range = match def {
77 DefWithBody::Function(f) => f.source(db).ast.syntax().text_range(), 76 DefWithBody::Function(f) => f.source(db).ast.syntax().text_range(),
78 DefWithBody::Const(c) => c.source(db).ast.syntax().text_range(), 77 DefWithBody::Const(c) => c.source(db).ast.syntax().text_range(),
79 DefWithBody::Static(s) => s.source(db).ast.syntax().text_range(), 78 DefWithBody::Static(s) => s.source(db).ast.syntax().text_range(),
80 }; 79 };
80 let mut res = FxHashMap::default();
81 res.insert(file_id, Some(range)); 81 res.insert(file_id, Some(range));
82 return SearchScope::new(res); 82 return SearchScope::new(res);
83 } 83 }
diff --git a/crates/ra_ide_api/src/snapshots/rainbow_highlighting.html b/crates/ra_ide_api/src/snapshots/rainbow_highlighting.html
index ed664817e..79f11ea80 100644
--- a/crates/ra_ide_api/src/snapshots/rainbow_highlighting.html
+++ b/crates/ra_ide_api/src/snapshots/rainbow_highlighting.html
@@ -20,14 +20,14 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
20.keyword\.control { color: #F0DFAF; font-weight: bold; } 20.keyword\.control { color: #F0DFAF; font-weight: bold; }
21</style> 21</style>
22<pre><code><span class="keyword">fn</span> <span class="function">main</span>() { 22<pre><code><span class="keyword">fn</span> <span class="function">main</span>() {
23 <span class="keyword">let</span> <span class="variable" data-binding-hash="3888301305669440875" style="color: hsl(242,59%,59%);">hello</span> = <span class="string">"hello"</span>; 23 <span class="keyword">let</span> <span class="variable" data-binding-hash="8723171760279909834" style="color: hsl(307,91%,75%);">hello</span> = <span class="string">"hello"</span>;
24 <span class="keyword">let</span> <span class="variable" data-binding-hash="5695551762718493399" style="color: hsl(272,48%,45%);">x</span> = <span class="variable" data-binding-hash="3888301305669440875" style="color: hsl(242,59%,59%);">hello</span>.<span class="text">to_string</span>(); 24 <span class="keyword">let</span> <span class="variable" data-binding-hash="14702933417323009544" style="color: hsl(108,90%,49%);">x</span> = <span class="variable" data-binding-hash="8723171760279909834" style="color: hsl(307,91%,75%);">hello</span>.<span class="text">to_string</span>();
25 <span class="keyword">let</span> <span class="variable" data-binding-hash="5435401749617022797" style="color: hsl(353,77%,74%);">y</span> = <span class="variable" data-binding-hash="3888301305669440875" style="color: hsl(242,59%,59%);">hello</span>.<span class="text">to_string</span>(); 25 <span class="keyword">let</span> <span class="variable" data-binding-hash="5443150872754369068" style="color: hsl(215,43%,43%);">y</span> = <span class="variable" data-binding-hash="8723171760279909834" style="color: hsl(307,91%,75%);">hello</span>.<span class="text">to_string</span>();
26 26
27 <span class="keyword">let</span> <span class="variable" data-binding-hash="1903207544374197704" style="color: hsl(58,61%,61%);">x</span> = <span class="string">"other color please!"</span>; 27 <span class="keyword">let</span> <span class="variable" data-binding-hash="17358108296605513516" style="color: hsl(331,46%,60%);">x</span> = <span class="string">"other color please!"</span>;
28 <span class="keyword">let</span> <span class="variable" data-binding-hash="14878783531007968800" style="color: hsl(265,73%,83%);">y</span> = <span class="variable" data-binding-hash="1903207544374197704" style="color: hsl(58,61%,61%);">x</span>.<span class="text">to_string</span>(); 28 <span class="keyword">let</span> <span class="variable" data-binding-hash="2073121142529774969" style="color: hsl(320,43%,74%);">y</span> = <span class="variable" data-binding-hash="17358108296605513516" style="color: hsl(331,46%,60%);">x</span>.<span class="text">to_string</span>();
29} 29}
30 30
31<span class="keyword">fn</span> <span class="function">bar</span>() { 31<span class="keyword">fn</span> <span class="function">bar</span>() {
32 <span class="keyword">let</span> <span class="keyword">mut</span> <span class="variable.mut" data-binding-hash="3888301305669440875" style="color: hsl(242,59%,59%);">hello</span> = <span class="string">"hello"</span>; 32 <span class="keyword">let</span> <span class="keyword">mut</span> <span class="variable.mut" data-binding-hash="8723171760279909834" style="color: hsl(307,91%,75%);">hello</span> = <span class="string">"hello"</span>;
33}</code></pre> \ No newline at end of file 33}</code></pre> \ No newline at end of file
diff --git a/crates/ra_ide_api/src/syntax_highlighting.rs b/crates/ra_ide_api/src/syntax_highlighting.rs
index 33f3caceb..d53a759ee 100644
--- a/crates/ra_ide_api/src/syntax_highlighting.rs
+++ b/crates/ra_ide_api/src/syntax_highlighting.rs
@@ -2,19 +2,17 @@
2 2
3use rustc_hash::{FxHashMap, FxHashSet}; 3use rustc_hash::{FxHashMap, FxHashSet};
4 4
5use hir::{Mutability, Ty}; 5use hir::{Mutability, Name};
6use ra_db::SourceDatabase; 6use ra_db::SourceDatabase;
7use ra_prof::profile; 7use ra_prof::profile;
8use ra_syntax::{ 8use ra_syntax::{ast, AstNode, Direction, SyntaxElement, SyntaxKind, SyntaxKind::*, TextRange, T};
9 ast::{self, NameOwner},
10 AstNode, Direction, SmolStr, SyntaxElement, SyntaxKind,
11 SyntaxKind::*,
12 TextRange, T,
13};
14 9
15use crate::{ 10use crate::{
16 db::RootDatabase, 11 db::RootDatabase,
17 references::{classify_name_ref, NameKind::*}, 12 references::{
13 classify_name, classify_name_ref,
14 NameKind::{self, *},
15 },
18 FileId, 16 FileId,
19}; 17};
20 18
@@ -40,32 +38,12 @@ fn is_control_keyword(kind: SyntaxKind) -> bool {
40 } 38 }
41} 39}
42 40
43fn is_variable_mutable(
44 db: &RootDatabase,
45 analyzer: &hir::SourceAnalyzer,
46 pat: ast::BindPat,
47) -> bool {
48 if pat.is_mutable() {
49 return true;
50 }
51
52 let ty = analyzer.type_of_pat(db, &pat.into()).unwrap_or(Ty::Unknown);
53 if let Some((_, mutability)) = ty.as_reference() {
54 match mutability {
55 Mutability::Shared => false,
56 Mutability::Mut => true,
57 }
58 } else {
59 false
60 }
61}
62
63pub(crate) fn highlight(db: &RootDatabase, file_id: FileId) -> Vec<HighlightedRange> { 41pub(crate) fn highlight(db: &RootDatabase, file_id: FileId) -> Vec<HighlightedRange> {
64 let _p = profile("highlight"); 42 let _p = profile("highlight");
65 let parse = db.parse(file_id); 43 let parse = db.parse(file_id);
66 let root = parse.tree().syntax().clone(); 44 let root = parse.tree().syntax().clone();
67 45
68 fn calc_binding_hash(file_id: FileId, text: &SmolStr, shadow_count: u32) -> u64 { 46 fn calc_binding_hash(file_id: FileId, name: &Name, shadow_count: u32) -> u64 {
69 fn hash<T: std::hash::Hash + std::fmt::Debug>(x: T) -> u64 { 47 fn hash<T: std::hash::Hash + std::fmt::Debug>(x: T) -> u64 {
70 use std::{collections::hash_map::DefaultHasher, hash::Hasher}; 48 use std::{collections::hash_map::DefaultHasher, hash::Hasher};
71 49
@@ -74,13 +52,13 @@ pub(crate) fn highlight(db: &RootDatabase, file_id: FileId) -> Vec<HighlightedRa
74 hasher.finish() 52 hasher.finish()
75 } 53 }
76 54
77 hash((file_id, text, shadow_count)) 55 hash((file_id, name, shadow_count))
78 } 56 }
79 57
80 // Visited nodes to handle highlighting priorities 58 // Visited nodes to handle highlighting priorities
81 // FIXME: retain only ranges here 59 // FIXME: retain only ranges here
82 let mut highlighted: FxHashSet<SyntaxElement> = FxHashSet::default(); 60 let mut highlighted: FxHashSet<SyntaxElement> = FxHashSet::default();
83 let mut bindings_shadow_count: FxHashMap<SmolStr, u32> = FxHashMap::default(); 61 let mut bindings_shadow_count: FxHashMap<Name, u32> = FxHashMap::default();
84 62
85 let mut res = Vec::new(); 63 let mut res = Vec::new();
86 for node in root.descendants_with_tokens() { 64 for node in root.descendants_with_tokens() {
@@ -100,81 +78,38 @@ pub(crate) fn highlight(db: &RootDatabase, file_id: FileId) -> Vec<HighlightedRa
100 if node.ancestors().any(|it| it.kind() == ATTR) { 78 if node.ancestors().any(|it| it.kind() == ATTR) {
101 continue; 79 continue;
102 } 80 }
103 if let Some(name_ref) = node.as_node().cloned().and_then(ast::NameRef::cast) {
104 let name_kind = classify_name_ref(db, file_id, &name_ref).map(|d| d.kind);
105 match name_kind {
106 Some(Macro(_)) => "macro",
107 Some(Field(_)) => "field",
108 Some(AssocItem(hir::AssocItem::Function(_))) => "function",
109 Some(AssocItem(hir::AssocItem::Const(_))) => "constant",
110 Some(AssocItem(hir::AssocItem::TypeAlias(_))) => "type",
111 Some(Def(hir::ModuleDef::Module(_))) => "module",
112 Some(Def(hir::ModuleDef::Function(_))) => "function",
113 Some(Def(hir::ModuleDef::Adt(_))) => "type",
114 Some(Def(hir::ModuleDef::EnumVariant(_))) => "constant",
115 Some(Def(hir::ModuleDef::Const(_))) => "constant",
116 Some(Def(hir::ModuleDef::Static(_))) => "constant",
117 Some(Def(hir::ModuleDef::Trait(_))) => "type",
118 Some(Def(hir::ModuleDef::TypeAlias(_))) => "type",
119 Some(Def(hir::ModuleDef::BuiltinType(_))) => "type",
120 Some(SelfType(_)) => "type",
121 Some(Pat((_, ptr))) => {
122 let pat = ptr.to_node(&root);
123 if let Some(name) = pat.name() {
124 let text = name.text();
125 let shadow_count =
126 bindings_shadow_count.entry(text.clone()).or_default();
127 binding_hash =
128 Some(calc_binding_hash(file_id, &text, *shadow_count))
129 }
130 81
131 let analyzer = 82 let name_ref = node.as_node().cloned().and_then(ast::NameRef::cast).unwrap();
132 hir::SourceAnalyzer::new(db, file_id, name_ref.syntax(), None); 83 let name_kind = classify_name_ref(db, file_id, &name_ref).map(|d| d.kind);
133 if is_variable_mutable(db, &analyzer, ptr.to_node(&root)) { 84
134 "variable.mut" 85 if let Some(Local(local)) = &name_kind {
135 } else { 86 if let Some(name) = local.name(db) {
136 "variable" 87 let shadow_count = bindings_shadow_count.entry(name.clone()).or_default();
137 } 88 binding_hash = Some(calc_binding_hash(file_id, &name, *shadow_count))
138 }
139 Some(SelfParam(_)) => "type",
140 Some(GenericParam(_)) => "type",
141 None => "text",
142 } 89 }
143 } else { 90 };
144 "text" 91
145 } 92 name_kind.map_or("text", |it| highlight_name(db, it))
146 } 93 }
147 NAME => { 94 NAME => {
148 if let Some(name) = node.as_node().cloned().and_then(ast::Name::cast) { 95 let name = node.as_node().cloned().and_then(ast::Name::cast).unwrap();
149 let analyzer = hir::SourceAnalyzer::new(db, file_id, name.syntax(), None); 96 let name_kind = classify_name(db, file_id, &name).map(|d| d.kind);
150 if let Some(pat) = name.syntax().ancestors().find_map(ast::BindPat::cast) { 97
151 if let Some(name) = pat.name() { 98 if let Some(Local(local)) = &name_kind {
152 let text = name.text(); 99 if let Some(name) = local.name(db) {
153 let shadow_count = 100 let shadow_count = bindings_shadow_count.entry(name.clone()).or_default();
154 bindings_shadow_count.entry(text.clone()).or_default(); 101 *shadow_count += 1;
155 *shadow_count += 1; 102 binding_hash = Some(calc_binding_hash(file_id, &name, *shadow_count))
156 binding_hash = Some(calc_binding_hash(file_id, &text, *shadow_count))
157 }
158
159 if is_variable_mutable(db, &analyzer, pat) {
160 "variable.mut"
161 } else {
162 "variable"
163 }
164 } else {
165 name.syntax()
166 .parent()
167 .map(|x| match x.kind() {
168 TYPE_PARAM | STRUCT_DEF | ENUM_DEF | TRAIT_DEF | TYPE_ALIAS_DEF => {
169 "type"
170 }
171 RECORD_FIELD_DEF => "field",
172 _ => "function",
173 })
174 .unwrap_or("function")
175 } 103 }
176 } else { 104 };
177 "text" 105
106 match name_kind {
107 Some(name_kind) => highlight_name(db, name_kind),
108 None => name.syntax().parent().map_or("function", |x| match x.kind() {
109 TYPE_PARAM | STRUCT_DEF | ENUM_DEF | TRAIT_DEF | TYPE_ALIAS_DEF => "type",
110 RECORD_FIELD_DEF => "field",
111 _ => "function",
112 }),
178 } 113 }
179 } 114 }
180 INT_NUMBER | FLOAT_NUMBER | CHAR | BYTE => "literal", 115 INT_NUMBER | FLOAT_NUMBER | CHAR | BYTE => "literal",
@@ -272,6 +207,37 @@ pub(crate) fn highlight_as_html(db: &RootDatabase, file_id: FileId, rainbow: boo
272 buf 207 buf
273} 208}
274 209
210fn highlight_name(db: &RootDatabase, name_kind: NameKind) -> &'static str {
211 match name_kind {
212 Macro(_) => "macro",
213 Field(_) => "field",
214 AssocItem(hir::AssocItem::Function(_)) => "function",
215 AssocItem(hir::AssocItem::Const(_)) => "constant",
216 AssocItem(hir::AssocItem::TypeAlias(_)) => "type",
217 Def(hir::ModuleDef::Module(_)) => "module",
218 Def(hir::ModuleDef::Function(_)) => "function",
219 Def(hir::ModuleDef::Adt(_)) => "type",
220 Def(hir::ModuleDef::EnumVariant(_)) => "constant",
221 Def(hir::ModuleDef::Const(_)) => "constant",
222 Def(hir::ModuleDef::Static(_)) => "constant",
223 Def(hir::ModuleDef::Trait(_)) => "type",
224 Def(hir::ModuleDef::TypeAlias(_)) => "type",
225 Def(hir::ModuleDef::BuiltinType(_)) => "type",
226 SelfType(_) => "type",
227 GenericParam(_) => "type",
228 Local(local) => {
229 if local.is_mut(db) {
230 "variable.mut"
231 } else {
232 match local.ty(db).as_reference() {
233 Some((_, Mutability::Mut)) => "variable.mut",
234 _ => "variable",
235 }
236 }
237 }
238 }
239}
240
275//FIXME: like, real html escaping 241//FIXME: like, real html escaping
276fn html_escape(text: &str) -> String { 242fn html_escape(text: &str) -> String {
277 text.replace("<", "&lt;").replace(">", "&gt;") 243 text.replace("<", "&lt;").replace(">", "&gt;")
diff --git a/crates/ra_syntax/src/syntax_error.rs b/crates/ra_syntax/src/syntax_error.rs
index d6eca2ad7..1f60a7aab 100644
--- a/crates/ra_syntax/src/syntax_error.rs
+++ b/crates/ra_syntax/src/syntax_error.rs
@@ -82,6 +82,7 @@ pub enum SyntaxErrorKind {
82 InvalidBlockAttr, 82 InvalidBlockAttr,
83 InvalidMatchInnerAttr, 83 InvalidMatchInnerAttr,
84 InvalidTupleIndexFormat, 84 InvalidTupleIndexFormat,
85 VisibilityNotAllowed,
85} 86}
86 87
87impl fmt::Display for SyntaxErrorKind { 88impl fmt::Display for SyntaxErrorKind {
@@ -99,6 +100,9 @@ impl fmt::Display for SyntaxErrorKind {
99 } 100 }
100 ParseError(msg) => write!(f, "{}", msg.0), 101 ParseError(msg) => write!(f, "{}", msg.0),
101 EscapeError(err) => write!(f, "{}", err), 102 EscapeError(err) => write!(f, "{}", err),
103 VisibilityNotAllowed => {
104 write!(f, "unnecessary visibility qualifier")
105 }
102 } 106 }
103 } 107 }
104} 108}
diff --git a/crates/ra_syntax/src/validation.rs b/crates/ra_syntax/src/validation.rs
index ab4f15908..2d596763e 100644
--- a/crates/ra_syntax/src/validation.rs
+++ b/crates/ra_syntax/src/validation.rs
@@ -6,7 +6,7 @@ use rustc_lexer::unescape;
6 6
7use crate::{ 7use crate::{
8 ast, match_ast, AstNode, SyntaxError, SyntaxErrorKind, 8 ast, match_ast, AstNode, SyntaxError, SyntaxErrorKind,
9 SyntaxKind::{BYTE, BYTE_STRING, CHAR, INT_NUMBER, STRING}, 9 SyntaxKind::{BYTE, BYTE_STRING, CHAR, CONST_DEF, FN_DEF, INT_NUMBER, STRING, TYPE_ALIAS_DEF},
10 SyntaxNode, SyntaxToken, TextUnit, T, 10 SyntaxNode, SyntaxToken, TextUnit, T,
11}; 11};
12 12
@@ -102,6 +102,7 @@ pub(crate) fn validate(root: &SyntaxNode) -> Vec<SyntaxError> {
102 ast::BlockExpr(it) => { block::validate_block_expr(it, &mut errors) }, 102 ast::BlockExpr(it) => { block::validate_block_expr(it, &mut errors) },
103 ast::FieldExpr(it) => { validate_numeric_name(it.name_ref(), &mut errors) }, 103 ast::FieldExpr(it) => { validate_numeric_name(it.name_ref(), &mut errors) },
104 ast::RecordField(it) => { validate_numeric_name(it.name_ref(), &mut errors) }, 104 ast::RecordField(it) => { validate_numeric_name(it.name_ref(), &mut errors) },
105 ast::Visibility(it) => { validate_visibility(it, &mut errors) },
105 _ => (), 106 _ => (),
106 } 107 }
107 } 108 }
@@ -206,3 +207,23 @@ fn validate_numeric_name(name_ref: Option<ast::NameRef>, errors: &mut Vec<Syntax
206 name_ref?.syntax().first_child_or_token()?.into_token().filter(|it| it.kind() == INT_NUMBER) 207 name_ref?.syntax().first_child_or_token()?.into_token().filter(|it| it.kind() == INT_NUMBER)
207 } 208 }
208} 209}
210
211fn validate_visibility(vis: ast::Visibility, errors: &mut Vec<SyntaxError>) {
212 let parent = match vis.syntax().parent() {
213 Some(it) => it,
214 None => return,
215 };
216 match parent.kind() {
217 FN_DEF | CONST_DEF | TYPE_ALIAS_DEF => (),
218 _ => return,
219 }
220 let impl_block = match parent.parent().and_then(|it| it.parent()).and_then(ast::ImplBlock::cast)
221 {
222 Some(it) => it,
223 None => return,
224 };
225 if impl_block.target_trait().is_some() {
226 errors
227 .push(SyntaxError::new(SyntaxErrorKind::VisibilityNotAllowed, vis.syntax.text_range()))
228 }
229}
diff --git a/crates/ra_syntax/test_data/parser/err/0037_visibility_in_traits.rs b/crates/ra_syntax/test_data/parser/err/0037_visibility_in_traits.rs
new file mode 100644
index 000000000..a43e7ef10
--- /dev/null
+++ b/crates/ra_syntax/test_data/parser/err/0037_visibility_in_traits.rs
@@ -0,0 +1,6 @@
1impl T for () {
2 fn foo() {}
3 pub fn bar() {}
4 pub(crate) type Baz = ();
5 pub(crate) const C: i32 = 92;
6}
diff --git a/crates/ra_syntax/test_data/parser/err/0037_visibility_in_traits.txt b/crates/ra_syntax/test_data/parser/err/0037_visibility_in_traits.txt
new file mode 100644
index 000000000..749c8cddb
--- /dev/null
+++ b/crates/ra_syntax/test_data/parser/err/0037_visibility_in_traits.txt
@@ -0,0 +1,99 @@
1SOURCE_FILE@[0; 118)
2 IMPL_BLOCK@[0; 117)
3 IMPL_KW@[0; 4) "impl"
4 WHITESPACE@[4; 5) " "
5 PATH_TYPE@[5; 6)
6 PATH@[5; 6)
7 PATH_SEGMENT@[5; 6)
8 NAME_REF@[5; 6)
9 IDENT@[5; 6) "T"
10 WHITESPACE@[6; 7) " "
11 FOR_KW@[7; 10) "for"
12 WHITESPACE@[10; 11) " "
13 TUPLE_TYPE@[11; 13)
14 L_PAREN@[11; 12) "("
15 R_PAREN@[12; 13) ")"
16 WHITESPACE@[13; 14) " "
17 ITEM_LIST@[14; 117)
18 L_CURLY@[14; 15) "{"
19 WHITESPACE@[15; 20) "\n "
20 FN_DEF@[20; 31)
21 FN_KW@[20; 22) "fn"
22 WHITESPACE@[22; 23) " "
23 NAME@[23; 26)
24 IDENT@[23; 26) "foo"
25 PARAM_LIST@[26; 28)
26 L_PAREN@[26; 27) "("
27 R_PAREN@[27; 28) ")"
28 WHITESPACE@[28; 29) " "
29 BLOCK_EXPR@[29; 31)
30 BLOCK@[29; 31)
31 L_CURLY@[29; 30) "{"
32 R_CURLY@[30; 31) "}"
33 WHITESPACE@[31; 36) "\n "
34 FN_DEF@[36; 51)
35 VISIBILITY@[36; 39)
36 PUB_KW@[36; 39) "pub"
37 WHITESPACE@[39; 40) " "
38 FN_KW@[40; 42) "fn"
39 WHITESPACE@[42; 43) " "
40 NAME@[43; 46)
41 IDENT@[43; 46) "bar"
42 PARAM_LIST@[46; 48)
43 L_PAREN@[46; 47) "("
44 R_PAREN@[47; 48) ")"
45 WHITESPACE@[48; 49) " "
46 BLOCK_EXPR@[49; 51)
47 BLOCK@[49; 51)
48 L_CURLY@[49; 50) "{"
49 R_CURLY@[50; 51) "}"
50 WHITESPACE@[51; 56) "\n "
51 TYPE_ALIAS_DEF@[56; 81)
52 VISIBILITY@[56; 66)
53 PUB_KW@[56; 59) "pub"
54 L_PAREN@[59; 60) "("
55 CRATE_KW@[60; 65) "crate"
56 R_PAREN@[65; 66) ")"
57 WHITESPACE@[66; 67) " "
58 TYPE_KW@[67; 71) "type"
59 WHITESPACE@[71; 72) " "
60 NAME@[72; 75)
61 IDENT@[72; 75) "Baz"
62 WHITESPACE@[75; 76) " "
63 EQ@[76; 77) "="
64 WHITESPACE@[77; 78) " "
65 TUPLE_TYPE@[78; 80)
66 L_PAREN@[78; 79) "("
67 R_PAREN@[79; 80) ")"
68 SEMI@[80; 81) ";"
69 WHITESPACE@[81; 86) "\n "
70 CONST_DEF@[86; 115)
71 VISIBILITY@[86; 96)
72 PUB_KW@[86; 89) "pub"
73 L_PAREN@[89; 90) "("
74 CRATE_KW@[90; 95) "crate"
75 R_PAREN@[95; 96) ")"
76 WHITESPACE@[96; 97) " "
77 CONST_KW@[97; 102) "const"
78 WHITESPACE@[102; 103) " "
79 NAME@[103; 104)
80 IDENT@[103; 104) "C"
81 COLON@[104; 105) ":"
82 WHITESPACE@[105; 106) " "
83 PATH_TYPE@[106; 109)
84 PATH@[106; 109)
85 PATH_SEGMENT@[106; 109)
86 NAME_REF@[106; 109)
87 IDENT@[106; 109) "i32"
88 WHITESPACE@[109; 110) " "
89 EQ@[110; 111) "="
90 WHITESPACE@[111; 112) " "
91 LITERAL@[112; 114)
92 INT_NUMBER@[112; 114) "92"
93 SEMI@[114; 115) ";"
94 WHITESPACE@[115; 116) "\n"
95 R_CURLY@[116; 117) "}"
96 WHITESPACE@[117; 118) "\n"
97error [36; 39): unnecessary visibility qualifier
98error [56; 66): unnecessary visibility qualifier
99error [86; 96): unnecessary visibility qualifier