diff --git a/crates/hir/src/base_db/source_db.rs b/crates/hir/src/base_db/source_db.rs index f535d8bd..e30419c6 100644 --- a/crates/hir/src/base_db/source_db.rs +++ b/crates/hir/src/base_db/source_db.rs @@ -19,9 +19,10 @@ mod preproc; pub(crate) use self::preproc::workspace_preproc_model_file_ids; pub use self::preproc::{ MappedSourcePreprocModel, PreprocManifestSource, PreprocSourceMap, PreprocSourceMapError, - PreprocSourceMapping, PreprocSpeculativeUniverseId, PreprocVirtualOrigin, - SourcePreprocContextIndex, SourcePreprocContextStatus, SourcePreprocQueryError, - SourcePreprocRelevantContexts, preproc_virtual_builtin_path, preproc_virtual_predefines_path, + PreprocSourceMapping, PreprocSpeculativeUniverseId, PreprocUnavailableReason, + PreprocVirtualOrigin, SourcePreprocContextIndex, SourcePreprocContextStatus, + SourcePreprocQueryError, SourcePreprocRelevantContexts, preproc_virtual_builtin_path, + preproc_virtual_predefines_path, preproc_virtual_predefines_text, preproc_virtual_speculative_path, }; #[cfg(test)] @@ -70,10 +71,14 @@ pub enum SourceFileKind { IncludeHeader, LibraryMap, ProjectManifest, + PreprocVirtual, } impl SourceFileKind { pub fn from_path(path: &VfsPath) -> Self { + if is_preproc_virtual_path(path) { + return Self::PreprocVirtual; + } match path.name_and_extension() { Some((name, Some(ext))) if name == "vide" && ext.eq_ignore_ascii_case("toml") => { Self::ProjectManifest @@ -97,12 +102,18 @@ impl SourceFileKind { } } +fn is_preproc_virtual_path(path: &VfsPath) -> bool { + path.starts_with(&VfsPath::new_virtual_path("/__vide/preproc".to_owned())) +} + fn parse_src(db: &dyn SourceDb, file_id: FileId) -> SyntaxTree { let _span = tracing::info_span!("slang.parse_src", ?file_id).entered(); let text = db.file_text(file_id); match db.file_kind(file_id) { - SourceFileKind::SystemVerilog | SourceFileKind::IncludeHeader => { + SourceFileKind::SystemVerilog + | SourceFileKind::IncludeHeader + | SourceFileKind::PreprocVirtual => { // HIR source maps are local to the queried file; project-aware // include expansion belongs to parse_src_for_compilation. let preprocess = db.file_preprocess_config(file_id); @@ -245,7 +256,7 @@ fn parsed_compilation_unit(db: &dyn SourceRootDb, file_id: FileId) -> ParsedComp syntax_tree: SyntaxTree::from_library_map_text(&text, &identity.name, &identity.path), preprocessor_trace: None, }, - SourceFileKind::ProjectManifest => ParsedCompilationUnit { + SourceFileKind::ProjectManifest | SourceFileKind::PreprocVirtual => ParsedCompilationUnit { syntax_tree: SyntaxTree::from_text("", "", ""), preprocessor_trace: None, }, @@ -269,7 +280,9 @@ fn parser_expected_syntax( let identity = source_file_identity(db, file_id); let offset = usize::from(offset); let expected = match db.file_kind(file_id) { - SourceFileKind::SystemVerilog | SourceFileKind::IncludeHeader => { + SourceFileKind::SystemVerilog + | SourceFileKind::IncludeHeader + | SourceFileKind::PreprocVirtual => { let options = syntax_tree_options_for_file(db, file_id); SyntaxTree::expected_syntax_at_offset_with_options( &text, @@ -540,7 +553,9 @@ fn compilation_profile_diagnostics( &identity.name, &identity.path, ), - SourceFileKind::IncludeHeader | SourceFileKind::ProjectManifest => continue, + SourceFileKind::IncludeHeader + | SourceFileKind::ProjectManifest + | SourceFileKind::PreprocVirtual => continue, }; compilation_root_count += 1; compilation_buffer_count += 1 + buffer_ids.source_buffers.len(); @@ -660,7 +675,7 @@ fn source_root_semantic_diagnostics( mod tests { use std::fmt; - use ::preproc::source::{PreprocSourceId, SourcePreprocUnavailable, SourceRange}; + use ::preproc::source::{PreprocSourceId, SourceRange}; use rustc_hash::FxHashSet; use syntax::{SourceBufferId, SourceBufferOrigin, SyntaxTreeOptions, preproc::Trace}; use utils::{ @@ -677,6 +692,7 @@ mod tests { const TOP: FileId = FileId(0); const MANIFEST: FileId = FileId(1); + const VIRTUAL: FileId = FileId(2); const ROOT: SourceRootId = SourceRootId(0); #[salsa::database(SourceDbStorage, SourceRootDbStorage)] @@ -701,12 +717,22 @@ mod tests { } fn db_with_root_file() -> TestDb { + db_with_root_file_and_virtuals(Vec::new()) + } + + fn db_with_root_file_and_virtuals(virtuals: Vec<(FileId, VfsPath, String)>) -> TestDb { let top_path = abs_path("rtl/top.v"); let mut file_set = FileSet::default(); file_set.insert(TOP, VfsPath::from(top_path.clone())); + for (file_id, path, _) in &virtuals { + file_set.insert(*file_id, path.clone()); + } let root = SourceRoot::new_local_with_source_files(file_set, vec![TOP]); let mut files = FxHashSet::default(); files.insert(TOP); + for (file_id, _, _) in &virtuals { + files.insert(*file_id); + } let mut db = TestDb::default(); db.set_files_with_durability(Box::new(files), Durability::HIGH); @@ -723,19 +749,42 @@ mod tests { Arc::from("module top; endmodule\n"), Durability::LOW, ); + for (file_id, path, text) in virtuals { + db.set_source_root_id_with_durability(file_id, ROOT, Durability::LOW); + db.set_file_path_with_durability(file_id, None, Durability::LOW); + db.set_file_kind_with_durability( + file_id, + SourceFileKind::from_path(&path), + Durability::LOW, + ); + db.set_file_text_with_durability(file_id, Arc::from(text.as_str()), Durability::LOW); + } db } fn db_with_root_and_manifest(manifest_text: &str) -> TestDb { + db_with_root_and_manifest_and_virtuals(manifest_text, Vec::new()) + } + + fn db_with_root_and_manifest_and_virtuals( + manifest_text: &str, + virtuals: Vec<(FileId, VfsPath, String)>, + ) -> TestDb { let top_path = abs_path("rtl/top.v"); let manifest_path = abs_path("vide.toml"); let mut file_set = FileSet::default(); file_set.insert(TOP, VfsPath::from(top_path.clone())); file_set.insert(MANIFEST, VfsPath::from(manifest_path.clone())); + for (file_id, path, _) in &virtuals { + file_set.insert(*file_id, path.clone()); + } let root = SourceRoot::new_local_with_source_files(file_set, vec![TOP]); let mut files = FxHashSet::default(); files.insert(TOP); files.insert(MANIFEST); + for (file_id, _, _) in &virtuals { + files.insert(*file_id); + } let mut db = TestDb::default(); db.set_files_with_durability(Box::new(files), Durability::HIGH); @@ -753,6 +802,16 @@ mod tests { db.set_file_kind_with_durability(file_id, kind, Durability::LOW); db.set_file_text_with_durability(file_id, Arc::from(text), Durability::LOW); } + for (file_id, path, text) in virtuals { + db.set_source_root_id_with_durability(file_id, ROOT, Durability::LOW); + db.set_file_path_with_durability(file_id, None, Durability::LOW); + db.set_file_kind_with_durability( + file_id, + SourceFileKind::from_path(&path), + Durability::LOW, + ); + db.set_file_text_with_durability(file_id, Arc::from(text.as_str()), Durability::LOW); + } db } @@ -794,6 +853,49 @@ mod tests { assert!(!kind.is_slang_parse_unit()); } + #[test] + fn preproc_virtual_files_are_not_slang_parse_or_semantic_units() { + let kind = SourceFileKind::from_path(&preproc_virtual_predefines_path(None)); + + assert_eq!(kind, SourceFileKind::PreprocVirtual); + assert!(!kind.is_slang_parse_unit()); + assert!(!kind.is_semantic_compilation_unit()); + } + + #[test] + fn materialized_preproc_virtual_files_do_not_enter_semantic_or_preproc_roots() { + let profile = CompilationProfileId(0); + let virtual_path = preproc_virtual_predefines_path(Some(profile)); + let virtual_text = materialized_predefine_text("FOO=1"); + let mut db = db_with_root_file_and_virtuals(vec![(VIRTUAL, virtual_path, virtual_text)]); + db.set_project_config_with_durability( + Arc::new(ProjectConfig::new( + vec![Some(profile)], + vec![CompilationProfile { + source_roots: vec![ROOT], + top_modules: Vec::new(), + preprocess: PreprocessConfig::with_predefine_strings(["FOO=1"], Vec::new()), + }], + )), + Durability::LOW, + ); + + assert_eq!(db.file_kind(VIRTUAL), SourceFileKind::PreprocVirtual); + assert!(!db.file_kind(VIRTUAL).is_semantic_compilation_unit()); + assert!(db.parse_diagnostics(VIRTUAL).is_empty()); + + let plan = db.compilation_plan_for_root(ROOT); + assert_eq!(plan.roots, vec![TOP]); + assert!(!plan.include_only.contains(&VIRTUAL)); + + let preproc_model_files = workspace_preproc_model_file_ids(&db, Some(profile)); + assert_eq!(preproc_model_files, vec![TOP]); + assert_eq!( + db.source_preproc_model(VIRTUAL).as_ref(), + &Err(SourcePreprocQueryError::UnsupportedFileKind(SourceFileKind::PreprocVirtual)) + ); + } + #[test] fn project_manifests_are_loadable_but_not_semantic_or_preproc_inputs() { let top_path = abs_path("rtl/top.sv"); @@ -881,8 +983,8 @@ mod tests { assert_eq!( source_map.get(PreprocSourceId::from(2)), - Some(&PreprocSourceMapping::Unmapped(SourcePreprocUnavailable::DetachedSource { - source: PreprocSourceId::from(2), + Some(&PreprocSourceMapping::Unmapped(PreprocUnavailableReason::DetachedSource { + buffer_id: 2, })) ); assert!(matches!( @@ -893,9 +995,14 @@ mod tests { #[test] fn source_preproc_mapping_records_predefines_by_verified_source_text() { - let db = db_with_root_file(); let first_text = materialized_predefine_text("FIRST=1"); let second_text = materialized_predefine_text("SECOND"); + let expected_path = preproc_virtual_predefines_path(None); + let db = db_with_root_file_and_virtuals(vec![( + VIRTUAL, + expected_path.clone(), + format!("{first_text}{second_text}"), + )]); let trace = Trace { root_buffer_id: 1, source_buffers: vec![ @@ -940,18 +1047,20 @@ mod tests { let first = PreprocSourceId::from(9); let second = PreprocSourceId::from(2); let extra = PreprocSourceId::from(4); - let expected_path = preproc_virtual_predefines_path(None); - let Some(PreprocSourceMapping::VirtualDisplay { path, origin }) = source_map.get(first) + let Some(PreprocSourceMapping::VirtualFile { file_id, path, origin }) = + source_map.get(first) else { - panic!("first predefine should map to display-only virtual source"); + panic!("first predefine should map to materialized virtual source"); }; + assert_eq!(*file_id, VIRTUAL); assert_eq!(path, &expected_path); assert_eq!(origin, &PreprocVirtualOrigin::Predefines { profile: None }); assert_eq!( source_map.get(second), - Some(&PreprocSourceMapping::VirtualDisplay { + Some(&PreprocSourceMapping::VirtualFile { + file_id: VIRTUAL, path: expected_path, origin: PreprocVirtualOrigin::Predefines { profile: None }, }) @@ -959,24 +1068,21 @@ mod tests { assert_eq!( source_map.get(extra), Some(&PreprocSourceMapping::Unmapped( - SourcePreprocUnavailable::UnverifiedPredefineSource { source: extra } + PreprocUnavailableReason::UnverifiedPredefineSource { buffer_id: extra.raw() } )) ); - assert!(matches!( - source_map.file_id(first), - Err(PreprocSourceMapError::DisplayOnlyVirtualSource { .. }) - )); + assert_eq!(source_map.file_id(first), Ok(VIRTUAL)); + assert_eq!(db.file_kind(VIRTUAL), SourceFileKind::PreprocVirtual); + assert!(!db.file_kind(VIRTUAL).is_semantic_compilation_unit()); let second_range = SourceRange { source: second, range: TextRange::new(TextSize::from(0), TextSize::from(7)), }; + let first_text_len = TextSize::of(&first_text); assert_eq!( source_map.map_range(second_range).unwrap(), - TextRange::new( - TextSize::from(u32::try_from(first_text.len()).unwrap()), - TextSize::from(u32::try_from(first_text.len() + 7).unwrap()), - ) + (VIRTUAL, TextRange::new(first_text_len, first_text_len + TextSize::from(7))) ); } @@ -993,8 +1099,12 @@ mod tests { TextSize::from(u32::try_from(second_start).unwrap()), TextSize::from(u32::try_from(second_start + "\"FOO=1\"".len()).unwrap()), ); - let db = db_with_root_and_manifest(manifest_text); let predefine_text = materialized_predefine_text("FOO"); + let virtual_path = preproc_virtual_predefines_path(None); + let db = db_with_root_and_manifest_and_virtuals( + manifest_text, + vec![(VIRTUAL, virtual_path, format!("{predefine_text}{predefine_text}"))], + ); let trace = Trace { root_buffer_id: 1, source_buffers: vec![ @@ -1044,11 +1154,16 @@ mod tests { let first = PreprocSourceId::from(2); let second = PreprocSourceId::from(3); - assert!(matches!(source_map.get(first), Some(PreprocSourceMapping::VirtualDisplay { .. }))); + assert!(matches!( + source_map.get(first), + Some(PreprocSourceMapping::VirtualFile { file_id: VIRTUAL, .. }) + )); assert!(matches!( source_map.get(second), - Some(PreprocSourceMapping::VirtualDisplay { .. }) + Some(PreprocSourceMapping::VirtualFile { file_id: VIRTUAL, .. }) )); + assert_eq!(source_map.file_id(first), Ok(VIRTUAL)); + assert_eq!(source_map.file_id(second), Ok(VIRTUAL)); assert_eq!(source_map.predefine_manifest_source(first).unwrap().range, first_range); assert_eq!(source_map.predefine_manifest_source(second).unwrap().range, second_range); assert_eq!( @@ -1056,16 +1171,19 @@ mod tests { source: first, range: TextRange::new(TextSize::from(0), TextSize::from(1)), }), - Ok(TextRange::new(TextSize::from(0), TextSize::from(1))) + Ok((VIRTUAL, TextRange::new(TextSize::from(0), TextSize::from(1)))) ); assert_eq!( source_map.map_range(SourceRange { source: second, range: TextRange::new(TextSize::from(0), TextSize::from(1)), }), - Ok(TextRange::new( - TextSize::from(u32::try_from(predefine_text.len()).unwrap()), - TextSize::from(u32::try_from(predefine_text.len() + 1).unwrap()), + Ok(( + VIRTUAL, + TextRange::new( + TextSize::of(&predefine_text), + TextSize::of(&predefine_text) + TextSize::from(1) + ) )) ); } @@ -1106,7 +1224,7 @@ mod tests { assert_eq!( source_map.get(source), Some(&PreprocSourceMapping::Unmapped( - SourcePreprocUnavailable::UnverifiedPredefineSource { source } + PreprocUnavailableReason::UnverifiedPredefineSource { buffer_id: source.raw() } )) ); assert!(matches!( @@ -1165,15 +1283,22 @@ mod tests { assert_eq!( source_map.get(source), Some(&PreprocSourceMapping::Unmapped( - SourcePreprocUnavailable::UnverifiedPredefineSource { source } + PreprocUnavailableReason::UnverifiedPredefineSource { buffer_id: source.raw() } )) ); } #[test] - fn source_preproc_mapping_records_external_include_buffer_as_display_virtual_source() { - let db = db_with_root_file(); + fn source_preproc_mapping_records_external_include_buffer_as_virtual_file() { let external_path = "/external/generated_defs.vh".to_owned(); + let virtual_path = VfsPath::new_virtual_path( + "/__vide/preproc/profile-7/include-buffer/4/generated_defs.svh".to_owned(), + ); + let db = db_with_root_file_and_virtuals(vec![( + VIRTUAL, + virtual_path.clone(), + "`define FROM_BUFFER 1\n".to_owned(), + )]); let trace = Trace { root_buffer_id: 1, source_buffers: vec![ @@ -1213,18 +1338,19 @@ mod tests { ) .unwrap(); let source = PreprocSourceId::from(4); - let Some(PreprocSourceMapping::VirtualDisplay { path, origin }) = source_map.get(source) + let Some(PreprocSourceMapping::VirtualFile { file_id, path, origin }) = + source_map.get(source) else { - panic!("external include buffer should map to display-only virtual source"); + panic!("external include buffer should map to materialized virtual source"); }; + assert_eq!(*file_id, VIRTUAL); + assert_eq!(path, &virtual_path); assert_eq!( - path, - &VfsPath::new_virtual_path( - "/__vide/preproc/profile-7/include-buffer/4/generated_defs.svh".to_owned() - ) + origin, + &PreprocVirtualOrigin::ExternalIncludeBuffer { buffer_id: source.raw() } ); - assert_eq!(origin, &PreprocVirtualOrigin::ExternalIncludeBuffer { source }); + assert_eq!(source_map.file_id(source), Ok(VIRTUAL)); assert!(matches!( source_map.map_range(SourceRange { source, diff --git a/crates/hir/src/base_db/source_db/preproc.rs b/crates/hir/src/base_db/source_db/preproc.rs index 39c6534e..7a224776 100644 --- a/crates/hir/src/base_db/source_db/preproc.rs +++ b/crates/hir/src/base_db/source_db/preproc.rs @@ -1,11 +1,11 @@ use ::preproc::source::{ - PreprocSourceId, SourceEmittedTokenId, SourceMacroCallId, SourceMacroReferenceId, - SourcePosition, SourcePreprocError, SourcePreprocModel, SourcePreprocUnavailable, SourceRange, - SourceTokenOrigin, + MacroDefinitionId, PreprocSourceId, SourceEmittedTokenId, SourceMacroCallId, + SourceMacroReferenceId, SourcePosition, SourcePreprocError, SourcePreprocModel, + SourcePreprocUnavailable, SourceRange, SourceTokenOrigin, }; use rustc_hash::{FxHashMap, FxHashSet}; use smol_str::SmolStr; -use syntax::{SourceBufferOrigin, SyntaxTreeOptions, preproc::Trace}; +use syntax::{SourceBufferOrigin, SourceBufferRange, SyntaxTreeOptions, preproc::Trace}; use triomphe::Arc; use utils::{ line_index::{TextRange, TextSize}, @@ -37,11 +37,11 @@ pub use self::{ range_index::MappedSourcePreprocModel, source_map::{ PreprocManifestSource, PreprocSourceMap, PreprocSourceMapError, PreprocSourceMapping, - PreprocSpeculativeUniverseId, PreprocVirtualOrigin, + PreprocSpeculativeUniverseId, PreprocUnavailableReason, PreprocVirtualOrigin, }, source_mapping::{ preproc_virtual_builtin_path, preproc_virtual_predefines_path, - preproc_virtual_speculative_path, + preproc_virtual_predefines_text, preproc_virtual_speculative_path, }, }; pub(super) use self::{ diff --git a/crates/hir/src/base_db/source_db/preproc/range_index.rs b/crates/hir/src/base_db/source_db/preproc/range_index.rs index bc21fd96..4387a87e 100644 --- a/crates/hir/src/base_db/source_db/preproc/range_index.rs +++ b/crates/hir/src/base_db/source_db/preproc/range_index.rs @@ -2,8 +2,8 @@ use super::*; #[derive(Debug, Clone, PartialEq, Eq)] pub struct MappedSourcePreprocModel { - pub model: SourcePreprocModel, - pub source_map: PreprocSourceMap, + pub(crate) model: SourcePreprocModel, + pub(crate) source_map: PreprocSourceMap, range_index: PreprocRangeIndex, } @@ -117,9 +117,7 @@ fn mapped_file_range( source_map: &PreprocSourceMap, source_range: SourceRange, ) -> Option<(FileId, TextRange)> { - let range = source_map.map_range(source_range).ok()?; - let file_id = source_map.file_id(source_range.source).ok()?; - Some((file_id, range)) + source_map.map_range(source_range).ok() } fn sort_indexed_ranges(ranges: &mut [IndexedRange]) { diff --git a/crates/hir/src/base_db/source_db/preproc/source_map.rs b/crates/hir/src/base_db/source_db/preproc/source_map.rs index 786c8ed9..51740604 100644 --- a/crates/hir/src/base_db/source_db/preproc/source_map.rs +++ b/crates/hir/src/base_db/source_db/preproc/source_map.rs @@ -12,8 +12,40 @@ pub struct PreprocSourceMap { pub enum PreprocSourceMapping { RealFile(FileId), VirtualFile { file_id: FileId, path: VfsPath, origin: PreprocVirtualOrigin }, - VirtualDisplay { path: VfsPath, origin: PreprocVirtualOrigin }, - Unmapped(SourcePreprocUnavailable), + Unmapped(PreprocUnavailableReason), +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum PreprocUnavailableReason { + DetachedSource { buffer_id: u32 }, + MissingPredefineSourceText { buffer_id: u32 }, + UnverifiedPredefineSource { buffer_id: u32 }, + MissingMacroCall { call: SourceMacroCallId }, + MissingMacroExpansion { call: SourceMacroCallId }, + UnknownMacroUsageDefinition { definition: MacroDefinitionId }, +} + +impl From for PreprocUnavailableReason { + fn from(reason: SourcePreprocUnavailable) -> Self { + match reason { + SourcePreprocUnavailable::DetachedSource { source } => { + Self::DetachedSource { buffer_id: source.raw() } + } + SourcePreprocUnavailable::MissingPredefineSourceText { source } => { + Self::MissingPredefineSourceText { buffer_id: source.raw() } + } + SourcePreprocUnavailable::UnverifiedPredefineSource { source } => { + Self::UnverifiedPredefineSource { buffer_id: source.raw() } + } + SourcePreprocUnavailable::MissingMacroCall { call } => Self::MissingMacroCall { call }, + SourcePreprocUnavailable::MissingMacroExpansion { call } => { + Self::MissingMacroExpansion { call } + } + SourcePreprocUnavailable::UnknownMacroUsageDefinition { definition } => { + Self::UnknownMacroUsageDefinition { definition } + } + } + } } #[derive(Debug, Clone, Copy, PartialEq, Eq)] @@ -26,7 +58,7 @@ pub struct PreprocManifestSource { pub enum PreprocVirtualOrigin { Predefines { profile: Option }, Builtin { name: SmolStr }, - ExternalIncludeBuffer { source: PreprocSourceId }, + ExternalIncludeBuffer { buffer_id: u32 }, Speculative { universe: PreprocSpeculativeUniverseId }, } @@ -35,26 +67,10 @@ pub struct PreprocSpeculativeUniverseId(pub u32); #[derive(Debug, Clone, PartialEq, Eq)] pub enum PreprocSourceMapError { - MissingSource { - source: PreprocSourceId, - }, - UnmappedSource { - source: PreprocSourceId, - reason: SourcePreprocUnavailable, - }, - RangeOutOfBounds { - source: PreprocSourceId, - range: TextRange, - mapped_range: TextRange, - text_len: usize, - }, - MissingEmittedToken { - token: SourceEmittedTokenId, - }, - DisplayOnlyVirtualSource { - path: VfsPath, - origin: PreprocVirtualOrigin, - }, + MissingSource { buffer_id: u32 }, + UnmappedSource { buffer_id: u32, reason: PreprocUnavailableReason }, + RangeOutOfBounds { buffer_id: u32, range: TextRange, mapped_range: TextRange, text_len: usize }, + MissingEmittedToken { token: SourceEmittedTokenId }, } mod mapping; diff --git a/crates/hir/src/base_db/source_db/preproc/source_map/mapping.rs b/crates/hir/src/base_db/source_db/preproc/source_map/mapping.rs index 9c6a18b3..2514961e 100644 --- a/crates/hir/src/base_db/source_db/preproc/source_map/mapping.rs +++ b/crates/hir/src/base_db/source_db/preproc/source_map/mapping.rs @@ -1,17 +1,22 @@ use super::*; impl PreprocSourceMap { - pub fn insert_real_file(&mut self, source: PreprocSourceId, file_id: FileId, text_len: usize) { + pub(crate) fn insert_real_file( + &mut self, + source: PreprocSourceId, + file_id: FileId, + text_len: usize, + ) { self.entries.insert(source, PreprocSourceMapping::RealFile(file_id)); self.predefine_sources.remove(&source); self.text_lengths.insert(source, text_len); self.range_offsets.insert(source, 0); } - pub fn insert_virtual_file( + pub(crate) fn insert_virtual_file( &mut self, source: PreprocSourceId, - file_id: Option, + file_id: FileId, path: VfsPath, origin: PreprocVirtualOrigin, text_len: usize, @@ -22,24 +27,24 @@ impl PreprocSourceMap { pub(in crate::base_db::source_db::preproc) fn insert_virtual_file_with_offset( &mut self, source: PreprocSourceId, - file_id: Option, + file_id: FileId, path: VfsPath, origin: PreprocVirtualOrigin, text_len: usize, range_offset: usize, ) { - let mapping = match file_id { - Some(file_id) => PreprocSourceMapping::VirtualFile { file_id, path, origin }, - None => PreprocSourceMapping::VirtualDisplay { path, origin }, - }; - self.entries.insert(source, mapping); + self.entries.insert(source, PreprocSourceMapping::VirtualFile { file_id, path, origin }); self.predefine_sources.remove(&source); self.text_lengths.insert(source, text_len); self.range_offsets.insert(source, range_offset); } - pub fn insert_unmapped(&mut self, source: PreprocSourceId, reason: SourcePreprocUnavailable) { - self.entries.insert(source, PreprocSourceMapping::Unmapped(reason)); + pub(crate) fn insert_unmapped( + &mut self, + source: PreprocSourceId, + reason: SourcePreprocUnavailable, + ) { + self.entries.insert(source, PreprocSourceMapping::Unmapped(reason.into())); self.predefine_sources.remove(&source); self.text_lengths.remove(&source); self.range_offsets.remove(&source); @@ -53,35 +58,22 @@ impl PreprocSourceMap { self.predefine_sources.insert(source, manifest_source); } - pub fn get(&self, source: PreprocSourceId) -> Option<&PreprocSourceMapping> { + pub(crate) fn get(&self, source: PreprocSourceId) -> Option<&PreprocSourceMapping> { self.entries.get(&source) } - pub fn predefine_manifest_source( + pub(crate) fn predefine_manifest_source( &self, source: PreprocSourceId, ) -> Option { self.predefine_sources.get(&source).copied() } - pub fn file_id(&self, source: PreprocSourceId) -> Result { - match self.get(source) { - Some(PreprocSourceMapping::RealFile(file_id)) => Ok(*file_id), - Some(PreprocSourceMapping::VirtualFile { file_id, .. }) => Ok(*file_id), - Some(PreprocSourceMapping::VirtualDisplay { path, origin }) => { - Err(PreprocSourceMapError::DisplayOnlyVirtualSource { - path: path.clone(), - origin: origin.clone(), - }) - } - Some(PreprocSourceMapping::Unmapped(reason)) => { - Err(PreprocSourceMapError::UnmappedSource { source, reason: reason.clone() }) - } - None => Err(PreprocSourceMapError::MissingSource { source }), - } + pub(crate) fn file_id(&self, source: PreprocSourceId) -> Result { + self.file_id_for_mapping(source, self.get(source)) } - pub fn source_positions_for_file_offset( + pub(crate) fn source_positions_for_file_offset( &self, file_id: FileId, offset: TextSize, @@ -95,7 +87,6 @@ impl PreprocSourceMap { | PreprocSourceMapping::VirtualFile { file_id: mapped_file_id, .. } => { *mapped_file_id } - PreprocSourceMapping::VirtualDisplay { .. } => return None, PreprocSourceMapping::Unmapped(_) => return None, }; if mapped_file_id != file_id { @@ -113,45 +104,71 @@ impl PreprocSourceMap { positions } - pub fn map_range(&self, source_range: SourceRange) -> Result { - match self.get(source_range.source) { - Some(PreprocSourceMapping::RealFile(_)) - | Some(PreprocSourceMapping::VirtualFile { .. }) - | Some(PreprocSourceMapping::VirtualDisplay { .. }) => {} - Some(PreprocSourceMapping::Unmapped(reason)) => { - return Err(PreprocSourceMapError::UnmappedSource { - source: source_range.source, - reason: reason.clone(), - }); - } - None => { - return Err(PreprocSourceMapError::MissingSource { source: source_range.source }); - } - } + pub(crate) fn map_range( + &self, + source_range: SourceRange, + ) -> Result<(FileId, TextRange), PreprocSourceMapError> { + let mapping = self.get(source_range.source); + let file_id = self.file_id_for_mapping(source_range.source, mapping)?; let range_offset = self.range_offsets.get(&source_range.source).copied().unwrap_or(0); let mapped_range = shift_text_range(source_range.range, range_offset).ok_or( PreprocSourceMapError::RangeOutOfBounds { - source: source_range.source, + buffer_id: source_range.source.raw(), range: source_range.range, mapped_range: source_range.range, text_len: usize::MAX, }, )?; - let text_len = self - .text_lengths - .get(&source_range.source) - .copied() - .ok_or(PreprocSourceMapError::MissingSource { source: source_range.source })?; + let text_len = + self.text_lengths.get(&source_range.source).copied().ok_or( + PreprocSourceMapError::MissingSource { buffer_id: source_range.source.raw() }, + )?; if usize::from(mapped_range.end()) <= text_len { - return Ok(mapped_range); + return Ok((file_id, mapped_range)); } Err(PreprocSourceMapError::RangeOutOfBounds { - source: source_range.source, + buffer_id: source_range.source.raw(), range: source_range.range, mapped_range, text_len, }) } + + pub(crate) fn map_buffer_range( + &self, + range: &SourceBufferRange, + ) -> Option> { + let source_range = source_range_from_buffer_range(range)?; + Some(self.map_range(source_range)) + } + + fn file_id_for_mapping( + &self, + source: PreprocSourceId, + mapping: Option<&PreprocSourceMapping>, + ) -> Result { + match mapping { + Some(PreprocSourceMapping::RealFile(file_id)) => Ok(*file_id), + Some(PreprocSourceMapping::VirtualFile { file_id, .. }) => Ok(*file_id), + Some(PreprocSourceMapping::Unmapped(reason)) => { + Err(PreprocSourceMapError::UnmappedSource { + buffer_id: source.raw(), + reason: reason.clone(), + }) + } + None => Err(PreprocSourceMapError::MissingSource { buffer_id: source.raw() }), + } + } +} + +fn source_range_from_buffer_range(range: &SourceBufferRange) -> Option { + Some(SourceRange { + source: PreprocSourceId::from(range.buffer_id), + range: TextRange::new( + TextSize::from(u32::try_from(range.range.start).ok()?), + TextSize::from(u32::try_from(range.range.end).ok()?), + ), + }) } diff --git a/crates/hir/src/base_db/source_db/preproc/source_mapping.rs b/crates/hir/src/base_db/source_db/preproc/source_mapping.rs index 4c9096be..08125e86 100644 --- a/crates/hir/src/base_db/source_db/preproc/source_mapping.rs +++ b/crates/hir/src/base_db/source_db/preproc/source_mapping.rs @@ -47,14 +47,22 @@ pub(in crate::base_db::source_db) fn source_preproc_file_ids( if let Some(text) = include_buffer_texts.get(&source.path) { let path = preproc_virtual_include_buffer_path(profile_id, source_id, &source.path); - let file_id = materialized_preproc_virtual_file_id(db, &path); - source_map.insert_virtual_file( - source_id, - file_id, - path, - PreprocVirtualOrigin::ExternalIncludeBuffer { source: source_id }, - text.len(), - ); + if let Some(file_id) = materialized_preproc_virtual_file_id(db, &path) { + source_map.insert_virtual_file( + source_id, + file_id, + path, + PreprocVirtualOrigin::ExternalIncludeBuffer { + buffer_id: source_id.raw(), + }, + text.len(), + ); + } else { + source_map.insert_unmapped( + source_id, + SourcePreprocUnavailable::DetachedSource { source: source_id }, + ); + } continue; } @@ -72,9 +80,18 @@ pub(in crate::base_db::source_db) fn source_preproc_file_ids( continue; } }; + let Some(file_id) = entry.file_id else { + source_map.insert_unmapped( + source_id, + SourcePreprocUnavailable::UnverifiedPredefineSource { + source: source_id, + }, + ); + continue; + }; source_map.insert_virtual_file_with_offset( source_id, - entry.file_id, + file_id, entry.path.clone(), PreprocVirtualOrigin::Predefines { profile: profile_id }, entry.text_len, @@ -173,6 +190,14 @@ fn include_buffer_texts_by_path(options: &SyntaxTreeOptions) -> FxHashMap String { + let mut text = String::new(); + for predefine in predefines { + text.push_str(&materialized_predefine_text(predefine.as_str())); + } + text +} + pub(in crate::base_db::source_db) fn materialized_predefine_text(predefine: &str) -> String { let mut definition = predefine.to_owned(); if let Some(index) = definition.find('=') { diff --git a/crates/hir/src/base_db/source_root.rs b/crates/hir/src/base_db/source_root.rs index 66ac2a60..a7264ef3 100644 --- a/crates/hir/src/base_db/source_root.rs +++ b/crates/hir/src/base_db/source_root.rs @@ -154,8 +154,12 @@ impl SourceRoot { let Some(source_files) = &self.source_files else { return kind; }; - if matches!(kind, SourceFileKind::LibraryMap | SourceFileKind::ProjectManifest) - || source_files.contains(file) + if matches!( + kind, + SourceFileKind::LibraryMap + | SourceFileKind::ProjectManifest + | SourceFileKind::PreprocVirtual + ) || source_files.contains(file) { kind } else { diff --git a/crates/hir/src/hir_def/macro_file.rs b/crates/hir/src/hir_def/macro_file.rs index 990301d6..f500bee4 100644 --- a/crates/hir/src/hir_def/macro_file.rs +++ b/crates/hir/src/hir_def/macro_file.rs @@ -107,9 +107,10 @@ pub fn macro_file_expansion(db: &dyn HirDb, macro_file: MacroFileId) -> Option Option { - let source_range = source_range_from_trace(token_range)?; - let range = source_map.map_range(source_range).ok()?; - let file = source_map.file_id(source_range.source).ok()?; + let (file, range) = source_map.map_buffer_range(token_range)?.ok()?; Some(OriginSource { file, range }) } -fn source_range_from_trace(range: &SourceBufferRange) -> Option { - Some(SourceRange { source: PreprocSourceId::from(range.buffer_id), range: text_range(range)? }) -} - fn text_range(range: &SourceBufferRange) -> Option { Some(TextRange::new( TextSize::from(u32::try_from(range.range.start).ok()?), diff --git a/crates/hir/src/hir_def/macro_file/tests.rs b/crates/hir/src/hir_def/macro_file/tests.rs index 88c59fd2..f4dd6671 100644 --- a/crates/hir/src/hir_def/macro_file/tests.rs +++ b/crates/hir/src/hir_def/macro_file/tests.rs @@ -222,7 +222,7 @@ fn macro_file_expansion_parses_emitted_tokens_and_maps_origins() { mapped .source_map .map_range(call.call_range) - .is_ok_and(|range| text_at_range(root_text, range) == "`DECL") + .is_ok_and(|(_, range)| text_at_range(root_text, range) == "`DECL") }) .expect("macro call should be recorded"); diff --git a/crates/hir/src/preproc.rs b/crates/hir/src/preproc.rs index 6110fcda..21ed55c4 100644 --- a/crates/hir/src/preproc.rs +++ b/crates/hir/src/preproc.rs @@ -16,7 +16,7 @@ use crate::{ base_db::{ project::{CompilationProfileId, Predefine}, source_db::{ - MappedSourcePreprocModel, PreprocSourceMapError, PreprocSourceMapping, SourceFileKind, + MappedSourcePreprocModel, PreprocSourceMapError, SourceFileKind, SourcePreprocContextStatus, SourcePreprocQueryError, SourceRootDb, workspace_preproc_model_file_ids, }, diff --git a/crates/hir/src/preproc/conditionals.rs b/crates/hir/src/preproc/conditionals.rs index e8d6d9dc..0fe4cf68 100644 --- a/crates/hir/src/preproc/conditionals.rs +++ b/crates/hir/src/preproc/conditionals.rs @@ -19,20 +19,13 @@ pub fn inactive_branches( }; for source_range in mapped.model.inactive_ranges() { - let (source, range) = match map_source_mapping_range(mapped, *source_range) { + let (branch_file_id, range) = match map_source_range(mapped, *source_range) { Ok(mapped_range) => mapped_range, Err(error) => { record_first_error(&mut first_error, error); continue; } }; - let branch_file_id = match require_file_backed_source(&source) { - Ok(file_id) => file_id, - Err(error) => { - record_first_error(&mut first_error, error); - continue; - } - }; if branch_file_id == file_id { let branch = InactiveBranch { file_id: branch_file_id, range }; branches.push_keyed(branch, InactiveBranchKey::from_branch); diff --git a/crates/hir/src/preproc/helpers/diagnostics.rs b/crates/hir/src/preproc/helpers/diagnostics.rs index bf3da135..7a569e4a 100644 --- a/crates/hir/src/preproc/helpers/diagnostics.rs +++ b/crates/hir/src/preproc/helpers/diagnostics.rs @@ -42,8 +42,7 @@ fn diagnostic_target_for_token( ) -> PreprocResult { Ok(match origin { SourceTokenOrigin::Source { token_range } => { - let (source, range) = map_source_mapping_range(mapped, *token_range)?; - let file_id = require_file_backed_source(&source)?; + let (file_id, range) = map_source_range(mapped, *token_range)?; TokenDiagnosticTarget::Target(DiagnosticTarget { origin: Origin::File { file: file_id, range }, file_id, @@ -58,8 +57,7 @@ fn diagnostic_target_for_token( .. } => { let _call = mapped_macro_call(mapped, *call)?; - let (source, range) = map_source_mapping_range(mapped, *body_token_range)?; - let file_id = require_file_backed_source(&source)?; + let (file_id, range) = map_source_range(mapped, *body_token_range)?; TokenDiagnosticTarget::Target(DiagnosticTarget { origin: Origin::MacroBody { call: hir_macro_call(db, model_file, *trace_call), @@ -78,11 +76,9 @@ fn diagnostic_target_for_token( .. } => { let _call = mapped_macro_call(mapped, *call)?; - let Ok((source, range)) = map_source_mapping_range(mapped, *argument_token_range) - else { + let Ok((file_id, range)) = map_source_range(mapped, *argument_token_range) else { return Ok(TokenDiagnosticTarget::Blocked); }; - let file_id = require_file_backed_source(&source)?; TokenDiagnosticTarget::Target(DiagnosticTarget { origin: Origin::MacroArg { call: hir_macro_call(db, model_file, *trace_call), @@ -102,7 +98,7 @@ fn diagnostic_target_for_token( TokenDiagnosticTarget::Blocked } SourceTokenOrigin::Predefine { source } => { - let _source = map_source_mapping_id(mapped, *source)?; + let _source = map_source_id(mapped, *source)?; TokenDiagnosticTarget::Skip } SourceTokenOrigin::Builtin { name, trace_call, call, .. } => { diff --git a/crates/hir/src/preproc/helpers/mapping/definitions.rs b/crates/hir/src/preproc/helpers/mapping/definitions.rs index 1fb481a5..de1b6e4f 100644 --- a/crates/hir/src/preproc/helpers/mapping/definitions.rs +++ b/crates/hir/src/preproc/helpers/mapping/definitions.rs @@ -4,7 +4,7 @@ pub(crate) fn map_macro_definition( mapped: &MappedSourcePreprocModel, definition: &SourceMacroDefinition, ) -> PreprocResult { - let (mut source, mut directive_range, mut name_range) = map_definition_ranges( + let (mut file_id, mut directive_range, mut name_range) = map_definition_ranges( mapped, definition.event_id.raw(), definition.directive_range, @@ -13,7 +13,7 @@ pub(crate) fn map_macro_definition( if let Some(manifest_source) = mapped.source_map.predefine_manifest_source(definition.name_range.source) { - source = PreprocSourceMapping::RealFile(manifest_source.file_id); + file_id = manifest_source.file_id; directive_range = manifest_source.range; name_range = manifest_source.range; } @@ -27,16 +27,13 @@ pub(crate) fn map_macro_definition( .map(|(param_index, param)| { let range = param .name_range - .map(|range| { - map_source_mapping_range(mapped, range).map(|(_, range)| range) - }) + .map(|range| map_source_range(mapped, range).map(|(_, range)| range)) .transpose()?; Ok(MacroDefinitionParam { param_index, name: param.name.clone(), range }) }) .collect::>>() }) .transpose()?; - let file_id = require_file_backed_source(&source)?; let source_range = definition_source_range(mapped, file_id, directive_range, definition); Ok(MacroDefinition { id: definition.id.into(), @@ -58,10 +55,7 @@ fn definition_source_range( ) -> TextRange { let mut source_range = directive_range; for token_range in definition.body_tokens.iter().filter_map(|token| token.range) { - let Ok((token_source, token_range)) = map_source_mapping_range(mapped, token_range) else { - continue; - }; - let Ok(token_file_id) = require_file_backed_source(&token_source) else { + let Ok((token_file_id, token_range)) = map_source_range(mapped, token_range) else { continue; }; if token_file_id == file_id && token_range.end() > source_range.end() { @@ -84,8 +78,7 @@ pub(in crate::preproc) fn map_macro_param_definition( return Ok(None); }; let macro_definition = map_macro_definition(mapped, definition)?; - let (source, range) = map_source_mapping_range(mapped, name_source_range)?; - let name_file_id = require_file_backed_source(&source)?; + let (name_file_id, range) = map_source_range(mapped, name_source_range)?; if name_file_id != macro_definition.file_id { return Err(PreprocError::MismatchedRangeFiles { kind: RangeFilesKind::Definition, @@ -96,7 +89,7 @@ pub(in crate::preproc) fn map_macro_param_definition( } let param_range = param .range - .map(|range| map_source_mapping_range(mapped, range).map(|(_, range)| range)) + .map(|range| map_source_range(mapped, range).map(|(_, range)| range)) .transpose()?; Ok(Some(MacroParamDefinition { @@ -113,13 +106,10 @@ pub(in crate::preproc) fn map_definition_ranges( event_id: u32, directive_source_range: SourceRange, name_source_range: SourceRange, -) -> PreprocResult<(PreprocSourceMapping, TextRange, TextRange)> { - let (directive_source, directive_range) = - map_source_mapping_range(mapped, directive_source_range)?; - let (name_source, name_range) = map_source_mapping_range(mapped, name_source_range)?; - if directive_source != name_source { - let directive_file_id = require_file_backed_source(&directive_source)?; - let name_file_id = require_file_backed_source(&name_source)?; +) -> PreprocResult<(FileId, TextRange, TextRange)> { + let (directive_file_id, directive_range) = map_source_range(mapped, directive_source_range)?; + let (name_file_id, name_range) = map_source_range(mapped, name_source_range)?; + if directive_file_id != name_file_id { return Err(PreprocError::MismatchedRangeFiles { kind: RangeFilesKind::Definition, event_id, @@ -127,7 +117,7 @@ pub(in crate::preproc) fn map_definition_ranges( name_file_id, }); } - Ok((directive_source, directive_range, name_range)) + Ok((directive_file_id, directive_range, name_range)) } pub(in crate::preproc) fn same_macro_definition( diff --git a/crates/hir/src/preproc/helpers/mapping/includes.rs b/crates/hir/src/preproc/helpers/mapping/includes.rs index dd7a140d..675c8ae1 100644 --- a/crates/hir/src/preproc/helpers/mapping/includes.rs +++ b/crates/hir/src/preproc/helpers/mapping/includes.rs @@ -11,7 +11,7 @@ pub(in crate::preproc) fn map_include_resolved_file( } pub(in crate::preproc) fn source_model_error(reason: SourcePreprocUnavailable) -> PreprocError { - PreprocError::SourceModel(reason) + PreprocError::SourceModel(reason.into()) } pub(in crate::preproc) fn map_include_chain( diff --git a/crates/hir/src/preproc/helpers/mapping/references.rs b/crates/hir/src/preproc/helpers/mapping/references.rs index 29c481ce..e4b88679 100644 --- a/crates/hir/src/preproc/helpers/mapping/references.rs +++ b/crates/hir/src/preproc/helpers/mapping/references.rs @@ -8,8 +8,7 @@ pub(in crate::preproc) fn map_macro_param_reference( token_range: SourceRange, ) -> PreprocResult { let macro_definition = map_macro_definition(mapped, definition)?; - let (source, range) = map_source_mapping_range(mapped, token_range)?; - let file_id = require_file_backed_source(&source)?; + let (file_id, range) = map_source_range(mapped, token_range)?; let name = definition .params .as_ref() @@ -28,8 +27,7 @@ pub(in crate::preproc) fn map_macro_reference( mapped: &MappedSourcePreprocModel, reference: &SourceMacroReference, ) -> PreprocResult { - let (source, name_range) = map_reference_ranges(mapped, reference)?; - let file_id = require_file_backed_source(&source)?; + let (file_id, name_range) = map_reference_ranges(mapped, reference)?; Ok(MacroReference { file_id, name: reference.name.clone(), range: name_range }) } @@ -37,13 +35,12 @@ pub(in crate::preproc) fn map_macro_call( mapped: &MappedSourcePreprocModel, call: &SourceMacroCall, ) -> PreprocResult { - let (source, range) = map_source_mapping_range(mapped, call.call_range)?; + let (file_id, range) = map_source_range(mapped, call.call_range)?; let arguments = call .arguments .iter() .map(|argument| map_macro_argument(mapped, argument)) .collect::>>()?; - let file_id = require_file_backed_source(&source)?; Ok(MacroCall { file_id, arguments, range }) } @@ -53,7 +50,7 @@ pub(in crate::preproc) fn map_macro_argument( ) -> PreprocResult { let range = argument .argument_range - .map(|range| map_source_mapping_range(mapped, range).map(|(_, range)| range)) + .map(|range| map_source_range(mapped, range).map(|(_, range)| range)) .transpose()?; Ok(MacroArgument { argument_index: argument.argument_index, range }) } @@ -61,12 +58,10 @@ pub(in crate::preproc) fn map_macro_argument( pub(in crate::preproc) fn map_reference_ranges( mapped: &MappedSourcePreprocModel, reference: &SourceMacroReference, -) -> PreprocResult<(PreprocSourceMapping, TextRange)> { - let (directive_source, _) = map_source_mapping_range(mapped, reference.directive_range)?; - let (name_source, name_range) = map_source_mapping_range(mapped, reference.name_range)?; - if directive_source != name_source { - let directive_file_id = require_file_backed_source(&directive_source)?; - let name_file_id = require_file_backed_source(&name_source)?; +) -> PreprocResult<(FileId, TextRange)> { + let (directive_file_id, _) = map_source_range(mapped, reference.directive_range)?; + let (name_file_id, name_range) = map_source_range(mapped, reference.name_range)?; + if directive_file_id != name_file_id { return Err(PreprocError::MismatchedRangeFiles { kind: RangeFilesKind::Reference, event_id: reference.event_id.raw(), @@ -74,5 +69,5 @@ pub(in crate::preproc) fn map_reference_ranges( name_file_id, }); } - Ok((directive_source, name_range)) + Ok((directive_file_id, name_range)) } diff --git a/crates/hir/src/preproc/helpers/source.rs b/crates/hir/src/preproc/helpers/source.rs index 86ee5ccb..ee80703e 100644 --- a/crates/hir/src/preproc/helpers/source.rs +++ b/crates/hir/src/preproc/helpers/source.rs @@ -1,27 +1,10 @@ use super::*; -pub(in crate::preproc) fn require_file_backed_source( - source: &PreprocSourceMapping, -) -> PreprocResult { - match source { - PreprocSourceMapping::RealFile(file_id) - | PreprocSourceMapping::VirtualFile { file_id, .. } => Ok(*file_id), - PreprocSourceMapping::VirtualDisplay { path, origin } => { - Err(PreprocError::SourceMap(PreprocSourceMapError::DisplayOnlyVirtualSource { - path: path.clone(), - origin: origin.clone(), - })) - } - PreprocSourceMapping::Unmapped(reason) => Err(PreprocError::SourceModel(reason.clone())), - } -} - pub(in crate::preproc) fn map_source_range( mapped: &MappedSourcePreprocModel, source_range: SourceRange, ) -> PreprocResult<(FileId, TextRange)> { - let (source, range) = map_source_mapping_range(mapped, source_range)?; - Ok((require_file_backed_source(&source)?, range)) + mapped.source_map.map_range(source_range).map_err(PreprocError::SourceMap) } pub(in crate::preproc) fn map_source_id( @@ -31,50 +14,12 @@ pub(in crate::preproc) fn map_source_id( mapped.source_map.file_id(source).map_err(PreprocError::SourceMap) } -pub(in crate::preproc) fn map_source_mapping_range( - mapped: &MappedSourcePreprocModel, - source_range: SourceRange, -) -> PreprocResult<(PreprocSourceMapping, TextRange)> { - let range = mapped.source_map.map_range(source_range).map_err(PreprocError::SourceMap)?; - let source = map_source_mapping_id(mapped, source_range.source)?; - Ok((source, range)) -} - pub(in crate::preproc) fn source_mapping_range_at_offset( mapped: &MappedSourcePreprocModel, source_range: SourceRange, file_id: FileId, offset: TextSize, ) -> PreprocResult> { - let (source, range) = map_source_mapping_range(mapped, source_range)?; - let source_file_id = require_file_backed_source(&source)?; + let (source_file_id, range) = map_source_range(mapped, source_range)?; Ok((source_file_id == file_id && range.contains(offset)).then_some((source_file_id, range))) } - -pub(in crate::preproc) fn map_source_mapping_id( - mapped: &MappedSourcePreprocModel, - source: PreprocSourceId, -) -> PreprocResult { - match mapped.source_map.get(source) { - Some(PreprocSourceMapping::RealFile(file_id)) => { - Ok(PreprocSourceMapping::RealFile(*file_id)) - } - Some(PreprocSourceMapping::VirtualFile { file_id, path, origin }) => { - Ok(PreprocSourceMapping::VirtualFile { - file_id: *file_id, - path: path.clone(), - origin: origin.clone(), - }) - } - Some(PreprocSourceMapping::VirtualDisplay { path, origin }) => { - Ok(PreprocSourceMapping::VirtualDisplay { path: path.clone(), origin: origin.clone() }) - } - Some(PreprocSourceMapping::Unmapped(reason)) => { - Err(PreprocError::SourceMap(PreprocSourceMapError::UnmappedSource { - source, - reason: reason.clone(), - })) - } - None => Err(PreprocError::SourceMap(PreprocSourceMapError::MissingSource { source })), - } -} diff --git a/crates/hir/src/preproc/reference_queries.rs b/crates/hir/src/preproc/reference_queries.rs index f8fa6a67..fcdd34ea 100644 --- a/crates/hir/src/preproc/reference_queries.rs +++ b/crates/hir/src/preproc/reference_queries.rs @@ -47,8 +47,7 @@ pub fn macro_usage_resolutions_at( } continue; }; - let (source, range) = map_reference_ranges(mapped, reference)?; - let usage_file_id = require_file_backed_source(&source)?; + let (usage_file_id, range) = map_reference_ranges(mapped, reference)?; let source_definition = mapped.model.macro_definitions().get(*definition).ok_or_else(|| { PreprocError::SourceQuery(SourcePreprocQueryError::Model( diff --git a/crates/hir/src/preproc/tests.rs b/crates/hir/src/preproc/tests.rs index ecd16b1e..87b1de41 100644 --- a/crates/hir/src/preproc/tests.rs +++ b/crates/hir/src/preproc/tests.rs @@ -20,7 +20,7 @@ use crate::{ salsa::{self, Durability}, source_db::{ FileLoader, SourceDb, SourceDbStorage, SourceFileKind, SourceRootDb, - SourceRootDbStorage, + SourceRootDbStorage, preproc_virtual_predefines_path, preproc_virtual_predefines_text, }, source_root::{SourceRoot, SourceRootId}, }, @@ -37,6 +37,7 @@ const TOP: FileId = FileId(0); const HEADER: FileId = FileId(1); const LEAF: FileId = FileId(2); const MANIFEST: FileId = FileId(3); +const PREDEFINES: FileId = FileId(4); const ROOT: SourceRootId = SourceRootId(0); const PROFILE: CompilationProfileId = CompilationProfileId(0); @@ -97,6 +98,12 @@ fn db_with_entries_and_predefine_entries( for (file_id, path, _) in entries { file_set.insert(*file_id, VfsPath::from(abs_path(path))); } + let predefine_virtual_path = preproc_virtual_predefines_path(Some(PROFILE)); + let predefine_virtual_text = preproc_virtual_predefines_text(&predefines); + let has_predefine_virtual_file = !predefines.is_empty(); + if has_predefine_virtual_file { + file_set.insert(PREDEFINES, predefine_virtual_path.clone()); + } let root = SourceRoot::new_local_with_source_files(file_set, vec![TOP]); let preprocess = PreprocessConfig { predefines, include_dirs: vec![include_dir.clone()] }; @@ -113,6 +120,9 @@ fn db_with_entries_and_predefine_entries( for (file_id, _, _) in entries { files.insert(*file_id); } + if has_predefine_virtual_file { + files.insert(PREDEFINES); + } let mut db = TestDb::default(); db.set_files_with_durability(Box::new(files), Durability::HIGH); @@ -140,6 +150,25 @@ fn db_with_entries_and_predefine_entries( Durability::LOW, ); } + if has_predefine_virtual_file { + db.set_source_root_id_with_durability(PREDEFINES, ROOT, Durability::LOW); + db.set_file_path_with_durability(PREDEFINES, None, Durability::LOW); + db.set_file_kind_with_durability( + PREDEFINES, + SourceFileKind::from_path(&predefine_virtual_path), + Durability::LOW, + ); + db.set_file_text_with_durability( + PREDEFINES, + Arc::from(predefine_virtual_text), + Durability::LOW, + ); + db.set_file_preprocess_config_with_durability( + PREDEFINES, + Arc::new(preprocess.clone()), + Durability::LOW, + ); + } db } diff --git a/crates/hir/src/preproc/types.rs b/crates/hir/src/preproc/types.rs index b87681ed..383c3b7d 100644 --- a/crates/hir/src/preproc/types.rs +++ b/crates/hir/src/preproc/types.rs @@ -11,7 +11,9 @@ use utils::{ use vfs::FileId; use crate::{ - base_db::source_db::{PreprocSourceMapError, SourcePreprocQueryError}, + base_db::source_db::{ + PreprocSourceMapError, PreprocUnavailableReason, SourcePreprocQueryError, + }, hir_def::macro_file::Origin, }; diff --git a/crates/hir/src/preproc/types/common.rs b/crates/hir/src/preproc/types/common.rs index ec1bae95..48c81698 100644 --- a/crates/hir/src/preproc/types/common.rs +++ b/crates/hir/src/preproc/types/common.rs @@ -24,7 +24,7 @@ pub enum PreprocError { /// Mapping a preproc source range back to file/text-range failed. SourceMap(PreprocSourceMapError), /// The source-side preproc model marked the requested data unavailable. - SourceModel(SourcePreprocUnavailable), + SourceModel(PreprocUnavailableReason), /// Multiple distinct preproc contexts produced conflicting answers and /// no single context can be selected. Ambiguous { kind: AmbiguousKind, count: usize }, diff --git a/crates/ide/src/verilog_2005.rs b/crates/ide/src/verilog_2005.rs index 7d3173b5..f3032f06 100644 --- a/crates/ide/src/verilog_2005.rs +++ b/crates/ide/src/verilog_2005.rs @@ -12,7 +12,7 @@ use hir::{ ProjectConfig, }, salsa::Durability, - source_db::SourceDb, + source_db::{SourceDb, preproc_virtual_predefines_path, preproc_virtual_predefines_text}, source_root::{SourceRoot, SourceRootId}, }, preproc::{IncludeTarget, include_directive_at}, @@ -219,19 +219,27 @@ fn setup_marked_with_predefines( predefines: Vec, ) -> (AnalysisHost, FileId, String, HashMap) { let (text, markers) = strip_markers(normalize_fixture_text(text)); + let predefines = predefines.into_iter().map(Predefine::new).collect::>(); let file_id = FileId(0); let mut file_set = FileSet::default(); file_set.insert(file_id, VfsPath::new_virtual_path("/feature.v".to_owned())); let mut change = Change::new(); + add_predefine_virtual_file( + &mut file_set, + &mut change, + FileId(10_000), + Some(CompilationProfileId(0)), + &predefines, + ); change.set_roots(vec![SourceRoot::new_local(file_set)]); change.set_project_config(Arc::new(ProjectConfig::new( vec![Some(CompilationProfileId(0))], vec![CompilationProfile { source_roots: vec![SourceRootId(0)], top_modules: Vec::new(), - preprocess: PreprocessConfig::with_predefine_strings(predefines, Vec::new()), + preprocess: PreprocessConfig { predefines, include_dirs: Vec::new() }, }], ))); change.add_changed_file(ChangedFile { @@ -244,6 +252,25 @@ fn setup_marked_with_predefines( (host, file_id, text, markers) } +fn add_predefine_virtual_file( + file_set: &mut FileSet, + change: &mut Change, + file_id: FileId, + profile_id: Option, + predefines: &[Predefine], +) { + if predefines.is_empty() { + return; + } + let path = preproc_virtual_predefines_path(profile_id); + let text = preproc_virtual_predefines_text(predefines); + file_set.insert(file_id, path); + change.add_changed_file(ChangedFile { + file_id, + change_kind: ChangeKind::Create(Arc::from(text), LineEnding::Unix), + }); +} + struct IncludeMacroFixture { _dir: TestDir, host: AnalysisHost, @@ -1083,21 +1110,26 @@ endmodule let mut file_set = FileSet::default(); file_set.insert(top_file_id, VfsPath::from(top_path)); file_set.insert(manifest_file_id, VfsPath::from(manifest_path.clone())); + let predefines = vec![Predefine::with_source( + "FROM_MANIFEST=1", + PredefineSource { path: manifest_path, range: manifest_range }, + )]; let mut change = Change::new(); + add_predefine_virtual_file( + &mut file_set, + &mut change, + FileId(10_001), + Some(CompilationProfileId(0)), + &predefines, + ); change.set_roots(vec![SourceRoot::new_local_with_source_files(file_set, vec![top_file_id])]); change.set_project_config(Arc::new(ProjectConfig::new( vec![Some(CompilationProfileId(0))], vec![CompilationProfile { source_roots: vec![SourceRootId(0)], top_modules: Vec::new(), - preprocess: PreprocessConfig { - predefines: vec![Predefine::with_source( - "FROM_MANIFEST=1", - PredefineSource { path: manifest_path, range: manifest_range }, - )], - include_dirs: Vec::new(), - }, + preprocess: PreprocessConfig { predefines, include_dirs: Vec::new() }, }], ))); change.add_changed_file(ChangedFile { @@ -1784,21 +1816,26 @@ endmodule let mut file_set = FileSet::default(); file_set.insert(top_file_id, VfsPath::from(top_path)); file_set.insert(manifest_file_id, VfsPath::from(manifest_path.clone())); + let predefines = vec![Predefine::with_source( + "LANE_WIDTH=12", + PredefineSource { path: manifest_path, range: manifest_range }, + )]; let mut change = Change::new(); + add_predefine_virtual_file( + &mut file_set, + &mut change, + FileId(10_001), + Some(CompilationProfileId(0)), + &predefines, + ); change.set_roots(vec![SourceRoot::new_local_with_source_files(file_set, vec![top_file_id])]); change.set_project_config(Arc::new(ProjectConfig::new( vec![Some(CompilationProfileId(0))], vec![CompilationProfile { source_roots: vec![SourceRootId(0)], top_modules: Vec::new(), - preprocess: PreprocessConfig { - predefines: vec![Predefine::with_source( - "LANE_WIDTH=12", - PredefineSource { path: manifest_path, range: manifest_range }, - )], - include_dirs: Vec::new(), - }, + preprocess: PreprocessConfig { predefines, include_dirs: Vec::new() }, }], ))); change.add_changed_file(ChangedFile { diff --git a/src/global_state.rs b/src/global_state.rs index f90674a2..28f9e7e4 100644 --- a/src/global_state.rs +++ b/src/global_state.rs @@ -5,6 +5,7 @@ pub(crate) mod event_loop; mod handlers; pub mod main_loop; mod mem_docs; +mod preproc_virtual_files; pub(crate) mod process_changes; mod project_status; mod qihe; diff --git a/src/global_state/preproc_virtual_files.rs b/src/global_state/preproc_virtual_files.rs new file mode 100644 index 00000000..9138c1dd --- /dev/null +++ b/src/global_state/preproc_virtual_files.rs @@ -0,0 +1,19 @@ +use hir::base_db::{ + project::ProjectConfig, + source_db::{preproc_virtual_predefines_path, preproc_virtual_predefines_text}, +}; +use utils::lines::LineEnding; +use vfs::{Vfs, loader::LoadResult}; + +pub(crate) fn materialize_preproc_virtual_files(project_config: &ProjectConfig, vfs: &mut Vfs) { + for profile_id in project_config.profile_ids() { + let preprocess = project_config.preprocess_for_profile(Some(profile_id)); + if preprocess.predefines.is_empty() { + continue; + } + + let path = preproc_virtual_predefines_path(Some(profile_id)); + let text = preproc_virtual_predefines_text(&preprocess.predefines); + vfs.set_file_contents(&path, LoadResult::Loaded(text, LineEnding::Unix)); + } +} diff --git a/src/global_state/process_changes.rs b/src/global_state/process_changes.rs index 5e97e91c..6821424a 100644 --- a/src/global_state/process_changes.rs +++ b/src/global_state/process_changes.rs @@ -12,6 +12,7 @@ use vfs::{ChangedFile, FileId, Vfs, VfsPath}; use super::{ DEFAULT_REQ_HANDLER, GlobalState, diagnostics::publisher::{PublishDiagnosticsBatch, PublishDiagnosticsTask}, + preproc_virtual_files::materialize_preproc_virtual_files, reload::should_refresh_for_change, task::Task, }; @@ -268,11 +269,18 @@ impl GlobalState { fn collect_changes( &self, - bytes: Vec, + mut bytes: Vec, line_ending_map: &mut IntMap, vfs: &mut Vfs, - has_structure_changes: bool, + mut has_structure_changes: bool, ) -> Change { + if has_structure_changes { + materialize_preproc_virtual_files(&self.config_state.project_config, vfs); + let virtual_changes = vfs.take_changes(); + has_structure_changes |= virtual_changes.iter().any(ChangedFile::is_created_or_deleted); + bytes.extend(virtual_changes); + } + let mut change = Change::new(); for changed_file in bytes { let file_id = changed_file.file_id; diff --git a/src/global_state/reload.rs b/src/global_state/reload.rs index de069f19..3ffed5b0 100644 --- a/src/global_state/reload.rs +++ b/src/global_state/reload.rs @@ -9,7 +9,7 @@ use utils::{ thread::ThreadIntent, }; -use super::task::Task; +use super::{preproc_virtual_files::materialize_preproc_virtual_files, task::Task}; use crate::{ config::{Config, FilesWatcher}, global_state::{ @@ -164,7 +164,11 @@ impl GlobalState { get_workspace_folder(&self.workspace.workspaces, &files_config.exclude); let mut change = Change::new(); { - let vfs = self.workspace.vfs.read(); + let mut vfs = self.workspace.vfs.write(); + materialize_preproc_virtual_files(&project_config, &mut vfs.0); + for changed_file in vfs.0.take_changes() { + change.add_changed_file(changed_file); + } change.set_roots(source_root_config.partition(&vfs.0)); } self.config_state.project_config = project_config.clone();