Unnamed repository; edit this file 'description' to name the repository.
Auto merge of #17863 - Veykril:include-diags, r=Veykril
fix: Resolve included files to their calling modules in IDE layer Fixes https://github.com/rust-lang/rust-analyzer/issues/17390 at the expense of reporting duplicate diagnostics for modules that have includes in them when both the calling and called file are included.
bors 2024-08-12
parent 9817294 · parent 2362975 · commit 5185324
-rw-r--r--crates/hir-def/src/db.rs4
-rw-r--r--crates/hir/src/semantics.rs91
-rw-r--r--crates/hir/src/semantics/source_to_def.rs30
-rw-r--r--crates/ide-diagnostics/src/handlers/macro_error.rs6
-rw-r--r--crates/ide-diagnostics/src/handlers/unlinked_file.rs12
-rw-r--r--crates/ide-diagnostics/src/tests.rs6
6 files changed, 93 insertions, 56 deletions
diff --git a/crates/hir-def/src/db.rs b/crates/hir-def/src/db.rs
index 9ba99788fb..b1103d35ca 100644
--- a/crates/hir-def/src/db.rs
+++ b/crates/hir-def/src/db.rs
@@ -240,14 +240,14 @@ pub trait DefDatabase: InternDatabase + ExpandDatabase + Upcast<dyn ExpandDataba
fn crate_supports_no_std(&self, crate_id: CrateId) -> bool;
- fn include_macro_invoc(&self, crate_id: CrateId) -> Vec<(MacroCallId, EditionedFileId)>;
+ fn include_macro_invoc(&self, crate_id: CrateId) -> Arc<[(MacroCallId, EditionedFileId)]>;
}
// return: macro call id and include file id
fn include_macro_invoc(
db: &dyn DefDatabase,
krate: CrateId,
-) -> Vec<(MacroCallId, EditionedFileId)> {
+) -> Arc<[(MacroCallId, EditionedFileId)]> {
db.crate_def_map(krate)
.modules
.values()
diff --git a/crates/hir/src/semantics.rs b/crates/hir/src/semantics.rs
index 29f98972dc..4089e52f51 100644
--- a/crates/hir/src/semantics.rs
+++ b/crates/hir/src/semantics.rs
@@ -770,59 +770,62 @@ impl<'db> SemanticsImpl<'db> {
let file_id = self.find_file(&parent).file_id.file_id()?;
// iterate related crates and find all include! invocations that include_file_id matches
- for (invoc, _) in self
+ for iter in self
.db
.relevant_crates(file_id.file_id())
.iter()
- .flat_map(|krate| self.db.include_macro_invoc(*krate))
- .filter(|&(_, include_file_id)| include_file_id == file_id)
+ .map(|krate| self.db.include_macro_invoc(*krate))
{
- let macro_file = invoc.as_macro_file();
- let expansion_info = {
- self.with_ctx(|ctx| {
- ctx.cache
- .expansion_info_cache
- .entry(macro_file)
- .or_insert_with(|| {
- let exp_info = macro_file.expansion_info(self.db.upcast());
+ for (invoc, _) in
+ iter.iter().filter(|&&(_, include_file_id)| include_file_id == file_id)
+ {
+ let macro_file = invoc.as_macro_file();
+ let expansion_info = {
+ self.with_ctx(|ctx| {
+ ctx.cache
+ .expansion_info_cache
+ .entry(macro_file)
+ .or_insert_with(|| {
+ let exp_info = macro_file.expansion_info(self.db.upcast());
- let InMacroFile { file_id, value } = exp_info.expanded();
- if let InFile { file_id, value: Some(value) } = exp_info.arg() {
- self.cache(value.ancestors().last().unwrap(), file_id);
- }
- self.cache(value, file_id.into());
+ let InMacroFile { file_id, value } = exp_info.expanded();
+ if let InFile { file_id, value: Some(value) } = exp_info.arg() {
+ self.cache(value.ancestors().last().unwrap(), file_id);
+ }
+ self.cache(value, file_id.into());
- exp_info
- })
- .clone()
- })
- };
+ exp_info
+ })
+ .clone()
+ })
+ };
- // FIXME: uncached parse
- // Create the source analyzer for the macro call scope
- let Some(sa) = expansion_info
- .arg()
- .value
- .and_then(|it| self.analyze_no_infer(&it.ancestors().last().unwrap()))
- else {
- continue;
- };
+ // FIXME: uncached parse
+ // Create the source analyzer for the macro call scope
+ let Some(sa) = expansion_info
+ .arg()
+ .value
+ .and_then(|it| self.analyze_no_infer(&it.ancestors().last().unwrap()))
+ else {
+ continue;
+ };
- // get mapped token in the include! macro file
- let span = span::Span {
- range: token.text_range(),
- anchor: span::SpanAnchor { file_id, ast_id: ROOT_ERASED_FILE_AST_ID },
- ctx: SyntaxContextId::ROOT,
- };
- let Some(InMacroFile { file_id, value: mut mapped_tokens }) =
- expansion_info.map_range_down_exact(span)
- else {
- continue;
- };
+ // get mapped token in the include! macro file
+ let span = span::Span {
+ range: token.text_range(),
+ anchor: span::SpanAnchor { file_id, ast_id: ROOT_ERASED_FILE_AST_ID },
+ ctx: SyntaxContextId::ROOT,
+ };
+ let Some(InMacroFile { file_id, value: mut mapped_tokens }) =
+ expansion_info.map_range_down_exact(span)
+ else {
+ continue;
+ };
- // if we find one, then return
- if let Some(t) = mapped_tokens.next() {
- return Some((sa, file_id.into(), t, span));
+ // if we find one, then return
+ if let Some(t) = mapped_tokens.next() {
+ return Some((sa, file_id.into(), t, span));
+ }
}
}
diff --git a/crates/hir/src/semantics/source_to_def.rs b/crates/hir/src/semantics/source_to_def.rs
index 1376dddf67..09df639d4a 100644
--- a/crates/hir/src/semantics/source_to_def.rs
+++ b/crates/hir/src/semantics/source_to_def.rs
@@ -94,8 +94,9 @@ use hir_def::{
},
hir::{BindingId, LabelId},
AdtId, BlockId, ConstId, ConstParamId, DefWithBodyId, EnumId, EnumVariantId, ExternCrateId,
- FieldId, FunctionId, GenericDefId, GenericParamId, ImplId, LifetimeParamId, MacroId, ModuleId,
- StaticId, StructId, TraitAliasId, TraitId, TypeAliasId, TypeParamId, UnionId, UseId, VariantId,
+ FieldId, FunctionId, GenericDefId, GenericParamId, ImplId, LifetimeParamId, Lookup, MacroId,
+ ModuleId, StaticId, StructId, TraitAliasId, TraitId, TypeAliasId, TypeParamId, UnionId, UseId,
+ VariantId,
};
use hir_expand::{
attrs::AttrId, name::AsName, ExpansionInfo, HirFileId, HirFileIdExt, MacroCallId,
@@ -131,11 +132,30 @@ impl SourceToDefCtx<'_, '_> {
for &crate_id in self.db.relevant_crates(file).iter() {
// Note: `mod` declarations in block modules cannot be supported here
let crate_def_map = self.db.crate_def_map(crate_id);
- mods.extend(
+ let n_mods = mods.len();
+ let modules = |file| {
crate_def_map
.modules_for_file(file)
- .map(|local_id| crate_def_map.module_id(local_id)),
- )
+ .map(|local_id| crate_def_map.module_id(local_id))
+ };
+ mods.extend(modules(file));
+ if mods.len() == n_mods {
+ mods.extend(
+ self.db
+ .include_macro_invoc(crate_id)
+ .iter()
+ .filter(|&&(_, file_id)| file_id == file)
+ .flat_map(|(call, _)| {
+ modules(
+ call.lookup(self.db.upcast())
+ .kind
+ .file_id()
+ .original_file(self.db.upcast())
+ .file_id(),
+ )
+ }),
+ );
+ }
}
if mods.is_empty() {
// FIXME: detached file
diff --git a/crates/ide-diagnostics/src/handlers/macro_error.rs b/crates/ide-diagnostics/src/handlers/macro_error.rs
index e59b63f288..6a976697c8 100644
--- a/crates/ide-diagnostics/src/handlers/macro_error.rs
+++ b/crates/ide-diagnostics/src/handlers/macro_error.rs
@@ -273,11 +273,7 @@ fn f() {
#[test]
fn include_does_not_break_diagnostics() {
- let mut config = DiagnosticsConfig::test_sample();
- config.disabled.insert("inactive-code".to_owned());
- config.disabled.insert("unlinked-file".to_owned());
- check_diagnostics_with_config(
- config,
+ check_diagnostics(
r#"
//- minicore: include
//- /lib.rs crate:lib
diff --git a/crates/ide-diagnostics/src/handlers/unlinked_file.rs b/crates/ide-diagnostics/src/handlers/unlinked_file.rs
index b229ba8084..a1573bab8a 100644
--- a/crates/ide-diagnostics/src/handlers/unlinked_file.rs
+++ b/crates/ide-diagnostics/src/handlers/unlinked_file.rs
@@ -502,4 +502,16 @@ mod bar {
"#,
);
}
+
+ #[test]
+ fn include_macro_works() {
+ check_diagnostics(
+ r#"
+//- minicore: include
+//- /main.rs
+include!("bar/foo/mod.rs");
+//- /bar/foo/mod.rs
+"#,
+ );
+ }
}
diff --git a/crates/ide-diagnostics/src/tests.rs b/crates/ide-diagnostics/src/tests.rs
index 46c6414cae..ec74640a54 100644
--- a/crates/ide-diagnostics/src/tests.rs
+++ b/crates/ide-diagnostics/src/tests.rs
@@ -251,6 +251,12 @@ pub(crate) fn check_diagnostics_with_config(config: DiagnosticsConfig, ra_fixtur
let mut actual = annotations.remove(&file_id).unwrap_or_default();
let expected = extract_annotations(&db.file_text(file_id));
actual.sort_by_key(|(range, _)| range.start());
+ // FIXME: We should panic on duplicates instead, but includes currently cause us to report
+ // diagnostics twice for the calling module when both files are queried.
+ actual.dedup();
+ // actual.iter().duplicates().for_each(|(range, msg)| {
+ // panic!("duplicate diagnostic at {:?}: {msg:?}", line_index.line_col(range.start()))
+ // });
if expected.is_empty() {
// makes minicore smoke test debuggable
for (e, _) in &actual {