From 422ebd9e552f37877c8d2ad1fc39c738b5757807 Mon Sep 17 00:00:00 2001 From: Jonatan Waern Date: Mon, 10 Feb 2025 14:15:51 +0100 Subject: [PATCH 1/5] Rename variants of basetypecontent Makes the naming slightly more consistent --- src/analysis/parsing/types.rs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/analysis/parsing/types.rs b/src/analysis/parsing/types.rs index e9745d2..c590c89 100644 --- a/src/analysis/parsing/types.rs +++ b/src/analysis/parsing/types.rs @@ -91,7 +91,7 @@ impl Parse for StructTypeContent { } } let rbrace = new_context.expect_next_kind(stream, TokenKind::RBrace); - BaseTypeContent::StructType(StructTypeContent { + BaseTypeContent::Struct(StructTypeContent { structtok, lbrace, members, @@ -466,7 +466,7 @@ impl Parse for HookTypeContent { args.push((arg, comma)); } let rparen = new_context.expect_next_kind(stream, TokenKind::RParen); - BaseTypeContent::HookType(HookTypeContent { + BaseTypeContent::Hook(HookTypeContent { hook, lparen, args, rparen }).into() } @@ -475,12 +475,12 @@ impl Parse for HookTypeContent { #[derive(Debug, Clone, PartialEq)] pub enum BaseTypeContent { Ident(LeafToken), - StructType(StructTypeContent), + Struct(StructTypeContent), Layout(LayoutContent), Bitfields(BitfieldsContent), TypeOf(TypeOfContent), Sequence(SequenceContent), - HookType(HookTypeContent), + Hook(HookTypeContent), } impl TreeElement for BaseTypeContent { @@ -499,23 +499,23 @@ impl TreeElement for BaseTypeContent { fn range(&self) -> ZeroRange { match self { Self::Ident(content) => content.range(), - Self::StructType(content) => content.range(), + Self::Struct(content) => content.range(), Self::Layout(content) => content.range(), Self::Bitfields(content) => content.range(), Self::TypeOf(content) => content.range(), Self::Sequence(content) => content.range(), - Self::HookType(content) => content.range(), + Self::Hook(content) => content.range(), } } fn subs(&self) -> TreeElements<'_> { match self { Self::Ident(content) => create_subs![content], - Self::StructType(content) => create_subs![content], + Self::Struct(content) => create_subs![content], Self::Layout(content) => create_subs![content], Self::Bitfields(content) => create_subs![content], Self::TypeOf(content) => create_subs![content], Self::Sequence(content) => create_subs![content], - Self::HookType(content) => create_subs![content], + Self::Hook(content) => create_subs![content], } } } From 2293d2acc2b7799a6b1988b119764cad4525cde9 Mon Sep 17 00:00:00 2001 From: Jonatan Waern Date: Mon, 3 Aug 2026 12:40:59 +0200 Subject: [PATCH 2/5] Lookup tests for types --- tests/lsp_lookup_tests.rs | 53 +++++++++++++++++++++++++--- tests/test_files/type_lookup.dml | 60 ++++++++++++++++++++++++++++++++ 2 files changed, 109 insertions(+), 4 deletions(-) create mode 100644 tests/test_files/type_lookup.dml diff --git a/tests/lsp_lookup_tests.rs b/tests/lsp_lookup_tests.rs index 23748a3..63c9851 100644 --- a/tests/lsp_lookup_tests.rs +++ b/tests/lsp_lookup_tests.rs @@ -64,6 +64,7 @@ enum OperationType { GotoDeclaration, // @goto-decl -> name GotoImplementation, // @goto-impl -> name,name,... FindReferences, // @goto-ref -> name,name,... + GotoTypeDefinition, // @goto-type-def -> name } impl std::fmt::Display for OperationType { @@ -73,6 +74,7 @@ impl std::fmt::Display for OperationType { OperationType::GotoDeclaration => write!(f, "goto-decl"), OperationType::GotoImplementation => write!(f, "goto-impl"), OperationType::FindReferences => write!(f, "goto-ref"), + OperationType::GotoTypeDefinition => write!(f, "goto-type-def"), } } } @@ -209,13 +211,13 @@ static RE_LOC: LazyLock = LazyLock::new(|| { /// Regex for operation annotations static RE_OP: LazyLock = LazyLock::new(|| { - Regex::new(r"@(goto-def-decl|goto-def|goto-decl|goto-impl|goto-ref)\[(\d+)\]->([^@]*)").unwrap() + Regex::new(r"@(goto-def-decl|goto-def|goto-decl|goto-impl|goto-ref|goto-type-def)\[(\d+)\]->([^@]*)").unwrap() }); /// Regex for an operation annotation that is missing the required column bracket. /// Used for detecting test-writer errors static RE_OP_NO_COL: LazyLock = LazyLock::new(|| { - Regex::new(r"@(goto-def-decl|goto-def|goto-decl|goto-impl|goto-ref)->").unwrap() + Regex::new(r"@(goto-def-decl|goto-def|goto-decl|goto-impl|goto-ref|goto-type-def)->").unwrap() }); /// Catch-all regex for any `@word[...]->` or `@word->` pattern. @@ -301,6 +303,7 @@ fn parse_annotations(content: &str, file_path: Option) -> (Vec vec![OperationType::GotoDefinition, OperationType::GotoDeclaration], "goto-impl" => vec![OperationType::GotoImplementation], "goto-ref" => vec![OperationType::FindReferences], + "goto-type-def" => vec![OperationType::GotoTypeDefinition], other => unreachable!("regex does not match '{}'", other), }; let col: u32 = cap[2].parse() @@ -326,11 +329,11 @@ fn parse_annotations(content: &str, file_path: Option) -> (Vec) -> Result, AnalysisLookupError>; + /// Stub for `textDocument/typeDefinition` support. The production + /// `type_definitions_at_fp` does not exist yet — see analysis notes on + /// the type-lookup system. Once implemented, replace this with an import + /// from `dls::actions::semantic_lookup`. + fn type_definitions_at_fp( + _ctx: &InitActionContext, + _fp: &ZeroFilePosition, + _limitations: &mut HashSet, + ) -> Result, AnalysisLookupError> { + Ok(vec![]) + } + fn init_logging() { let _ = env_logger::try_init(); } @@ -994,6 +1009,33 @@ mod tests { run_annotation_tests(&setup.ctx, &setup.main_file, setup.annotations); } + /// Aspirational tests for the type-lookup system. + /// + /// Exercises goto-def, goto-ref, and goto-type-def on: + /// - a `typedef` of a primitive type, + /// - a `typedef` of a struct with fields, + /// - method arguments (including one with a struct-typed argument), + /// - `saved` variables (one primitive, one struct-typed). + /// + /// Marked `#[ignore]` because the required plumbing does not exist yet: + /// - `DMLSymbolKind::Typedef` global lookup returns `Ok(vec![])` + /// ([`analysis::mod`](../../src/analysis/mod.rs)), + /// - `ReferenceKind::Type` global lookup is a no-op, + /// - `Symbol::typed` is never populated (`eval_type` returns a dummy), + /// - no `textDocument/typeDefinition` handler exists; the harness + /// stubs `type_definitions_at_fp` to return an empty vec. + /// + /// Once the type-lookup work lands, drop the `#[ignore]` and swap the + /// stub for `dls::actions::semantic_lookup::type_definitions_at_fp`. + #[test] + #[ignore] + fn test_type_lookup() { + init_logging(); + let setup = setup_test(&["type_lookup.dml"]); + + run_annotation_tests(&setup.ctx, &setup.main_file, setup.annotations); + } + /// Helper function to run annotation tests. #[track_caller] fn run_annotation_tests( @@ -1005,6 +1047,7 @@ mod tests { let mut goto_decl = Vec::new(); let mut goto_impl = Vec::new(); let mut find_refs = Vec::new(); + let mut goto_type_def = Vec::new(); for ann in &annotations { match ann.operation_type { @@ -1012,6 +1055,7 @@ mod tests { OperationType::GotoDeclaration => goto_decl.push(ann), OperationType::GotoImplementation => goto_impl.push(ann), OperationType::FindReferences => find_refs.push(ann), + OperationType::GotoTypeDefinition => goto_type_def.push(ann), } } @@ -1038,6 +1082,7 @@ mod tests { run(&goto_decl, OperationType::GotoDeclaration, declarations_at_fp); run(&goto_impl, OperationType::GotoImplementation, implementations_at_fp); run(&find_refs, OperationType::FindReferences, references_at_fp); + run(&goto_type_def, OperationType::GotoTypeDefinition, type_definitions_at_fp); assert!(all_sections.is_empty(), "{} annotation failure(s):\n\n{}", diff --git a/tests/test_files/type_lookup.dml b/tests/test_files/type_lookup.dml new file mode 100644 index 0000000..ea5361d --- /dev/null +++ b/tests/test_files/type_lookup.dml @@ -0,0 +1,60 @@ +// © 2024 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 and MIT + +dml 1.4; + +device type_test_device; + +// @loc[16]=register_addr_t_def +typedef uint32 register_addr_t; // @goto-ref[16]->register_addr_t_use_saved,register_addr_t_use_arg + +typedef struct { + // @loc[12]=packet_addr_field_def + uint32 addr; // @goto-ref[12]->packet_addr_field_use,packet_addr_field_use_2 + // @loc[12]=packet_size_field_def + uint32 size; // @goto-ref[12]->packet_size_field_use + uint8 data[16]; +// @loc[3]=packet_t_def +} packet_t; // @goto-ref[3]->packet_t_use_arg,packet_t_use_saved + +saved register_addr_t current_addr; // @goto-def-decl[7]->register_addr_t_def @goto-type-def[23]->register_addr_t_def @goto-ref[23]->current_addr_use_1,current_addr_use_2 + +// @loc[7]=packet_t_use_saved +// @loc[16]=last_packet_def +saved packet_t last_packet; // @goto-def-decl[7]->packet_t_def @goto-type-def[16]->packet_t_def @goto-ref[16]->last_packet_use,last_packet_use_2 + +// @loc[8]=process_method_def +method process( // @goto-ref[8]->process_method_use + // @loc[5]=packet_t_use_arg + // @loc[14]=pkt_arg_def + packet_t pkt, // @goto-ref[14]->pkt_arg_use_1,pkt_arg_use_2,pkt_arg_use_3,pkt_arg_use_4 + // @loc[5]=register_addr_t_use_arg + // @loc[21]=base_addr_arg_def + register_addr_t base_addr // @goto-ref[21]->base_addr_arg_use_1,base_addr_arg_use_2 +) { + // @loc[5]=current_addr_use_1 + // @loc[20]=base_addr_arg_use_1 + current_addr = base_addr; // @goto-def-decl[5]->current_addr_def @goto-def-decl[20]->base_addr_arg_def + + // @loc[20]=pkt_arg_use_1 + // @loc[24]=packet_addr_field_use + // @loc[31]=base_addr_arg_use_2 + // @loc[5]=current_addr_use_2 + current_addr = pkt.addr + base_addr; // @goto-def-decl[5]->current_addr_def @goto-def-decl[20]->pkt_arg_def @goto-def-decl[24]->packet_addr_field_def @goto-def-decl[31]->base_addr_arg_def + + // @loc[34]=pkt_arg_use_2 + // @loc[38]=packet_addr_field_use_2 + // @loc[44]=pkt_arg_use_3 + // @loc[48]=packet_size_field_use + log info: "addr=%d size=%d", pkt.addr, pkt.size; // @goto-def-decl[34]->pkt_arg_def @goto-def-decl[38]->packet_addr_field_def @goto-def-decl[44]->pkt_arg_def @goto-def-decl[48]->packet_size_field_def + + // @loc[5]=last_packet_use + // @loc[19]=pkt_arg_use_4 + last_packet = pkt; // @goto-def-decl[5]->last_packet_def @goto-def-decl[19]->pkt_arg_def @goto-type-def[5]->packet_t_def @goto-type-def[19]->packet_t_def +} + +method kickoff() { + // @loc[5]=process_method_use + // @loc[13]=last_packet_use_2 + process(last_packet, 0); // @goto-def-decl[5]->process_method_def @goto-def-decl[13]->last_packet_def +} From 1d62c3814cd076b7d5c3997c4a655e55d72ddbbb Mon Sep 17 00:00:00 2001 From: Jonatan Waern Date: Wed, 19 Aug 2026 15:13:52 +0200 Subject: [PATCH 3/5] Fix bug in template cycle detection Signed-off-by: Jonatan Waern --- CHANGELOG.md | 2 ++ src/analysis/templating/topology.rs | 9 ++++++--- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index cc16d9a..fc84463 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,8 @@ - Made GetKnownContexts custom request not wait for analysis results by default, reducing the chances of overfilling workpool capacity while analysises are running. - Fix issue where the server would internally format URIs incorrectly in some cases - The language server logs will now be in local time of whatever machine they are running on, rather than UTC. +- Fixed bug in template cycle detection that could cause crashes when an object + instantiated a template involved in a cycle ## 0.9.19 - Added configuration option to control the max cache size while resolving references in semantic analysis, defaulting to 500MB diff --git a/src/analysis/templating/topology.rs b/src/analysis/templating/topology.rs index c3e9ad1..8937147 100644 --- a/src/analysis/templating/topology.rs +++ b/src/analysis/templating/topology.rs @@ -565,10 +565,13 @@ pub fn rank_templates_aux<'t>(mut templates: HashMap<&'t str, in_eachs, unconditional_references, } = dependencies(template.get_spec(), imp_map); + let referenced: HashSet<&'t str> - = inferior.keys().filter(|s|!invalid_isimps.values().flatten(). - any(|s2|&s2 == s)) - .cloned().collect(); + = inferior.iter().filter( + |(name, kind)| invalid_isimps.get(kind) + .is_none_or(|invalid_names| !invalid_names.contains(name))) + .map(|(name, _)| *name) + .collect(); trace!("Template {:?} requires {:?}", template.get_name(), referenced); required_templates.insert(template.get_name(), referenced.clone()); let all_missing: HashSet<&'t str> = referenced.difference( From 6ba637ade926d002de417dd1cd38b76d22554c3e Mon Sep 17 00:00:00 2001 From: Jonatan Waern Date: Wed, 26 Aug 2026 16:52:41 +0200 Subject: [PATCH 4/5] Type system semantics Signed-off-by: Jonatan Waern --- CHANGELOG.md | 3 + src/actions/requests.rs | 59 +- src/actions/semantic_lookup.rs | 142 +- src/analysis/mod.rs | 514 +++++- src/analysis/parsing/tree.rs | 22 +- src/analysis/parsing/types.rs | 79 +- src/analysis/reference.rs | 23 +- src/analysis/structure/expressions.rs | 52 +- src/analysis/structure/mod.rs | 55 + src/analysis/structure/objects.rs | 148 +- src/analysis/structure/statements.rs | 68 +- src/analysis/structure/toplevel.rs | 12 +- src/analysis/structure/types.rs | 1618 ++++++++++++++++- src/analysis/symbols.rs | 42 +- src/analysis/templating/methods.rs | 106 +- src/analysis/templating/mod.rs | 6 +- src/analysis/templating/objects.rs | 132 +- src/analysis/templating/topology.rs | 24 +- src/analysis/templating/traits.rs | 106 +- src/analysis/templating/types.rs | 601 +++++- src/lib.rs | 4 +- src/server/dispatch.rs | 1 + src/server/mod.rs | 5 +- tests/error_reporting_tests.rs | 432 +++++ tests/lsp_lookup_tests.rs | 89 +- tests/test_files/errors_bad_version.dml | 7 + .../test_files/errors_builtin_type_lookup.dml | 16 + tests/test_files/errors_isolated_misc.dml | 36 + tests/test_files/errors_methods.dml | 82 + tests/test_files/errors_object_params.dml | 37 + tests/test_files/errors_template_cycle.dml | 13 + tests/test_files/errors_templates_traits.dml | 57 + tests/test_files/errors_typedef_cyclic.dml | 13 + tests/test_files/errors_typedef_duplicate.dml | 20 + .../errors_typedef_self_ref_pointer.dml | 17 + tests/test_files/errors_unknown_type.dml | 10 + .../extern_typedef_unknown_type.dml | 15 + tests/test_files/goto_impl_test.dml | 13 +- tests/test_files/type_lookup.dml | 58 +- tests/test_files/type_shadow_lookup.dml | 27 + 40 files changed, 4148 insertions(+), 616 deletions(-) create mode 100644 tests/error_reporting_tests.rs create mode 100644 tests/test_files/errors_bad_version.dml create mode 100644 tests/test_files/errors_builtin_type_lookup.dml create mode 100644 tests/test_files/errors_isolated_misc.dml create mode 100644 tests/test_files/errors_methods.dml create mode 100644 tests/test_files/errors_object_params.dml create mode 100644 tests/test_files/errors_template_cycle.dml create mode 100644 tests/test_files/errors_templates_traits.dml create mode 100644 tests/test_files/errors_typedef_cyclic.dml create mode 100644 tests/test_files/errors_typedef_duplicate.dml create mode 100644 tests/test_files/errors_typedef_self_ref_pointer.dml create mode 100644 tests/test_files/errors_unknown_type.dml create mode 100644 tests/test_files/extern_typedef_unknown_type.dml create mode 100644 tests/test_files/type_shadow_lookup.dml diff --git a/CHANGELOG.md b/CHANGELOG.md index fc84463..3b51189 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,9 @@ - The language server logs will now be in local time of whatever machine they are running on, rather than UTC. - Fixed bug in template cycle detection that could cause crashes when an object instantiated a template involved in a cycle +- Fixed template cycle breaking so it will be deterministic in all cases +- Added basic type system. Goto definition/declaration and find + references will now work on types. Goto type-def will work on simple references ## 0.9.19 - Added configuration option to control the max cache size while resolving references in semantic analysis, defaulting to 500MB diff --git a/src/actions/requests.rs b/src/actions/requests.rs index 19be68b..f04a7bc 100644 --- a/src/actions/requests.rs +++ b/src/actions/requests.rs @@ -14,7 +14,7 @@ use crate::actions::{AnalysisProgressKind, AnalysisWaitKind, ContextDefinition, InitActionContext, rpc_error_code}; use crate::actions::notifications::ContextDefinitionKindParam; -use crate::actions::semantic_lookup::{DLSLimitation, declarations_at_fp, definitions_at_fp, implementations_at_fp, references_at_fp}; +use crate::actions::semantic_lookup::{DLSLimitation, declarations_at_fp, definitions_at_fp, implementations_at_fp, references_at_fp, type_definitions_at_fp}; use crate::analysis::{Named, DeclarationSpan, LocationSpan}; use crate::analysis::symbols::SimpleSymbol; use crate::config::Config; @@ -31,6 +31,7 @@ pub use crate::lsp_data::request::{ Formatting, GotoDeclaration, GotoDeclarationResponse, GotoDefinition, + GotoTypeDefinition, GotoImplementation, GotoImplementationResponse, HoverRequest, RangeFormatting, @@ -570,6 +571,62 @@ impl RequestAction for GotoDefinition { } } +impl RequestAction for GotoTypeDefinition { + type Response = ResponseWithMessage>; + + fn timeout() -> std::time::Duration { + crate::server::dispatch::DEFAULT_REQUEST_TIMEOUT * 5 + } + + fn fallback_response() -> Result { + Ok(None.into()) + } + + fn get_identifier(params: &Self::Params) -> String { + Self::request_identifier( + &text_document_position_to_ident( + ¶ms.text_document_position_params)) + } + + fn handle( + ctx: InitActionContext, + params: Self::Params, + ) -> Result { + debug!("Requesting type definitions with params {:?}", params); + let fp = { + let maybe_fp = ctx.text_doc_pos_to_pos( + ¶ms.text_document_position_params, + "goto_type_def"); + if maybe_fp.is_none() { + return Self::fallback_response(); + } + maybe_fp.unwrap() + }; + let canon_path = make_canon_path!(fp.path())?; + wait_for_device_path!(ctx, canon_path); + + let mut limitations = HashSet::new(); + match type_definitions_at_fp(&ctx, &fp, &mut limitations) { + Ok(locs) => { + let lsp_locations: Vec<_> = locs.into_iter() + .map(|l|ls_util::dls_to_location(&l)) + .collect(); + Ok(response_maybe_with_limitations( + &fp.path(), + Some(GotoDefinitionResponse::Array(lsp_locations)), + limitations, + &ctx)) + }, + Err(lookuperror) => { + let main_file_name = fp.path(); + warn_miss_lookup(lookuperror, + main_file_name.to_str()); + Self::fallback_response() + }, + } + } +} + impl RequestAction for References { type Response = ResponseWithMessage>; diff --git a/src/actions/semantic_lookup.rs b/src/actions/semantic_lookup.rs index 0499b19..1d29494 100644 --- a/src/actions/semantic_lookup.rs +++ b/src/actions/semantic_lookup.rs @@ -17,7 +17,7 @@ use crate::analysis::symbols::DMLSymbolKind; use crate::analysis::{DeviceAnalysis, IsolatedAnalysis, LocationSpan, SymbolRef, ZeroRange}; use crate::analysis::parsing::tree::{ZeroSpan, ZeroFilePosition}; -use crate::analysis::reference::{CodeReference, Reference, ReferenceKind}; +use crate::analysis::reference::{CodeReference, Reference}; use crate::file_management::CanonPath; use crate::server::Output; @@ -42,14 +42,6 @@ impl fmt::Display for DLSLimitation { } } -pub fn type_semantic_limitation() -> DLSLimitation { - DLSLimitation { - issue_num: 65, - description: "The DLS does not currently support semantic analysis of \ - types, including reference finding".to_string(), - } -} - pub fn isolated_template_limitation(template_name: &str) -> DLSLimitation { DLSLimitation { issue_num: 31, @@ -62,10 +54,6 @@ pub fn isolated_template_limitation(template_name: &str) -> DLSLimitation { } } -// Because symbols need to be tied to their source analysis that result is -// a [(DeviceAnalysis, [SymbolRef])] list -// The reference comes from an isolated analysis, and thus is disconnected from a device -// context type DeviceSymbols<'t> = Vec<(&'t DeviceAnalysis, Vec)>; enum SymbolsOrReference<'t> { Symbols(DeviceSymbols<'t>), @@ -182,32 +170,28 @@ fn get_refs_and_syms_at_fp<'t>( -> Result, AnalysisLookupError> { debug!("Looking up references and symbols at position {:?}", fp); let ref_at_pos = analysis_info.isolated_analysis.lookup_reference(fp); - - let context_sym_at_pos = context_symbol_at_pos(analysis_info.isolated_analysis, fp); - let symbols_at_fp = context_sym_at_pos.map(|cs| { - analysis_info.device_analysises.iter().map( - |a|(*a, match a.lookup_symbols(&cs, relevant_limitations) { - Ok(syms) => syms, - Err(e) => { - internal_error!("failed to find context symbol at {:?}: {}", fp, e); - vec![] - } - })) - .collect::>() - }); - if let Some(syms) = symbols_at_fp { + + if let Some(decl) = declaration_at_pos(fp, analysis_info) { if ref_at_pos.is_some() { error!("Obtained both symbol and reference at {:?}\ (reference is {:?}), defaulted to symbol", &fp, ref_at_pos); } + let syms = match decl { + PositionDeclaration::Contexted(cs) => + analysis_info.device_analysises.iter().map( + |a|(*a, match a.lookup_symbols(&cs, relevant_limitations) { + Ok(syms) => syms, + Err(e) => { + internal_error!("failed to find context symbol at {:?}: {}", fp, e); + vec![] + } + })) + .collect::>(), + PositionDeclaration::TypeMember(syms) => syms, + }; return Ok(SymbolsOrReference::Symbols(syms)); } - if let Some(refr) = ref_at_pos.and_then(|r|r.as_code_ref()) { - if refr.reference_kind() == ReferenceKind::Type { - relevant_limitations.insert(type_semantic_limitation()); - } - } if let Some(refr) = ref_at_pos { Ok(SymbolsOrReference::Reference(refr.clone())) } else { @@ -285,6 +269,26 @@ fn context_symbol_at_pos<'t>(isolated_analysis: &'t IsolatedAnalysis, pos: &Zero context } +enum PositionDeclaration<'t> { + Contexted(ContextedSymbol<'t>), + TypeMember(DeviceSymbols<'t>), +} + +fn declaration_at_pos<'t>(fp: &ZeroFilePosition, analysis_info: &AnalysisInfo<'t>) + -> Option> { + if let Some(cs) = context_symbol_at_pos(analysis_info.isolated_analysis, fp) { + return Some(PositionDeclaration::Contexted(cs)); + } + let member_syms: DeviceSymbols<'t> = analysis_info.device_analysises.iter() + .map(|a|(*a, a.member_symbol_at_pos(fp, &analysis_info.isolated_analysis.ast) + .into_iter().collect())) + .collect(); + if member_syms.iter().any(|(_, syms)|!syms.is_empty()) { + return Some(PositionDeclaration::TypeMember(member_syms)); + } + None +} + fn symbol_implementations_of_symbol<'t>(symbol: &'t SymbolRef, analysis: &'t DeviceAnalysis) -> Vec { @@ -459,3 +463,77 @@ pub fn references_at_fp(context: &InitActionContext, .flat_map(|s|s.lock().unwrap().references.clone()) .collect()) } + +/// Walk a resolved `DMLType`, peeling pointer/array/vector wrappers and +/// typedef indirections, and return the span of the outermost +/// typedef/template-as-type declaration, if any. +fn outermost_typedef_span(ty: &crate::analysis::templating::types::DMLType) + -> Option { + use crate::analysis::templating::types::DMLConcreteType; + let mut cur = ty.clone(); + let mut outermost = None; + loop { + let concrete = cur?; + match concrete.as_ref() { + DMLConcreteType::Typedef(td) => { outermost.get_or_insert(td.decl_name.span); } + DMLConcreteType::Trait(t) => { outermost.get_or_insert(t.decl_name.span); } + _ => {} + } + cur = match concrete.peel_one() { + Some(next) => next.clone(), + None => return outermost, + }; + } +} + +/// Resolve the `DMLType` referred to by a symbol's source, if any. +fn resolved_type_from_symbol_source(source: &crate::analysis::symbols::SymbolSource) + -> Option<&crate::analysis::templating::types::DMLType> { + source.resolved_type() +} + + +pub fn type_definitions_at_fp(context: &InitActionContext, + fp: &ZeroFilePosition, + relevant_limitations: &mut HashSet) + -> Result, AnalysisLookupError> { + let analysis_lock = context.analysis.lock().unwrap(); + let mut semantic_lookup = SemanticLookup::create_lookup( + fp, + &analysis_lock, + context)?; + mem::swap(relevant_limitations, &mut semantic_lookup.recognized_limitations); + + let mut results: Vec = Vec::new(); + for (_device, symbols) in &semantic_lookup.stored_symbols { + for sym in symbols { + let (kind, source) = { + let lock = sym.lock().unwrap(); + (lock.kind, lock.source.clone()) + }; + match kind { + // A typedef is its own type-definition. + DMLSymbolKind::Typedef => { + let lock = sym.lock().unwrap(); + results.extend(lock.definitions.iter().copied()); + } + // For variables and method args, walk the resolved type. + DMLSymbolKind::Saved + | DMLSymbolKind::Session + | DMLSymbolKind::Extern + | DMLSymbolKind::Local + | DMLSymbolKind::Constant + | DMLSymbolKind::MethodArg => { + if let Some(ty) = resolved_type_from_symbol_source(&source) { + if let Some(span) = outermost_typedef_span(ty) { + results.push(span); + } + } + } + _ => {} + } + } + } + Ok(results) +} + diff --git a/src/analysis/mod.rs b/src/analysis/mod.rs index ab86ed9..002a6ba 100644 --- a/src/analysis/mod.rs +++ b/src/analysis/mod.rs @@ -1,6 +1,32 @@ // © 2024 Intel Corporation // SPDX-License-Identifier: Apache-2.0 and MIT // Load parser and tree first to ensure existance of macros + +// This macro is made for transparently re-implmeneting trait fns into a +// wrapper struct +macro_rules! impl_trait_fns { + ($type: ty, $trait:ident, $field:tt, + $(fn $fn_name:ident(&self $(, $arg:ident: $arg_ty:ty)*) -> $ret:ty),*) => { + impl $trait for $type { + $( + fn $fn_name(&self, $($arg: $arg_ty),*) -> $ret { + self.$field.$fn_name($($arg),*) + } + )* + } + }; + ($type: ty, $trait:ident, $field:tt, + $(fn $fn_name:ident(self $(, $arg:ident: $arg_ty:ty)*) -> $ret:ty),*) => { + impl $trait for $type { + $( + fn $fn_name(self, $($arg: $arg_ty),*) -> $ret { + self.$field.$fn_name($($arg),*) + } + )* + } + }; +} + #[macro_use] pub mod parsing; #[macro_use] @@ -41,11 +67,12 @@ use crate::analysis::provisionals::ProvisionalsManager; pub use crate::analysis::parsing::tree:: {ZeroRange, ZeroSpan, ZeroPosition, ZeroFilePosition}; -use crate::analysis::parsing::tree::{MissingToken, MissingContent, TreeElement}; +use crate::analysis::parsing::tree::{MissingToken, MissingContent, TreeElement, TreeElementMember}; +use crate::analysis::parsing::types::{struct_or_layout_at_pos, StructOrLayoutRef}; use crate::analysis::structure::objects::{CompObjectKind, Import, MaybeAbstract, ParamValue, Template}; use crate::analysis::structure::statements::{ForPre, Statement, StatementKind}; use crate::analysis::structure::toplevel::{ObjectDecl, TopLevel}; -use crate::analysis::structure::types::DMLType; +use crate::analysis::structure::types::UnresolvedType; use crate::analysis::structure::expressions::{Expression, ExpressionKind, DMLString}; use crate::analysis::templating::objects::{make_device, DMLObject, @@ -62,7 +89,7 @@ use crate::analysis::templating::topology::{RankMaker, use crate::analysis::templating::methods::{DMLMethodArg, DMLMethodRef, DefaultCallReference, MethodDeclaration}; use crate::analysis::templating::traits::{DMLTemplate, TemplateTraitInfo}; -use crate::analysis::templating::types::DMLResolvedType; +use crate::analysis::templating::types::{DMLConcreteType, DMLStructType, DMLTraitType, DMLType, GlobalTypeStorage, eval_type_simple}; use crate::concurrency::AliveStatus; use crate::file_management::{PathResolver, CanonPath}; @@ -163,6 +190,11 @@ impl LocationFile for T { } } +// Used by things which we identify by their span +pub trait IdentitySpan { + fn identity_span(&self) -> &ZeroSpan; +} + #[derive(Debug, Clone, PartialEq, Eq)] pub struct DMLError { pub span: ZeroSpan, @@ -350,15 +382,38 @@ pub struct SymbolStorage { pub method_symbols: HashMap>, // constants, sessions, saveds, hooks, method args pub variable_symbols: HashMap, + // Typedefs, indexed by decl location + pub type_symbols: HashMap, + // struct member fields, indexed by containing struct decl location + pub struct_member_symbols: HashMap>, + // trait member fields, indexed by trait name + pub trait_member_symbols: HashMap>, } impl SymbolStorage { - pub fn all_symbols<'a>(&'a self) -> impl Iterator { - self.template_symbols.values() - .chain(self.param_symbols.values().flat_map(|h|h.values())) - .chain(self.object_symbols.values()) - .chain(self.method_symbols.values().flat_map(|h|h.values())) - .chain(self.variable_symbols.values()) + pub fn all_symbols(&self) -> impl Iterator + '_ { + self.template_symbols.values().cloned() + .chain(self.param_symbols.values().flat_map(|h|h.values().cloned())) + .chain(self.object_symbols.values().cloned()) + .chain(self.method_symbols.values().flat_map(|h|h.values().cloned())) + .chain(self.variable_symbols.values().cloned()) + .chain(self.type_symbols.values().cloned()) + .chain(self.struct_member_symbols.values().flat_map(|h|h.values().cloned())) + .chain(self.trait_member_symbols.values().flat_map(|h|h.values().cloned())) + } +} + +fn for_each_struct_type(ty: &DMLType, visited: &mut HashSet, + visit: &mut impl FnMut(&DMLStructType)) { + let Some(FieldContainer::Struct(st)) = DeviceAnalysis::peel_to_field_container(ty) else { + return; + }; + if !visited.insert(*st.identity_span()) { + return; + } + visit(&st); + for (_, member_ty) in &st.members { + for_each_struct_type(member_ty, visited, visit); } } @@ -379,6 +434,8 @@ pub struct DeviceAnalysis { pub reference_info: ReferenceStorage, pub template_object_implementation_map: HashMap>, + pub type_storage: GlobalTypeStorage, + pub symbol_maker: Arc, pub path: CanonPath, pub dependant_files: Vec, pub clientpath: PathBuf, @@ -492,8 +549,8 @@ impl ReferenceMatches { } } -/// TODO: Consider usage and variants of type hints -pub type TypeHint = DMLResolvedType; +// TODO: Consider usage and variants of type hints +pub type TypeHint = DMLType; // Agnostic reference type AgnRef = Vec; @@ -718,6 +775,21 @@ impl From for AnalysisProcessResult<()> { } } +enum FieldContainer { + Struct(DMLStructType), + Trait(DMLTraitType), +} + +// For things which are identified by their spans +impl IdentitySpan for FieldContainer { + fn identity_span(&self) -> &ZeroSpan { + match self { + Self::Struct(st) => st.identity_span(), + Self::Trait(t) => t.identity_span(), + } + } +} + impl DeviceAnalysis { pub fn lookup_symbols<'t>(&self, context_sym: &ContextedSymbol<'t>, limitations: &mut HashSet) -> Result, String> { @@ -1134,7 +1206,28 @@ impl DeviceAnalysis { } }, // TODO: type lookup - ReferenceKind::Type => (), + ReferenceKind::Type => { + if let Some(loc) = self.type_storage.typedef_decl_span(&reference.name) { + if let Some(type_sym) = + self.symbol_info.type_symbols.get(&loc) { + ref_matches.add_match(Arc::clone(type_sym)); + } else { + error!("Unexpectedly missing a typedef symbol {}", + reference.name); + } + } else if let Some(templ) = self.templates + .templates.get(&reference.name) { + // A Type-kind reference may name a template (DML "trait + // type"). Fall back to the template's symbol so + // goto-def/decl/ref work on template-as-type uses. + if let Some(templ_loc) = &templ.location { + if let Some(templ_sym) = + self.symbol_info.template_symbols.get(templ_loc) { + ref_matches.add_match(Arc::clone(templ_sym)); + } + } + } + }, _ => error!("Invalid global reference kind in {:?}", reference), } ref_matches @@ -1184,8 +1277,17 @@ impl DeviceAnalysis { Err(format!("Unexpectedly missing template matching {:?}", sym)) } }, - // TODO: DMLType lookup - DMLSymbolKind::Typedef => Ok(vec![]), + DMLSymbolKind::Typedef => { + if let Some(loc) = self.type_storage.typedef_decl_span(sym.name.as_str()) { + if let Some(type_sym) = self.symbol_info.type_symbols.get(&loc) { + Ok(vec![Arc::clone(type_sym)]) + } else { + Err(format!("Unexpectedly missing typedef symbol matching {:?}", sym)) + } + } else { + Ok(vec![]) + } + }, // TODO: Extern lookup DMLSymbolKind::Extern => Ok(vec![]), e => { @@ -1226,10 +1328,55 @@ impl DeviceAnalysis { // We can ignore messages from lookup defs here, as this lookup is // live and reporting additional things from here makes no sense if let Some(matches) = refs.as_matches() { - Ok(matches.into_iter().collect()) - } else { - Ok(vec![]) + return Ok(matches.into_iter().collect()); + } + // No device-tree object implements the innermost enclosing context. + // This is expected (not an error) for a param/session/saved declared + // inside a template that is only ever used as a type and never + // `is`-instantiated: such a template has no corresponding object in + // the device tree at all, but its members are still meaningful as + // type members. `extend_with_trait_members` registers a symbol for + // each of them directly in `self.symbol_info.trait_member_symbols`, + // so look it up there instead. + if let Some(ContextKey::Template(templ)) = sym.contexts.last() { + if let Some(member_sym) = self.symbol_info.trait_member_symbols.get(&templ.get_name()) + .and_then(|m|m.get(&sym.symbol.get_name())) { + // We successfully resolved this via the template's + // type-member symbols, so the "cannot evaluate without + // an instantiating object" limitation inserted by + // `context_to_objs` above doesn't apply here. + limitations.remove( + &isolated_template_limitation(&templ.get_name())); + return Ok(vec![Arc::clone(member_sym)]); + } } + Ok(vec![]) + } + + // Looks up a member-decl. Takes ast passed-in from above to find + // containing spans for structs + pub fn member_symbol_at_pos(&self, pos: &ZeroFilePosition, + ast: &dyn TreeElementMember) -> Option { + if let Some(found) = struct_or_layout_at_pos(ast, pos.position) { + let range = match found { + StructOrLayoutRef::Struct(s) => s.range(), + StructOrLayoutRef::Layout(l) => l.range(), + }; + let span = ZeroSpan::from_range(range, pos.path()); + if let Some(member_sym) = self.symbol_info.struct_member_symbols.get(&span) + .and_then(|m|m.values() + .find(|sym|sym.lock().unwrap().loc.contains_pos(pos)) + .cloned()) { + return Some(member_sym); + } + } + let container = self.templates.templates.values() + .filter(|templ|templ.spec.span().contains_pos(pos)) + .max_by_key(|templ|templ.spec.span().range.start())?; + self.symbol_info.trait_member_symbols.get(&container.name)? + .values() + .find(|sym|sym.lock().unwrap().loc.contains_pos(pos)) + .map(Arc::clone) } fn resolve_noderef_in_symbol<'t>(&'t self, @@ -1241,28 +1388,153 @@ impl DeviceAnalysis { let sym = symbol.lock().unwrap(); match &sym.source { SymbolSource::DMLObject(obj) => { - // The performance overhead is cloning here - // is _probably_ smaller than the one of holding the key + // The performance overhead of cloning here is _probably_ + // smaller than the one of holding the lock let obj_copy = obj.clone(); drop(sym); self.resolve_noderef_in_obj(&obj_copy, node, method_structure, ref_matches); + // Fall back to resolving the noderef as a field reference + if ref_matches.as_matches().is_none() { + if let Some(ty) = obj_copy.resolved_type() { + self.resolve_struct_field_in_type(ty, node, ref_matches); + } + } }, SymbolSource::Method(key, method) => { self.resolve_noderef_in_method(key, method, node, method_structure, ref_matches); }, - // TODO: Cannot be resolved without constant folding - SymbolSource::MethodArg(_method, _name) => (), - SymbolSource::MethodLocal(_method, _name) => (), - // TODO: Fix once type system is sorted - SymbolSource::Type(_typed) => (), + // TODO: For now, we can only resolve noderefs through method args and + // locals through their perhaps-struct-typed fields + + // Method args and locals can only be usefully sub-referenced when + // their resolved type is (a chain of typedefs down to) a struct + // type; in that case a `Simple` sub-ref names a struct field. + SymbolSource::MethodArg(..) + | SymbolSource::MethodLocal(..) + | SymbolSource::Type(..) => { + let resolved_type = sym.source.resolved_type().cloned(); + drop(sym); + if let Some(ty) = resolved_type { + self.resolve_struct_field_in_type(&ty, node, ref_matches); + } + }, // TODO: Handle lookups inside templates SymbolSource::Template(_templ) => (), } } + // Get the containing struct/template of a field + fn peel_to_field_container(ty: &DMLType) -> Option { + let mut cur: DMLType = ty.clone(); + loop { + let concrete = cur?; + cur = match concrete.as_ref() { + DMLConcreteType::StructType(st) => + return Some(FieldContainer::Struct(st.clone())), + DMLConcreteType::Trait(t) => + return Some(FieldContainer::Trait(t.clone())), + other => other.peel_one()?.clone(), + }; + } + } + + fn struct_field(st: &DMLStructType, name: &str) -> Option<(ZeroSpan, DMLType)> { + for (member_name, ty) in &st.members { + if let Some(n) = member_name { + if n.val == name { + return Some((n.span, ty.clone())); + } + } + } + None + } + + fn make_struct_field_symbol(&self, + field_span: ZeroSpan, + field_type: DMLType) -> SymbolRef { + let sym = self.symbol_maker.new_symbol( + field_span, + // local symbol kind is similar enough to a field declaration we can re-use it here. + DMLSymbolKind::Local, + SymbolSource::Type(field_type), + ); + { + let mut lock = sym.lock().unwrap(); + lock.definitions.push(field_span); + lock.declarations.push(field_span); + } + sym + } + + fn field_symbol_of(&self, container: &FieldContainer, name: &str) + -> Option { + match container { + FieldContainer::Struct(st) => + self.symbol_info.struct_member_symbols.get(st.identity_span())? + .get(name).map(Arc::clone), + FieldContainer::Trait(t) => + self.symbol_info.trait_member_symbols.get(&t.decl_name.val)? + .get(name).map(Arc::clone), + } + } + + pub fn fields_of_type_symbol(&self, type_sym: &SymbolRef) + -> Option> { + let lock = type_sym.lock().unwrap(); + let ty = lock.source.resolved_type()?; + match Self::peel_to_field_container(ty)? { + FieldContainer::Struct(st) => + self.symbol_info.struct_member_symbols.get(st.identity_span()).cloned(), + FieldContainer::Trait(t) => + self.symbol_info.trait_member_symbols.get(&t.decl_name.val).cloned(), + } + } + + fn resolve_struct_field_in_type(&self, + ty: &DMLType, + node: &NodeRef, + ref_matches: &mut ReferenceMatches) { + match node { + NodeRef::Simple(simple) => { + let Some(container) = Self::peel_to_field_container(ty) else { return; }; + if let Some(sym) = self.field_symbol_of(&container, &simple.val) { + ref_matches.add_match(sym); + } else if let FieldContainer::Struct(st) = &container { + if let Some((span, field_ty)) = Self::struct_field(st, &simple.val) { + // Fallback: struct wasn't pre-registered (anonymous or + // otherwise unreachable at analysis-build time). Still + // produce a match so goto-def works, but goto-ref may + // not include this reference. + let sym = self.make_struct_field_symbol(span, field_ty); + ref_matches.add_match(sym); + } + } + } + NodeRef::Sub(subnode, simple, _) => { + // Recursively resolve the intermediate expression's type + // against this same struct chain. + let mut intermediate = ReferenceMatches::new(); + self.resolve_struct_field_in_type(ty, subnode, &mut intermediate); + if let Some(syms) = intermediate.as_matches() { + for sym in syms { + // Clone the typeref so we can drop the lock immediately + let inner_ty = sym.lock().unwrap().source.resolved_type().cloned(); + if let Some(inner_ty) = inner_ty { + let wrapped = NodeRef::Simple(simple.clone()); + self.resolve_struct_field_in_type( + &inner_ty, &wrapped, ref_matches); + } + } + } else { + ref_matches.merge_with(intermediate); + } + } + } + } + fn get_method_symbol(&self, method: &Arc, parent_obj_key: &StructureKey) @@ -1885,6 +2157,7 @@ impl IsolatedAnalysis { fn objects_to_symbols(maker: &SymbolMaker, objects: &StructureContainer, errors: &mut Vec, + types: &mut GlobalTypeStorage, method_structure: &mut HashMap ) -> SymbolStorage { let mut storage = SymbolStorage::default(); @@ -1900,6 +2173,7 @@ fn objects_to_symbols(maker: &SymbolMaker, add_new_symbol_from_shallow(maker, shallow, errors, + types, &mut storage, method_structure); } @@ -1939,6 +2213,85 @@ fn extend_with_templates(maker: &SymbolMaker, } } +fn extend_with_types(maker: &SymbolMaker, + storage: &mut SymbolStorage, + type_storage: &GlobalTypeStorage) { + for (name, loc) in type_storage.typedef_decl_spans() { + // Fetch the fully-resolved `DMLType` for this typedef from the + // global type storage. Since the typedef declaration itself is the + // "definition of the type", the source carries the resolved + // `Typedef`-wrapped DMLType. + let resolved = type_storage.typedef_as_type(name); + let sym = symbol_ref!( + maker, + loc, + DMLSymbolKind::Typedef, + SymbolSource::Type(resolved), + bases = vec![loc], + definitions = vec![loc], + declarations = vec![loc] + ); + if let Some(prev) = storage.type_symbols.insert(loc, sym) { + internal_error!("Unexpectedly two type symbols defined in the same location"); + error!("Previous was {:?}", prev); + } + } +} + +fn extend_with_struct_fields(maker: &SymbolMaker, symbols: &mut SymbolStorage, + type_storage: &GlobalTypeStorage) { + let mut visited: HashSet = HashSet::new(); + for ty in type_storage.resolved_types() { + for_each_struct_type(ty, &mut visited, &mut |st| { + symbols.struct_member_symbols.entry(*st.identity_span()).or_insert_with(|| { + st.members.iter() + .filter_map(|(member_name, member_ty)| { + let name = member_name.as_ref()?; + let field_span = name.span; + let sym = symbol_ref!( + maker, + field_span, + DMLSymbolKind::Local, + SymbolSource::Type(member_ty.clone()), + bases = vec![field_span], + definitions = vec![field_span], + declarations = vec![field_span] + ); + Some((name.val.clone(), sym)) + }) + .collect() + }); + }); + } +} + +fn extend_with_trait_members(maker: &SymbolMaker, symbols: &mut SymbolStorage, + templates: &TemplateTraitInfo) { + for (name, template) in &templates.templates { + let trait_info = &template.traitspec; + symbols.trait_member_symbols.entry(name.clone()).or_insert_with(|| { + trait_info.params.values() + .chain(trait_info.sessions.values()) + .chain(trait_info.saveds.values()) + .map(|decl| { + let member_span = decl.name.span; + let sym = symbol_ref!( + maker, + member_span, + DMLSymbolKind::Local, + SymbolSource::Type(decl.type_ref.clone()), + bases = vec![member_span], + definitions = vec![member_span], + declarations = vec![member_span] + ); + (decl.name.val.clone(), sym) + }) + .collect() + }); + } +} + + fn new_symbol_from_object(maker: &SymbolMaker, object: &DMLCompositeObject) -> SymbolRef { let all_decl_defs = &object.all_decls; @@ -1964,7 +2317,8 @@ fn new_symbol_from_arg(maker: &SymbolMaker, maker, *arg.loc_span(), DMLSymbolKind::MethodArg, - SymbolSource::MethodArg(Arc::clone(methref), arg.name().clone()), + SymbolSource::MethodArg(Arc::clone(methref), arg.name().clone(), + arg.resolved_type()), bases = bases, definitions = definitions, declarations = declarations @@ -2000,7 +2354,7 @@ where K: std::hash::Hash + Eq + Clone + std::fmt::Debug, // Create a symbol for each level of overriding for each object where the method // is actualized. We then end up with many symbols for the same decl location, // and leave it to requests to collect the aggregate information at the point -fn add_new_symbol_from_method(maker: &SymbolMaker, parent_obj_key: &StructureKey, method_ref: &Arc, errors: &mut Vec, storage: &mut SymbolStorage, method_structure: &mut HashMap) { +fn add_new_symbol_from_method(maker: &SymbolMaker, parent_obj_key: &StructureKey, method_ref: &Arc, errors: &mut Vec, types: &mut GlobalTypeStorage, storage: &mut SymbolStorage, method_structure: &mut HashMap) { let (bases, definitions, declarations) = ( method_ref.get_bases().iter().map(|b|*b.location()).collect(), vec![*method_ref.get_decl().location()], @@ -2022,10 +2376,10 @@ fn add_new_symbol_from_method(maker: &SymbolMaker, parent_obj_key: &StructureKey let new_argsymbol = new_symbol_from_arg(maker, method_ref, arg); log_non_same_insert(&mut storage.variable_symbols, *arg.loc_span(), new_argsymbol); } - add_method_scope_symbols(maker, method_ref, method_structure, storage, errors); + add_method_scope_symbols(maker, method_ref, method_structure, types, storage, errors); if let Some(defaults) = method_ref.get_default() { for default in defaults.flat_refs() { - add_new_symbol_from_method(maker, parent_obj_key, default, errors, storage, method_structure); + add_new_symbol_from_method(maker, parent_obj_key, default, errors, types, storage, method_structure); } } } @@ -2035,6 +2389,7 @@ fn add_new_symbol_from_method(maker: &SymbolMaker, parent_obj_key: &StructureKey fn add_new_symbol_from_shallow(maker: &SymbolMaker, shallow: &DMLShallowObject, errors: &mut Vec, + types: &mut GlobalTypeStorage, storage: &mut SymbolStorage, method_structure: &mut HashMap ) { @@ -2046,7 +2401,7 @@ fn add_new_symbol_from_shallow(maker: &SymbolMaker, param.declarations.iter() .map(|(_, def)|*def.loc_span()).collect()), DMLShallowObjectVariant::Method(method_ref) => - return add_new_symbol_from_method(maker, &shallow.parent, method_ref, errors, storage, method_structure), + return add_new_symbol_from_method(maker, &shallow.parent, method_ref, errors, types, storage, method_structure), DMLShallowObjectVariant::Constant(constant) => (vec![*constant.loc_span()], vec![*constant.loc_span()], @@ -2101,6 +2456,7 @@ fn add_method_scope_symbols(maker: &SymbolMaker, method: &Arc, method_structure: &mut HashMap, + types: &mut GlobalTypeStorage, storage: &mut SymbolStorage, errors: &mut Vec) { let mut entry = RangeEntry { @@ -2113,6 +2469,7 @@ fn add_method_scope_symbols(maker: &SymbolMaker, method, &method.get_decl().body, errors, + types, storage, &mut entry); } @@ -2126,20 +2483,22 @@ fn add_method_scope_symbols(maker: &SymbolMaker, fn add_new_method_scope_symbol(maker: &SymbolMaker, method: &Arc, sym: &T, - _typ: &DMLType, + typ: &UnresolvedType, + types: &mut GlobalTypeStorage, + errors: &mut Vec, storage: &mut SymbolStorage, scope: &mut RangeEntry) where T : StructureSymbol + DMLNamed + LocationSpan { + let resolved = eval_type_simple(typ, types, errors); let symbol = symbol_ref!( maker, *sym.loc_span(), sym.kind(), - SymbolSource::MethodLocal(Arc::clone(method), sym.name().clone()), + SymbolSource::MethodLocal(Arc::clone(method), sym.name().clone(), resolved), definitions = vec![*sym.loc_span()], declarations = vec![*sym.loc_span()] - // TODO: resolve type ); scope.symbols.insert(sym.name().val.clone(), Arc::clone(&symbol)); storage.variable_symbols.insert(*sym.loc_span(), symbol); @@ -2151,6 +2510,7 @@ fn enter_new_method_scope(maker: &SymbolMaker, stmnt: &Statement, scope_span: &ZeroSpan, errors: &mut Vec, + types: &mut GlobalTypeStorage, storage: &mut SymbolStorage, scope: &mut RangeEntry) { // In DMLC, there is no error or warning about this (even if a declaration @@ -2176,6 +2536,7 @@ fn enter_new_method_scope(maker: &SymbolMaker, method, stmnt, errors, + types, storage, &mut entry); scope.sub_ranges.push(entry); @@ -2185,6 +2546,7 @@ fn add_new_method_scope_symbols(maker: &SymbolMaker, method: &Arc, stmnt: &Statement, errors: &mut Vec, + types: &mut GlobalTypeStorage, storage: &mut SymbolStorage, scope: &mut RangeEntry) { match &**stmnt { @@ -2194,6 +2556,7 @@ fn add_new_method_scope_symbols(maker: &SymbolMaker, method, sub_stmnt, errors, + types, storage, scope); }, @@ -2204,6 +2567,7 @@ fn add_new_method_scope_symbols(maker: &SymbolMaker, &content.ifbody, content.ifbody.span(), errors, + types, storage, scope); if let Some(elsebody) = &content.elsebody { @@ -2213,6 +2577,7 @@ fn add_new_method_scope_symbols(maker: &SymbolMaker, elsebody, elsebody.span(), errors, + types, storage, scope); } @@ -2225,6 +2590,7 @@ fn add_new_method_scope_symbols(maker: &SymbolMaker, &content.ifbody, content.ifbody.span(), errors, + types, storage, scope); if let Some(elsebody) = &content.elsebody { @@ -2234,6 +2600,7 @@ fn add_new_method_scope_symbols(maker: &SymbolMaker, elsebody, elsebody.span(), errors, + types, storage, scope); } @@ -2245,6 +2612,7 @@ fn add_new_method_scope_symbols(maker: &SymbolMaker, &content.body, content.body.span(), errors, + types, storage, scope), StatementKind::DoWhile(content) => @@ -2254,6 +2622,7 @@ fn add_new_method_scope_symbols(maker: &SymbolMaker, &content.body, content.body.span(), errors, + types, storage, scope), StatementKind::For(content) => { @@ -2269,6 +2638,8 @@ fn add_new_method_scope_symbols(maker: &SymbolMaker, method, decl, &decl.typed, + types, + errors, storage, &mut entry); } @@ -2279,6 +2650,7 @@ fn add_new_method_scope_symbols(maker: &SymbolMaker, &content.body, content.body.span(), errors, + types, storage, &mut entry); scope.sub_ranges.push(entry); @@ -2295,7 +2667,11 @@ fn add_new_method_scope_symbols(maker: &SymbolMaker, method, &content.ident, // TODO: infer type - content.ident.loc_span(), + // (element of inexpr type) + &UnresolvedType::make_invalid( + *content.inexpr.span()), + types, + errors, storage, &mut entry); enter_new_method_scope(maker, @@ -2304,6 +2680,7 @@ fn add_new_method_scope_symbols(maker: &SymbolMaker, &content.selectbranch, content.selectbranch.span(), errors, + types, storage, &mut entry); scope.sub_ranges.push(entry); @@ -2313,6 +2690,7 @@ fn add_new_method_scope_symbols(maker: &SymbolMaker, &content.elsebranch, content.elsebranch.span(), errors, + types, storage, scope); }, @@ -2323,6 +2701,7 @@ fn add_new_method_scope_symbols(maker: &SymbolMaker, &content.tryblock, content.tryblock.span(), errors, + types, storage, scope); enter_new_method_scope(maker, @@ -2331,6 +2710,7 @@ fn add_new_method_scope_symbols(maker: &SymbolMaker, &content.catchblock, content.catchblock.span(), errors, + types, storage, scope); }, @@ -2341,6 +2721,8 @@ fn add_new_method_scope_symbols(maker: &SymbolMaker, method, decl, &decl.typed, + types, + errors, storage, scope); }, @@ -2356,6 +2738,7 @@ impl DeviceAnalysis { &str, &ObjectDecl