From b1cb049a051101ae44e1f5f2ff2a6a2d2e6d3661 Mon Sep 17 00:00:00 2001 From: hongjr03 Date: Tue, 9 Jun 2026 20:07:01 +0800 Subject: [PATCH] fix(ide): resolve macro operation source targets --- crates/hir/src/preproc/expansion.rs | 4 +- crates/hir/src/preproc/helpers/expansion.rs | 14 +++-- crates/hir/src/preproc/helpers/facts.rs | 18 ++++++- crates/hir/src/preproc/tests.rs | 5 +- crates/hir/src/preproc/types.rs | 60 +++++++++++++++++++-- crates/ide/src/source_targets.rs | 50 +++++++++++++++-- crates/ide/src/verilog_2005.rs | 38 ++++++++++++- crates/preproc/src/source/trace.rs | 3 ++ crates/preproc/src/source/types.rs | 3 ++ crates/slang/bindings/rust/lib.rs | 6 +++ 10 files changed, 181 insertions(+), 20 deletions(-) diff --git a/crates/hir/src/preproc/expansion.rs b/crates/hir/src/preproc/expansion.rs index 10be0cb4..9bbeaa0c 100644 --- a/crates/hir/src/preproc/expansion.rs +++ b/crates/hir/src/preproc/expansion.rs @@ -424,8 +424,8 @@ fn apply_context_capability_to_macro_expansion_provenance( TokenProvenance::MacroBody { call, .. } | TokenProvenance::MacroArgument { call, .. } | TokenProvenance::Builtin { call, .. } - | TokenProvenance::TokenPaste { call } - | TokenProvenance::Stringification { call } => { + | TokenProvenance::TokenPaste { call, .. } + | TokenProvenance::Stringification { call, .. } => { apply_context_capability_to_macro_call(contexts, call); } TokenProvenance::SourceToken { .. } diff --git a/crates/hir/src/preproc/helpers/expansion.rs b/crates/hir/src/preproc/helpers/expansion.rs index 79db2a88..bf98aff4 100644 --- a/crates/hir/src/preproc/helpers/expansion.rs +++ b/crates/hir/src/preproc/helpers/expansion.rs @@ -367,11 +367,15 @@ pub(in crate::preproc) fn map_token_provenance( range, } } - SourceTokenProvenanceFact::TokenPaste { call, .. } => { - TokenProvenance::TokenPaste { call: mapped_macro_call(mapped, *call)? } - } - SourceTokenProvenanceFact::Stringification { call, .. } => { - TokenProvenance::Stringification { call: mapped_macro_call(mapped, *call)? } + SourceTokenProvenanceFact::TokenPaste { call, identity } => TokenProvenance::TokenPaste { + identity: (*identity).into(), + call: mapped_macro_call(mapped, *call)?, + }, + SourceTokenProvenanceFact::Stringification { call, identity } => { + TokenProvenance::Stringification { + identity: (*identity).into(), + call: mapped_macro_call(mapped, *call)?, + } } SourceTokenProvenanceFact::Predefine { source } => { TokenProvenance::Predefine { source: map_mapped_source_id(mapped, *source)? } diff --git a/crates/hir/src/preproc/helpers/facts.rs b/crates/hir/src/preproc/helpers/facts.rs index 1689392c..e3fdcb66 100644 --- a/crates/hir/src/preproc/helpers/facts.rs +++ b/crates/hir/src/preproc/helpers/facts.rs @@ -191,10 +191,26 @@ pub(in crate::preproc) fn map_macro_argument( argument_index: argument.argument_index, source, range, - tokens: argument.tokens.iter().map(|token| token.raw.clone()).collect(), + tokens: argument + .tokens + .iter() + .map(|token| map_macro_argument_token(mapped, token)) + .collect::>>()?, }) } +fn map_macro_argument_token( + mapped: &MappedSourcePreprocModel, + token: &preproc::source::SourceMacroToken, +) -> PreprocResult { + let (source, range) = token + .range + .map(|range| map_mapped_source_range(mapped, range)) + .transpose()? + .map_or((None, None), |(source, range)| (Some(source), Some(range))); + Ok(MacroArgumentToken { raw: token.raw.clone(), source, range }) +} + pub(in crate::preproc) fn map_macro_resolution( mapped: &MappedSourcePreprocModel, resolution: &SourceMacroResolutionFact, diff --git a/crates/hir/src/preproc/tests.rs b/crates/hir/src/preproc/tests.rs index ffb1b27a..e60fe2fa 100644 --- a/crates/hir/src/preproc/tests.rs +++ b/crates/hir/src/preproc/tests.rs @@ -519,7 +519,10 @@ endmodule .expect("NEXT call should expose its written actual argument"); assert_eq!(argument.source.as_ref().and_then(MappedPreprocSource::file_id), Some(TOP)); assert_eq!(text_at_range(root_text, argument.range.unwrap()), "`PAYL"); - assert_eq!(argument.tokens, vec![SmolStr::new("`PAYL")]); + assert_eq!( + argument.tokens.iter().map(|token| token.raw.as_str()).collect::>(), + vec!["`PAYL"] + ); let payload = provenance .tokens diff --git a/crates/hir/src/preproc/types.rs b/crates/hir/src/preproc/types.rs index 21f95c25..9f465579 100644 --- a/crates/hir/src/preproc/types.rs +++ b/crates/hir/src/preproc/types.rs @@ -4,7 +4,8 @@ use preproc::source::{ SourceEmittedTokenId, SourceEmittedTokenRange, SourceIncludeDirectiveId, SourceMacroArgumentIdentity, SourceMacroBodyIdentity, SourceMacroCallId, SourceMacroCallKey, SourceMacroDefinitionId, SourceMacroDefinitionKey, SourceMacroExpansionId, - SourceMacroExpansionKey, SourceMacroReferenceId, SourcePreprocError, SourcePreprocUnavailable, + SourceMacroExpansionKey, SourceMacroOperationIdentity, SourceMacroReferenceId, + SourcePreprocError, SourcePreprocUnavailable, }; use smol_str::SmolStr; use utils::{ @@ -285,7 +286,14 @@ pub struct MacroArgument { pub argument_index: usize, pub source: Option, pub range: Option, - pub tokens: Vec, + pub tokens: Vec, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct MacroArgumentToken { + pub raw: SmolStr, + pub source: Option, + pub range: Option, } #[derive(Debug, Clone, PartialEq, Eq)] @@ -373,9 +381,11 @@ pub enum TokenProvenance { call: MacroCall, }, TokenPaste { + identity: MacroOperationTokenIdentity, call: MacroCall, }, Stringification { + identity: MacroOperationTokenIdentity, call: MacroCall, }, Unavailable(PreprocUnavailable), @@ -516,10 +526,50 @@ impl From for MacroArgumentToken } } +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub struct MacroOperationTokenIdentity { + pub call: MacroCallIdentity, + pub definition: MacroDefinitionIdentity, + pub expansion: MacroExpansionIdentity, + pub parent_expansion: Option, + pub body_token_index: usize, + pub argument_index: Option, + pub argument_token_index: Option, +} + +impl From for MacroOperationTokenIdentity { + fn from(value: SourceMacroOperationIdentity) -> Self { + Self { + call: value.call.into(), + definition: value.definition.into(), + expansion: value.expansion.into(), + parent_expansion: value.parent_expansion.map(Into::into), + body_token_index: value.body_token_index, + argument_index: value.argument_index, + argument_token_index: value.argument_token_index, + } + } +} + +impl From for MacroOperationTokenIdentity { + fn from(value: syntax::PreprocessorTraceMacroOperationIdentity) -> Self { + Self { + call: value.call_id.into(), + definition: value.definition_id.into(), + expansion: value.expansion_id.into(), + parent_expansion: value.parent_expansion_id.map(Into::into), + body_token_index: value.body_token_index as usize, + argument_index: value.argument_index.map(|index| index as usize), + argument_token_index: value.argument_token_index.map(|index| index as usize), + } + } +} + #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub enum MacroTokenIdentity { Body(MacroBodyTokenIdentity), Argument(MacroArgumentTokenIdentity), + Operation(MacroOperationTokenIdentity), } impl MacroTokenIdentity { @@ -533,10 +583,12 @@ impl MacroTokenIdentity { syntax::PreprocessorTraceTokenProvenance::MacroArgument { identity, .. } => { Some(Self::Argument(identity.into())) } + syntax::PreprocessorTraceTokenProvenance::TokenPaste { identity } + | syntax::PreprocessorTraceTokenProvenance::Stringification { identity } => { + Some(Self::Operation(identity.into())) + } syntax::PreprocessorTraceTokenProvenance::Source { .. } | syntax::PreprocessorTraceTokenProvenance::Builtin { .. } - | syntax::PreprocessorTraceTokenProvenance::TokenPaste { .. } - | syntax::PreprocessorTraceTokenProvenance::Stringification { .. } | syntax::PreprocessorTraceTokenProvenance::Unavailable => None, } } diff --git a/crates/ide/src/source_targets.rs b/crates/ide/src/source_targets.rs index 56da6f8d..184bf134 100644 --- a/crates/ide/src/source_targets.rs +++ b/crates/ide/src/source_targets.rs @@ -1,9 +1,10 @@ use hir::{ base_db::source_db::{SourceDb, SourcePreprocQueryError}, preproc::{ - EmittedTokenProvenance, MacroArgumentTokenIdentity, MacroBodyTokenIdentity, - MacroDefinitionId, MacroExpansionProvenance, MacroTokenIdentity, MappedPreprocSource, - PreprocError, TokenProvenance, macro_expansion_provenances_at, + EmittedTokenProvenance, MacroArgumentTokenIdentity, MacroBodyTokenIdentity, MacroCall, + MacroDefinitionId, MacroExpansionProvenance, MacroOperationTokenIdentity, + MacroTokenIdentity, MappedPreprocSource, PreprocError, TokenProvenance, + macro_expansion_provenances_at, }, }; use rustc_hash::FxHashMap; @@ -170,6 +171,12 @@ pub(crate) enum PreprocTokenProvenance { source: MappedPreprocSource, range: TextRange, }, + MacroOperation { + identity: MacroOperationTokenIdentity, + call: usize, + source: MappedPreprocSource, + range: TextRange, + }, } #[derive(Debug, Clone, PartialEq, Eq)] @@ -373,10 +380,24 @@ fn preproc_hit_for_token( PreprocSourceTarget::SourceToken { source: source.clone(), range: *range }, call.id.raw(), ), + TokenProvenance::TokenPaste { identity, call } + | TokenProvenance::Stringification { identity, call } => { + let (source, range) = macro_operation_source_token(call, *identity)?; + ( + source.clone(), + range, + PreprocTokenProvenance::MacroOperation { + identity: *identity, + call: call.id.raw(), + source: source.clone(), + range, + }, + PreprocSourceTarget::SourceToken { source: source.clone(), range }, + call.id.raw(), + ) + } TokenProvenance::Predefine { .. } | TokenProvenance::Builtin { .. } - | TokenProvenance::TokenPaste { .. } - | TokenProvenance::Stringification { .. } | TokenProvenance::Unavailable(_) => return None, }; @@ -395,6 +416,22 @@ fn preproc_hit_for_token( }) } +fn macro_operation_source_token( + call: &MacroCall, + identity: MacroOperationTokenIdentity, +) -> Option<(&MappedPreprocSource, TextRange)> { + let argument_index = identity.argument_index?; + let argument_token_index = identity.argument_token_index?; + let argument = call.arguments.get(argument_index)?; + if argument.argument_index != argument_index { + return None; + } + let token = argument.tokens.get(argument_token_index)?; + let source = token.source.as_ref()?; + let range = token.range?; + Some((source, range)) +} + fn push_unique_preproc_hit(hits: &mut Vec, hit: PreprocTokenHit) { if hits.iter().any(|existing| existing.target == hit.target) { return; @@ -427,6 +464,9 @@ fn macro_token_identity_for_hit(hit: &PreprocTokenHit) -> Option { Some(MacroTokenIdentity::Argument(identity)) } + PreprocTokenProvenance::MacroOperation { identity, .. } => { + Some(MacroTokenIdentity::Operation(identity)) + } PreprocTokenProvenance::SourceToken { .. } => None, } } diff --git a/crates/ide/src/verilog_2005.rs b/crates/ide/src/verilog_2005.rs index 3ffdac5c..1068eac9 100644 --- a/crates/ide/src/verilog_2005.rs +++ b/crates/ide/src/verilog_2005.rs @@ -1696,11 +1696,13 @@ fn preproc_macro_hover_expands_through_configured_predefine_argument() { end module macro_hover_top( input wire logic clk_i, - input wire logic rst_ni + input wire logic rst_ni, + input wire logic [`LANE_WIDTH-1:0] sample_i ); `DECL_PIPE(trace, `LANE_WIDTH); `/*marker:decl_call*/DECL_PIPE(sample, `LANE_WIDTH); - `/*marker:assign_call*/PIPE_ASSIGN(trace, /*marker:assign_arg*/sample_q ^ {{(`LANE_WIDTH-1){1'b0}}, 1'b1}); + `PIPE_ASSIGN(/*marker:sample_name*/sample, /*marker:sample_input*/sample_i); + `/*marker:assign_call*/PIPE_ASSIGN(/*marker:trace_name*/trace, /*marker:assign_arg*/sample_q ^ {{(`LANE_WIDTH-1){1'b0}}, 1'b1}); endmodule "#, ); @@ -1795,6 +1797,38 @@ endmodule && !argument_info.contains("trace_q"), "actual argument hover should stay on sample_q only: {argument_info}" ); + + let sample_name_hover = analysis + .hover( + position(top_file_id, &top_markers, "sample_name"), + HoverConfig { format: HoverFormat::PlainText }, + ) + .unwrap() + .expect("PIPE_ASSIGN pasted sample name hover expected"); + let sample_name_info = sample_name_hover.info.as_str(); + assert!( + sample_name_info.contains("sample_q") + && !sample_name_info.contains("clk_i") + && !sample_name_info.contains("rst_ni") + && !sample_name_info.contains("trace_q"), + "pasted sample name hover should resolve to sample_q only: {sample_name_info}" + ); + + let trace_name_hover = analysis + .hover( + position(top_file_id, &top_markers, "trace_name"), + HoverConfig { format: HoverFormat::PlainText }, + ) + .unwrap() + .expect("PIPE_ASSIGN pasted trace name hover expected"); + let trace_name_info = trace_name_hover.info.as_str(); + assert!( + trace_name_info.contains("trace_q") + && !trace_name_info.contains("clk_i") + && !trace_name_info.contains("rst_ni") + && !trace_name_info.contains("sample_q"), + "pasted trace name hover should resolve to trace_q only: {trace_name_info}" + ); } #[test] diff --git a/crates/preproc/src/source/trace.rs b/crates/preproc/src/source/trace.rs index d2fb696a..9fdc076d 100644 --- a/crates/preproc/src/source/trace.rs +++ b/crates/preproc/src/source/trace.rs @@ -81,6 +81,9 @@ impl From for SourceMacroOperationIdent definition: SourceMacroDefinitionKey::from(value.definition_id), expansion: SourceMacroExpansionKey::from(value.expansion_id), parent_expansion: value.parent_expansion_id.map(SourceMacroExpansionKey::from), + body_token_index: value.body_token_index as usize, + argument_index: value.argument_index.map(|index| index as usize), + argument_token_index: value.argument_token_index.map(|index| index as usize), } } } diff --git a/crates/preproc/src/source/types.rs b/crates/preproc/src/source/types.rs index 17187e77..7584ba3b 100644 --- a/crates/preproc/src/source/types.rs +++ b/crates/preproc/src/source/types.rs @@ -101,6 +101,9 @@ pub struct SourceMacroOperationIdentity { pub definition: SourceMacroDefinitionKey, pub expansion: SourceMacroExpansionKey, pub parent_expansion: Option, + pub body_token_index: usize, + pub argument_index: Option, + pub argument_token_index: Option, } #[derive(Debug, Clone, PartialEq, Eq)] diff --git a/crates/slang/bindings/rust/lib.rs b/crates/slang/bindings/rust/lib.rs index bb124802..0d6bd442 100644 --- a/crates/slang/bindings/rust/lib.rs +++ b/crates/slang/bindings/rust/lib.rs @@ -255,6 +255,9 @@ pub struct PreprocessorTraceMacroOperationIdentity { pub definition_id: PreprocessorTraceMacroDefinitionId, pub expansion_id: PreprocessorTraceMacroExpansionId, pub parent_expansion_id: Option, + pub body_token_index: u32, + pub argument_index: Option, + pub argument_token_index: Option, } #[derive(Debug, Clone, PartialEq, Eq)] @@ -728,6 +731,9 @@ impl PreprocessorTraceMacroOperationIdentity { parent_expansion_id: raw .has_parent_expansion_id .then_some(PreprocessorTraceMacroExpansionId(raw.parent_expansion_id)), + body_token_index: raw.has_body_token_index.then_some(raw.body_token_index)?, + argument_index: raw.has_argument_index.then_some(raw.argument_index), + argument_token_index: raw.has_argument_token_index.then_some(raw.argument_token_index), }) } }