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.
This commit is contained in:
bors 2024-08-12 11:48:32 +00:00
commit ff63552892
6 changed files with 93 additions and 56 deletions

View File

@ -240,14 +240,14 @@ fn fields_attrs_source_map(
fn crate_supports_no_std(&self, crate_id: CrateId) -> bool; 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 // return: macro call id and include file id
fn include_macro_invoc( fn include_macro_invoc(
db: &dyn DefDatabase, db: &dyn DefDatabase,
krate: CrateId, krate: CrateId,
) -> Vec<(MacroCallId, EditionedFileId)> { ) -> Arc<[(MacroCallId, EditionedFileId)]> {
db.crate_def_map(krate) db.crate_def_map(krate)
.modules .modules
.values() .values()

View File

@ -770,12 +770,14 @@ fn is_from_include_file(
let file_id = self.find_file(&parent).file_id.file_id()?; let file_id = self.find_file(&parent).file_id.file_id()?;
// iterate related crates and find all include! invocations that include_file_id matches // iterate related crates and find all include! invocations that include_file_id matches
for (invoc, _) in self for iter in self
.db .db
.relevant_crates(file_id.file_id()) .relevant_crates(file_id.file_id())
.iter() .iter()
.flat_map(|krate| self.db.include_macro_invoc(*krate)) .map(|krate| self.db.include_macro_invoc(*krate))
.filter(|&(_, include_file_id)| include_file_id == file_id) {
for (invoc, _) in
iter.iter().filter(|&&(_, include_file_id)| include_file_id == file_id)
{ {
let macro_file = invoc.as_macro_file(); let macro_file = invoc.as_macro_file();
let expansion_info = { let expansion_info = {
@ -825,6 +827,7 @@ fn is_from_include_file(
return Some((sa, file_id.into(), t, span)); return Some((sa, file_id.into(), t, span));
} }
} }
}
None None
} }

View File

@ -94,8 +94,9 @@
}, },
hir::{BindingId, LabelId}, hir::{BindingId, LabelId},
AdtId, BlockId, ConstId, ConstParamId, DefWithBodyId, EnumId, EnumVariantId, ExternCrateId, AdtId, BlockId, ConstId, ConstParamId, DefWithBodyId, EnumId, EnumVariantId, ExternCrateId,
FieldId, FunctionId, GenericDefId, GenericParamId, ImplId, LifetimeParamId, MacroId, ModuleId, FieldId, FunctionId, GenericDefId, GenericParamId, ImplId, LifetimeParamId, Lookup, MacroId,
StaticId, StructId, TraitAliasId, TraitId, TypeAliasId, TypeParamId, UnionId, UseId, VariantId, ModuleId, StaticId, StructId, TraitAliasId, TraitId, TypeAliasId, TypeParamId, UnionId, UseId,
VariantId,
}; };
use hir_expand::{ use hir_expand::{
attrs::AttrId, name::AsName, ExpansionInfo, HirFileId, HirFileIdExt, MacroCallId, attrs::AttrId, name::AsName, ExpansionInfo, HirFileId, HirFileIdExt, MacroCallId,
@ -131,11 +132,30 @@ impl SourceToDefCtx<'_, '_> {
for &crate_id in self.db.relevant_crates(file).iter() { for &crate_id in self.db.relevant_crates(file).iter() {
// Note: `mod` declarations in block modules cannot be supported here // Note: `mod` declarations in block modules cannot be supported here
let crate_def_map = self.db.crate_def_map(crate_id); let crate_def_map = self.db.crate_def_map(crate_id);
mods.extend( let n_mods = mods.len();
let modules = |file| {
crate_def_map crate_def_map
.modules_for_file(file) .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() { if mods.is_empty() {
// FIXME: detached file // FIXME: detached file

View File

@ -273,11 +273,7 @@ fn f() {
#[test] #[test]
fn include_does_not_break_diagnostics() { fn include_does_not_break_diagnostics() {
let mut config = DiagnosticsConfig::test_sample(); check_diagnostics(
config.disabled.insert("inactive-code".to_owned());
config.disabled.insert("unlinked-file".to_owned());
check_diagnostics_with_config(
config,
r#" r#"
//- minicore: include //- minicore: include
//- /lib.rs crate:lib //- /lib.rs crate:lib

View File

@ -499,6 +499,18 @@ mod bar {
mod bar { mod bar {
mod foo; mod foo;
} }
"#,
);
}
#[test]
fn include_macro_works() {
check_diagnostics(
r#"
//- minicore: include
//- /main.rs
include!("bar/foo/mod.rs");
//- /bar/foo/mod.rs
"#, "#,
); );
} }

View File

@ -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 mut actual = annotations.remove(&file_id).unwrap_or_default();
let expected = extract_annotations(&db.file_text(file_id)); let expected = extract_annotations(&db.file_text(file_id));
actual.sort_by_key(|(range, _)| range.start()); 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() { if expected.is_empty() {
// makes minicore smoke test debuggable // makes minicore smoke test debuggable
for (e, _) in &actual { for (e, _) in &actual {