aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorbors[bot] <26634292+bors[bot]@users.noreply.github.com>2021-02-28 11:29:44 +0000
committerGitHub <[email protected]>2021-02-28 11:29:44 +0000
commit7ed2da652df2f8d81ca0d76a978da2074f8c64f3 (patch)
tree5e0c37057750cc897031a02ec4860462a4cea9ef
parentc412d5f8d13cd55dc1873fb55e742a317c7846a8 (diff)
parent05729fd3c4aa542d162b54e7352c0d4bade62684 (diff)
Merge #7805
7805: For unresolved macros, hightlight only the last segment r=matklad a=matklad bors r+ 🤖 Co-authored-by: Aleksey Kladov <[email protected]>
-rw-r--r--crates/hir/src/diagnostics.rs4
-rw-r--r--crates/hir/src/semantics.rs8
-rw-r--r--crates/hir_def/src/body.rs2
-rw-r--r--crates/hir_def/src/diagnostics.rs28
-rw-r--r--crates/hir_def/src/lib.rs114
-rw-r--r--crates/hir_def/src/nameres.rs14
-rw-r--r--crates/hir_def/src/nameres/collector.rs81
-rw-r--r--crates/ide/src/diagnostics.rs67
8 files changed, 209 insertions, 109 deletions
diff --git a/crates/hir/src/diagnostics.rs b/crates/hir/src/diagnostics.rs
index 5343a036c..b1ebba516 100644
--- a/crates/hir/src/diagnostics.rs
+++ b/crates/hir/src/diagnostics.rs
@@ -1,5 +1,7 @@
1//! FIXME: write short doc here 1//! FIXME: write short doc here
2pub use hir_def::diagnostics::{InactiveCode, UnresolvedModule, UnresolvedProcMacro}; 2pub use hir_def::diagnostics::{
3 InactiveCode, UnresolvedMacroCall, UnresolvedModule, UnresolvedProcMacro,
4};
3pub use hir_expand::diagnostics::{ 5pub use hir_expand::diagnostics::{
4 Diagnostic, DiagnosticCode, DiagnosticSink, DiagnosticSinkBuilder, 6 Diagnostic, DiagnosticCode, DiagnosticSink, DiagnosticSinkBuilder,
5}; 7};
diff --git a/crates/hir/src/semantics.rs b/crates/hir/src/semantics.rs
index 59292d5a2..144851f83 100644
--- a/crates/hir/src/semantics.rs
+++ b/crates/hir/src/semantics.rs
@@ -16,13 +16,12 @@ use rustc_hash::{FxHashMap, FxHashSet};
16use syntax::{ 16use syntax::{
17 algo::find_node_at_offset, 17 algo::find_node_at_offset,
18 ast::{self, GenericParamsOwner, LoopBodyOwner}, 18 ast::{self, GenericParamsOwner, LoopBodyOwner},
19 match_ast, AstNode, SyntaxNode, SyntaxToken, TextSize, 19 match_ast, AstNode, SyntaxNode, SyntaxNodePtr, SyntaxToken, TextSize,
20}; 20};
21 21
22use crate::{ 22use crate::{
23 code_model::Access, 23 code_model::Access,
24 db::HirDatabase, 24 db::HirDatabase,
25 diagnostics::Diagnostic,
26 semantics::source_to_def::{ChildContainer, SourceToDefCache, SourceToDefCtx}, 25 semantics::source_to_def::{ChildContainer, SourceToDefCache, SourceToDefCtx},
27 source_analyzer::{resolve_hir_path, SourceAnalyzer}, 26 source_analyzer::{resolve_hir_path, SourceAnalyzer},
28 AssocItem, Callable, ConstParam, Crate, Field, Function, HirFileId, Impl, InFile, Label, 27 AssocItem, Callable, ConstParam, Crate, Field, Function, HirFileId, Impl, InFile, Label,
@@ -141,7 +140,7 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> {
141 self.imp.original_range(node) 140 self.imp.original_range(node)
142 } 141 }
143 142
144 pub fn diagnostics_display_range(&self, diagnostics: &dyn Diagnostic) -> FileRange { 143 pub fn diagnostics_display_range(&self, diagnostics: InFile<SyntaxNodePtr>) -> FileRange {
145 self.imp.diagnostics_display_range(diagnostics) 144 self.imp.diagnostics_display_range(diagnostics)
146 } 145 }
147 146
@@ -385,8 +384,7 @@ impl<'db> SemanticsImpl<'db> {
385 node.as_ref().original_file_range(self.db.upcast()) 384 node.as_ref().original_file_range(self.db.upcast())
386 } 385 }
387 386
388 fn diagnostics_display_range(&self, diagnostics: &dyn Diagnostic) -> FileRange { 387 fn diagnostics_display_range(&self, src: InFile<SyntaxNodePtr>) -> FileRange {
389 let src = diagnostics.display_source();
390 let root = self.db.parse_or_expand(src.file_id).unwrap(); 388 let root = self.db.parse_or_expand(src.file_id).unwrap();
391 let node = src.value.to_node(&root); 389 let node = src.value.to_node(&root);
392 self.cache(root, src.file_id); 390 self.cache(root, src.file_id);
diff --git a/crates/hir_def/src/body.rs b/crates/hir_def/src/body.rs
index 9a432f7d1..ff4b4a0cf 100644
--- a/crates/hir_def/src/body.rs
+++ b/crates/hir_def/src/body.rs
@@ -123,7 +123,7 @@ impl Expander {
123 Some(it) => it, 123 Some(it) => it,
124 None => { 124 None => {
125 if err.is_none() { 125 if err.is_none() {
126 eprintln!("no error despite `as_call_id_with_errors` returning `None`"); 126 log::warn!("no error despite `as_call_id_with_errors` returning `None`");
127 } 127 }
128 return ExpandResult { value: None, err }; 128 return ExpandResult { value: None, err };
129 } 129 }
diff --git a/crates/hir_def/src/diagnostics.rs b/crates/hir_def/src/diagnostics.rs
index ab3f059ce..ac7474f63 100644
--- a/crates/hir_def/src/diagnostics.rs
+++ b/crates/hir_def/src/diagnostics.rs
@@ -95,6 +95,34 @@ impl Diagnostic for UnresolvedImport {
95 } 95 }
96} 96}
97 97
98// Diagnostic: unresolved-macro-call
99//
100// This diagnostic is triggered if rust-analyzer is unable to resolove path to a
101// macro in a macro invocation.
102#[derive(Debug)]
103pub struct UnresolvedMacroCall {
104 pub file: HirFileId,
105 pub node: AstPtr<ast::MacroCall>,
106}
107
108impl Diagnostic for UnresolvedMacroCall {
109 fn code(&self) -> DiagnosticCode {
110 DiagnosticCode("unresolved-macro-call")
111 }
112 fn message(&self) -> String {
113 "unresolved macro call".to_string()
114 }
115 fn display_source(&self) -> InFile<SyntaxNodePtr> {
116 InFile::new(self.file, self.node.clone().into())
117 }
118 fn as_any(&self) -> &(dyn Any + Send + 'static) {
119 self
120 }
121 fn is_experimental(&self) -> bool {
122 true
123 }
124}
125
98// Diagnostic: inactive-code 126// Diagnostic: inactive-code
99// 127//
100// This diagnostic is shown for code with inactive `#[cfg]` attributes. 128// This diagnostic is shown for code with inactive `#[cfg]` attributes.
diff --git a/crates/hir_def/src/lib.rs b/crates/hir_def/src/lib.rs
index b50923747..6802bc250 100644
--- a/crates/hir_def/src/lib.rs
+++ b/crates/hir_def/src/lib.rs
@@ -57,8 +57,10 @@ use std::{
57 57
58use base_db::{impl_intern_key, salsa, CrateId}; 58use base_db::{impl_intern_key, salsa, CrateId};
59use hir_expand::{ 59use hir_expand::{
60 ast_id_map::FileAstId, eager::expand_eager_macro, hygiene::Hygiene, AstId, HirFileId, InFile, 60 ast_id_map::FileAstId,
61 MacroCallId, MacroCallKind, MacroDefId, MacroDefKind, 61 eager::{expand_eager_macro, ErrorEmitted},
62 hygiene::Hygiene,
63 AstId, HirFileId, InFile, MacroCallId, MacroCallKind, MacroDefId, MacroDefKind,
62}; 64};
63use la_arena::Idx; 65use la_arena::Idx;
64use nameres::DefMap; 66use nameres::DefMap;
@@ -592,8 +594,15 @@ impl AsMacroCall for InFile<&ast::MacroCall> {
592 error_sink(mbe::ExpandError::Other("malformed macro invocation".into())); 594 error_sink(mbe::ExpandError::Other("malformed macro invocation".into()));
593 } 595 }
594 596
595 AstIdWithPath::new(ast_id.file_id, ast_id.value, path?) 597 macro_call_as_call_id(
596 .as_call_id_with_errors(db, krate, resolver, error_sink) 598 &AstIdWithPath::new(ast_id.file_id, ast_id.value, path?),
599 db,
600 krate,
601 resolver,
602 error_sink,
603 )
604 .ok()?
605 .ok()
597 } 606 }
598} 607}
599 608
@@ -610,61 +619,50 @@ impl<T: ast::AstNode> AstIdWithPath<T> {
610 } 619 }
611} 620}
612 621
613impl AsMacroCall for AstIdWithPath<ast::MacroCall> { 622struct UnresolvedMacro;
614 fn as_call_id_with_errors( 623
615 &self, 624fn macro_call_as_call_id(
616 db: &dyn db::DefDatabase, 625 call: &AstIdWithPath<ast::MacroCall>,
617 krate: CrateId, 626 db: &dyn db::DefDatabase,
618 resolver: impl Fn(path::ModPath) -> Option<MacroDefId>, 627 krate: CrateId,
619 error_sink: &mut dyn FnMut(mbe::ExpandError), 628 resolver: impl Fn(path::ModPath) -> Option<MacroDefId>,
620 ) -> Option<MacroCallId> { 629 error_sink: &mut dyn FnMut(mbe::ExpandError),
621 let def: MacroDefId = resolver(self.path.clone()).or_else(|| { 630) -> Result<Result<MacroCallId, ErrorEmitted>, UnresolvedMacro> {
622 error_sink(mbe::ExpandError::Other(format!("could not resolve macro `{}`", self.path))); 631 let def: MacroDefId = resolver(call.path.clone()).ok_or(UnresolvedMacro)?;
623 None 632
624 })?; 633 let res = if let MacroDefKind::BuiltInEager(_) = def.kind {
625 634 let macro_call = InFile::new(call.ast_id.file_id, call.ast_id.to_node(db.upcast()));
626 if let MacroDefKind::BuiltInEager(_) = def.kind { 635 let hygiene = Hygiene::new(db.upcast(), call.ast_id.file_id);
627 let macro_call = InFile::new(self.ast_id.file_id, self.ast_id.to_node(db.upcast())); 636
628 let hygiene = Hygiene::new(db.upcast(), self.ast_id.file_id); 637 expand_eager_macro(
629 638 db.upcast(),
630 Some( 639 krate,
631 expand_eager_macro( 640 macro_call,
632 db.upcast(), 641 def,
633 krate, 642 &|path: ast::Path| resolver(path::ModPath::from_src(path, &hygiene)?),
634 macro_call, 643 error_sink,
635 def, 644 )
636 &|path: ast::Path| resolver(path::ModPath::from_src(path, &hygiene)?), 645 .map(MacroCallId::from)
637 error_sink, 646 } else {
638 ) 647 Ok(def.as_lazy_macro(db.upcast(), krate, MacroCallKind::FnLike(call.ast_id)).into())
639 .ok()? 648 };
640 .into(), 649 Ok(res)
641 )
642 } else {
643 Some(def.as_lazy_macro(db.upcast(), krate, MacroCallKind::FnLike(self.ast_id)).into())
644 }
645 }
646} 650}
647 651
648impl AsMacroCall for AstIdWithPath<ast::Item> { 652fn item_attr_as_call_id(
649 fn as_call_id_with_errors( 653 item_attr: &AstIdWithPath<ast::Item>,
650 &self, 654 db: &dyn db::DefDatabase,
651 db: &dyn db::DefDatabase, 655 krate: CrateId,
652 krate: CrateId, 656 resolver: impl Fn(path::ModPath) -> Option<MacroDefId>,
653 resolver: impl Fn(path::ModPath) -> Option<MacroDefId>, 657) -> Result<MacroCallId, UnresolvedMacro> {
654 error_sink: &mut dyn FnMut(mbe::ExpandError), 658 let def: MacroDefId = resolver(item_attr.path.clone()).ok_or(UnresolvedMacro)?;
655 ) -> Option<MacroCallId> { 659 let last_segment = item_attr.path.segments().last().ok_or(UnresolvedMacro)?;
656 let def: MacroDefId = resolver(self.path.clone()).or_else(|| { 660 let res = def
657 error_sink(mbe::ExpandError::Other(format!("could not resolve macro `{}`", self.path))); 661 .as_lazy_macro(
658 None 662 db.upcast(),
659 })?; 663 krate,
660 664 MacroCallKind::Attr(item_attr.ast_id, last_segment.to_string()),
661 Some(
662 def.as_lazy_macro(
663 db.upcast(),
664 krate,
665 MacroCallKind::Attr(self.ast_id, self.path.segments().last()?.to_string()),
666 )
667 .into(),
668 ) 665 )
669 } 666 .into();
667 Ok(res)
670} 668}
diff --git a/crates/hir_def/src/nameres.rs b/crates/hir_def/src/nameres.rs
index f92232eb3..6a3456f2e 100644
--- a/crates/hir_def/src/nameres.rs
+++ b/crates/hir_def/src/nameres.rs
@@ -417,6 +417,8 @@ mod diagnostics {
417 417
418 UnresolvedProcMacro { ast: MacroCallKind }, 418 UnresolvedProcMacro { ast: MacroCallKind },
419 419
420 UnresolvedMacroCall { ast: AstId<ast::MacroCall> },
421
420 MacroError { ast: MacroCallKind, message: String }, 422 MacroError { ast: MacroCallKind, message: String },
421 } 423 }
422 424
@@ -477,6 +479,13 @@ mod diagnostics {
477 Self { in_module: container, kind: DiagnosticKind::MacroError { ast, message } } 479 Self { in_module: container, kind: DiagnosticKind::MacroError { ast, message } }
478 } 480 }
479 481
482 pub(super) fn unresolved_macro_call(
483 container: LocalModuleId,
484 ast: AstId<ast::MacroCall>,
485 ) -> Self {
486 Self { in_module: container, kind: DiagnosticKind::UnresolvedMacroCall { ast } }
487 }
488
480 pub(super) fn add_to( 489 pub(super) fn add_to(
481 &self, 490 &self,
482 db: &dyn DefDatabase, 491 db: &dyn DefDatabase,
@@ -589,6 +598,11 @@ mod diagnostics {
589 }); 598 });
590 } 599 }
591 600
601 DiagnosticKind::UnresolvedMacroCall { ast } => {
602 let node = ast.to_node(db.upcast());
603 sink.push(UnresolvedMacroCall { file: ast.file_id, node: AstPtr::new(&node) });
604 }
605
592 DiagnosticKind::MacroError { ast, message } => { 606 DiagnosticKind::MacroError { ast, message } => {
593 let (file, ast) = match ast { 607 let (file, ast) = match ast {
594 MacroCallKind::FnLike(ast) => { 608 MacroCallKind::FnLike(ast) => {
diff --git a/crates/hir_def/src/nameres/collector.rs b/crates/hir_def/src/nameres/collector.rs
index 9996a0807..e51d89b43 100644
--- a/crates/hir_def/src/nameres/collector.rs
+++ b/crates/hir_def/src/nameres/collector.rs
@@ -13,7 +13,7 @@ use hir_expand::{
13 builtin_macro::find_builtin_macro, 13 builtin_macro::find_builtin_macro,
14 name::{AsName, Name}, 14 name::{AsName, Name},
15 proc_macro::ProcMacroExpander, 15 proc_macro::ProcMacroExpander,
16 HirFileId, MacroCallId, MacroCallKind, MacroDefId, MacroDefKind, 16 HirFileId, MacroCallId, MacroDefId, MacroDefKind,
17}; 17};
18use hir_expand::{InFile, MacroCallLoc}; 18use hir_expand::{InFile, MacroCallLoc};
19use rustc_hash::{FxHashMap, FxHashSet}; 19use rustc_hash::{FxHashMap, FxHashSet};
@@ -24,11 +24,13 @@ use tt::{Leaf, TokenTree};
24use crate::{ 24use crate::{
25 attr::Attrs, 25 attr::Attrs,
26 db::DefDatabase, 26 db::DefDatabase,
27 item_attr_as_call_id,
27 item_scope::{ImportType, PerNsGlobImports}, 28 item_scope::{ImportType, PerNsGlobImports},
28 item_tree::{ 29 item_tree::{
29 self, FileItemTreeId, ItemTree, ItemTreeId, MacroCall, MacroRules, Mod, ModItem, ModKind, 30 self, FileItemTreeId, ItemTree, ItemTreeId, MacroCall, MacroRules, Mod, ModItem, ModKind,
30 StructDefKind, 31 StructDefKind,
31 }, 32 },
33 macro_call_as_call_id,
32 nameres::{ 34 nameres::{
33 diagnostics::DefDiagnostic, mod_resolution::ModDir, path_resolution::ReachedFixedPoint, 35 diagnostics::DefDiagnostic, mod_resolution::ModDir, path_resolution::ReachedFixedPoint,
34 BuiltinShadowMode, DefMap, ModuleData, ModuleOrigin, ResolveMode, 36 BuiltinShadowMode, DefMap, ModuleData, ModuleOrigin, ResolveMode,
@@ -36,9 +38,9 @@ use crate::{
36 path::{ImportAlias, ModPath, PathKind}, 38 path::{ImportAlias, ModPath, PathKind},
37 per_ns::PerNs, 39 per_ns::PerNs,
38 visibility::{RawVisibility, Visibility}, 40 visibility::{RawVisibility, Visibility},
39 AdtId, AsMacroCall, AstId, AstIdWithPath, ConstLoc, ContainerId, EnumLoc, EnumVariantId, 41 AdtId, AstId, AstIdWithPath, ConstLoc, ContainerId, EnumLoc, EnumVariantId, FunctionLoc,
40 FunctionLoc, ImplLoc, Intern, LocalModuleId, ModuleDefId, StaticLoc, StructLoc, TraitLoc, 42 ImplLoc, Intern, LocalModuleId, ModuleDefId, StaticLoc, StructLoc, TraitLoc, TypeAliasLoc,
41 TypeAliasLoc, UnionLoc, 43 UnionLoc, UnresolvedMacro,
42}; 44};
43 45
44const GLOB_RECURSION_LIMIT: usize = 100; 46const GLOB_RECURSION_LIMIT: usize = 100;
@@ -790,8 +792,11 @@ impl DefCollector<'_> {
790 return false; 792 return false;
791 } 793 }
792 794
793 if let Some(call_id) = 795 match macro_call_as_call_id(
794 directive.ast_id.as_call_id(self.db, self.def_map.krate, |path| { 796 &directive.ast_id,
797 self.db,
798 self.def_map.krate,
799 |path| {
795 let resolved_res = self.def_map.resolve_path_fp_with_macro( 800 let resolved_res = self.def_map.resolve_path_fp_with_macro(
796 self.db, 801 self.db,
797 ResolveMode::Other, 802 ResolveMode::Other,
@@ -800,24 +805,29 @@ impl DefCollector<'_> {
800 BuiltinShadowMode::Module, 805 BuiltinShadowMode::Module,
801 ); 806 );
802 resolved_res.resolved_def.take_macros() 807 resolved_res.resolved_def.take_macros()
803 }) 808 },
804 { 809 &mut |_err| (),
805 resolved.push((directive.module_id, call_id, directive.depth)); 810 ) {
806 res = ReachedFixedPoint::No; 811 Ok(Ok(call_id)) => {
807 return false; 812 resolved.push((directive.module_id, call_id, directive.depth));
813 res = ReachedFixedPoint::No;
814 return false;
815 }
816 Err(UnresolvedMacro) | Ok(Err(_)) => {}
808 } 817 }
809 818
810 true 819 true
811 }); 820 });
812 attribute_macros.retain(|directive| { 821 attribute_macros.retain(|directive| {
813 if let Some(call_id) = 822 match item_attr_as_call_id(&directive.ast_id, self.db, self.def_map.krate, |path| {
814 directive.ast_id.as_call_id(self.db, self.def_map.krate, |path| { 823 self.resolve_attribute_macro(&directive, &path)
815 self.resolve_attribute_macro(&directive, &path) 824 }) {
816 }) 825 Ok(call_id) => {
817 { 826 resolved.push((directive.module_id, call_id, 0));
818 resolved.push((directive.module_id, call_id, 0)); 827 res = ReachedFixedPoint::No;
819 res = ReachedFixedPoint::No; 828 return false;
820 return false; 829 }
830 Err(UnresolvedMacro) => (),
821 } 831 }
822 832
823 true 833 true
@@ -902,7 +912,8 @@ impl DefCollector<'_> {
902 912
903 for directive in &self.unexpanded_macros { 913 for directive in &self.unexpanded_macros {
904 let mut error = None; 914 let mut error = None;
905 directive.ast_id.as_call_id_with_errors( 915 match macro_call_as_call_id(
916 &directive.ast_id,
906 self.db, 917 self.db,
907 self.def_map.krate, 918 self.def_map.krate,
908 |path| { 919 |path| {
@@ -918,15 +929,15 @@ impl DefCollector<'_> {
918 &mut |e| { 929 &mut |e| {
919 error.get_or_insert(e); 930 error.get_or_insert(e);
920 }, 931 },
921 ); 932 ) {
922 933 Ok(_) => (),
923 if let Some(err) = error { 934 Err(UnresolvedMacro) => {
924 self.def_map.diagnostics.push(DefDiagnostic::macro_error( 935 self.def_map.diagnostics.push(DefDiagnostic::unresolved_macro_call(
925 directive.module_id, 936 directive.module_id,
926 MacroCallKind::FnLike(directive.ast_id.ast_id), 937 directive.ast_id.ast_id,
927 err.to_string(), 938 ));
928 )); 939 }
929 } 940 };
930 } 941 }
931 942
932 // Emit diagnostics for all remaining unresolved imports. 943 // Emit diagnostics for all remaining unresolved imports.
@@ -1446,8 +1457,11 @@ impl ModCollector<'_, '_> {
1446 let mut ast_id = AstIdWithPath::new(self.file_id, mac.ast_id, mac.path.clone()); 1457 let mut ast_id = AstIdWithPath::new(self.file_id, mac.ast_id, mac.path.clone());
1447 1458
1448 // Case 1: try to resolve in legacy scope and expand macro_rules 1459 // Case 1: try to resolve in legacy scope and expand macro_rules
1449 if let Some(macro_call_id) = 1460 if let Ok(Ok(macro_call_id)) = macro_call_as_call_id(
1450 ast_id.as_call_id(self.def_collector.db, self.def_collector.def_map.krate, |path| { 1461 &ast_id,
1462 self.def_collector.db,
1463 self.def_collector.def_map.krate,
1464 |path| {
1451 path.as_ident().and_then(|name| { 1465 path.as_ident().and_then(|name| {
1452 self.def_collector.def_map.with_ancestor_maps( 1466 self.def_collector.def_map.with_ancestor_maps(
1453 self.def_collector.db, 1467 self.def_collector.db,
@@ -1455,8 +1469,9 @@ impl ModCollector<'_, '_> {
1455 &mut |map, module| map[module].scope.get_legacy_macro(&name), 1469 &mut |map, module| map[module].scope.get_legacy_macro(&name),
1456 ) 1470 )
1457 }) 1471 })
1458 }) 1472 },
1459 { 1473 &mut |_err| (),
1474 ) {
1460 self.def_collector.unexpanded_macros.push(MacroDirective { 1475 self.def_collector.unexpanded_macros.push(MacroDirective {
1461 module_id: self.module_id, 1476 module_id: self.module_id,
1462 ast_id, 1477 ast_id,
diff --git a/crates/ide/src/diagnostics.rs b/crates/ide/src/diagnostics.rs
index 8607139ba..fe32f39b6 100644
--- a/crates/ide/src/diagnostics.rs
+++ b/crates/ide/src/diagnostics.rs
@@ -10,15 +10,16 @@ mod field_shorthand;
10use std::cell::RefCell; 10use std::cell::RefCell;
11 11
12use hir::{ 12use hir::{
13 db::AstDatabase,
13 diagnostics::{Diagnostic as _, DiagnosticCode, DiagnosticSinkBuilder}, 14 diagnostics::{Diagnostic as _, DiagnosticCode, DiagnosticSinkBuilder},
14 Semantics, 15 InFile, Semantics,
15}; 16};
16use ide_db::{base_db::SourceDatabase, RootDatabase}; 17use ide_db::{base_db::SourceDatabase, RootDatabase};
17use itertools::Itertools; 18use itertools::Itertools;
18use rustc_hash::FxHashSet; 19use rustc_hash::FxHashSet;
19use syntax::{ 20use syntax::{
20 ast::{self, AstNode}, 21 ast::{self, AstNode},
21 SyntaxNode, TextRange, 22 SyntaxNode, SyntaxNodePtr, TextRange,
22}; 23};
23use text_edit::TextEdit; 24use text_edit::TextEdit;
24 25
@@ -147,20 +148,38 @@ pub(crate) fn diagnostics(
147 148
148 // Override severity and mark as unused. 149 // Override severity and mark as unused.
149 res.borrow_mut().push( 150 res.borrow_mut().push(
150 Diagnostic::hint(sema.diagnostics_display_range(d).range, d.message()) 151 Diagnostic::hint(
151 .with_unused(true) 152 sema.diagnostics_display_range(d.display_source()).range,
152 .with_code(Some(d.code())), 153 d.message(),
154 )
155 .with_unused(true)
156 .with_code(Some(d.code())),
153 ); 157 );
154 }) 158 })
155 .on::<hir::diagnostics::UnresolvedProcMacro, _>(|d| { 159 .on::<hir::diagnostics::UnresolvedProcMacro, _>(|d| {
156 // Use more accurate position if available. 160 // Use more accurate position if available.
157 let display_range = 161 let display_range = d
158 d.precise_location.unwrap_or_else(|| sema.diagnostics_display_range(d).range); 162 .precise_location
163 .unwrap_or_else(|| sema.diagnostics_display_range(d.display_source()).range);
159 164
160 // FIXME: it would be nice to tell the user whether proc macros are currently disabled 165 // FIXME: it would be nice to tell the user whether proc macros are currently disabled
161 res.borrow_mut() 166 res.borrow_mut()
162 .push(Diagnostic::hint(display_range, d.message()).with_code(Some(d.code()))); 167 .push(Diagnostic::hint(display_range, d.message()).with_code(Some(d.code())));
163 }) 168 })
169 .on::<hir::diagnostics::UnresolvedMacroCall, _>(|d| {
170 let last_path_segment = sema.db.parse_or_expand(d.file).and_then(|root| {
171 d.node
172 .to_node(&root)
173 .path()
174 .and_then(|it| it.segment())
175 .and_then(|it| it.name_ref())
176 .map(|it| InFile::new(d.file, SyntaxNodePtr::new(it.syntax())))
177 });
178 let diagnostics = last_path_segment.unwrap_or_else(|| d.display_source());
179 let display_range = sema.diagnostics_display_range(diagnostics).range;
180 res.borrow_mut()
181 .push(Diagnostic::error(display_range, d.message()).with_code(Some(d.code())));
182 })
164 // Only collect experimental diagnostics when they're enabled. 183 // Only collect experimental diagnostics when they're enabled.
165 .filter(|diag| !(diag.is_experimental() && config.disable_experimental)) 184 .filter(|diag| !(diag.is_experimental() && config.disable_experimental))
166 .filter(|diag| !config.disabled.contains(diag.code().as_str())); 185 .filter(|diag| !config.disabled.contains(diag.code().as_str()));
@@ -170,8 +189,11 @@ pub(crate) fn diagnostics(
170 // Diagnostics not handled above get no fix and default treatment. 189 // Diagnostics not handled above get no fix and default treatment.
171 .build(|d| { 190 .build(|d| {
172 res.borrow_mut().push( 191 res.borrow_mut().push(
173 Diagnostic::error(sema.diagnostics_display_range(d).range, d.message()) 192 Diagnostic::error(
174 .with_code(Some(d.code())), 193 sema.diagnostics_display_range(d.display_source()).range,
194 d.message(),
195 )
196 .with_code(Some(d.code())),
175 ); 197 );
176 }); 198 });
177 199
@@ -183,13 +205,13 @@ pub(crate) fn diagnostics(
183} 205}
184 206
185fn diagnostic_with_fix<D: DiagnosticWithFix>(d: &D, sema: &Semantics<RootDatabase>) -> Diagnostic { 207fn diagnostic_with_fix<D: DiagnosticWithFix>(d: &D, sema: &Semantics<RootDatabase>) -> Diagnostic {
186 Diagnostic::error(sema.diagnostics_display_range(d).range, d.message()) 208 Diagnostic::error(sema.diagnostics_display_range(d.display_source()).range, d.message())
187 .with_fix(d.fix(&sema)) 209 .with_fix(d.fix(&sema))
188 .with_code(Some(d.code())) 210 .with_code(Some(d.code()))
189} 211}
190 212
191fn warning_with_fix<D: DiagnosticWithFix>(d: &D, sema: &Semantics<RootDatabase>) -> Diagnostic { 213fn warning_with_fix<D: DiagnosticWithFix>(d: &D, sema: &Semantics<RootDatabase>) -> Diagnostic {
192 Diagnostic::hint(sema.diagnostics_display_range(d).range, d.message()) 214 Diagnostic::hint(sema.diagnostics_display_range(d.display_source()).range, d.message())
193 .with_fix(d.fix(&sema)) 215 .with_fix(d.fix(&sema))
194 .with_code(Some(d.code())) 216 .with_code(Some(d.code()))
195} 217}
@@ -646,6 +668,29 @@ fn test_fn() {
646 } 668 }
647 669
648 #[test] 670 #[test]
671 fn test_unresolved_macro_range() {
672 check_expect(
673 r#"foo::bar!(92);"#,
674 expect![[r#"
675 [
676 Diagnostic {
677 message: "unresolved macro call",
678 range: 5..8,
679 severity: Error,
680 fix: None,
681 unused: false,
682 code: Some(
683 DiagnosticCode(
684 "unresolved-macro-call",
685 ),
686 ),
687 },
688 ]
689 "#]],
690 );
691 }
692
693 #[test]
649 fn range_mapping_out_of_macros() { 694 fn range_mapping_out_of_macros() {
650 // FIXME: this is very wrong, but somewhat tricky to fix. 695 // FIXME: this is very wrong, but somewhat tricky to fix.
651 check_fix( 696 check_fix(