From 68de7b30e04ed56c7acd61f5aecc5f447691be12 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Sun, 20 Mar 2022 19:07:44 +0100 Subject: [PATCH] feat: Tag macro calls as unsafe if they expand to unsafe expressions --- crates/hir/src/semantics.rs | 10 +++ crates/hir/src/source_analyzer.rs | 78 ++++++++++++------- crates/hir_def/src/body.rs | 10 +++ crates/hir_def/src/body/lower.rs | 56 +++++++++---- crates/hir_ty/src/diagnostics.rs | 2 +- crates/hir_ty/src/diagnostics/unsafe_check.rs | 49 ++++++------ .../ide/src/syntax_highlighting/highlight.rs | 10 +++ crates/ide/src/syntax_highlighting/html.rs | 1 + .../test_data/highlight_assoc_functions.html | 1 + .../test_data/highlight_attributes.html | 1 + .../test_data/highlight_crate_root.html | 1 + .../test_data/highlight_default_library.html | 1 + .../test_data/highlight_doctest.html | 1 + .../test_data/highlight_extern_crate.html | 1 + .../test_data/highlight_general.html | 1 + .../test_data/highlight_injection.html | 1 + .../test_data/highlight_keywords.html | 1 + .../test_data/highlight_lifetimes.html | 1 + .../test_data/highlight_macros.html | 1 + .../test_data/highlight_operators.html | 1 + .../test_data/highlight_rainbow.html | 1 + .../test_data/highlight_strings.html | 1 + .../test_data/highlight_unsafe.html | 21 ++++- crates/ide/src/syntax_highlighting/tests.rs | 18 +++++ crates/ide_db/src/syntax_helpers/node_ext.rs | 7 ++ 25 files changed, 208 insertions(+), 68 deletions(-) diff --git a/crates/hir/src/semantics.rs b/crates/hir/src/semantics.rs index c2b7e9bb52..4afdde6494 100644 --- a/crates/hir/src/semantics.rs +++ b/crates/hir/src/semantics.rs @@ -358,6 +358,10 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> { self.imp.resolve_macro_call(macro_call) } + pub fn is_unsafe_macro_call(&self, macro_call: &ast::MacroCall) -> bool { + self.imp.is_unsafe_macro_call(macro_call) + } + pub fn resolve_attr_macro_call(&self, item: &ast::Item) -> Option { self.imp.resolve_attr_macro_call(item) } @@ -961,6 +965,12 @@ impl<'db> SemanticsImpl<'db> { sa.resolve_macro_call(self.db, macro_call) } + fn is_unsafe_macro_call(&self, macro_call: &ast::MacroCall) -> bool { + let sa = self.analyze(macro_call.syntax()); + let macro_call = self.find_file(macro_call.syntax()).with_value(macro_call); + sa.is_unsafe_macro_call(self.db, macro_call) + } + fn resolve_attr_macro_call(&self, item: &ast::Item) -> Option { let item_in_file = self.wrap_node_infile(item.clone()); let id = self.with_ctx(|ctx| { diff --git a/crates/hir/src/source_analyzer.rs b/crates/hir/src/source_analyzer.rs index 576d063c43..6b2120b253 100644 --- a/crates/hir/src/source_analyzer.rs +++ b/crates/hir/src/source_analyzer.rs @@ -25,7 +25,10 @@ use hir_def::{ }; use hir_expand::{hygiene::Hygiene, name::AsName, HirFileId, InFile}; use hir_ty::{ - diagnostics::{record_literal_missing_fields, record_pattern_missing_fields}, + diagnostics::{ + record_literal_missing_fields, record_pattern_missing_fields, unsafe_expressions, + UnsafeExpr, + }, Adjust, Adjustment, AutoBorrow, InferenceResult, Interner, Substitution, TyExt, TyLoweringContext, }; @@ -46,8 +49,7 @@ use base_db::CrateId; pub(crate) struct SourceAnalyzer { pub(crate) file_id: HirFileId, pub(crate) resolver: Resolver, - body: Option>, - body_source_map: Option>, + def: Option<(DefWithBodyId, Arc, Arc)>, infer: Option>, } @@ -67,8 +69,7 @@ impl SourceAnalyzer { let resolver = resolver_for_scope(db.upcast(), def, scope); SourceAnalyzer { resolver, - body: Some(body), - body_source_map: Some(source_map), + def: Some((def, body, source_map)), infer: Some(db.infer(def)), file_id, } @@ -87,26 +88,21 @@ impl SourceAnalyzer { Some(offset) => scope_for_offset(db, &scopes, &source_map, node.with_value(offset)), }; let resolver = resolver_for_scope(db.upcast(), def, scope); - SourceAnalyzer { - resolver, - body: Some(body), - body_source_map: Some(source_map), - infer: None, - file_id, - } + SourceAnalyzer { resolver, def: Some((def, body, source_map)), infer: None, file_id } } pub(crate) fn new_for_resolver( resolver: Resolver, node: InFile<&SyntaxNode>, ) -> SourceAnalyzer { - SourceAnalyzer { - resolver, - body: None, - body_source_map: None, - infer: None, - file_id: node.file_id, - } + SourceAnalyzer { resolver, def: None, infer: None, file_id: node.file_id } + } + + fn body_source_map(&self) -> Option<&BodySourceMap> { + self.def.as_ref().map(|(.., source_map)| &**source_map) + } + fn body(&self) -> Option<&Body> { + self.def.as_ref().map(|(_, body, _)| &**body) } fn expr_id(&self, db: &dyn HirDatabase, expr: &ast::Expr) -> Option { @@ -116,14 +112,14 @@ impl SourceAnalyzer { } _ => InFile::new(self.file_id, expr.clone()), }; - let sm = self.body_source_map.as_ref()?; + let sm = self.body_source_map()?; sm.node_expr(src.as_ref()) } fn pat_id(&self, pat: &ast::Pat) -> Option { // FIXME: macros, see `expr_id` let src = InFile { file_id: self.file_id, value: pat }; - self.body_source_map.as_ref()?.node_pat(src) + self.body_source_map()?.node_pat(src) } fn expand_expr( @@ -131,7 +127,7 @@ impl SourceAnalyzer { db: &dyn HirDatabase, expr: InFile, ) -> Option> { - let macro_file = self.body_source_map.as_ref()?.node_macro_file(expr.as_ref())?; + let macro_file = self.body_source_map()?.node_macro_file(expr.as_ref())?; let expanded = db.parse_or_expand(macro_file)?; let res = match ast::MacroCall::cast(expanded.clone()) { @@ -196,7 +192,7 @@ impl SourceAnalyzer { param: &ast::SelfParam, ) -> Option { let src = InFile { file_id: self.file_id, value: param }; - let pat_id = self.body_source_map.as_ref()?.node_self_param(src)?; + let pat_id = self.body_source_map()?.node_self_param(src)?; let ty = self.infer.as_ref()?[pat_id].clone(); Type::new_with_resolver(db, &self.resolver, ty) } @@ -226,7 +222,7 @@ impl SourceAnalyzer { ) -> Option<(Field, Option, Type)> { let record_expr = ast::RecordExpr::cast(field.syntax().parent().and_then(|p| p.parent())?)?; let expr = ast::Expr::from(record_expr); - let expr_id = self.body_source_map.as_ref()?.node_expr(InFile::new(self.file_id, &expr))?; + let expr_id = self.body_source_map()?.node_expr(InFile::new(self.file_id, &expr))?; let local_name = field.field_name()?.as_name(); let local = if field.name_ref().is_some() { @@ -279,7 +275,7 @@ impl SourceAnalyzer { pat: &ast::IdentPat, ) -> Option { let pat_id = self.pat_id(&pat.clone().into())?; - let body = self.body.as_ref()?; + let body = self.body()?; let path = match &body[pat_id] { Pat::Path(path) => path, _ => return None, @@ -415,7 +411,7 @@ impl SourceAnalyzer { literal: &ast::RecordExpr, ) -> Option> { let krate = self.resolver.krate()?; - let body = self.body.as_ref()?; + let body = self.body()?; let infer = self.infer.as_ref()?; let expr_id = self.expr_id(db, &literal.clone().into())?; @@ -433,7 +429,7 @@ impl SourceAnalyzer { pattern: &ast::RecordPat, ) -> Option> { let krate = self.resolver.krate()?; - let body = self.body.as_ref()?; + let body = self.body()?; let infer = self.infer.as_ref()?; let pat_id = self.pat_id(&pattern.clone().into())?; @@ -488,6 +484,34 @@ impl SourceAnalyzer { let expr_id = self.expr_id(db, &record_lit.into())?; infer.variant_resolution_for_expr(expr_id) } + + pub(crate) fn is_unsafe_macro_call( + &self, + db: &dyn HirDatabase, + macro_call: InFile<&ast::MacroCall>, + ) -> bool { + if let (Some((def, body, sm)), Some(infer)) = (&self.def, &self.infer) { + if let Some(expr_ids) = sm.macro_expansion_expr(macro_call) { + let mut is_unsafe = false; + for &expr_id in expr_ids { + unsafe_expressions( + db, + infer, + *def, + body, + expr_id, + &mut |UnsafeExpr { inside_unsafe_block, .. }| { + is_unsafe |= !inside_unsafe_block + }, + ); + if is_unsafe { + return true; + } + } + } + } + false + } } fn scope_for( diff --git a/crates/hir_def/src/body.rs b/crates/hir_def/src/body.rs index ebb15e20cb..57dd3a3502 100644 --- a/crates/hir_def/src/body.rs +++ b/crates/hir_def/src/body.rs @@ -19,6 +19,7 @@ use la_arena::{Arena, ArenaMap}; use limit::Limit; use profile::Count; use rustc_hash::FxHashMap; +use smallvec::SmallVec; use syntax::{ast, AstNode, AstPtr, SyntaxNodePtr}; use crate::{ @@ -293,6 +294,10 @@ pub struct BodySourceMap { field_map: FxHashMap>, ExprId>, field_map_back: FxHashMap>>, + /// Maps a macro call to its lowered expressions, a single one if it expands to an expression, + /// or multiple if it expands to MacroStmts. + macro_call_to_exprs: FxHashMap>, SmallVec<[ExprId; 1]>>, + expansions: FxHashMap>, HirFileId>, /// Diagnostics accumulated during body lowering. These contain `AstPtr`s and so are stored in @@ -461,6 +466,11 @@ impl BodySourceMap { self.field_map.get(&src).cloned() } + pub fn macro_expansion_expr(&self, node: InFile<&ast::MacroCall>) -> Option<&[ExprId]> { + let src = node.map(AstPtr::new); + self.macro_call_to_exprs.get(&src).map(|it| &**it) + } + /// Get a reference to the body source map's diagnostics. pub fn diagnostics(&self) -> &[BodyDiagnostic] { &self.diagnostics diff --git a/crates/hir_def/src/body/lower.rs b/crates/hir_def/src/body/lower.rs index 512a9312a7..661486d6e0 100644 --- a/crates/hir_def/src/body/lower.rs +++ b/crates/hir_def/src/body/lower.rs @@ -13,6 +13,7 @@ use hir_expand::{ use la_arena::Arena; use profile::Count; use rustc_hash::FxHashMap; +use smallvec::smallvec; use syntax::{ ast::{ self, ArrayExprKind, AstChildren, HasArgList, HasLoopBody, HasName, LiteralKind, @@ -507,14 +508,21 @@ impl ExprCollector<'_> { } ast::Expr::MacroCall(e) => { let macro_ptr = AstPtr::new(&e); - let mut ids = None; - self.collect_macro_call(e, macro_ptr, true, |this, expansion| { - ids.get_or_insert(match expansion { - Some(it) => this.collect_expr(it), - None => this.alloc_expr(Expr::Missing, syntax_ptr.clone()), - }); + let mut id = None; + self.collect_macro_call(e, macro_ptr.clone(), true, |this, expansion| { + if let Some(it) = expansion { + id.get_or_insert(this.collect_expr(it)); + } }); - ids.unwrap_or_else(|| self.alloc_expr(Expr::Missing, syntax_ptr.clone())) + match id { + Some(id) => { + self.source_map + .macro_call_to_exprs + .insert(self.expander.to_source(macro_ptr), smallvec![id]); + id + } + None => self.alloc_expr(Expr::Missing, syntax_ptr.clone()), + } } ast::Expr::MacroStmts(e) => { e.statements().for_each(|s| self.collect_stmt(s)); @@ -529,12 +537,12 @@ impl ExprCollector<'_> { }) } - fn collect_macro_call), T: ast::AstNode>( + fn collect_macro_call), T: ast::AstNode>( &mut self, mcall: ast::MacroCall, syntax_ptr: AstPtr, record_diagnostics: bool, - mut collector: F, + collector: F, ) { // File containing the macro call. Expansion errors will be attached here. let outer_file = self.expander.current_file_id; @@ -625,17 +633,17 @@ impl ExprCollector<'_> { let macro_ptr = AstPtr::new(&m); let syntax_ptr = AstPtr::new(&stmt.expr().unwrap()); - self.collect_macro_call( - m, - macro_ptr, - false, - |this, expansion| match expansion { + let prev_stmt = self.statements_in_scope.len(); + let mut tail = None; + self.collect_macro_call(m, macro_ptr.clone(), false, |this, expansion| { + match expansion { Some(expansion) => { let statements: ast::MacroStmts = expansion; statements.statements().for_each(|stmt| this.collect_stmt(stmt)); if let Some(expr) = statements.expr() { let expr = this.collect_expr(expr); + tail = Some(expr); this.statements_in_scope .push(Statement::Expr { expr, has_semi }); } @@ -644,8 +652,24 @@ impl ExprCollector<'_> { let expr = this.alloc_expr(Expr::Missing, syntax_ptr.clone()); this.statements_in_scope.push(Statement::Expr { expr, has_semi }); } - }, - ); + } + }); + let mut macro_exprs = smallvec![]; + for stmt in &self.statements_in_scope[prev_stmt..] { + match *stmt { + Statement::Let { initializer, else_branch, .. } => { + macro_exprs.extend(initializer); + macro_exprs.extend(else_branch); + } + Statement::Expr { expr, .. } => macro_exprs.push(expr), + } + } + macro_exprs.extend(tail); + if !macro_exprs.is_empty() { + self.source_map + .macro_call_to_exprs + .insert(self.expander.to_source(macro_ptr), macro_exprs); + } } else { let expr = self.collect_expr_opt(stmt.expr()); self.statements_in_scope.push(Statement::Expr { expr, has_semi }); diff --git a/crates/hir_ty/src/diagnostics.rs b/crates/hir_ty/src/diagnostics.rs index a4702715e5..37eb06be1d 100644 --- a/crates/hir_ty/src/diagnostics.rs +++ b/crates/hir_ty/src/diagnostics.rs @@ -9,5 +9,5 @@ pub use crate::diagnostics::{ expr::{ record_literal_missing_fields, record_pattern_missing_fields, BodyValidationDiagnostic, }, - unsafe_check::missing_unsafe, + unsafe_check::{missing_unsafe, unsafe_expressions, UnsafeExpr}, }; diff --git a/crates/hir_ty/src/diagnostics/unsafe_check.rs b/crates/hir_ty/src/diagnostics/unsafe_check.rs index 2a18df253e..4e0dcf7b61 100644 --- a/crates/hir_ty/src/diagnostics/unsafe_check.rs +++ b/crates/hir_ty/src/diagnostics/unsafe_check.rs @@ -12,54 +12,57 @@ use crate::{db::HirDatabase, InferenceResult, Interner, TyExt, TyKind}; pub fn missing_unsafe(db: &dyn HirDatabase, def: DefWithBodyId) -> Vec { let infer = db.infer(def); + let mut res = Vec::new(); let is_unsafe = match def { DefWithBodyId::FunctionId(it) => db.function_data(it).is_unsafe(), DefWithBodyId::StaticId(_) | DefWithBodyId::ConstId(_) => false, }; if is_unsafe { - return Vec::new(); + return res; } - unsafe_expressions(db, &infer, def) - .into_iter() - .filter(|it| !it.inside_unsafe_block) - .map(|it| it.expr) - .collect() + let body = db.body(def); + unsafe_expressions(db, &infer, def, &body, body.body_expr, &mut |expr| { + if !expr.inside_unsafe_block { + res.push(expr.expr); + } + }); + + res } -struct UnsafeExpr { - pub(crate) expr: ExprId, - pub(crate) inside_unsafe_block: bool, +pub struct UnsafeExpr { + pub expr: ExprId, + pub inside_unsafe_block: bool, } -fn unsafe_expressions( +pub fn unsafe_expressions( db: &dyn HirDatabase, infer: &InferenceResult, def: DefWithBodyId, -) -> Vec { - let mut unsafe_exprs = vec![]; - let body = db.body(def); - walk_unsafe(&mut unsafe_exprs, db, infer, def, &body, body.body_expr, false); - - unsafe_exprs + body: &Body, + current: ExprId, + unsafe_expr_cb: &mut dyn FnMut(UnsafeExpr), +) { + walk_unsafe(db, infer, def, body, current, false, unsafe_expr_cb) } fn walk_unsafe( - unsafe_exprs: &mut Vec, db: &dyn HirDatabase, infer: &InferenceResult, def: DefWithBodyId, body: &Body, current: ExprId, inside_unsafe_block: bool, + unsafe_expr_cb: &mut dyn FnMut(UnsafeExpr), ) { let expr = &body.exprs[current]; match expr { &Expr::Call { callee, .. } => { if let Some(func) = infer[callee].as_fn_def(db) { if db.function_data(func).is_unsafe() { - unsafe_exprs.push(UnsafeExpr { expr: current, inside_unsafe_block }); + unsafe_expr_cb(UnsafeExpr { expr: current, inside_unsafe_block }); } } } @@ -68,7 +71,7 @@ fn walk_unsafe( let value_or_partial = resolver.resolve_path_in_value_ns(db.upcast(), path.mod_path()); if let Some(ResolveValueResult::ValueNs(ValueNs::StaticId(id))) = value_or_partial { if db.static_data(id).mutable { - unsafe_exprs.push(UnsafeExpr { expr: current, inside_unsafe_block }); + unsafe_expr_cb(UnsafeExpr { expr: current, inside_unsafe_block }); } } } @@ -78,21 +81,21 @@ fn walk_unsafe( .map(|(func, _)| db.function_data(func).is_unsafe()) .unwrap_or(false) { - unsafe_exprs.push(UnsafeExpr { expr: current, inside_unsafe_block }); + unsafe_expr_cb(UnsafeExpr { expr: current, inside_unsafe_block }); } } Expr::UnaryOp { expr, op: UnaryOp::Deref } => { if let TyKind::Raw(..) = &infer[*expr].kind(Interner) { - unsafe_exprs.push(UnsafeExpr { expr: current, inside_unsafe_block }); + unsafe_expr_cb(UnsafeExpr { expr: current, inside_unsafe_block }); } } Expr::Unsafe { body: child } => { - return walk_unsafe(unsafe_exprs, db, infer, def, body, *child, true); + return walk_unsafe(db, infer, def, body, *child, true, unsafe_expr_cb); } _ => {} } expr.walk_child_exprs(|child| { - walk_unsafe(unsafe_exprs, db, infer, def, body, child, inside_unsafe_block); + walk_unsafe(db, infer, def, body, child, inside_unsafe_block, unsafe_expr_cb); }); } diff --git a/crates/ide/src/syntax_highlighting/highlight.rs b/crates/ide/src/syntax_highlighting/highlight.rs index e7aa83dd9e..7ac6775685 100644 --- a/crates/ide/src/syntax_highlighting/highlight.rs +++ b/crates/ide/src/syntax_highlighting/highlight.rs @@ -248,6 +248,16 @@ fn highlight_name_ref( } } } + Definition::Macro(_) => { + if let Some(macro_call) = + ide_db::syntax_helpers::node_ext::full_path_of_name_ref(&name_ref) + .and_then(|it| it.syntax().parent().and_then(ast::MacroCall::cast)) + { + if sema.is_unsafe_macro_call(¯o_call) { + h |= HlMod::Unsafe; + } + } + } _ => (), } diff --git a/crates/ide/src/syntax_highlighting/html.rs b/crates/ide/src/syntax_highlighting/html.rs index 0491ce3635..9777c014c7 100644 --- a/crates/ide/src/syntax_highlighting/html.rs +++ b/crates/ide/src/syntax_highlighting/html.rs @@ -71,6 +71,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd .operator.unsafe { color: #BC8383; } .mutable.unsafe { color: #BC8383; text-decoration: underline; } .keyword.unsafe { color: #BC8383; font-weight: bold; } +.macro.unsafe { color: #BC8383; } .parameter { color: #94BFF3; } .text { color: #DCDCCC; } .type { color: #7CB8BB; } diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_assoc_functions.html b/crates/ide/src/syntax_highlighting/test_data/highlight_assoc_functions.html index b035e786d3..e07fd3925c 100644 --- a/crates/ide/src/syntax_highlighting/test_data/highlight_assoc_functions.html +++ b/crates/ide/src/syntax_highlighting/test_data/highlight_assoc_functions.html @@ -19,6 +19,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd .operator.unsafe { color: #BC8383; } .mutable.unsafe { color: #BC8383; text-decoration: underline; } .keyword.unsafe { color: #BC8383; font-weight: bold; } +.macro.unsafe { color: #BC8383; } .parameter { color: #94BFF3; } .text { color: #DCDCCC; } .type { color: #7CB8BB; } diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_attributes.html b/crates/ide/src/syntax_highlighting/test_data/highlight_attributes.html index 9fe2b50cde..1a43988147 100644 --- a/crates/ide/src/syntax_highlighting/test_data/highlight_attributes.html +++ b/crates/ide/src/syntax_highlighting/test_data/highlight_attributes.html @@ -19,6 +19,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd .operator.unsafe { color: #BC8383; } .mutable.unsafe { color: #BC8383; text-decoration: underline; } .keyword.unsafe { color: #BC8383; font-weight: bold; } +.macro.unsafe { color: #BC8383; } .parameter { color: #94BFF3; } .text { color: #DCDCCC; } .type { color: #7CB8BB; } diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_crate_root.html b/crates/ide/src/syntax_highlighting/test_data/highlight_crate_root.html index 37c8b56682..6704e5e8d5 100644 --- a/crates/ide/src/syntax_highlighting/test_data/highlight_crate_root.html +++ b/crates/ide/src/syntax_highlighting/test_data/highlight_crate_root.html @@ -19,6 +19,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd .operator.unsafe { color: #BC8383; } .mutable.unsafe { color: #BC8383; text-decoration: underline; } .keyword.unsafe { color: #BC8383; font-weight: bold; } +.macro.unsafe { color: #BC8383; } .parameter { color: #94BFF3; } .text { color: #DCDCCC; } .type { color: #7CB8BB; } diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_default_library.html b/crates/ide/src/syntax_highlighting/test_data/highlight_default_library.html index 3e20b2f351..5d66f832da 100644 --- a/crates/ide/src/syntax_highlighting/test_data/highlight_default_library.html +++ b/crates/ide/src/syntax_highlighting/test_data/highlight_default_library.html @@ -19,6 +19,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd .operator.unsafe { color: #BC8383; } .mutable.unsafe { color: #BC8383; text-decoration: underline; } .keyword.unsafe { color: #BC8383; font-weight: bold; } +.macro.unsafe { color: #BC8383; } .parameter { color: #94BFF3; } .text { color: #DCDCCC; } .type { color: #7CB8BB; } 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 96044751b8..d9126421cd 100644 --- a/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html +++ b/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html @@ -19,6 +19,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd .operator.unsafe { color: #BC8383; } .mutable.unsafe { color: #BC8383; text-decoration: underline; } .keyword.unsafe { color: #BC8383; font-weight: bold; } +.macro.unsafe { color: #BC8383; } .parameter { color: #94BFF3; } .text { color: #DCDCCC; } .type { color: #7CB8BB; } diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_extern_crate.html b/crates/ide/src/syntax_highlighting/test_data/highlight_extern_crate.html index 3208770d7f..87b9da46e2 100644 --- a/crates/ide/src/syntax_highlighting/test_data/highlight_extern_crate.html +++ b/crates/ide/src/syntax_highlighting/test_data/highlight_extern_crate.html @@ -19,6 +19,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd .operator.unsafe { color: #BC8383; } .mutable.unsafe { color: #BC8383; text-decoration: underline; } .keyword.unsafe { color: #BC8383; font-weight: bold; } +.macro.unsafe { color: #BC8383; } .parameter { color: #94BFF3; } .text { color: #DCDCCC; } .type { color: #7CB8BB; } diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_general.html b/crates/ide/src/syntax_highlighting/test_data/highlight_general.html index 86ed8dbd77..d5c930e049 100644 --- a/crates/ide/src/syntax_highlighting/test_data/highlight_general.html +++ b/crates/ide/src/syntax_highlighting/test_data/highlight_general.html @@ -19,6 +19,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd .operator.unsafe { color: #BC8383; } .mutable.unsafe { color: #BC8383; text-decoration: underline; } .keyword.unsafe { color: #BC8383; font-weight: bold; } +.macro.unsafe { color: #BC8383; } .parameter { color: #94BFF3; } .text { color: #DCDCCC; } .type { color: #7CB8BB; } diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_injection.html b/crates/ide/src/syntax_highlighting/test_data/highlight_injection.html index 023e791f8b..ced7d22f03 100644 --- a/crates/ide/src/syntax_highlighting/test_data/highlight_injection.html +++ b/crates/ide/src/syntax_highlighting/test_data/highlight_injection.html @@ -19,6 +19,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd .operator.unsafe { color: #BC8383; } .mutable.unsafe { color: #BC8383; text-decoration: underline; } .keyword.unsafe { color: #BC8383; font-weight: bold; } +.macro.unsafe { color: #BC8383; } .parameter { color: #94BFF3; } .text { color: #DCDCCC; } .type { color: #7CB8BB; } diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_keywords.html b/crates/ide/src/syntax_highlighting/test_data/highlight_keywords.html index 145bba2a06..278e0d84ee 100644 --- a/crates/ide/src/syntax_highlighting/test_data/highlight_keywords.html +++ b/crates/ide/src/syntax_highlighting/test_data/highlight_keywords.html @@ -19,6 +19,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd .operator.unsafe { color: #BC8383; } .mutable.unsafe { color: #BC8383; text-decoration: underline; } .keyword.unsafe { color: #BC8383; font-weight: bold; } +.macro.unsafe { color: #BC8383; } .parameter { color: #94BFF3; } .text { color: #DCDCCC; } .type { color: #7CB8BB; } diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_lifetimes.html b/crates/ide/src/syntax_highlighting/test_data/highlight_lifetimes.html index 9e36b56d28..2d85fc8c92 100644 --- a/crates/ide/src/syntax_highlighting/test_data/highlight_lifetimes.html +++ b/crates/ide/src/syntax_highlighting/test_data/highlight_lifetimes.html @@ -19,6 +19,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd .operator.unsafe { color: #BC8383; } .mutable.unsafe { color: #BC8383; text-decoration: underline; } .keyword.unsafe { color: #BC8383; font-weight: bold; } +.macro.unsafe { color: #BC8383; } .parameter { color: #94BFF3; } .text { color: #DCDCCC; } .type { color: #7CB8BB; } diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_macros.html b/crates/ide/src/syntax_highlighting/test_data/highlight_macros.html index c3f71d443f..71add7e495 100644 --- a/crates/ide/src/syntax_highlighting/test_data/highlight_macros.html +++ b/crates/ide/src/syntax_highlighting/test_data/highlight_macros.html @@ -19,6 +19,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd .operator.unsafe { color: #BC8383; } .mutable.unsafe { color: #BC8383; text-decoration: underline; } .keyword.unsafe { color: #BC8383; font-weight: bold; } +.macro.unsafe { color: #BC8383; } .parameter { color: #94BFF3; } .text { color: #DCDCCC; } .type { color: #7CB8BB; } diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_operators.html b/crates/ide/src/syntax_highlighting/test_data/highlight_operators.html index 46220993e6..2369071ae2 100644 --- a/crates/ide/src/syntax_highlighting/test_data/highlight_operators.html +++ b/crates/ide/src/syntax_highlighting/test_data/highlight_operators.html @@ -19,6 +19,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd .operator.unsafe { color: #BC8383; } .mutable.unsafe { color: #BC8383; text-decoration: underline; } .keyword.unsafe { color: #BC8383; font-weight: bold; } +.macro.unsafe { color: #BC8383; } .parameter { color: #94BFF3; } .text { color: #DCDCCC; } .type { color: #7CB8BB; } diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_rainbow.html b/crates/ide/src/syntax_highlighting/test_data/highlight_rainbow.html index e0287f1185..bff35c897e 100644 --- a/crates/ide/src/syntax_highlighting/test_data/highlight_rainbow.html +++ b/crates/ide/src/syntax_highlighting/test_data/highlight_rainbow.html @@ -19,6 +19,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd .operator.unsafe { color: #BC8383; } .mutable.unsafe { color: #BC8383; text-decoration: underline; } .keyword.unsafe { color: #BC8383; font-weight: bold; } +.macro.unsafe { color: #BC8383; } .parameter { color: #94BFF3; } .text { color: #DCDCCC; } .type { color: #7CB8BB; } 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 3715164bbf..e26c017f95 100644 --- a/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html +++ b/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html @@ -19,6 +19,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd .operator.unsafe { color: #BC8383; } .mutable.unsafe { color: #BC8383; text-decoration: underline; } .keyword.unsafe { color: #BC8383; font-weight: bold; } +.macro.unsafe { color: #BC8383; } .parameter { color: #94BFF3; } .text { color: #DCDCCC; } .type { color: #7CB8BB; } 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 25bfcb6482..113463aa7a 100644 --- a/crates/ide/src/syntax_highlighting/test_data/highlight_unsafe.html +++ b/crates/ide/src/syntax_highlighting/test_data/highlight_unsafe.html @@ -19,6 +19,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd .operator.unsafe { color: #BC8383; } .mutable.unsafe { color: #BC8383; text-decoration: underline; } .keyword.unsafe { color: #BC8383; font-weight: bold; } +.macro.unsafe { color: #BC8383; } .parameter { color: #94BFF3; } .text { color: #DCDCCC; } .type { color: #7CB8BB; } @@ -41,7 +42,17 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd .unresolved_reference { color: #FC5555; text-decoration: wavy underline; } -
static mut MUT_GLOBAL: Struct = Struct { field: 0 };
+
macro_rules! id {
+    ($($tt:tt)*) => {
+        $($tt)*
+    };
+}
+macro_rules! unsafe_deref {
+    () => {
+        *(&() as *const ())
+    };
+}
+static mut MUT_GLOBAL: Struct = Struct { field: 0 };
 static GLOBAL: Struct = Struct { field: 0 };
 unsafe fn unsafe_fn() {}
 
@@ -77,7 +88,15 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 fn main() {
     let x = &5 as *const _ as *const usize;
     let u = Union { b: 0 };
+
+    id! {
+        unsafe { unsafe_deref!() }
+    };
+
     unsafe {
+        unsafe_deref!();
+        id! { unsafe_deref!() };
+
         // unsafe fn and method calls
         unsafe_fn();
         let b = u.b;
diff --git a/crates/ide/src/syntax_highlighting/tests.rs b/crates/ide/src/syntax_highlighting/tests.rs
index a8c69875e0..c74bced637 100644
--- a/crates/ide/src/syntax_highlighting/tests.rs
+++ b/crates/ide/src/syntax_highlighting/tests.rs
@@ -483,6 +483,16 @@ fn main() {
 fn test_unsafe_highlighting() {
     check_highlighting(
         r#"
+macro_rules! id {
+    ($($tt:tt)*) => {
+        $($tt)*
+    };
+}
+macro_rules! unsafe_deref {
+    () => {
+        *(&() as *const ())
+    };
+}
 static mut MUT_GLOBAL: Struct = Struct { field: 0 };
 static GLOBAL: Struct = Struct { field: 0 };
 unsafe fn unsafe_fn() {}
@@ -519,7 +529,15 @@ impl DoTheAutoref for u16 {
 fn main() {
     let x = &5 as *const _ as *const usize;
     let u = Union { b: 0 };
+
+    id! {
+        unsafe { unsafe_deref!() }
+    };
+
     unsafe {
+        unsafe_deref!();
+        id! { unsafe_deref!() };
+
         // unsafe fn and method calls
         unsafe_fn();
         let b = u.b;
diff --git a/crates/ide_db/src/syntax_helpers/node_ext.rs b/crates/ide_db/src/syntax_helpers/node_ext.rs
index c0f0529966..3d72045838 100644
--- a/crates/ide_db/src/syntax_helpers/node_ext.rs
+++ b/crates/ide_db/src/syntax_helpers/node_ext.rs
@@ -15,6 +15,13 @@ pub fn expr_as_name_ref(expr: &ast::Expr) -> Option {
     }
 }
 
+pub fn full_path_of_name_ref(name_ref: &ast::NameRef) -> Option {
+    let mut ancestors = name_ref.syntax().ancestors();
+    let _ = ancestors.next()?; // skip self
+    let _ = ancestors.next().filter(|it| ast::PathSegment::can_cast(it.kind()))?; // skip self
+    ancestors.take_while(|it| ast::Path::can_cast(it.kind())).last().and_then(ast::Path::cast)
+}
+
 pub fn block_as_lone_tail(block: &ast::BlockExpr) -> Option {
     block.statements().next().is_none().then(|| block.tail_expr()).flatten()
 }