From 9a1b9b3c781ebf339eaa99a1318bf31e99d78550 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Mon, 28 Jun 2021 21:34:30 +0200 Subject: [PATCH] Resolve attribute paths in attribute highlighting --- crates/hir_expand/src/builtin_attr.rs | 2 +- crates/ide/src/syntax_highlighting.rs | 3 +- .../ide/src/syntax_highlighting/highlight.rs | 130 ++++++++++-------- crates/ide/src/syntax_highlighting/tags.rs | 2 + .../test_data/highlight_doctest.html | 10 +- .../test_data/highlight_strings.html | 2 +- .../test_data/highlight_unsafe.html | 2 +- .../test_data/highlighting.html | 12 +- crates/ide/src/syntax_highlighting/tests.rs | 2 +- crates/rust-analyzer/src/semantic_tokens.rs | 1 + crates/rust-analyzer/src/to_proto.rs | 1 + 11 files changed, 95 insertions(+), 72 deletions(-) diff --git a/crates/hir_expand/src/builtin_attr.rs b/crates/hir_expand/src/builtin_attr.rs index c8432005ef5..d39206f2b00 100644 --- a/crates/hir_expand/src/builtin_attr.rs +++ b/crates/hir_expand/src/builtin_attr.rs @@ -1,4 +1,4 @@ -//! Builtin derives. +//! Builtin attributes. use syntax::ast; diff --git a/crates/ide/src/syntax_highlighting.rs b/crates/ide/src/syntax_highlighting.rs index 5259d86d252..55ee85434a5 100644 --- a/crates/ide/src/syntax_highlighting.rs +++ b/crates/ide/src/syntax_highlighting.rs @@ -102,7 +102,8 @@ pub struct HlRange { // //- // // [horizontal] -// attribute:: Emitted for attributes. +// attribute:: Emitted for the `#[` `]` tokens. +// builtinAttribute:: Emitted for names to builtin attributes in attribute path, the `repr` in `#[repr(u8)]` for example. // builtinType:: Emitted for builtin types like `u32`, `str` and `f32`. // comment:: Emitted for comments. // constParameter:: Emitted for const parameters. diff --git a/crates/ide/src/syntax_highlighting/highlight.rs b/crates/ide/src/syntax_highlighting/highlight.rs index 82e16a51b5f..e9b8eb407c5 100644 --- a/crates/ide/src/syntax_highlighting/highlight.rs +++ b/crates/ide/src/syntax_highlighting/highlight.rs @@ -71,68 +71,88 @@ pub(super) fn element( } // Highlight references like the definitions they resolve to NAME_REF if element.ancestors().any(|it| it.kind() == ATTR) => { - // even though we track whether we are in an attribute or not we still need this special case - // as otherwise we would emit unresolved references for name refs inside attributes - SymbolKind::Function.into() + // FIXME: We highlight paths in attributes slightly differently to work around this module + // currently not knowing about tool attributes and rustc builtin attributes as + // we do not want to resolve those to functions that may be defined in scope. + let name_ref = element.into_node().and_then(ast::NameRef::cast).unwrap(); + match NameRefClass::classify(sema, &name_ref) { + Some(name_class) => match name_class { + NameRefClass::Definition(Definition::ModuleDef(hir::ModuleDef::Module(_))) + if name_ref + .syntax() + .ancestors() + .find_map(ast::Path::cast) + .map_or(false, |it| it.parent_path().is_some()) => + { + HlTag::Symbol(SymbolKind::Module) + } + NameRefClass::Definition(Definition::Macro(m)) + if m.kind() == hir::MacroKind::Attr => + { + HlTag::Symbol(SymbolKind::Macro) + } + _ => HlTag::BuiltinAttr, + }, + None => HlTag::BuiltinAttr, + } + .into() } NAME_REF => { let name_ref = element.into_node().and_then(ast::NameRef::cast).unwrap(); - highlight_func_by_name_ref(sema, krate, &name_ref).unwrap_or_else(|| { - let is_self = name_ref.self_token().is_some(); - let h = match NameRefClass::classify(sema, &name_ref) { - Some(name_kind) => match name_kind { - NameRefClass::ExternCrate(_) => SymbolKind::Module.into(), - NameRefClass::Definition(def) => { - if let Definition::Local(local) = &def { - if let Some(name) = local.name(db) { - let shadow_count = - bindings_shadow_count.entry(name.clone()).or_default(); - binding_hash = Some(calc_binding_hash(&name, *shadow_count)) - } - }; + highlight_method_call_by_name_ref(sema, krate, &name_ref).unwrap_or_else(|| { + let name_class = match NameRefClass::classify(sema, &name_ref) { + Some(name_kind) => name_kind, + None => { + return if syntactic_name_ref_highlighting { + highlight_name_ref_by_syntax(name_ref, sema, krate) + } else { + HlTag::UnresolvedReference.into() + } + } + }; + let h = match name_class { + NameRefClass::ExternCrate(_) => SymbolKind::Module.into(), + NameRefClass::Definition(def) => { + if let Definition::Local(local) = &def { + if let Some(name) = local.name(db) { + let shadow_count = + bindings_shadow_count.entry(name.clone()).or_default(); + binding_hash = Some(calc_binding_hash(&name, *shadow_count)) + } + }; - let mut h = highlight_def(db, krate, def); + let mut h = highlight_def(db, krate, def); - match def { - Definition::Local(local) - if is_consumed_lvalue( - name_ref.syntax().clone().into(), - &local, - db, - ) => - { - h |= HlMod::Consuming; + match def { + Definition::Local(local) + if is_consumed_lvalue(name_ref.syntax(), &local, db) => + { + h |= HlMod::Consuming; + } + Definition::ModuleDef(hir::ModuleDef::Trait(trait_)) + if trait_.is_unsafe(db) => + { + if ast::Impl::for_trait_name_ref(&name_ref).is_some() { + h |= HlMod::Unsafe; } - Definition::ModuleDef(hir::ModuleDef::Trait(trait_)) - if trait_.is_unsafe(db) => - { - if ast::Impl::for_trait_name_ref(&name_ref).is_some() { - h |= HlMod::Unsafe; - } - } - Definition::Field(field) => { - if let Some(parent) = name_ref.syntax().parent() { - if matches!(parent.kind(), FIELD_EXPR | RECORD_PAT_FIELD) { - if let hir::VariantDef::Union(_) = field.parent_def(db) - { - h |= HlMod::Unsafe; - } + } + Definition::Field(field) => { + if let Some(parent) = name_ref.syntax().parent() { + if matches!(parent.kind(), FIELD_EXPR | RECORD_PAT_FIELD) { + if let hir::VariantDef::Union(_) = field.parent_def(db) { + h |= HlMod::Unsafe; } } } - _ => (), } - - h + _ => (), } - NameRefClass::FieldShorthand { .. } => SymbolKind::Field.into(), - }, - None if syntactic_name_ref_highlighting => { - highlight_name_ref_by_syntax(name_ref, sema, krate) + + h } - None => HlTag::UnresolvedReference.into(), + NameRefClass::FieldShorthand { .. } => SymbolKind::Field.into(), }; - if h.tag == HlTag::Symbol(SymbolKind::Module) && is_self { + if h.tag == HlTag::Symbol(SymbolKind::Module) && name_ref.self_token().is_some() { SymbolKind::SelfParam.into() } else { h @@ -172,6 +192,7 @@ pub(super) fn element( _ => Highlight::from(SymbolKind::LifetimeParam) | HlMod::Definition, } } + IDENT if parent_matches::(&element) => HlTag::None.into(), p if p.is_punct() => match p { T![&] if parent_matches::(&element) => HlOperator::Bitwise.into(), T![&] => { @@ -450,7 +471,7 @@ fn highlight_def(db: &RootDatabase, krate: Option, def: Definition) h } -fn highlight_func_by_name_ref( +fn highlight_method_call_by_name_ref( sema: &Semantics, krate: Option, name_ref: &ast::NameRef, @@ -599,13 +620,10 @@ fn highlight_name_ref_by_syntax( } } -fn is_consumed_lvalue( - node: NodeOrToken, - local: &hir::Local, - db: &RootDatabase, -) -> bool { +fn is_consumed_lvalue(node: &SyntaxNode, local: &hir::Local, db: &RootDatabase) -> bool { // When lvalues are passed as arguments and they're not Copy, then mark them as Consuming. - parents_match(node, &[PATH_SEGMENT, PATH, PATH_EXPR, ARG_LIST]) && !local.ty(db).is_copy(db) + parents_match(node.clone().into(), &[PATH_SEGMENT, PATH, PATH_EXPR, ARG_LIST]) + && !local.ty(db).is_copy(db) } /// Returns true if the parent nodes of `node` all match the `SyntaxKind`s in `kinds` exactly. diff --git a/crates/ide/src/syntax_highlighting/tags.rs b/crates/ide/src/syntax_highlighting/tags.rs index b4d59d00b11..82c5b609eb8 100644 --- a/crates/ide/src/syntax_highlighting/tags.rs +++ b/crates/ide/src/syntax_highlighting/tags.rs @@ -20,6 +20,7 @@ pub enum HlTag { Attribute, BoolLiteral, + BuiltinAttr, BuiltinType, ByteLiteral, CharLiteral, @@ -141,6 +142,7 @@ fn as_str(self) -> &'static str { }, HlTag::Attribute => "attribute", HlTag::BoolLiteral => "bool_literal", + HlTag::BuiltinAttr => "builtin_attr", HlTag::BuiltinType => "builtin_type", HlTag::ByteLiteral => "byte_literal", HlTag::CharLiteral => "char_literal", diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html b/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html index 1f2f83a08d0..4ae702ba5d5 100644 --- a/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html +++ b/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html @@ -70,7 +70,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd /// # Examples /// /// ``` - /// # #![allow(unused_mut)] + /// # #![allow(unused_mut)] /// let mut foo: Foo = Foo::new(); /// ``` pub const fn new() -> Foo { @@ -140,12 +140,12 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd /// /// ``` /// loop {} -#[cfg_attr(not(feature = "false"), doc = "loop {}")] -#[doc = "loop {}"] +#[cfg_attr(not(feature = "false"), doc = "loop {}")] +#[doc = "loop {}"] /// ``` /// -#[cfg_attr(feature = "alloc", doc = "```rust")] -#[cfg_attr(not(feature = "alloc"), doc = "```ignore")] +#[cfg_attr(feature = "alloc", doc = "```rust")] +#[cfg_attr(not(feature = "alloc"), doc = "```ignore")] /// let _ = example(&alloc::vec![1, 2, 3]); /// ``` pub fn mix_and_match() {} diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html b/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html index 96cb09642b8..b409a676595 100644 --- a/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html +++ b/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html @@ -43,7 +43,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd $crate::io::_print($crate::format_args_nl!($($arg)*)); }) } -#[rustc_builtin_macro] +#[rustc_builtin_macro] macro_rules! format_args_nl { ($fmt:expr) => {{ /* compiler built-in */ }}; ($fmt:expr, $($args:tt)*) => {{ /* compiler built-in */ }}; diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_unsafe.html b/crates/ide/src/syntax_highlighting/test_data/highlight_unsafe.html index 55453468b76..bf745f40560 100644 --- a/crates/ide/src/syntax_highlighting/test_data/highlight_unsafe.html +++ b/crates/ide/src/syntax_highlighting/test_data/highlight_unsafe.html @@ -57,7 +57,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd static mut global_mut: TypeForStaticMut = TypeForStaticMut { a: 0 }; -#[repr(packed)] +#[repr(packed)] struct Packed { a: u16, } diff --git a/crates/ide/src/syntax_highlighting/test_data/highlighting.html b/crates/ide/src/syntax_highlighting/test_data/highlighting.html index 63daf25c6f7..96969a7bb33 100644 --- a/crates/ide/src/syntax_highlighting/test_data/highlighting.html +++ b/crates/ide/src/syntax_highlighting/test_data/highlighting.html @@ -41,23 +41,23 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
use inner::{self as inner_mod};
 mod inner {}
 
-#[rustc_builtin_macro]
+#[rustc_builtin_macro]
 macro Copy {}
 
 // Needed for function consuming vs normal
 pub mod marker {
-    #[lang = "copy"]
+    #[lang = "copy"]
     pub trait Copy {}
 }
 
 pub mod ops {
-    #[lang = "fn_once"]
+    #[lang = "fn_once"]
     pub trait FnOnce<Args> {}
 
-    #[lang = "fn_mut"]
+    #[lang = "fn_mut"]
     pub trait FnMut<Args>: FnOnce<Args> {}
 
-    #[lang = "fn"]
+    #[lang = "fn"]
     pub trait Fn<Args>: FnMut<Args> {}
 }
 
@@ -91,7 +91,7 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
     }
 }
 
-#[derive(Copy)]
+#[derive(Copy)]
 struct FooCopy {
     x: u32,
 }
diff --git a/crates/ide/src/syntax_highlighting/tests.rs b/crates/ide/src/syntax_highlighting/tests.rs
index 502a88af258..81a98a606ef 100644
--- a/crates/ide/src/syntax_highlighting/tests.rs
+++ b/crates/ide/src/syntax_highlighting/tests.rs
@@ -378,7 +378,7 @@ fn benchmark_syntax_highlighting_parser() {
             .filter(|it| it.highlight.tag == HlTag::Symbol(SymbolKind::Function))
             .count()
     };
-    assert_eq!(hash, 1632);
+    assert_eq!(hash, 1616);
 }
 
 #[test]
diff --git a/crates/rust-analyzer/src/semantic_tokens.rs b/crates/rust-analyzer/src/semantic_tokens.rs
index 3f6c52e910f..8535e8a135a 100644
--- a/crates/rust-analyzer/src/semantic_tokens.rs
+++ b/crates/rust-analyzer/src/semantic_tokens.rs
@@ -45,6 +45,7 @@ macro_rules! define_semantic_token_types {
     (BOOLEAN, "boolean"),
     (BRACE, "brace"),
     (BRACKET, "bracket"),
+    (BUILTIN_ATTRIBUTE, "builtinAttribute"),
     (BUILTIN_TYPE, "builtinType"),
     (CHAR, "character"),
     (COLON, "colon"),
diff --git a/crates/rust-analyzer/src/to_proto.rs b/crates/rust-analyzer/src/to_proto.rs
index f9497f76f39..5e440f0d996 100644
--- a/crates/rust-analyzer/src/to_proto.rs
+++ b/crates/rust-analyzer/src/to_proto.rs
@@ -464,6 +464,7 @@ fn semantic_token_type_and_modifiers(
         },
         HlTag::Attribute => semantic_tokens::ATTRIBUTE,
         HlTag::BoolLiteral => semantic_tokens::BOOLEAN,
+        HlTag::BuiltinAttr => semantic_tokens::BUILTIN_ATTRIBUTE,
         HlTag::BuiltinType => semantic_tokens::BUILTIN_TYPE,
         HlTag::ByteLiteral | HlTag::NumericLiteral => lsp_types::SemanticTokenType::NUMBER,
         HlTag::CharLiteral => semantic_tokens::CHAR,