aboutsummaryrefslogtreecommitdiff
path: root/crates
diff options
context:
space:
mode:
Diffstat (limited to 'crates')
-rw-r--r--crates/assists/src/handlers/add_turbo_fish.rs4
-rw-r--r--crates/assists/src/handlers/auto_import.rs55
-rw-r--r--crates/assists/src/handlers/expand_glob_import.rs4
-rw-r--r--crates/hir_ty/src/infer.rs24
-rw-r--r--crates/hir_ty/src/infer/expr.rs18
-rw-r--r--crates/hir_ty/src/tests/simple.rs89
-rw-r--r--crates/ide/src/diagnostics.rs95
-rw-r--r--crates/ide/src/diagnostics/field_shorthand.rs206
-rw-r--r--crates/ide/src/doc_links.rs6
-rw-r--r--crates/ide/src/goto_definition.rs8
-rw-r--r--crates/ide/src/hover.rs29
-rw-r--r--crates/ide/src/references.rs6
-rw-r--r--crates/ide/src/references/rename.rs6
-rw-r--r--crates/ide/src/syntax_highlighting.rs224
-rw-r--r--crates/ide/src/syntax_highlighting/format.rs78
-rw-r--r--crates/ide/src/syntax_highlighting/macro_rules.rs129
-rw-r--r--crates/ide_db/src/defs.rs377
-rw-r--r--crates/ide_db/src/imports_locator.rs4
-rw-r--r--crates/ide_db/src/search.rs12
19 files changed, 873 insertions, 501 deletions
diff --git a/crates/assists/src/handlers/add_turbo_fish.rs b/crates/assists/src/handlers/add_turbo_fish.rs
index f4f997d8e..e3d84d698 100644
--- a/crates/assists/src/handlers/add_turbo_fish.rs
+++ b/crates/assists/src/handlers/add_turbo_fish.rs
@@ -1,4 +1,4 @@
1use ide_db::defs::{classify_name_ref, Definition, NameRefClass}; 1use ide_db::defs::{Definition, NameRefClass};
2use syntax::{ast, AstNode, SyntaxKind, T}; 2use syntax::{ast, AstNode, SyntaxKind, T};
3use test_utils::mark; 3use test_utils::mark;
4 4
@@ -39,7 +39,7 @@ pub(crate) fn add_turbo_fish(acc: &mut Assists, ctx: &AssistContext) -> Option<(
39 return None; 39 return None;
40 } 40 }
41 let name_ref = ast::NameRef::cast(ident.parent())?; 41 let name_ref = ast::NameRef::cast(ident.parent())?;
42 let def = match classify_name_ref(&ctx.sema, &name_ref)? { 42 let def = match NameRefClass::classify(&ctx.sema, &name_ref)? {
43 NameRefClass::Definition(def) => def, 43 NameRefClass::Definition(def) => def,
44 NameRefClass::ExternCrate(_) | NameRefClass::FieldShorthand { .. } => return None, 44 NameRefClass::ExternCrate(_) | NameRefClass::FieldShorthand { .. } => return None,
45 }; 45 };
diff --git a/crates/assists/src/handlers/auto_import.rs b/crates/assists/src/handlers/auto_import.rs
index e595b5b93..4a7059c83 100644
--- a/crates/assists/src/handlers/auto_import.rs
+++ b/crates/assists/src/handlers/auto_import.rs
@@ -6,6 +6,61 @@ use crate::{
6 AssistContext, AssistId, AssistKind, Assists, GroupLabel, 6 AssistContext, AssistId, AssistKind, Assists, GroupLabel,
7}; 7};
8 8
9// Feature: Import Insertion
10//
11// Using the `auto-import` assist it is possible to insert missing imports for unresolved items.
12// When inserting an import it will do so in a structured manner by keeping imports grouped,
13// separated by a newline in the following order:
14//
15// - `std` and `core`
16// - External Crates
17// - Current Crate, paths prefixed by `crate`
18// - Current Module, paths prefixed by `self`
19// - Super Module, paths prefixed by `super`
20//
21// Example:
22// ```rust
23// use std::fs::File;
24//
25// use itertools::Itertools;
26// use syntax::ast;
27//
28// use crate::utils::insert_use;
29//
30// use self::auto_import;
31//
32// use super::AssistContext;
33// ```
34//
35// .Merge Behaviour
36//
37// It is possible to configure how use-trees are merged with the `importMergeBehaviour` setting.
38// It has the following configurations:
39//
40// - `full`: This setting will cause auto-import to always completely merge use-trees that share the
41// same path prefix while also merging inner trees that share the same path-prefix. This kind of
42// nesting is only supported in Rust versions later than 1.24.
43// - `last`: This setting will cause auto-import to merge use-trees as long as the resulting tree
44// will only contain a nesting of single segment paths at the very end.
45// - `none`: This setting will cause auto-import to never merge use-trees keeping them as simple
46// paths.
47//
48// In `VS Code` the configuration for this is `rust-analyzer.assist.importMergeBehaviour`.
49//
50// .Import Prefix
51//
52// The style of imports in the same crate is configurable through the `importPrefix` setting.
53// It has the following configurations:
54//
55// - `by_crate`: This setting will force paths to be always absolute, starting with the `crate`
56// prefix, unless the item is defined outside of the current crate.
57// - `by_self`: This setting will force paths that are relative to the current module to always
58// start with `self`. This will result in paths that always start with either `crate`, `self`,
59// `super` or an extern crate identifier.
60// - `plain`: This setting does not impose any restrictions in imports.
61//
62// In `VS Code` the configuration for this is `rust-analyzer.assist.importPrefix`.
63
9// Assist: auto_import 64// Assist: auto_import
10// 65//
11// If the name is unresolved, provides all possible imports for it. 66// If the name is unresolved, provides all possible imports for it.
diff --git a/crates/assists/src/handlers/expand_glob_import.rs b/crates/assists/src/handlers/expand_glob_import.rs
index d1adff972..316a58d88 100644
--- a/crates/assists/src/handlers/expand_glob_import.rs
+++ b/crates/assists/src/handlers/expand_glob_import.rs
@@ -1,7 +1,7 @@
1use either::Either; 1use either::Either;
2use hir::{AssocItem, MacroDef, Module, ModuleDef, Name, PathResolution, ScopeDef}; 2use hir::{AssocItem, MacroDef, Module, ModuleDef, Name, PathResolution, ScopeDef};
3use ide_db::{ 3use ide_db::{
4 defs::{classify_name_ref, Definition, NameRefClass}, 4 defs::{Definition, NameRefClass},
5 search::SearchScope, 5 search::SearchScope,
6}; 6};
7use syntax::{ 7use syntax::{
@@ -217,7 +217,7 @@ fn find_imported_defs(ctx: &AssistContext, star: SyntaxToken) -> Option<Vec<Def>
217 .flatten() 217 .flatten()
218 .filter_map(|n| Some(n.descendants().filter_map(ast::NameRef::cast))) 218 .filter_map(|n| Some(n.descendants().filter_map(ast::NameRef::cast)))
219 .flatten() 219 .flatten()
220 .filter_map(|r| match classify_name_ref(&ctx.sema, &r)? { 220 .filter_map(|r| match NameRefClass::classify(&ctx.sema, &r)? {
221 NameRefClass::Definition(Definition::ModuleDef(def)) => Some(Def::ModuleDef(def)), 221 NameRefClass::Definition(Definition::ModuleDef(def)) => Some(Def::ModuleDef(def)),
222 NameRefClass::Definition(Definition::Macro(def)) => Some(Def::MacroDef(def)), 222 NameRefClass::Definition(Definition::Macro(def)) => Some(Def::MacroDef(def)),
223 _ => None, 223 _ => None,
diff --git a/crates/hir_ty/src/infer.rs b/crates/hir_ty/src/infer.rs
index 9a7785c76..644ebd42d 100644
--- a/crates/hir_ty/src/infer.rs
+++ b/crates/hir_ty/src/infer.rs
@@ -22,7 +22,7 @@ use arena::map::ArenaMap;
22use hir_def::{ 22use hir_def::{
23 body::Body, 23 body::Body,
24 data::{ConstData, FunctionData, StaticData}, 24 data::{ConstData, FunctionData, StaticData},
25 expr::{BindingAnnotation, ExprId, PatId}, 25 expr::{ArithOp, BinaryOp, BindingAnnotation, ExprId, PatId},
26 lang_item::LangItemTarget, 26 lang_item::LangItemTarget,
27 path::{path, Path}, 27 path::{path, Path},
28 resolver::{HasResolver, Resolver, TypeNs}, 28 resolver::{HasResolver, Resolver, TypeNs},
@@ -586,6 +586,28 @@ impl<'a> InferenceContext<'a> {
586 self.db.trait_data(trait_).associated_type_by_name(&name![Output]) 586 self.db.trait_data(trait_).associated_type_by_name(&name![Output])
587 } 587 }
588 588
589 fn resolve_binary_op_output(&self, bop: &BinaryOp) -> Option<TypeAliasId> {
590 let lang_item = match bop {
591 BinaryOp::ArithOp(aop) => match aop {
592 ArithOp::Add => "add",
593 ArithOp::Sub => "sub",
594 ArithOp::Mul => "mul",
595 ArithOp::Div => "div",
596 ArithOp::Shl => "shl",
597 ArithOp::Shr => "shr",
598 ArithOp::Rem => "rem",
599 ArithOp::BitXor => "bitxor",
600 ArithOp::BitOr => "bitor",
601 ArithOp::BitAnd => "bitand",
602 },
603 _ => return None,
604 };
605
606 let trait_ = self.resolve_lang_item(lang_item)?.as_trait();
607
608 self.db.trait_data(trait_?).associated_type_by_name(&name![Output])
609 }
610
589 fn resolve_boxed_box(&self) -> Option<AdtId> { 611 fn resolve_boxed_box(&self) -> Option<AdtId> {
590 let struct_ = self.resolve_lang_item("owned_box")?.as_struct()?; 612 let struct_ = self.resolve_lang_item("owned_box")?.as_struct()?;
591 Some(struct_.into()) 613 Some(struct_.into())
diff --git a/crates/hir_ty/src/infer/expr.rs b/crates/hir_ty/src/infer/expr.rs
index 0a141b9cb..8ac4cf89a 100644
--- a/crates/hir_ty/src/infer/expr.rs
+++ b/crates/hir_ty/src/infer/expr.rs
@@ -12,6 +12,7 @@ use hir_def::{
12}; 12};
13use hir_expand::name::{name, Name}; 13use hir_expand::name::{name, Name};
14use syntax::ast::RangeOp; 14use syntax::ast::RangeOp;
15use test_utils::mark;
15 16
16use crate::{ 17use crate::{
17 autoderef, method_resolution, op, 18 autoderef, method_resolution, op,
@@ -531,13 +532,22 @@ impl<'a> InferenceContext<'a> {
531 _ => Expectation::none(), 532 _ => Expectation::none(),
532 }; 533 };
533 let lhs_ty = self.infer_expr(*lhs, &lhs_expectation); 534 let lhs_ty = self.infer_expr(*lhs, &lhs_expectation);
534 // FIXME: find implementation of trait corresponding to operation
535 // symbol and resolve associated `Output` type
536 let rhs_expectation = op::binary_op_rhs_expectation(*op, lhs_ty.clone()); 535 let rhs_expectation = op::binary_op_rhs_expectation(*op, lhs_ty.clone());
537 let rhs_ty = self.infer_expr(*rhs, &Expectation::has_type(rhs_expectation)); 536 let rhs_ty = self.infer_expr(*rhs, &Expectation::has_type(rhs_expectation));
538 537
539 // FIXME: similar as above, return ty is often associated trait type 538 let ret = op::binary_op_return_ty(*op, lhs_ty.clone(), rhs_ty.clone());
540 op::binary_op_return_ty(*op, lhs_ty, rhs_ty) 539
540 if ret == Ty::Unknown {
541 mark::hit!(infer_expr_inner_binary_operator_overload);
542
543 self.resolve_associated_type_with_params(
544 lhs_ty,
545 self.resolve_binary_op_output(op),
546 &[rhs_ty],
547 )
548 } else {
549 ret
550 }
541 } 551 }
542 _ => Ty::Unknown, 552 _ => Ty::Unknown,
543 }, 553 },
diff --git a/crates/hir_ty/src/tests/simple.rs b/crates/hir_ty/src/tests/simple.rs
index 5b07948f3..4f72582b6 100644
--- a/crates/hir_ty/src/tests/simple.rs
+++ b/crates/hir_ty/src/tests/simple.rs
@@ -1,4 +1,5 @@
1use expect_test::expect; 1use expect_test::expect;
2use test_utils::mark;
2 3
3use super::{check_infer, check_types}; 4use super::{check_infer, check_types};
4 5
@@ -2225,3 +2226,91 @@ fn generic_default_depending_on_other_type_arg_forward() {
2225 "#]], 2226 "#]],
2226 ); 2227 );
2227} 2228}
2229
2230#[test]
2231fn infer_operator_overload() {
2232 mark::check!(infer_expr_inner_binary_operator_overload);
2233
2234 check_infer(
2235 r#"
2236 struct V2([f32; 2]);
2237
2238 #[lang = "add"]
2239 pub trait Add<Rhs = Self> {
2240 /// The resulting type after applying the `+` operator.
2241 type Output;
2242
2243 /// Performs the `+` operation.
2244 #[must_use]
2245 fn add(self, rhs: Rhs) -> Self::Output;
2246 }
2247
2248 impl Add<V2> for V2 {
2249 type Output = V2;
2250
2251 fn add(self, rhs: V2) -> V2 {
2252 let x = self.0[0] + rhs.0[0];
2253 let y = self.0[1] + rhs.0[1];
2254 V2([x, y])
2255 }
2256 }
2257
2258 fn test() {
2259 let va = V2([0.0, 1.0]);
2260 let vb = V2([0.0, 1.0]);
2261
2262 let r = va + vb;
2263 }
2264
2265 "#,
2266 expect![[r#"
2267 207..211 'self': Self
2268 213..216 'rhs': Rhs
2269 299..303 'self': V2
2270 305..308 'rhs': V2
2271 320..422 '{ ... }': V2
2272 334..335 'x': f32
2273 338..342 'self': V2
2274 338..344 'self.0': [f32; _]
2275 338..347 'self.0[0]': {unknown}
2276 338..358 'self.0...s.0[0]': f32
2277 345..346 '0': i32
2278 350..353 'rhs': V2
2279 350..355 'rhs.0': [f32; _]
2280 350..358 'rhs.0[0]': {unknown}
2281 356..357 '0': i32
2282 372..373 'y': f32
2283 376..380 'self': V2
2284 376..382 'self.0': [f32; _]
2285 376..385 'self.0[1]': {unknown}
2286 376..396 'self.0...s.0[1]': f32
2287 383..384 '1': i32
2288 388..391 'rhs': V2
2289 388..393 'rhs.0': [f32; _]
2290 388..396 'rhs.0[1]': {unknown}
2291 394..395 '1': i32
2292 406..408 'V2': V2([f32; _]) -> V2
2293 406..416 'V2([x, y])': V2
2294 409..415 '[x, y]': [f32; _]
2295 410..411 'x': f32
2296 413..414 'y': f32
2297 436..519 '{ ... vb; }': ()
2298 446..448 'va': V2
2299 451..453 'V2': V2([f32; _]) -> V2
2300 451..465 'V2([0.0, 1.0])': V2
2301 454..464 '[0.0, 1.0]': [f32; _]
2302 455..458 '0.0': f32
2303 460..463 '1.0': f32
2304 475..477 'vb': V2
2305 480..482 'V2': V2([f32; _]) -> V2
2306 480..494 'V2([0.0, 1.0])': V2
2307 483..493 '[0.0, 1.0]': [f32; _]
2308 484..487 '0.0': f32
2309 489..492 '1.0': f32
2310 505..506 'r': V2
2311 509..511 'va': V2
2312 509..516 'va + vb': V2
2313 514..516 'vb': V2
2314 "#]],
2315 );
2316}
diff --git a/crates/ide/src/diagnostics.rs b/crates/ide/src/diagnostics.rs
index b30cdb6ed..1e5ea4617 100644
--- a/crates/ide/src/diagnostics.rs
+++ b/crates/ide/src/diagnostics.rs
@@ -5,6 +5,7 @@
5//! original files. So we need to map the ranges. 5//! original files. So we need to map the ranges.
6 6
7mod fixes; 7mod fixes;
8mod field_shorthand;
8 9
9use std::cell::RefCell; 10use std::cell::RefCell;
10 11
@@ -80,7 +81,7 @@ pub(crate) fn diagnostics(
80 81
81 for node in parse.tree().syntax().descendants() { 82 for node in parse.tree().syntax().descendants() {
82 check_unnecessary_braces_in_use_statement(&mut res, file_id, &node); 83 check_unnecessary_braces_in_use_statement(&mut res, file_id, &node);
83 check_struct_shorthand_initialization(&mut res, file_id, &node); 84 field_shorthand::check(&mut res, file_id, &node);
84 } 85 }
85 let res = RefCell::new(res); 86 let res = RefCell::new(res);
86 let sink_builder = DiagnosticSinkBuilder::new() 87 let sink_builder = DiagnosticSinkBuilder::new()
@@ -188,42 +189,6 @@ fn text_edit_for_remove_unnecessary_braces_with_self_in_use_statement(
188 None 189 None
189} 190}
190 191
191fn check_struct_shorthand_initialization(
192 acc: &mut Vec<Diagnostic>,
193 file_id: FileId,
194 node: &SyntaxNode,
195) -> Option<()> {
196 let record_lit = ast::RecordExpr::cast(node.clone())?;
197 let record_field_list = record_lit.record_expr_field_list()?;
198 for record_field in record_field_list.fields() {
199 if let (Some(name_ref), Some(expr)) = (record_field.name_ref(), record_field.expr()) {
200 let field_name = name_ref.syntax().text().to_string();
201 let field_expr = expr.syntax().text().to_string();
202 let field_name_is_tup_index = name_ref.as_tuple_field().is_some();
203 if field_name == field_expr && !field_name_is_tup_index {
204 let mut edit_builder = TextEdit::builder();
205 edit_builder.delete(record_field.syntax().text_range());
206 edit_builder.insert(record_field.syntax().text_range().start(), field_name);
207 let edit = edit_builder.finish();
208
209 let field_range = record_field.syntax().text_range();
210 acc.push(Diagnostic {
211 // name: None,
212 range: field_range,
213 message: "Shorthand struct initialization".to_string(),
214 severity: Severity::WeakWarning,
215 fix: Some(Fix::new(
216 "Use struct shorthand initialization",
217 SourceFileEdit { file_id, edit }.into(),
218 field_range,
219 )),
220 });
221 }
222 }
223 }
224 Some(())
225}
226
227#[cfg(test)] 192#[cfg(test)]
228mod tests { 193mod tests {
229 use expect_test::{expect, Expect}; 194 use expect_test::{expect, Expect};
@@ -237,7 +202,7 @@ mod tests {
237 /// * a diagnostic is produced 202 /// * a diagnostic is produced
238 /// * this diagnostic fix trigger range touches the input cursor position 203 /// * this diagnostic fix trigger range touches the input cursor position
239 /// * that the contents of the file containing the cursor match `after` after the diagnostic fix is applied 204 /// * that the contents of the file containing the cursor match `after` after the diagnostic fix is applied
240 fn check_fix(ra_fixture_before: &str, ra_fixture_after: &str) { 205 pub(super) fn check_fix(ra_fixture_before: &str, ra_fixture_after: &str) {
241 let after = trim_indent(ra_fixture_after); 206 let after = trim_indent(ra_fixture_after);
242 207
243 let (analysis, file_position) = fixture::position(ra_fixture_before); 208 let (analysis, file_position) = fixture::position(ra_fixture_before);
@@ -319,7 +284,7 @@ mod tests {
319 284
320 /// Takes a multi-file input fixture with annotated cursor position and checks that no diagnostics 285 /// Takes a multi-file input fixture with annotated cursor position and checks that no diagnostics
321 /// apply to the file containing the cursor. 286 /// apply to the file containing the cursor.
322 fn check_no_diagnostics(ra_fixture: &str) { 287 pub(crate) fn check_no_diagnostics(ra_fixture: &str) {
323 let (analysis, files) = fixture::files(ra_fixture); 288 let (analysis, files) = fixture::files(ra_fixture);
324 let diagnostics = files 289 let diagnostics = files
325 .into_iter() 290 .into_iter()
@@ -720,58 +685,6 @@ mod a {
720 } 685 }
721 686
722 #[test] 687 #[test]
723 fn test_check_struct_shorthand_initialization() {
724 check_no_diagnostics(
725 r#"
726struct A { a: &'static str }
727fn main() { A { a: "hello" } }
728"#,
729 );
730 check_no_diagnostics(
731 r#"
732struct A(usize);
733fn main() { A { 0: 0 } }
734"#,
735 );
736
737 check_fix(
738 r#"
739struct A { a: &'static str }
740fn main() {
741 let a = "haha";
742 A { a<|>: a }
743}
744"#,
745 r#"
746struct A { a: &'static str }
747fn main() {
748 let a = "haha";
749 A { a }
750}
751"#,
752 );
753
754 check_fix(
755 r#"
756struct A { a: &'static str, b: &'static str }
757fn main() {
758 let a = "haha";
759 let b = "bb";
760 A { a<|>: a, b }
761}
762"#,
763 r#"
764struct A { a: &'static str, b: &'static str }
765fn main() {
766 let a = "haha";
767 let b = "bb";
768 A { a, b }
769}
770"#,
771 );
772 }
773
774 #[test]
775 fn test_add_field_from_usage() { 688 fn test_add_field_from_usage() {
776 check_fix( 689 check_fix(
777 r" 690 r"
diff --git a/crates/ide/src/diagnostics/field_shorthand.rs b/crates/ide/src/diagnostics/field_shorthand.rs
new file mode 100644
index 000000000..2c4acd783
--- /dev/null
+++ b/crates/ide/src/diagnostics/field_shorthand.rs
@@ -0,0 +1,206 @@
1//! Suggests shortening `Foo { field: field }` to `Foo { field }` in both
2//! expressions and patterns.
3
4use base_db::FileId;
5use ide_db::source_change::SourceFileEdit;
6use syntax::{ast, match_ast, AstNode, SyntaxNode};
7use text_edit::TextEdit;
8
9use crate::{Diagnostic, Fix, Severity};
10
11pub(super) fn check(acc: &mut Vec<Diagnostic>, file_id: FileId, node: &SyntaxNode) {
12 match_ast! {
13 match node {
14 ast::RecordExpr(it) => check_expr_field_shorthand(acc, file_id, it),
15 ast::RecordPat(it) => check_pat_field_shorthand(acc, file_id, it),
16 _ => ()
17 }
18 };
19}
20
21fn check_expr_field_shorthand(
22 acc: &mut Vec<Diagnostic>,
23 file_id: FileId,
24 record_expr: ast::RecordExpr,
25) {
26 let record_field_list = match record_expr.record_expr_field_list() {
27 Some(it) => it,
28 None => return,
29 };
30 for record_field in record_field_list.fields() {
31 let (name_ref, expr) = match record_field.name_ref().zip(record_field.expr()) {
32 Some(it) => it,
33 None => continue,
34 };
35
36 let field_name = name_ref.syntax().text().to_string();
37 let field_expr = expr.syntax().text().to_string();
38 let field_name_is_tup_index = name_ref.as_tuple_field().is_some();
39 if field_name != field_expr || field_name_is_tup_index {
40 continue;
41 }
42
43 let mut edit_builder = TextEdit::builder();
44 edit_builder.delete(record_field.syntax().text_range());
45 edit_builder.insert(record_field.syntax().text_range().start(), field_name);
46 let edit = edit_builder.finish();
47
48 let field_range = record_field.syntax().text_range();
49 acc.push(Diagnostic {
50 // name: None,
51 range: field_range,
52 message: "Shorthand struct initialization".to_string(),
53 severity: Severity::WeakWarning,
54 fix: Some(Fix::new(
55 "Use struct shorthand initialization",
56 SourceFileEdit { file_id, edit }.into(),
57 field_range,
58 )),
59 });
60 }
61}
62
63fn check_pat_field_shorthand(
64 acc: &mut Vec<Diagnostic>,
65 file_id: FileId,
66 record_pat: ast::RecordPat,
67) {
68 let record_pat_field_list = match record_pat.record_pat_field_list() {
69 Some(it) => it,
70 None => return,
71 };
72 for record_pat_field in record_pat_field_list.fields() {
73 let (name_ref, pat) = match record_pat_field.name_ref().zip(record_pat_field.pat()) {
74 Some(it) => it,
75 None => continue,
76 };
77
78 let field_name = name_ref.syntax().text().to_string();
79 let field_pat = pat.syntax().text().to_string();
80 let field_name_is_tup_index = name_ref.as_tuple_field().is_some();
81 if field_name != field_pat || field_name_is_tup_index {
82 continue;
83 }
84
85 let mut edit_builder = TextEdit::builder();
86 edit_builder.delete(record_pat_field.syntax().text_range());
87 edit_builder.insert(record_pat_field.syntax().text_range().start(), field_name);
88 let edit = edit_builder.finish();
89
90 let field_range = record_pat_field.syntax().text_range();
91 acc.push(Diagnostic {
92 // name: None,
93 range: field_range,
94 message: "Shorthand struct pattern".to_string(),
95 severity: Severity::WeakWarning,
96 fix: Some(Fix::new(
97 "Use struct field shorthand",
98 SourceFileEdit { file_id, edit }.into(),
99 field_range,
100 )),
101 });
102 }
103}
104
105#[cfg(test)]
106mod tests {
107 use crate::diagnostics::tests::{check_fix, check_no_diagnostics};
108
109 #[test]
110 fn test_check_expr_field_shorthand() {
111 check_no_diagnostics(
112 r#"
113struct A { a: &'static str }
114fn main() { A { a: "hello" } }
115"#,
116 );
117 check_no_diagnostics(
118 r#"
119struct A(usize);
120fn main() { A { 0: 0 } }
121"#,
122 );
123
124 check_fix(
125 r#"
126struct A { a: &'static str }
127fn main() {
128 let a = "haha";
129 A { a<|>: a }
130}
131"#,
132 r#"
133struct A { a: &'static str }
134fn main() {
135 let a = "haha";
136 A { a }
137}
138"#,
139 );
140
141 check_fix(
142 r#"
143struct A { a: &'static str, b: &'static str }
144fn main() {
145 let a = "haha";
146 let b = "bb";
147 A { a<|>: a, b }
148}
149"#,
150 r#"
151struct A { a: &'static str, b: &'static str }
152fn main() {
153 let a = "haha";
154 let b = "bb";
155 A { a, b }
156}
157"#,
158 );
159 }
160
161 #[test]
162 fn test_check_pat_field_shorthand() {
163 check_no_diagnostics(
164 r#"
165struct A { a: &'static str }
166fn f(a: A) { let A { a: hello } = a; }
167"#,
168 );
169 check_no_diagnostics(
170 r#"
171struct A(usize);
172fn f(a: A) { let A { 0: 0 } = a; }
173"#,
174 );
175
176 check_fix(
177 r#"
178struct A { a: &'static str }
179fn f(a: A) {
180 let A { a<|>: a } = a;
181}
182"#,
183 r#"
184struct A { a: &'static str }
185fn f(a: A) {
186 let A { a } = a;
187}
188"#,
189 );
190
191 check_fix(
192 r#"
193struct A { a: &'static str, b: &'static str }
194fn f(a: A) {
195 let A { a<|>: a, b } = a;
196}
197"#,
198 r#"
199struct A { a: &'static str, b: &'static str }
200fn f(a: A) {
201 let A { a, b } = a;
202}
203"#,
204 );
205 }
206}
diff --git a/crates/ide/src/doc_links.rs b/crates/ide/src/doc_links.rs
index db3f911c8..d9dc63b33 100644
--- a/crates/ide/src/doc_links.rs
+++ b/crates/ide/src/doc_links.rs
@@ -14,7 +14,7 @@ use hir::{
14 ModuleDef, 14 ModuleDef,
15}; 15};
16use ide_db::{ 16use ide_db::{
17 defs::{classify_name, classify_name_ref, Definition}, 17 defs::{Definition, NameClass, NameRefClass},
18 RootDatabase, 18 RootDatabase,
19}; 19};
20use syntax::{ast, match_ast, AstNode, SyntaxKind::*, SyntaxToken, TokenAtOffset, T}; 20use syntax::{ast, match_ast, AstNode, SyntaxKind::*, SyntaxToken, TokenAtOffset, T};
@@ -232,8 +232,8 @@ pub(crate) fn external_docs(
232 let node = token.parent(); 232 let node = token.parent();
233 let definition = match_ast! { 233 let definition = match_ast! {
234 match node { 234 match node {
235 ast::NameRef(name_ref) => classify_name_ref(&sema, &name_ref).map(|d| d.definition(sema.db)), 235 ast::NameRef(name_ref) => NameRefClass::classify(&sema, &name_ref).map(|d| d.referenced(sema.db)),
236 ast::Name(name) => classify_name(&sema, &name).map(|d| d.definition(sema.db)), 236 ast::Name(name) => NameClass::classify(&sema, &name).map(|d| d.referenced_or_defined(sema.db)),
237 _ => None, 237 _ => None,
238 } 238 }
239 }; 239 };
diff --git a/crates/ide/src/goto_definition.rs b/crates/ide/src/goto_definition.rs
index 582bf4837..a87e31019 100644
--- a/crates/ide/src/goto_definition.rs
+++ b/crates/ide/src/goto_definition.rs
@@ -1,6 +1,6 @@
1use hir::Semantics; 1use hir::Semantics;
2use ide_db::{ 2use ide_db::{
3 defs::{classify_name, classify_name_ref}, 3 defs::{NameClass, NameRefClass},
4 symbol_index, RootDatabase, 4 symbol_index, RootDatabase,
5}; 5};
6use syntax::{ 6use syntax::{
@@ -40,7 +40,7 @@ pub(crate) fn goto_definition(
40 reference_definition(&sema, &name_ref).to_vec() 40 reference_definition(&sema, &name_ref).to_vec()
41 }, 41 },
42 ast::Name(name) => { 42 ast::Name(name) => {
43 let def = classify_name(&sema, &name)?.definition(sema.db); 43 let def = NameClass::classify(&sema, &name)?.referenced_or_defined(sema.db);
44 let nav = def.try_to_nav(sema.db)?; 44 let nav = def.try_to_nav(sema.db)?;
45 vec![nav] 45 vec![nav]
46 }, 46 },
@@ -81,9 +81,9 @@ pub(crate) fn reference_definition(
81 sema: &Semantics<RootDatabase>, 81 sema: &Semantics<RootDatabase>,
82 name_ref: &ast::NameRef, 82 name_ref: &ast::NameRef,
83) -> ReferenceResult { 83) -> ReferenceResult {
84 let name_kind = classify_name_ref(sema, name_ref); 84 let name_kind = NameRefClass::classify(sema, name_ref);
85 if let Some(def) = name_kind { 85 if let Some(def) = name_kind {
86 let def = def.definition(sema.db); 86 let def = def.referenced(sema.db);
87 return match def.try_to_nav(sema.db) { 87 return match def.try_to_nav(sema.db) {
88 Some(nav) => ReferenceResult::Exact(nav), 88 Some(nav) => ReferenceResult::Exact(nav),
89 None => ReferenceResult::Approximate(Vec::new()), 89 None => ReferenceResult::Approximate(Vec::new()),
diff --git a/crates/ide/src/hover.rs b/crates/ide/src/hover.rs
index 6290b35bd..845333e2a 100644
--- a/crates/ide/src/hover.rs
+++ b/crates/ide/src/hover.rs
@@ -4,7 +4,7 @@ use hir::{
4 Module, ModuleDef, ModuleSource, Semantics, 4 Module, ModuleDef, ModuleSource, Semantics,
5}; 5};
6use ide_db::{ 6use ide_db::{
7 defs::{classify_name, classify_name_ref, Definition}, 7 defs::{Definition, NameClass, NameRefClass},
8 RootDatabase, 8 RootDatabase,
9}; 9};
10use itertools::Itertools; 10use itertools::Itertools;
@@ -107,8 +107,8 @@ pub(crate) fn hover(
107 let node = token.parent(); 107 let node = token.parent();
108 let definition = match_ast! { 108 let definition = match_ast! {
109 match node { 109 match node {
110 ast::NameRef(name_ref) => classify_name_ref(&sema, &name_ref).map(|d| d.definition(sema.db)), 110 ast::Name(name) => NameClass::classify(&sema, &name).and_then(|d| d.defined(sema.db)),
111 ast::Name(name) => classify_name(&sema, &name).map(|d| d.definition(sema.db)), 111 ast::NameRef(name_ref) => NameRefClass::classify(&sema, &name_ref).map(|d| d.referenced(sema.db)),
112 _ => None, 112 _ => None,
113 } 113 }
114 }; 114 };
@@ -3232,4 +3232,27 @@ fn main() { let foo_test = name_with_dashes::wrapper::Thing::new<|>(); }
3232 "#]], 3232 "#]],
3233 ) 3233 )
3234 } 3234 }
3235
3236 #[test]
3237 fn hover_field_pat_shorthand_ref_match_ergonomics() {
3238 check(
3239 r#"
3240struct S {
3241 f: i32,
3242}
3243
3244fn main() {
3245 let s = S { f: 0 };
3246 let S { f<|> } = &s;
3247}
3248"#,
3249 expect![[r#"
3250 *f*
3251
3252 ```rust
3253 &i32
3254 ```
3255 "#]],
3256 );
3257 }
3235} 3258}
diff --git a/crates/ide/src/references.rs b/crates/ide/src/references.rs
index 88e2f2db3..67ec257a8 100644
--- a/crates/ide/src/references.rs
+++ b/crates/ide/src/references.rs
@@ -13,7 +13,7 @@ pub(crate) mod rename;
13 13
14use hir::Semantics; 14use hir::Semantics;
15use ide_db::{ 15use ide_db::{
16 defs::{classify_name, classify_name_ref, Definition}, 16 defs::{Definition, NameClass, NameRefClass},
17 search::SearchScope, 17 search::SearchScope,
18 RootDatabase, 18 RootDatabase,
19}; 19};
@@ -132,13 +132,13 @@ fn find_name(
132 opt_name: Option<ast::Name>, 132 opt_name: Option<ast::Name>,
133) -> Option<RangeInfo<Definition>> { 133) -> Option<RangeInfo<Definition>> {
134 if let Some(name) = opt_name { 134 if let Some(name) = opt_name {
135 let def = classify_name(sema, &name)?.definition(sema.db); 135 let def = NameClass::classify(sema, &name)?.referenced_or_defined(sema.db);
136 let range = name.syntax().text_range(); 136 let range = name.syntax().text_range();
137 return Some(RangeInfo::new(range, def)); 137 return Some(RangeInfo::new(range, def));
138 } 138 }
139 let name_ref = 139 let name_ref =
140 sema.find_node_at_offset_with_descend::<ast::NameRef>(&syntax, position.offset)?; 140 sema.find_node_at_offset_with_descend::<ast::NameRef>(&syntax, position.offset)?;
141 let def = classify_name_ref(sema, &name_ref)?.definition(sema.db); 141 let def = NameRefClass::classify(sema, &name_ref)?.referenced(sema.db);
142 let range = name_ref.syntax().text_range(); 142 let range = name_ref.syntax().text_range();
143 Some(RangeInfo::new(range, def)) 143 Some(RangeInfo::new(range, def))
144} 144}
diff --git a/crates/ide/src/references/rename.rs b/crates/ide/src/references/rename.rs
index f9a11e43d..35aafc49d 100644
--- a/crates/ide/src/references/rename.rs
+++ b/crates/ide/src/references/rename.rs
@@ -3,7 +3,7 @@
3use base_db::SourceDatabaseExt; 3use base_db::SourceDatabaseExt;
4use hir::{Module, ModuleDef, ModuleSource, Semantics}; 4use hir::{Module, ModuleDef, ModuleSource, Semantics};
5use ide_db::{ 5use ide_db::{
6 defs::{classify_name, classify_name_ref, Definition, NameClass, NameRefClass}, 6 defs::{Definition, NameClass, NameRefClass},
7 RootDatabase, 7 RootDatabase,
8}; 8};
9 9
@@ -88,13 +88,13 @@ fn find_module_at_offset(
88 let module = match_ast! { 88 let module = match_ast! {
89 match (ident.parent()) { 89 match (ident.parent()) {
90 ast::NameRef(name_ref) => { 90 ast::NameRef(name_ref) => {
91 match classify_name_ref(sema, &name_ref)? { 91 match NameRefClass::classify(sema, &name_ref)? {
92 NameRefClass::Definition(Definition::ModuleDef(ModuleDef::Module(module))) => module, 92 NameRefClass::Definition(Definition::ModuleDef(ModuleDef::Module(module))) => module,
93 _ => return None, 93 _ => return None,
94 } 94 }
95 }, 95 },
96 ast::Name(name) => { 96 ast::Name(name) => {
97 match classify_name(&sema, &name)? { 97 match NameClass::classify(&sema, &name)? {
98 NameClass::Definition(Definition::ModuleDef(ModuleDef::Module(module))) => module, 98 NameClass::Definition(Definition::ModuleDef(ModuleDef::Module(module))) => module,
99 _ => return None, 99 _ => return None,
100 } 100 }
diff --git a/crates/ide/src/syntax_highlighting.rs b/crates/ide/src/syntax_highlighting.rs
index 6aafd6fd5..b35c03162 100644
--- a/crates/ide/src/syntax_highlighting.rs
+++ b/crates/ide/src/syntax_highlighting.rs
@@ -1,12 +1,14 @@
1mod tags; 1mod format;
2mod html; 2mod html;
3mod injection; 3mod injection;
4mod macro_rules;
5mod tags;
4#[cfg(test)] 6#[cfg(test)]
5mod tests; 7mod tests;
6 8
7use hir::{Local, Name, Semantics, VariantDef}; 9use hir::{Local, Name, Semantics, VariantDef};
8use ide_db::{ 10use ide_db::{
9 defs::{classify_name, classify_name_ref, Definition, NameClass, NameRefClass}, 11 defs::{Definition, NameClass, NameRefClass},
10 RootDatabase, 12 RootDatabase,
11}; 13};
12use rustc_hash::FxHashMap; 14use rustc_hash::FxHashMap;
@@ -17,9 +19,11 @@ use syntax::{
17 SyntaxNode, SyntaxToken, TextRange, WalkEvent, T, 19 SyntaxNode, SyntaxToken, TextRange, WalkEvent, T,
18}; 20};
19 21
20use crate::FileId; 22use crate::{
23 syntax_highlighting::{format::FormatStringHighlighter, macro_rules::MacroRulesHighlighter},
24 FileId,
25};
21 26
22use ast::FormatSpecifier;
23pub(crate) use html::highlight_as_html; 27pub(crate) use html::highlight_as_html;
24pub use tags::{Highlight, HighlightModifier, HighlightModifiers, HighlightTag}; 28pub use tags::{Highlight, HighlightModifier, HighlightModifiers, HighlightTag};
25 29
@@ -68,8 +72,9 @@ pub(crate) fn highlight(
68 // When we leave a node, the we use it to flatten the highlighted ranges. 72 // When we leave a node, the we use it to flatten the highlighted ranges.
69 let mut stack = HighlightedRangeStack::new(); 73 let mut stack = HighlightedRangeStack::new();
70 74
71 let mut current_macro_call: Option<(ast::MacroCall, Option<MacroMatcherParseState>)> = None; 75 let mut current_macro_call: Option<ast::MacroCall> = None;
72 let mut format_string: Option<SyntaxElement> = None; 76 let mut format_string_highlighter = FormatStringHighlighter::default();
77 let mut macro_rules_highlighter = MacroRulesHighlighter::default();
73 78
74 // Walk all nodes, keeping track of whether we are inside a macro or not. 79 // Walk all nodes, keeping track of whether we are inside a macro or not.
75 // If in macro, expand it first and highlight the expanded code. 80 // If in macro, expand it first and highlight the expanded code.
@@ -99,9 +104,8 @@ pub(crate) fn highlight(
99 binding_hash: None, 104 binding_hash: None,
100 }); 105 });
101 } 106 }
102 let mut is_macro_rules = None;
103 if let Some(name) = mc.is_macro_rules() { 107 if let Some(name) = mc.is_macro_rules() {
104 is_macro_rules = Some(MacroMatcherParseState::new()); 108 macro_rules_highlighter.init();
105 if let Some((highlight, binding_hash)) = highlight_element( 109 if let Some((highlight, binding_hash)) = highlight_element(
106 &sema, 110 &sema,
107 &mut bindings_shadow_count, 111 &mut bindings_shadow_count,
@@ -115,13 +119,14 @@ pub(crate) fn highlight(
115 }); 119 });
116 } 120 }
117 } 121 }
118 current_macro_call = Some((mc.clone(), is_macro_rules)); 122 current_macro_call = Some(mc.clone());
119 continue; 123 continue;
120 } 124 }
121 WalkEvent::Leave(Some(mc)) => { 125 WalkEvent::Leave(Some(mc)) => {
122 assert!(current_macro_call.map(|it| it.0) == Some(mc)); 126 assert!(current_macro_call == Some(mc));
123 current_macro_call = None; 127 current_macro_call = None;
124 format_string = None; 128 format_string_highlighter = FormatStringHighlighter::default();
129 macro_rules_highlighter = MacroRulesHighlighter::default();
125 } 130 }
126 _ => (), 131 _ => (),
127 } 132 }
@@ -148,20 +153,6 @@ pub(crate) fn highlight(
148 WalkEvent::Leave(_) => continue, 153 WalkEvent::Leave(_) => continue,
149 }; 154 };
150 155
151 // check if in matcher part of a macro_rules rule
152 if let Some((_, Some(ref mut state))) = current_macro_call {
153 if let Some(tok) = element.as_token() {
154 if matches!(
155 update_macro_rules_state(tok, state),
156 RuleState::Matcher | RuleState::Expander
157 ) {
158 if skip_metavariables(element.clone()) {
159 continue;
160 }
161 }
162 }
163 }
164
165 let range = element.text_range(); 156 let range = element.text_range();
166 157
167 let element_to_highlight = if current_macro_call.is_some() && element.kind() != COMMENT { 158 let element_to_highlight = if current_macro_call.is_some() && element.kind() != COMMENT {
@@ -173,29 +164,9 @@ pub(crate) fn highlight(
173 let token = sema.descend_into_macros(token.clone()); 164 let token = sema.descend_into_macros(token.clone());
174 let parent = token.parent(); 165 let parent = token.parent();
175 166
176 // Check if macro takes a format string and remember it for highlighting later. 167 format_string_highlighter.check_for_format_string(&parent);
177 // The macros that accept a format string expand to a compiler builtin macros 168 if let Some(tok) = element.as_token() {
178 // `format_args` and `format_args_nl`. 169 macro_rules_highlighter.advance(tok);
179 if let Some(name) = parent
180 .parent()
181 .and_then(ast::MacroCall::cast)
182 .and_then(|mc| mc.path())
183 .and_then(|p| p.segment())
184 .and_then(|s| s.name_ref())
185 {
186 match name.text().as_str() {
187 "format_args" | "format_args_nl" => {
188 format_string = parent
189 .children_with_tokens()
190 .filter(|t| t.kind() != WHITESPACE)
191 .nth(1)
192 .filter(|e| {
193 ast::String::can_cast(e.kind())
194 || ast::RawString::can_cast(e.kind())
195 })
196 }
197 _ => {}
198 }
199 } 170 }
200 171
201 // We only care Name and Name_ref 172 // We only care Name and Name_ref
@@ -214,31 +185,20 @@ pub(crate) fn highlight(
214 } 185 }
215 } 186 }
216 187
217 let is_format_string = format_string.as_ref() == Some(&element_to_highlight);
218
219 if let Some((highlight, binding_hash)) = highlight_element( 188 if let Some((highlight, binding_hash)) = highlight_element(
220 &sema, 189 &sema,
221 &mut bindings_shadow_count, 190 &mut bindings_shadow_count,
222 syntactic_name_ref_highlighting, 191 syntactic_name_ref_highlighting,
223 element_to_highlight.clone(), 192 element_to_highlight.clone(),
224 ) { 193 ) {
225 stack.add(HighlightedRange { range, highlight, binding_hash }); 194 if macro_rules_highlighter.highlight(element_to_highlight.clone()).is_none() {
195 stack.add(HighlightedRange { range, highlight, binding_hash });
196 }
197
226 if let Some(string) = 198 if let Some(string) =
227 element_to_highlight.as_token().cloned().and_then(ast::String::cast) 199 element_to_highlight.as_token().cloned().and_then(ast::String::cast)
228 { 200 {
229 if is_format_string { 201 format_string_highlighter.highlight_format_string(&mut stack, &string, range);
230 stack.push();
231 string.lex_format_specifier(|piece_range, kind| {
232 if let Some(highlight) = highlight_format_specifier(kind) {
233 stack.add(HighlightedRange {
234 range: piece_range + range.start(),
235 highlight: highlight.into(),
236 binding_hash: None,
237 });
238 }
239 });
240 stack.pop();
241 }
242 // Highlight escape sequences 202 // Highlight escape sequences
243 if let Some(char_ranges) = string.char_ranges() { 203 if let Some(char_ranges) = string.char_ranges() {
244 stack.push(); 204 stack.push();
@@ -256,19 +216,7 @@ pub(crate) fn highlight(
256 } else if let Some(string) = 216 } else if let Some(string) =
257 element_to_highlight.as_token().cloned().and_then(ast::RawString::cast) 217 element_to_highlight.as_token().cloned().and_then(ast::RawString::cast)
258 { 218 {
259 if is_format_string { 219 format_string_highlighter.highlight_format_string(&mut stack, &string, range);
260 stack.push();
261 string.lex_format_specifier(|piece_range, kind| {
262 if let Some(highlight) = highlight_format_specifier(kind) {
263 stack.add(HighlightedRange {
264 range: piece_range + range.start(),
265 highlight: highlight.into(),
266 binding_hash: None,
267 });
268 }
269 });
270 stack.pop();
271 }
272 } 220 }
273 } 221 }
274 } 222 }
@@ -436,24 +384,6 @@ impl HighlightedRangeStack {
436 } 384 }
437} 385}
438 386
439fn highlight_format_specifier(kind: FormatSpecifier) -> Option<HighlightTag> {
440 Some(match kind {
441 FormatSpecifier::Open
442 | FormatSpecifier::Close
443 | FormatSpecifier::Colon
444 | FormatSpecifier::Fill
445 | FormatSpecifier::Align
446 | FormatSpecifier::Sign
447 | FormatSpecifier::NumberSign
448 | FormatSpecifier::DollarSign
449 | FormatSpecifier::Dot
450 | FormatSpecifier::Asterisk
451 | FormatSpecifier::QuestionMark => HighlightTag::FormatSpecifier,
452 FormatSpecifier::Integer | FormatSpecifier::Zero => HighlightTag::NumericLiteral,
453 FormatSpecifier::Identifier => HighlightTag::Local,
454 })
455}
456
457fn macro_call_range(macro_call: &ast::MacroCall) -> Option<TextRange> { 387fn macro_call_range(macro_call: &ast::MacroCall) -> Option<TextRange> {
458 let path = macro_call.path()?; 388 let path = macro_call.path()?;
459 let name_ref = path.segment()?.name_ref()?; 389 let name_ref = path.segment()?.name_ref()?;
@@ -513,7 +443,7 @@ fn highlight_element(
513 // Highlight definitions depending on the "type" of the definition. 443 // Highlight definitions depending on the "type" of the definition.
514 NAME => { 444 NAME => {
515 let name = element.into_node().and_then(ast::Name::cast).unwrap(); 445 let name = element.into_node().and_then(ast::Name::cast).unwrap();
516 let name_kind = classify_name(sema, &name); 446 let name_kind = NameClass::classify(sema, &name);
517 447
518 if let Some(NameClass::Definition(Definition::Local(local))) = &name_kind { 448 if let Some(NameClass::Definition(Definition::Local(local))) = &name_kind {
519 if let Some(name) = local.name(db) { 449 if let Some(name) = local.name(db) {
@@ -529,9 +459,9 @@ fn highlight_element(
529 highlight_def(db, def) | HighlightModifier::Definition 459 highlight_def(db, def) | HighlightModifier::Definition
530 } 460 }
531 Some(NameClass::ConstReference(def)) => highlight_def(db, def), 461 Some(NameClass::ConstReference(def)) => highlight_def(db, def),
532 Some(NameClass::FieldShorthand { field, .. }) => { 462 Some(NameClass::PatFieldShorthand { field_ref, .. }) => {
533 let mut h = HighlightTag::Field.into(); 463 let mut h = HighlightTag::Field.into();
534 if let Definition::Field(field) = field { 464 if let Definition::Field(field) = field_ref {
535 if let VariantDef::Union(_) = field.parent_def(db) { 465 if let VariantDef::Union(_) = field.parent_def(db) {
536 h |= HighlightModifier::Unsafe; 466 h |= HighlightModifier::Unsafe;
537 } 467 }
@@ -550,7 +480,7 @@ fn highlight_element(
550 NAME_REF => { 480 NAME_REF => {
551 let name_ref = element.into_node().and_then(ast::NameRef::cast).unwrap(); 481 let name_ref = element.into_node().and_then(ast::NameRef::cast).unwrap();
552 highlight_func_by_name_ref(sema, &name_ref).unwrap_or_else(|| { 482 highlight_func_by_name_ref(sema, &name_ref).unwrap_or_else(|| {
553 match classify_name_ref(sema, &name_ref) { 483 match NameRefClass::classify(sema, &name_ref) {
554 Some(name_kind) => match name_kind { 484 Some(name_kind) => match name_kind {
555 NameRefClass::ExternCrate(_) => HighlightTag::Module.into(), 485 NameRefClass::ExternCrate(_) => HighlightTag::Module.into(),
556 NameRefClass::Definition(def) => { 486 NameRefClass::Definition(def) => {
@@ -934,99 +864,3 @@ fn highlight_name_ref_by_syntax(name: ast::NameRef, sema: &Semantics<RootDatabas
934 _ => default.into(), 864 _ => default.into(),
935 } 865 }
936} 866}
937
938struct MacroMatcherParseState {
939 /// Opening and corresponding closing bracket of the matcher or expander of the current rule
940 paren_ty: Option<(SyntaxKind, SyntaxKind)>,
941 paren_level: usize,
942 rule_state: RuleState,
943 /// Whether we are inside the outer `{` `}` macro block that holds the rules
944 in_invoc_body: bool,
945}
946
947impl MacroMatcherParseState {
948 fn new() -> Self {
949 MacroMatcherParseState {
950 paren_ty: None,
951 paren_level: 0,
952 in_invoc_body: false,
953 rule_state: RuleState::None,
954 }
955 }
956}
957
958#[derive(Copy, Clone, PartialEq)]
959enum RuleState {
960 Matcher,
961 Expander,
962 Between,
963 None,
964}
965
966impl RuleState {
967 fn transition(&mut self) {
968 *self = match self {
969 RuleState::Matcher => RuleState::Between,
970 RuleState::Expander => RuleState::None,
971 RuleState::Between => RuleState::Expander,
972 RuleState::None => RuleState::Matcher,
973 };
974 }
975}
976
977fn update_macro_rules_state(tok: &SyntaxToken, state: &mut MacroMatcherParseState) -> RuleState {
978 if !state.in_invoc_body {
979 if tok.kind() == T!['{'] {
980 state.in_invoc_body = true;
981 }
982 return state.rule_state;
983 }
984
985 match state.paren_ty {
986 Some((open, close)) => {
987 if tok.kind() == open {
988 state.paren_level += 1;
989 } else if tok.kind() == close {
990 state.paren_level -= 1;
991 if state.paren_level == 0 {
992 let res = state.rule_state;
993 state.rule_state.transition();
994 state.paren_ty = None;
995 return res;
996 }
997 }
998 }
999 None => {
1000 match tok.kind() {
1001 T!['('] => {
1002 state.paren_ty = Some((T!['('], T![')']));
1003 }
1004 T!['{'] => {
1005 state.paren_ty = Some((T!['{'], T!['}']));
1006 }
1007 T!['['] => {
1008 state.paren_ty = Some((T!['['], T![']']));
1009 }
1010 _ => (),
1011 }
1012 if state.paren_ty.is_some() {
1013 state.paren_level = 1;
1014 state.rule_state.transition();
1015 }
1016 }
1017 }
1018 state.rule_state
1019}
1020
1021fn skip_metavariables(element: SyntaxElement) -> bool {
1022 let tok = match element.as_token() {
1023 Some(tok) => tok,
1024 None => return false,
1025 };
1026 let is_fragment = || tok.prev_token().map(|tok| tok.kind()) == Some(T![$]);
1027 match tok.kind() {
1028 IDENT if is_fragment() => true,
1029 kind if kind.is_keyword() && is_fragment() => true,
1030 _ => false,
1031 }
1032}
diff --git a/crates/ide/src/syntax_highlighting/format.rs b/crates/ide/src/syntax_highlighting/format.rs
new file mode 100644
index 000000000..71bde24f0
--- /dev/null
+++ b/crates/ide/src/syntax_highlighting/format.rs
@@ -0,0 +1,78 @@
1//! Syntax highlighting for format macro strings.
2use syntax::{
3 ast::{self, FormatSpecifier, HasFormatSpecifier},
4 AstNode, AstToken, SyntaxElement, SyntaxKind, SyntaxNode, TextRange,
5};
6
7use crate::{syntax_highlighting::HighlightedRangeStack, HighlightTag, HighlightedRange};
8
9#[derive(Default)]
10pub(super) struct FormatStringHighlighter {
11 format_string: Option<SyntaxElement>,
12}
13
14impl FormatStringHighlighter {
15 pub(super) fn check_for_format_string(&mut self, parent: &SyntaxNode) {
16 // Check if macro takes a format string and remember it for highlighting later.
17 // The macros that accept a format string expand to a compiler builtin macros
18 // `format_args` and `format_args_nl`.
19 if let Some(name) = parent
20 .parent()
21 .and_then(ast::MacroCall::cast)
22 .and_then(|mc| mc.path())
23 .and_then(|p| p.segment())
24 .and_then(|s| s.name_ref())
25 {
26 match name.text().as_str() {
27 "format_args" | "format_args_nl" => {
28 self.format_string = parent
29 .children_with_tokens()
30 .filter(|t| t.kind() != SyntaxKind::WHITESPACE)
31 .nth(1)
32 .filter(|e| {
33 ast::String::can_cast(e.kind()) || ast::RawString::can_cast(e.kind())
34 })
35 }
36 _ => {}
37 }
38 }
39 }
40 pub(super) fn highlight_format_string(
41 &self,
42 range_stack: &mut HighlightedRangeStack,
43 string: &impl HasFormatSpecifier,
44 range: TextRange,
45 ) {
46 if self.format_string.as_ref() == Some(&SyntaxElement::from(string.syntax().clone())) {
47 range_stack.push();
48 string.lex_format_specifier(|piece_range, kind| {
49 if let Some(highlight) = highlight_format_specifier(kind) {
50 range_stack.add(HighlightedRange {
51 range: piece_range + range.start(),
52 highlight: highlight.into(),
53 binding_hash: None,
54 });
55 }
56 });
57 range_stack.pop();
58 }
59 }
60}
61
62fn highlight_format_specifier(kind: FormatSpecifier) -> Option<HighlightTag> {
63 Some(match kind {
64 FormatSpecifier::Open
65 | FormatSpecifier::Close
66 | FormatSpecifier::Colon
67 | FormatSpecifier::Fill
68 | FormatSpecifier::Align
69 | FormatSpecifier::Sign
70 | FormatSpecifier::NumberSign
71 | FormatSpecifier::DollarSign
72 | FormatSpecifier::Dot
73 | FormatSpecifier::Asterisk
74 | FormatSpecifier::QuestionMark => HighlightTag::FormatSpecifier,
75 FormatSpecifier::Integer | FormatSpecifier::Zero => HighlightTag::NumericLiteral,
76 FormatSpecifier::Identifier => HighlightTag::Local,
77 })
78}
diff --git a/crates/ide/src/syntax_highlighting/macro_rules.rs b/crates/ide/src/syntax_highlighting/macro_rules.rs
new file mode 100644
index 000000000..4462af47e
--- /dev/null
+++ b/crates/ide/src/syntax_highlighting/macro_rules.rs
@@ -0,0 +1,129 @@
1//! Syntax highlighting for macro_rules!.
2use syntax::{SyntaxElement, SyntaxKind, SyntaxToken, TextRange, T};
3
4use crate::{HighlightTag, HighlightedRange};
5
6#[derive(Default)]
7pub(super) struct MacroRulesHighlighter {
8 state: Option<MacroMatcherParseState>,
9}
10
11impl MacroRulesHighlighter {
12 pub(super) fn init(&mut self) {
13 self.state = Some(MacroMatcherParseState::default());
14 }
15
16 pub(super) fn advance(&mut self, token: &SyntaxToken) {
17 if let Some(state) = self.state.as_mut() {
18 update_macro_rules_state(state, token);
19 }
20 }
21
22 pub(super) fn highlight(&self, element: SyntaxElement) -> Option<HighlightedRange> {
23 if let Some(state) = self.state.as_ref() {
24 if matches!(state.rule_state, RuleState::Matcher | RuleState::Expander) {
25 if let Some(range) = is_metavariable(element) {
26 return Some(HighlightedRange {
27 range,
28 highlight: HighlightTag::UnresolvedReference.into(),
29 binding_hash: None,
30 });
31 }
32 }
33 }
34 None
35 }
36}
37
38struct MacroMatcherParseState {
39 /// Opening and corresponding closing bracket of the matcher or expander of the current rule
40 paren_ty: Option<(SyntaxKind, SyntaxKind)>,
41 paren_level: usize,
42 rule_state: RuleState,
43 /// Whether we are inside the outer `{` `}` macro block that holds the rules
44 in_invoc_body: bool,
45}
46
47impl Default for MacroMatcherParseState {
48 fn default() -> Self {
49 MacroMatcherParseState {
50 paren_ty: None,
51 paren_level: 0,
52 in_invoc_body: false,
53 rule_state: RuleState::None,
54 }
55 }
56}
57
58#[derive(Copy, Clone, Debug, PartialEq)]
59enum RuleState {
60 Matcher,
61 Expander,
62 Between,
63 None,
64}
65
66impl RuleState {
67 fn transition(&mut self) {
68 *self = match self {
69 RuleState::Matcher => RuleState::Between,
70 RuleState::Expander => RuleState::None,
71 RuleState::Between => RuleState::Expander,
72 RuleState::None => RuleState::Matcher,
73 };
74 }
75}
76
77fn update_macro_rules_state(state: &mut MacroMatcherParseState, tok: &SyntaxToken) {
78 if !state.in_invoc_body {
79 if tok.kind() == T!['{'] {
80 state.in_invoc_body = true;
81 }
82 return;
83 }
84
85 match state.paren_ty {
86 Some((open, close)) => {
87 if tok.kind() == open {
88 state.paren_level += 1;
89 } else if tok.kind() == close {
90 state.paren_level -= 1;
91 if state.paren_level == 0 {
92 state.rule_state.transition();
93 state.paren_ty = None;
94 }
95 }
96 }
97 None => {
98 match tok.kind() {
99 T!['('] => {
100 state.paren_ty = Some((T!['('], T![')']));
101 }
102 T!['{'] => {
103 state.paren_ty = Some((T!['{'], T!['}']));
104 }
105 T!['['] => {
106 state.paren_ty = Some((T!['['], T![']']));
107 }
108 _ => (),
109 }
110 if state.paren_ty.is_some() {
111 state.paren_level = 1;
112 state.rule_state.transition();
113 }
114 }
115 }
116}
117
118fn is_metavariable(element: SyntaxElement) -> Option<TextRange> {
119 let tok = element.as_token()?;
120 match tok.kind() {
121 kind if kind == SyntaxKind::IDENT || kind.is_keyword() => {
122 if let Some(_dollar) = tok.prev_token().filter(|t| t.kind() == SyntaxKind::DOLLAR) {
123 return Some(tok.text_range());
124 }
125 }
126 _ => (),
127 };
128 None
129}
diff --git a/crates/ide_db/src/defs.rs b/crates/ide_db/src/defs.rs
index f8c7aa491..201a3d6fa 100644
--- a/crates/ide_db/src/defs.rs
+++ b/crates/ide_db/src/defs.rs
@@ -81,146 +81,152 @@ impl Definition {
81pub enum NameClass { 81pub enum NameClass {
82 ExternCrate(Crate), 82 ExternCrate(Crate),
83 Definition(Definition), 83 Definition(Definition),
84 /// `None` in `if let None = Some(82) {}` 84 /// `None` in `if let None = Some(82) {}`.
85 ConstReference(Definition), 85 ConstReference(Definition),
86 FieldShorthand { 86 /// `field` in `if let Foo { field } = foo`.
87 local: Local, 87 PatFieldShorthand {
88 field: Definition, 88 local_def: Local,
89 field_ref: Definition,
89 }, 90 },
90} 91}
91 92
92impl NameClass { 93impl NameClass {
93 pub fn into_definition(self, db: &dyn HirDatabase) -> Option<Definition> { 94 /// `Definition` defined by this name.
94 Some(match self { 95 pub fn defined(self, db: &dyn HirDatabase) -> Option<Definition> {
96 let res = match self {
95 NameClass::ExternCrate(krate) => Definition::ModuleDef(krate.root_module(db).into()), 97 NameClass::ExternCrate(krate) => Definition::ModuleDef(krate.root_module(db).into()),
96 NameClass::Definition(it) => it, 98 NameClass::Definition(it) => it,
97 NameClass::ConstReference(_) => return None, 99 NameClass::ConstReference(_) => return None,
98 NameClass::FieldShorthand { local, field: _ } => Definition::Local(local), 100 NameClass::PatFieldShorthand { local_def, field_ref: _ } => {
99 }) 101 Definition::Local(local_def)
102 }
103 };
104 Some(res)
100 } 105 }
101 106
102 pub fn definition(self, db: &dyn HirDatabase) -> Definition { 107 /// `Definition` referenced or defined by this name.
108 pub fn referenced_or_defined(self, db: &dyn HirDatabase) -> Definition {
103 match self { 109 match self {
104 NameClass::ExternCrate(krate) => Definition::ModuleDef(krate.root_module(db).into()), 110 NameClass::ExternCrate(krate) => Definition::ModuleDef(krate.root_module(db).into()),
105 NameClass::Definition(it) | NameClass::ConstReference(it) => it, 111 NameClass::Definition(it) | NameClass::ConstReference(it) => it,
106 NameClass::FieldShorthand { local: _, field } => field, 112 NameClass::PatFieldShorthand { local_def: _, field_ref } => field_ref,
107 } 113 }
108 } 114 }
109}
110 115
111pub fn classify_name(sema: &Semantics<RootDatabase>, name: &ast::Name) -> Option<NameClass> { 116 pub fn classify(sema: &Semantics<RootDatabase>, name: &ast::Name) -> Option<NameClass> {
112 let _p = profile::span("classify_name"); 117 let _p = profile::span("classify_name");
113 118
114 let parent = name.syntax().parent()?; 119 let parent = name.syntax().parent()?;
115 120
116 if let Some(bind_pat) = ast::IdentPat::cast(parent.clone()) { 121 if let Some(bind_pat) = ast::IdentPat::cast(parent.clone()) {
117 if let Some(def) = sema.resolve_bind_pat_to_const(&bind_pat) { 122 if let Some(def) = sema.resolve_bind_pat_to_const(&bind_pat) {
118 return Some(NameClass::ConstReference(Definition::ModuleDef(def))); 123 return Some(NameClass::ConstReference(Definition::ModuleDef(def)));
124 }
119 } 125 }
120 }
121 126
122 match_ast! { 127 match_ast! {
123 match parent { 128 match parent {
124 ast::Rename(it) => { 129 ast::Rename(it) => {
125 if let Some(use_tree) = it.syntax().parent().and_then(ast::UseTree::cast) { 130 if let Some(use_tree) = it.syntax().parent().and_then(ast::UseTree::cast) {
126 let path = use_tree.path()?; 131 let path = use_tree.path()?;
127 let path_segment = path.segment()?; 132 let path_segment = path.segment()?;
128 let name_ref_class = path_segment 133 let name_ref_class = path_segment
129 .name_ref() 134 .name_ref()
130 // The rename might be from a `self` token, so fallback to the name higher 135 // The rename might be from a `self` token, so fallback to the name higher
131 // in the use tree. 136 // in the use tree.
132 .or_else(||{ 137 .or_else(||{
133 if path_segment.self_token().is_none() { 138 if path_segment.self_token().is_none() {
134 return None; 139 return None;
135 } 140 }
136 141
137 let use_tree = use_tree 142 let use_tree = use_tree
138 .syntax() 143 .syntax()
139 .parent() 144 .parent()
140 .as_ref() 145 .as_ref()
141 // Skip over UseTreeList 146 // Skip over UseTreeList
142 .and_then(SyntaxNode::parent) 147 .and_then(SyntaxNode::parent)
143 .and_then(ast::UseTree::cast)?; 148 .and_then(ast::UseTree::cast)?;
144 let path = use_tree.path()?; 149 let path = use_tree.path()?;
145 let path_segment = path.segment()?; 150 let path_segment = path.segment()?;
146 path_segment.name_ref() 151 path_segment.name_ref()
147 }) 152 })
148 .and_then(|name_ref| classify_name_ref(sema, &name_ref))?; 153 .and_then(|name_ref| NameRefClass::classify(sema, &name_ref))?;
149 154
150 Some(NameClass::Definition(name_ref_class.definition(sema.db))) 155 Some(NameClass::Definition(name_ref_class.referenced(sema.db)))
151 } else { 156 } else {
152 let extern_crate = it.syntax().parent().and_then(ast::ExternCrate::cast)?; 157 let extern_crate = it.syntax().parent().and_then(ast::ExternCrate::cast)?;
153 let resolved = sema.resolve_extern_crate(&extern_crate)?; 158 let resolved = sema.resolve_extern_crate(&extern_crate)?;
154 Some(NameClass::ExternCrate(resolved)) 159 Some(NameClass::ExternCrate(resolved))
155 } 160 }
156 }, 161 },
157 ast::IdentPat(it) => { 162 ast::IdentPat(it) => {
158 let local = sema.to_def(&it)?; 163 let local = sema.to_def(&it)?;
159 164
160 if let Some(record_pat_field) = it.syntax().parent().and_then(ast::RecordPatField::cast) { 165 if let Some(record_pat_field) = it.syntax().parent().and_then(ast::RecordPatField::cast) {
161 if record_pat_field.name_ref().is_none() { 166 if record_pat_field.name_ref().is_none() {
162 if let Some(field) = sema.resolve_record_pat_field(&record_pat_field) { 167 if let Some(field) = sema.resolve_record_pat_field(&record_pat_field) {
163 let field = Definition::Field(field); 168 let field = Definition::Field(field);
164 return Some(NameClass::FieldShorthand { local, field }); 169 return Some(NameClass::PatFieldShorthand { local_def: local, field_ref: field });
170 }
165 } 171 }
166 } 172 }
167 }
168 173
169 Some(NameClass::Definition(Definition::Local(local))) 174 Some(NameClass::Definition(Definition::Local(local)))
170 }, 175 },
171 ast::RecordField(it) => { 176 ast::RecordField(it) => {
172 let field: hir::Field = sema.to_def(&it)?; 177 let field: hir::Field = sema.to_def(&it)?;
173 Some(NameClass::Definition(Definition::Field(field))) 178 Some(NameClass::Definition(Definition::Field(field)))
174 }, 179 },
175 ast::Module(it) => { 180 ast::Module(it) => {
176 let def = sema.to_def(&it)?; 181 let def = sema.to_def(&it)?;
177 Some(NameClass::Definition(Definition::ModuleDef(def.into()))) 182 Some(NameClass::Definition(Definition::ModuleDef(def.into())))
178 }, 183 },
179 ast::Struct(it) => { 184 ast::Struct(it) => {
180 let def: hir::Struct = sema.to_def(&it)?; 185 let def: hir::Struct = sema.to_def(&it)?;
181 Some(NameClass::Definition(Definition::ModuleDef(def.into()))) 186 Some(NameClass::Definition(Definition::ModuleDef(def.into())))
182 }, 187 },
183 ast::Union(it) => { 188 ast::Union(it) => {
184 let def: hir::Union = sema.to_def(&it)?; 189 let def: hir::Union = sema.to_def(&it)?;
185 Some(NameClass::Definition(Definition::ModuleDef(def.into()))) 190 Some(NameClass::Definition(Definition::ModuleDef(def.into())))
186 }, 191 },
187 ast::Enum(it) => { 192 ast::Enum(it) => {
188 let def: hir::Enum = sema.to_def(&it)?; 193 let def: hir::Enum = sema.to_def(&it)?;
189 Some(NameClass::Definition(Definition::ModuleDef(def.into()))) 194 Some(NameClass::Definition(Definition::ModuleDef(def.into())))
190 }, 195 },
191 ast::Trait(it) => { 196 ast::Trait(it) => {
192 let def: hir::Trait = sema.to_def(&it)?; 197 let def: hir::Trait = sema.to_def(&it)?;
193 Some(NameClass::Definition(Definition::ModuleDef(def.into()))) 198 Some(NameClass::Definition(Definition::ModuleDef(def.into())))
194 }, 199 },
195 ast::Static(it) => { 200 ast::Static(it) => {
196 let def: hir::Static = sema.to_def(&it)?; 201 let def: hir::Static = sema.to_def(&it)?;
197 Some(NameClass::Definition(Definition::ModuleDef(def.into()))) 202 Some(NameClass::Definition(Definition::ModuleDef(def.into())))
198 }, 203 },
199 ast::Variant(it) => { 204 ast::Variant(it) => {
200 let def: hir::EnumVariant = sema.to_def(&it)?; 205 let def: hir::EnumVariant = sema.to_def(&it)?;
201 Some(NameClass::Definition(Definition::ModuleDef(def.into()))) 206 Some(NameClass::Definition(Definition::ModuleDef(def.into())))
202 }, 207 },
203 ast::Fn(it) => { 208 ast::Fn(it) => {
204 let def: hir::Function = sema.to_def(&it)?; 209 let def: hir::Function = sema.to_def(&it)?;
205 Some(NameClass::Definition(Definition::ModuleDef(def.into()))) 210 Some(NameClass::Definition(Definition::ModuleDef(def.into())))
206 }, 211 },
207 ast::Const(it) => { 212 ast::Const(it) => {
208 let def: hir::Const = sema.to_def(&it)?; 213 let def: hir::Const = sema.to_def(&it)?;
209 Some(NameClass::Definition(Definition::ModuleDef(def.into()))) 214 Some(NameClass::Definition(Definition::ModuleDef(def.into())))
210 }, 215 },
211 ast::TypeAlias(it) => { 216 ast::TypeAlias(it) => {
212 let def: hir::TypeAlias = sema.to_def(&it)?; 217 let def: hir::TypeAlias = sema.to_def(&it)?;
213 Some(NameClass::Definition(Definition::ModuleDef(def.into()))) 218 Some(NameClass::Definition(Definition::ModuleDef(def.into())))
214 }, 219 },
215 ast::MacroCall(it) => { 220 ast::MacroCall(it) => {
216 let def = sema.to_def(&it)?; 221 let def = sema.to_def(&it)?;
217 Some(NameClass::Definition(Definition::Macro(def))) 222 Some(NameClass::Definition(Definition::Macro(def)))
218 }, 223 },
219 ast::TypeParam(it) => { 224 ast::TypeParam(it) => {
220 let def = sema.to_def(&it)?; 225 let def = sema.to_def(&it)?;
221 Some(NameClass::Definition(Definition::TypeParam(def))) 226 Some(NameClass::Definition(Definition::TypeParam(def)))
222 }, 227 },
223 _ => None, 228 _ => None,
229 }
224 } 230 }
225 } 231 }
226} 232}
@@ -229,102 +235,109 @@ pub fn classify_name(sema: &Semantics<RootDatabase>, name: &ast::Name) -> Option
229pub enum NameRefClass { 235pub enum NameRefClass {
230 ExternCrate(Crate), 236 ExternCrate(Crate),
231 Definition(Definition), 237 Definition(Definition),
232 FieldShorthand { local: Local, field: Definition }, 238 FieldShorthand { local_ref: Local, field_ref: Definition },
233} 239}
234 240
235impl NameRefClass { 241impl NameRefClass {
236 pub fn definition(self, db: &dyn HirDatabase) -> Definition { 242 /// `Definition`, which this name refers to.
243 pub fn referenced(self, db: &dyn HirDatabase) -> Definition {
237 match self { 244 match self {
238 NameRefClass::ExternCrate(krate) => Definition::ModuleDef(krate.root_module(db).into()), 245 NameRefClass::ExternCrate(krate) => Definition::ModuleDef(krate.root_module(db).into()),
239 NameRefClass::Definition(def) => def, 246 NameRefClass::Definition(def) => def,
240 NameRefClass::FieldShorthand { local, field: _ } => Definition::Local(local), 247 NameRefClass::FieldShorthand { local_ref, field_ref: _ } => {
248 // FIXME: this is inherently ambiguous -- this name refers to
249 // two different defs....
250 Definition::Local(local_ref)
251 }
241 } 252 }
242 } 253 }
243}
244 254
245// Note: we don't have unit-tests for this rather important function. 255 // Note: we don't have unit-tests for this rather important function.
246// It is primarily exercised via goto definition tests in `ide`. 256 // It is primarily exercised via goto definition tests in `ide`.
247pub fn classify_name_ref( 257 pub fn classify(
248 sema: &Semantics<RootDatabase>, 258 sema: &Semantics<RootDatabase>,
249 name_ref: &ast::NameRef, 259 name_ref: &ast::NameRef,
250) -> Option<NameRefClass> { 260 ) -> Option<NameRefClass> {
251 let _p = profile::span("classify_name_ref"); 261 let _p = profile::span("classify_name_ref");
252 262
253 let parent = name_ref.syntax().parent()?; 263 let parent = name_ref.syntax().parent()?;
254 264
255 if let Some(method_call) = ast::MethodCallExpr::cast(parent.clone()) { 265 if let Some(method_call) = ast::MethodCallExpr::cast(parent.clone()) {
256 if let Some(func) = sema.resolve_method_call(&method_call) { 266 if let Some(func) = sema.resolve_method_call(&method_call) {
257 return Some(NameRefClass::Definition(Definition::ModuleDef(func.into()))); 267 return Some(NameRefClass::Definition(Definition::ModuleDef(func.into())));
268 }
258 } 269 }
259 }
260 270
261 if let Some(field_expr) = ast::FieldExpr::cast(parent.clone()) { 271 if let Some(field_expr) = ast::FieldExpr::cast(parent.clone()) {
262 if let Some(field) = sema.resolve_field(&field_expr) { 272 if let Some(field) = sema.resolve_field(&field_expr) {
263 return Some(NameRefClass::Definition(Definition::Field(field))); 273 return Some(NameRefClass::Definition(Definition::Field(field)));
274 }
264 } 275 }
265 }
266 276
267 if let Some(record_field) = ast::RecordExprField::for_field_name(name_ref) { 277 if let Some(record_field) = ast::RecordExprField::for_field_name(name_ref) {
268 if let Some((field, local)) = sema.resolve_record_field(&record_field) { 278 if let Some((field, local)) = sema.resolve_record_field(&record_field) {
269 let field = Definition::Field(field); 279 let field = Definition::Field(field);
270 let res = match local { 280 let res = match local {
271 None => NameRefClass::Definition(field), 281 None => NameRefClass::Definition(field),
272 Some(local) => NameRefClass::FieldShorthand { field, local }, 282 Some(local) => {
273 }; 283 NameRefClass::FieldShorthand { field_ref: field, local_ref: local }
274 return Some(res); 284 }
285 };
286 return Some(res);
287 }
275 } 288 }
276 }
277 289
278 if let Some(record_pat_field) = ast::RecordPatField::cast(parent.clone()) { 290 if let Some(record_pat_field) = ast::RecordPatField::cast(parent.clone()) {
279 if let Some(field) = sema.resolve_record_pat_field(&record_pat_field) { 291 if let Some(field) = sema.resolve_record_pat_field(&record_pat_field) {
280 let field = Definition::Field(field); 292 let field = Definition::Field(field);
281 return Some(NameRefClass::Definition(field)); 293 return Some(NameRefClass::Definition(field));
294 }
282 } 295 }
283 }
284 296
285 if ast::AssocTypeArg::cast(parent.clone()).is_some() { 297 if ast::AssocTypeArg::cast(parent.clone()).is_some() {
286 // `Trait<Assoc = Ty>` 298 // `Trait<Assoc = Ty>`
287 // ^^^^^ 299 // ^^^^^
288 let path = name_ref.syntax().ancestors().find_map(ast::Path::cast)?; 300 let path = name_ref.syntax().ancestors().find_map(ast::Path::cast)?;
289 let resolved = sema.resolve_path(&path)?; 301 let resolved = sema.resolve_path(&path)?;
290 if let PathResolution::Def(ModuleDef::Trait(tr)) = resolved { 302 if let PathResolution::Def(ModuleDef::Trait(tr)) = resolved {
291 if let Some(ty) = tr 303 if let Some(ty) = tr
292 .items(sema.db) 304 .items(sema.db)
293 .iter() 305 .iter()
294 .filter_map(|assoc| match assoc { 306 .filter_map(|assoc| match assoc {
295 hir::AssocItem::TypeAlias(it) => Some(*it), 307 hir::AssocItem::TypeAlias(it) => Some(*it),
296 _ => None, 308 _ => None,
297 }) 309 })
298 .find(|alias| alias.name(sema.db).to_string() == **name_ref.text()) 310 .find(|alias| alias.name(sema.db).to_string() == **name_ref.text())
299 { 311 {
300 return Some(NameRefClass::Definition(Definition::ModuleDef( 312 return Some(NameRefClass::Definition(Definition::ModuleDef(
301 ModuleDef::TypeAlias(ty), 313 ModuleDef::TypeAlias(ty),
302 ))); 314 )));
315 }
303 } 316 }
304 } 317 }
305 }
306 318
307 if let Some(macro_call) = parent.ancestors().find_map(ast::MacroCall::cast) { 319 if let Some(macro_call) = parent.ancestors().find_map(ast::MacroCall::cast) {
308 if let Some(path) = macro_call.path() { 320 if let Some(path) = macro_call.path() {
309 if path.qualifier().is_none() { 321 if path.qualifier().is_none() {
310 // Only use this to resolve single-segment macro calls like `foo!()`. Multi-segment 322 // Only use this to resolve single-segment macro calls like `foo!()`. Multi-segment
311 // paths are handled below (allowing `log<|>::info!` to resolve to the log crate). 323 // paths are handled below (allowing `log<|>::info!` to resolve to the log crate).
312 if let Some(macro_def) = sema.resolve_macro_call(&macro_call) { 324 if let Some(macro_def) = sema.resolve_macro_call(&macro_call) {
313 return Some(NameRefClass::Definition(Definition::Macro(macro_def))); 325 return Some(NameRefClass::Definition(Definition::Macro(macro_def)));
326 }
314 } 327 }
315 } 328 }
316 } 329 }
317 }
318 330
319 if let Some(path) = name_ref.syntax().ancestors().find_map(ast::Path::cast) { 331 if let Some(path) = name_ref.syntax().ancestors().find_map(ast::Path::cast) {
320 if let Some(resolved) = sema.resolve_path(&path) { 332 if let Some(resolved) = sema.resolve_path(&path) {
321 return Some(NameRefClass::Definition(resolved.into())); 333 return Some(NameRefClass::Definition(resolved.into()));
334 }
322 } 335 }
323 }
324 336
325 let extern_crate = ast::ExternCrate::cast(parent)?; 337 let extern_crate = ast::ExternCrate::cast(parent)?;
326 let resolved = sema.resolve_extern_crate(&extern_crate)?; 338 let resolved = sema.resolve_extern_crate(&extern_crate)?;
327 Some(NameRefClass::ExternCrate(resolved)) 339 Some(NameRefClass::ExternCrate(resolved))
340 }
328} 341}
329 342
330impl From<PathResolution> for Definition { 343impl From<PathResolution> for Definition {
diff --git a/crates/ide_db/src/imports_locator.rs b/crates/ide_db/src/imports_locator.rs
index ed67e3553..df74be00b 100644
--- a/crates/ide_db/src/imports_locator.rs
+++ b/crates/ide_db/src/imports_locator.rs
@@ -5,7 +5,7 @@ use hir::{Crate, MacroDef, ModuleDef, Semantics};
5use syntax::{ast, AstNode, SyntaxKind::NAME}; 5use syntax::{ast, AstNode, SyntaxKind::NAME};
6 6
7use crate::{ 7use crate::{
8 defs::{classify_name, Definition}, 8 defs::{Definition, NameClass},
9 symbol_index::{self, FileSymbol, Query}, 9 symbol_index::{self, FileSymbol, Query},
10 RootDatabase, 10 RootDatabase,
11}; 11};
@@ -60,5 +60,5 @@ fn get_name_definition<'a>(
60 candidate_node 60 candidate_node
61 }; 61 };
62 let name = ast::Name::cast(candidate_name_node)?; 62 let name = ast::Name::cast(candidate_name_node)?;
63 classify_name(sema, &name)?.into_definition(sema.db) 63 NameClass::classify(sema, &name)?.defined(sema.db)
64} 64}
diff --git a/crates/ide_db/src/search.rs b/crates/ide_db/src/search.rs
index 8e3dcd99c..a24335240 100644
--- a/crates/ide_db/src/search.rs
+++ b/crates/ide_db/src/search.rs
@@ -14,7 +14,7 @@ use syntax::{ast, match_ast, AstNode, TextRange, TextSize};
14 14
15use crate::defs::NameClass; 15use crate::defs::NameClass;
16use crate::{ 16use crate::{
17 defs::{classify_name, classify_name_ref, Definition, NameRefClass}, 17 defs::{Definition, NameRefClass},
18 RootDatabase, 18 RootDatabase,
19}; 19};
20 20
@@ -276,7 +276,7 @@ impl<'a> FindUsages<'a> {
276 name_ref: &ast::NameRef, 276 name_ref: &ast::NameRef,
277 sink: &mut dyn FnMut(Reference) -> bool, 277 sink: &mut dyn FnMut(Reference) -> bool,
278 ) -> bool { 278 ) -> bool {
279 match classify_name_ref(self.sema, &name_ref) { 279 match NameRefClass::classify(self.sema, &name_ref) {
280 Some(NameRefClass::Definition(def)) if &def == self.def => { 280 Some(NameRefClass::Definition(def)) if &def == self.def => {
281 let kind = if is_record_lit_name_ref(&name_ref) || is_call_expr_name_ref(&name_ref) 281 let kind = if is_record_lit_name_ref(&name_ref) || is_call_expr_name_ref(&name_ref)
282 { 282 {
@@ -292,7 +292,7 @@ impl<'a> FindUsages<'a> {
292 }; 292 };
293 sink(reference) 293 sink(reference)
294 } 294 }
295 Some(NameRefClass::FieldShorthand { local, field }) => { 295 Some(NameRefClass::FieldShorthand { local_ref: local, field_ref: field }) => {
296 let reference = match self.def { 296 let reference = match self.def {
297 Definition::Field(_) if &field == self.def => Reference { 297 Definition::Field(_) if &field == self.def => Reference {
298 file_range: self.sema.original_range(name_ref.syntax()), 298 file_range: self.sema.original_range(name_ref.syntax()),
@@ -313,10 +313,10 @@ impl<'a> FindUsages<'a> {
313 } 313 }
314 314
315 fn found_name(&self, name: &ast::Name, sink: &mut dyn FnMut(Reference) -> bool) -> bool { 315 fn found_name(&self, name: &ast::Name, sink: &mut dyn FnMut(Reference) -> bool) -> bool {
316 match classify_name(self.sema, name) { 316 match NameClass::classify(self.sema, name) {
317 Some(NameClass::FieldShorthand { local: _, field }) => { 317 Some(NameClass::PatFieldShorthand { local_def: _, field_ref }) => {
318 let reference = match self.def { 318 let reference = match self.def {
319 Definition::Field(_) if &field == self.def => Reference { 319 Definition::Field(_) if &field_ref == self.def => Reference {
320 file_range: self.sema.original_range(name.syntax()), 320 file_range: self.sema.original_range(name.syntax()),
321 kind: ReferenceKind::FieldShorthandForField, 321 kind: ReferenceKind::FieldShorthandForField,
322 // FIXME: mutable patterns should have `Write` access 322 // FIXME: mutable patterns should have `Write` access