aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--README.md1
-rw-r--r--crates/base_db/src/change.rs2
-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
-rw-r--r--crates/ide_db/src/apply_change.rs2
-rw-r--r--crates/ide_db/src/source_change.rs2
-rw-r--r--docs/dev/guide.md10
13 files changed, 218 insertions, 117 deletions
diff --git a/README.md b/README.md
index a499a1651..74f944ae4 100644
--- a/README.md
+++ b/README.md
@@ -42,6 +42,7 @@ https://rust-lang.zulipchat.com/#narrow/stream/185405-t-compiler.2Frls-2.2E0
42* Website: https://rust-analyzer.github.io/ 42* Website: https://rust-analyzer.github.io/
43* Metrics: https://rust-analyzer.github.io/metrics/ 43* Metrics: https://rust-analyzer.github.io/metrics/
44* API docs: https://rust-analyzer.github.io/rust-analyzer/ide/ 44* API docs: https://rust-analyzer.github.io/rust-analyzer/ide/
45* Changelog: https://rust-analyzer.github.io/thisweek
45 46
46## License 47## License
47 48
diff --git a/crates/base_db/src/change.rs b/crates/base_db/src/change.rs
index 043e03bba..04e294e41 100644
--- a/crates/base_db/src/change.rs
+++ b/crates/base_db/src/change.rs
@@ -19,7 +19,7 @@ pub struct Change {
19 19
20impl fmt::Debug for Change { 20impl fmt::Debug for Change {
21 fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { 21 fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
22 let mut d = fmt.debug_struct("AnalysisChange"); 22 let mut d = fmt.debug_struct("Change");
23 if let Some(roots) = &self.roots { 23 if let Some(roots) = &self.roots {
24 d.field("roots", roots); 24 d.field("roots", roots);
25 } 25 }
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(
diff --git a/crates/ide_db/src/apply_change.rs b/crates/ide_db/src/apply_change.rs
index 23974cff8..104ee113f 100644
--- a/crates/ide_db/src/apply_change.rs
+++ b/crates/ide_db/src/apply_change.rs
@@ -32,7 +32,7 @@ struct RootChange {
32 32
33impl fmt::Debug for RootChange { 33impl fmt::Debug for RootChange {
34 fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { 34 fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
35 fmt.debug_struct("AnalysisChange") 35 fmt.debug_struct("RootChange")
36 .field("added", &self.added.len()) 36 .field("added", &self.added.len())
37 .field("removed", &self.removed.len()) 37 .field("removed", &self.removed.len())
38 .finish() 38 .finish()
diff --git a/crates/ide_db/src/source_change.rs b/crates/ide_db/src/source_change.rs
index f76bac151..b36455d49 100644
--- a/crates/ide_db/src/source_change.rs
+++ b/crates/ide_db/src/source_change.rs
@@ -1,7 +1,7 @@
1//! This modules defines type to represent changes to the source code, that flow 1//! This modules defines type to represent changes to the source code, that flow
2//! from the server to the client. 2//! from the server to the client.
3//! 3//!
4//! It can be viewed as a dual for `AnalysisChange`. 4//! It can be viewed as a dual for `Change`.
5 5
6use std::{ 6use std::{
7 collections::hash_map::Entry, 7 collections::hash_map::Entry,
diff --git a/docs/dev/guide.md b/docs/dev/guide.md
index b5a5d7c93..c1a55c56c 100644
--- a/docs/dev/guide.md
+++ b/docs/dev/guide.md
@@ -65,11 +65,11 @@ Next, let's talk about what the inputs to the `Analysis` are, precisely.
65 65
66Rust Analyzer never does any I/O itself, all inputs get passed explicitly via 66Rust Analyzer never does any I/O itself, all inputs get passed explicitly via
67the `AnalysisHost::apply_change` method, which accepts a single argument, a 67the `AnalysisHost::apply_change` method, which accepts a single argument, a
68`AnalysisChange`. [`AnalysisChange`] is a builder for a single change 68`Change`. [`Change`] is a builder for a single change
69"transaction", so it suffices to study its methods to understand all of the 69"transaction", so it suffices to study its methods to understand all of the
70input data. 70input data.
71 71
72[`AnalysisChange`]: https://github.com/rust-analyzer/rust-analyzer/blob/guide-2019-01/crates/ide_api/src/lib.rs#L119-L167 72[`Change`]: https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/base_db/src/change.rs#L14-L89
73 73
74The `(add|change|remove)_file` methods control the set of the input files, where 74The `(add|change|remove)_file` methods control the set of the input files, where
75each file has an integer id (`FileId`, picked by the client), text (`String`) 75each file has an integer id (`FileId`, picked by the client), text (`String`)
@@ -158,7 +158,7 @@ it should be possible to dynamically reconfigure it later without restart.
158[main_loop.rs#L62-L70](https://github.com/rust-analyzer/rust-analyzer/blob/guide-2019-01/crates/ra_lsp_server/src/main_loop.rs#L62-L70) 158[main_loop.rs#L62-L70](https://github.com/rust-analyzer/rust-analyzer/blob/guide-2019-01/crates/ra_lsp_server/src/main_loop.rs#L62-L70)
159 159
160The [`ProjectModel`] we get after this step is very Cargo and sysroot specific, 160The [`ProjectModel`] we get after this step is very Cargo and sysroot specific,
161it needs to be lowered to get the input in the form of `AnalysisChange`. This 161it needs to be lowered to get the input in the form of `Change`. This
162happens in [`ServerWorldState::new`] method. Specifically 162happens in [`ServerWorldState::new`] method. Specifically
163 163
164* Create a `SourceRoot` for each Cargo package and sysroot. 164* Create a `SourceRoot` for each Cargo package and sysroot.
@@ -175,7 +175,7 @@ of the main loop, just like any other change. Here's where we handle:
175* [File system changes](https://github.com/rust-analyzer/rust-analyzer/blob/guide-2019-01/crates/ra_lsp_server/src/main_loop.rs#L194) 175* [File system changes](https://github.com/rust-analyzer/rust-analyzer/blob/guide-2019-01/crates/ra_lsp_server/src/main_loop.rs#L194)
176* [Changes from the editor](https://github.com/rust-analyzer/rust-analyzer/blob/guide-2019-01/crates/ra_lsp_server/src/main_loop.rs#L377) 176* [Changes from the editor](https://github.com/rust-analyzer/rust-analyzer/blob/guide-2019-01/crates/ra_lsp_server/src/main_loop.rs#L377)
177 177
178After a single loop's turn, we group the changes into one `AnalysisChange` and 178After a single loop's turn, we group the changes into one `Change` and
179[apply] it. This always happens on the main thread and blocks the loop. 179[apply] it. This always happens on the main thread and blocks the loop.
180 180
181[apply]: https://github.com/rust-analyzer/rust-analyzer/blob/guide-2019-01/crates/ra_lsp_server/src/server_world.rs#L216 181[apply]: https://github.com/rust-analyzer/rust-analyzer/blob/guide-2019-01/crates/ra_lsp_server/src/server_world.rs#L216
@@ -256,7 +256,7 @@ database.
256[`RootDatabase`]: https://github.com/rust-analyzer/rust-analyzer/blob/guide-2019-01/crates/ide_api/src/db.rs#L88-L134 256[`RootDatabase`]: https://github.com/rust-analyzer/rust-analyzer/blob/guide-2019-01/crates/ide_api/src/db.rs#L88-L134
257 257
258Salsa input queries are defined in [`FilesDatabase`] (which is a part of 258Salsa input queries are defined in [`FilesDatabase`] (which is a part of
259`RootDatabase`). They closely mirror the familiar `AnalysisChange` structure: 259`RootDatabase`). They closely mirror the familiar `Change` structure:
260indeed, what `apply_change` does is it sets the values of input queries. 260indeed, what `apply_change` does is it sets the values of input queries.
261 261
262[`FilesDatabase`]: https://github.com/rust-analyzer/rust-analyzer/blob/guide-2019-01/crates/base_db/src/input.rs#L150-L174 262[`FilesDatabase`]: https://github.com/rust-analyzer/rust-analyzer/blob/guide-2019-01/crates/base_db/src/input.rs#L150-L174