Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -541,7 +541,7 @@
/// sibling in the enclosing action body, tracked by <see cref="CollectActionBodyChildren"/>
/// since the grammar's leading marker itself carries no identity.
/// </summary>
public override SysmlNode? VisitActionBodyItem(SysMLv2Parser.ActionBodyItemContext context)

Check warning on line 544 in src/DemaConsulting.SysML2Tools.Language/Semantic/Model/AstBuilder.cs

View workflow job for this annotation

GitHub Actions / Build / Build macos-latest

Refactor this method to reduce its Cognitive Complexity from 20 to the 15 allowed.

Check warning on line 544 in src/DemaConsulting.SysML2Tools.Language/Semantic/Model/AstBuilder.cs

View workflow job for this annotation

GitHub Actions / Build / Build macos-latest

Refactor this method to reduce its Cognitive Complexity from 20 to the 15 allowed.

Check warning on line 544 in src/DemaConsulting.SysML2Tools.Language/Semantic/Model/AstBuilder.cs

View workflow job for this annotation

GitHub Actions / Build / Build windows-latest

Refactor this method to reduce its Cognitive Complexity from 20 to the 15 allowed.

Check warning on line 544 in src/DemaConsulting.SysML2Tools.Language/Semantic/Model/AstBuilder.cs

View workflow job for this annotation

GitHub Actions / Build / Build ubuntu-latest

Refactor this method to reduce its Cognitive Complexity from 20 to the 15 allowed.
{
if (context.nonBehaviorBodyItem() is { } nonBehaviorBodyItem)
{
Expand Down Expand Up @@ -1401,7 +1401,7 @@
/// <c>VisitChildren</c> aggregation silently discards every child result but the last,
/// dropping both the preceding usage and any earlier attached transitions.
/// </summary>
public override SysmlNode? VisitStateBodyItem(SysMLv2Parser.StateBodyItemContext context)

Check warning on line 1404 in src/DemaConsulting.SysML2Tools.Language/Semantic/Model/AstBuilder.cs

View workflow job for this annotation

GitHub Actions / Build / Build macos-latest

Refactor this method to reduce its Cognitive Complexity from 16 to the 15 allowed.

Check warning on line 1404 in src/DemaConsulting.SysML2Tools.Language/Semantic/Model/AstBuilder.cs

View workflow job for this annotation

GitHub Actions / Build / Build windows-latest

Refactor this method to reduce its Cognitive Complexity from 16 to the 15 allowed.

Check warning on line 1404 in src/DemaConsulting.SysML2Tools.Language/Semantic/Model/AstBuilder.cs

View workflow job for this annotation

GitHub Actions / Build / Build ubuntu-latest

Refactor this method to reduce its Cognitive Complexity from 16 to the 15 allowed.
{
if (context.nonBehaviorBodyItem() is { } nonBehaviorBodyItem)
{
Expand Down Expand Up @@ -2081,7 +2081,7 @@
return null;
}

var (qn, isWildcard, bracketFilterText, _) = ExtractImportTarget(decl.namespaceImport(), decl.membershipImport());
var (qn, isWildcard, bracketFilterText, isRecursive) = ExtractImportTarget(decl.namespaceImport(), decl.membershipImport());
if (qn is null)
{
return null;
Expand All @@ -2092,6 +2092,8 @@
ImportedNamespace = qn,
ImportedNames = [qn],
IsWildcard = isWildcard,
IsRecursive = isRecursive,
IsMembershipImport = decl.membershipImport() is not null,
BracketFilterExpressionText = bracketFilterText,
};
}
Expand All @@ -2115,7 +2117,7 @@
/// always true for bracket-filtered forms by design, since the grammar only reaches that
/// alternative via <c>::**[filterExpr]</c>).
/// </returns>
private static (string? QualifiedName, bool IsWildcard, string? BracketFilterExpressionText, bool IsRecursive) ExtractImportTarget(

Check warning on line 2120 in src/DemaConsulting.SysML2Tools.Language/Semantic/Model/AstBuilder.cs

View workflow job for this annotation

GitHub Actions / Build / Build macos-latest

Refactor this method to reduce its Cognitive Complexity from 24 to the 15 allowed.

Check warning on line 2120 in src/DemaConsulting.SysML2Tools.Language/Semantic/Model/AstBuilder.cs

View workflow job for this annotation

GitHub Actions / Build / Build windows-latest

Refactor this method to reduce its Cognitive Complexity from 24 to the 15 allowed.

Check warning on line 2120 in src/DemaConsulting.SysML2Tools.Language/Semantic/Model/AstBuilder.cs

View workflow job for this annotation

GitHub Actions / Build / Build ubuntu-latest

Refactor this method to reduce its Cognitive Complexity from 24 to the 15 allowed.
SysMLv2Parser.NamespaceImportContext? namespaceImport,
SysMLv2Parser.MembershipImportContext? membershipImport)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -401,7 +401,7 @@
/// <see langword="true"/> if the name resolves to a known symbol;
/// <see langword="false"/> otherwise.
/// </returns>
private bool TryResolve(

Check warning on line 404 in src/DemaConsulting.SysML2Tools.Language/Semantic/Model/ReferenceResolver.cs

View workflow job for this annotation

GitHub Actions / Build / Build macos-latest

Refactor this method to reduce its Cognitive Complexity from 26 to the 15 allowed.

Check warning on line 404 in src/DemaConsulting.SysML2Tools.Language/Semantic/Model/ReferenceResolver.cs

View workflow job for this annotation

GitHub Actions / Build / Build macos-latest

Refactor this method to reduce its Cognitive Complexity from 26 to the 15 allowed.

Check warning on line 404 in src/DemaConsulting.SysML2Tools.Language/Semantic/Model/ReferenceResolver.cs

View workflow job for this annotation

GitHub Actions / Build / Build windows-latest

Refactor this method to reduce its Cognitive Complexity from 26 to the 15 allowed.

Check warning on line 404 in src/DemaConsulting.SysML2Tools.Language/Semantic/Model/ReferenceResolver.cs

View workflow job for this annotation

GitHub Actions / Build / Build windows-latest

Refactor this method to reduce its Cognitive Complexity from 26 to the 15 allowed.

Check warning on line 404 in src/DemaConsulting.SysML2Tools.Language/Semantic/Model/ReferenceResolver.cs

View workflow job for this annotation

GitHub Actions / Build / Build ubuntu-latest

Refactor this method to reduce its Cognitive Complexity from 26 to the 15 allowed.

Check warning on line 404 in src/DemaConsulting.SysML2Tools.Language/Semantic/Model/ReferenceResolver.cs

View workflow job for this annotation

GitHub Actions / Build / Build ubuntu-latest

Refactor this method to reduce its Cognitive Complexity from 26 to the 15 allowed.
string name,
IReadOnlyList<string> namespaceStack,
IReadOnlyList<SysmlImportNode> imports,
Expand Down Expand Up @@ -440,6 +440,32 @@
resolvedName = candidate;
return true;
}

// A recursive *membership* import (`import X::Y::**;`) brings Y itself into scope by
// its own short name — not just Y's nested descendants — since Y is the explicit
// membership-import target, not merely a containing namespace being wildcard-searched.
// e.g. `import VersionMark::Cli::**;` should let an unqualified `Cli` reference resolve
// to `VersionMark::Cli` itself, in addition to reaching Cli's own nested members below.
if (wildcard.IsMembershipImport &&
LastSegment(resolvedNamespace) == name &&
_symbolTable.Contains(resolvedNamespace))
{
resolvedName = resolvedNamespace;
return true;
}

// Recursive wildcard import (`import X::*::**;`) additionally reaches members of any
// namespace nested within X at any depth, not just X's own direct members — e.g.
// `import VersionMark::*::**;` should let an unqualified `CliSubsystem` resolve to
// `VersionMark::Cli::CliSubsystem` even though `Cli` is a nested package, not X itself.
// Prefer the least-nested match for determinism when more than one namespace at
// different depths happens to declare a same-named member.
if (wildcard.IsRecursive &&
FindRecursiveWildcardMatch(resolvedNamespace, name) is { Length: > 0 } recursiveMatch)
{
resolvedName = recursiveMatch;
return true;
}
}

// Step 4: Explicit named imports — for each `import X::Y` where Y == name,
Expand Down Expand Up @@ -468,6 +494,60 @@
return false;
}

/// <summary>
/// Searches the symbol table for a member named <paramref name="name"/> declared in any
/// namespace nested (at any depth) within <paramref name="resolvedNamespace"/>, for
/// resolving a recursive wildcard import's (<c>import X::*::**;</c>) unqualified
/// references. Returns the shallowest (least-nested) match when more than one namespace at
/// different depths declares a same-named member, and <see cref="string.Empty"/> when no
/// match exists.
/// </summary>
private string FindRecursiveWildcardMatch(string resolvedNamespace, string name)
{
var prefix = resolvedNamespace + "::";
var suffix = "::" + name;
string? best = null;
var bestDepth = int.MaxValue;
foreach (var qualifiedName in _symbolTable.Symbols.Keys)
{
if (!qualifiedName.StartsWith(prefix, StringComparison.Ordinal) ||
!qualifiedName.EndsWith(suffix, StringComparison.Ordinal) ||
qualifiedName.Length < prefix.Length + suffix.Length)
{
continue;
}

// Depth is the number of intermediate "::"-separated segments between
// resolvedNamespace and name — count separators in the portion of qualifiedName
// strictly between the matched prefix and suffix, not raw string length (segment
// names vary in length, so a deeper match can have a shorter qualified-name string
// than a shallower one). The length guard above excludes the exact one-level match
// "prefix + name" (already handled by the direct-candidate check before this method
// is called), where prefix and suffix overlap and slicing out a "middle" would
// otherwise be an invalid (negative-length) range.
var middle = qualifiedName[prefix.Length..^suffix.Length];
var depth = middle.Length == 0 ? 0 : middle.Split("::").Length;
if (depth < bestDepth)
{
best = qualifiedName;
bestDepth = depth;
}
}

return best ?? string.Empty;
}

/// <summary>
/// Returns the final <c>::</c>-separated segment of a qualified name (e.g.
/// <c>"VersionMark::Cli"</c> → <c>"Cli"</c>), or the whole string when it contains no
/// <c>::</c> separator.
/// </summary>
private static string LastSegment(string qualifiedName)
{
var lastSep = qualifiedName.LastIndexOf("::", StringComparison.Ordinal);
return lastSep >= 0 ? qualifiedName[(lastSep + 2)..] : qualifiedName;
}

/// <summary>
/// Resolves a raw (possibly nested-relative) namespace name referenced by an <c>import</c>
/// statement to its fully-qualified form, trying a direct symbol-table lookup first and
Expand Down Expand Up @@ -529,7 +609,7 @@
/// resolved immediately; appended to by this method, resolved later by
/// <see cref="ResolveBareRedefinitions"/>.
/// </param>
private void ResolveNode(

Check warning on line 612 in src/DemaConsulting.SysML2Tools.Language/Semantic/Model/ReferenceResolver.cs

View workflow job for this annotation

GitHub Actions / Build / Build macos-latest

Refactor this method to reduce its Cognitive Complexity from 89 to the 15 allowed.

Check warning on line 612 in src/DemaConsulting.SysML2Tools.Language/Semantic/Model/ReferenceResolver.cs

View workflow job for this annotation

GitHub Actions / Build / Build macos-latest

Refactor this method to reduce its Cognitive Complexity from 89 to the 15 allowed.

Check warning on line 612 in src/DemaConsulting.SysML2Tools.Language/Semantic/Model/ReferenceResolver.cs

View workflow job for this annotation

GitHub Actions / Build / Build windows-latest

Refactor this method to reduce its Cognitive Complexity from 89 to the 15 allowed.

Check warning on line 612 in src/DemaConsulting.SysML2Tools.Language/Semantic/Model/ReferenceResolver.cs

View workflow job for this annotation

GitHub Actions / Build / Build windows-latest

Refactor this method to reduce its Cognitive Complexity from 89 to the 15 allowed.

Check warning on line 612 in src/DemaConsulting.SysML2Tools.Language/Semantic/Model/ReferenceResolver.cs

View workflow job for this annotation

GitHub Actions / Build / Build ubuntu-latest

Refactor this method to reduce its Cognitive Complexity from 89 to the 15 allowed.

Check warning on line 612 in src/DemaConsulting.SysML2Tools.Language/Semantic/Model/ReferenceResolver.cs

View workflow job for this annotation

GitHub Actions / Build / Build ubuntu-latest

Refactor this method to reduce its Cognitive Complexity from 89 to the 15 allowed.
SysmlNode node,
string filePath,
HashSet<string> resolvedInFile,
Expand Down Expand Up @@ -910,7 +990,7 @@
/// Aggregate list of all edges resolved so far across the whole file-root traversal;
/// appended to by this method.
/// </param>
private void ResolveFeatureChains(

Check warning on line 993 in src/DemaConsulting.SysML2Tools.Language/Semantic/Model/ReferenceResolver.cs

View workflow job for this annotation

GitHub Actions / Build / Build macos-latest

Refactor this method to reduce its Cognitive Complexity from 22 to the 15 allowed.

Check warning on line 993 in src/DemaConsulting.SysML2Tools.Language/Semantic/Model/ReferenceResolver.cs

View workflow job for this annotation

GitHub Actions / Build / Build windows-latest

Refactor this method to reduce its Cognitive Complexity from 22 to the 15 allowed.

Check warning on line 993 in src/DemaConsulting.SysML2Tools.Language/Semantic/Model/ReferenceResolver.cs

View workflow job for this annotation

GitHub Actions / Build / Build windows-latest

Refactor this method to reduce its Cognitive Complexity from 22 to the 15 allowed.

Check warning on line 993 in src/DemaConsulting.SysML2Tools.Language/Semantic/Model/ReferenceResolver.cs

View workflow job for this annotation

GitHub Actions / Build / Build ubuntu-latest

Refactor this method to reduce its Cognitive Complexity from 22 to the 15 allowed.

Check warning on line 993 in src/DemaConsulting.SysML2Tools.Language/Semantic/Model/ReferenceResolver.cs

View workflow job for this annotation

GitHub Actions / Build / Build ubuntu-latest

Refactor this method to reduce its Cognitive Complexity from 22 to the 15 allowed.
SysmlNode node,
string filePath,
HashSet<string> resolvedInFile,
Expand Down Expand Up @@ -1105,7 +1185,7 @@
/// explicitly-scoped simplification, not a general inherited-member resolver, and must not
/// be reused for any other reference kind or name.
/// </summary>
private bool TryResolveInheritedActionMember(

Check warning on line 1188 in src/DemaConsulting.SysML2Tools.Language/Semantic/Model/ReferenceResolver.cs

View workflow job for this annotation

GitHub Actions / Build / Build macos-latest

Refactor this method to reduce its Cognitive Complexity from 16 to the 15 allowed.

Check warning on line 1188 in src/DemaConsulting.SysML2Tools.Language/Semantic/Model/ReferenceResolver.cs

View workflow job for this annotation

GitHub Actions / Build / Build windows-latest

Refactor this method to reduce its Cognitive Complexity from 16 to the 15 allowed.

Check warning on line 1188 in src/DemaConsulting.SysML2Tools.Language/Semantic/Model/ReferenceResolver.cs

View workflow job for this annotation

GitHub Actions / Build / Build ubuntu-latest

Refactor this method to reduce its Cognitive Complexity from 16 to the 15 allowed.
IReadOnlyList<string> namespaceStack,
string name,
out string resolvedName)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,28 @@ public sealed class SysmlImportNode : SysmlNode
/// </summary>
public bool IsWildcard { get; init; }

/// <summary>
/// Gets a value indicating whether this import is recursive — derived from a trailing
/// <c>::**</c> on a namespace-import wildcard (<c>import X::*::**;</c>), which per the
/// KerML/SysML v2 grammar imports members of <c>X</c> and every namespace nested within it
/// at any depth, not just <c>X</c>'s direct members. <see langword="false"/> for a plain
/// <c>import X::*;</c> (direct members only) or any non-wildcard import.
/// </summary>
public bool IsRecursive { get; init; }

/// <summary>
/// Gets a value indicating whether this import was parsed from the membership-import
/// grammar form (<c>import X::Y;</c> or its recursive <c>import X::Y::**;</c> variant, where
/// <c>ImportedNamespace</c> is the fully-qualified path to the specific member <c>Y</c>)
/// rather than the namespace-import wildcard form (<c>import X::*;</c> or
/// <c>import X::*::**;</c>, where <c>ImportedNamespace</c> is the containing namespace whose
/// members are being imported). This distinction matters for recursive resolution: a
/// recursive membership import must additionally bring <c>Y</c> itself into scope by its
/// own short name (in addition to <c>Y</c>'s nested descendants), whereas a recursive
/// namespace import must not — <c>X</c> itself was never a name being imported.
/// </summary>
public bool IsMembershipImport { get; init; }

/// <summary>
/// Gets the raw source text of this import's bracketed filter expression (from
/// <c>expose &lt;path&gt;::**[&lt;expr&gt;]</c>'s <c>filterPackageMember().ownedExpression().GetText()</c>),
Expand Down
223 changes: 223 additions & 0 deletions test/DemaConsulting.SysML2Tools.Tests/Semantic/WorkspaceLoaderTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -397,6 +397,229 @@ part def Foo specializes Bar {}
}
}

// Level 12b: Unqualified name resolves via recursive wildcard import
/// <summary>
/// A recursive wildcard import (<c>import X::*::**;</c>) must reach members declared in
/// namespaces nested at any depth under <c>X</c>, not just <c>X</c>'s own direct members.
/// This mirrors the official OMG conformance construct in
/// <c>test/SysMLModels/OMG/examples/SimpleTests/ImportTest.sysml</c> (<c>private import
/// Pkg211::*::**;</c>), which previously parsed without error but never actually resolved
/// nested-namespace references — the OMG corpus sweep only checks for parse errors, never
/// resolution correctness, so this gap went uncaught.
/// </summary>
[Fact]
public async Task WorkspaceLoader_LoadAsync_UnqualifiedNameViaRecursiveWildcardImport_ResolvesWithoutWarning()
{
// Arrange — Foo and Bar each live two namespace levels below package A, in the
// nested Sub1 and Sub2 sub-packages respectively. Consumer uses a recursive
// wildcard import targeting A and references both by short name.
var tempFile = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName() + ".sysml");
try
{
await File.WriteAllTextAsync(tempFile, """
package TestNs {
package A {
package Sub1 {
part def Foo;
}
package Sub2 {
part def Bar;
}
}
}
package Consumer {
import TestNs::A::*::**;

part def UsesBoth {
part f : Foo;
part b : Bar;
}
}
""", TestContext.Current.CancellationToken);

// Act
var (stdlibTable, _) = StdlibProvider.GetSymbolTable();
var result = await WorkspaceLoader.LoadAsync([tempFile], stdlibTable);

// Assert — "Foo" and "Bar" both resolve via the recursive wildcard import, even
// though they're nested two levels below the imported namespace.
Assert.NotNull(result.Workspace);
Assert.DoesNotContain(result.Diagnostics,
d => d.Severity == DemaConsulting.SysML2Tools.Parser.DiagnosticSeverity.Warning &&
(d.Message.Contains("Foo") || d.Message.Contains("Bar")));
}
finally
{
File.Delete(tempFile);
}
}

// Level 12d: Ambiguous recursive wildcard match resolves to the shallowest namespace depth,
// not the shortest qualified-name string
/// <summary>
/// When two namespaces at different nesting depths under a recursive wildcard import's
/// target both declare a same-named member, resolution must prefer the genuinely
/// shallowest (least-nested) match — measured by the number of intermediate namespace
/// segments, not by comparing raw qualified-name string length. This uses deliberately
/// long package names at the shallow depth and short ones at the deep depth so a
/// length-based tie-break (an easy mistake) would pick the wrong one.
/// </summary>
[Fact]
public async Task WorkspaceLoader_LoadAsync_RecursiveWildcardImport_AmbiguousMatch_PrefersShallowestDepth()
{
// Arrange — Foo is declared twice under A: once one level down in a namespace with a
// deliberately long name (shallow, but long string), and once three levels down through
// short-named namespaces (deep, but short string). The shallow one must win.
var tempFile = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName() + ".sysml");
try
{
await File.WriteAllTextAsync(tempFile, """
package TestNs {
package A {
package AVeryLongIntermediateNamespaceName {
part def Foo;
}
package B {
package C {
package D {
part def Foo {}
}
}
}
}
}
package Consumer {
import TestNs::A::*::**;

part def Uses {
part f : Foo;
}
}
""", TestContext.Current.CancellationToken);

// Act
var (stdlibTable, _) = StdlibProvider.GetSymbolTable();
var result = await WorkspaceLoader.LoadAsync([tempFile], stdlibTable);
Assert.NotNull(result.Workspace);
var index = result.Workspace!.Index;

// Assert — "Foo" resolves as a Typing edge to the shallower (one-level-deep) match,
// not the deeper-but-shorter-string one.
Assert.Contains(index.GetOutgoingEdges("Consumer::Uses::f"),
e => e.Kind == DemaConsulting.SysML2Tools.Semantic.Model.SysmlEdgeKind.Typing &&
e.TargetQualifiedName == "TestNs::A::AVeryLongIntermediateNamespaceName::Foo");
}
finally
{
File.Delete(tempFile);
}
}

// Level 12e: Unqualified name resolves via recursive membership import, including the
// imported member's own name
/// <summary>
/// A recursive <em>membership</em> import (<c>import X::Y::**;</c>, as distinct from the
/// recursive namespace-import form covered by the previous test) must bring both the
/// explicitly named member <c>Y</c> itself into scope by its own short name, and every
/// name declared in a namespace nested under <c>Y</c> at any depth. This is a distinct
/// grammar shape from <c>import X::*::**;</c>: there, <c>X</c> is a containing namespace
/// being wildcard-searched and is never itself a name being imported, whereas here <c>Y</c>
/// is the explicit membership-import target and must resolve by its own name too.
/// </summary>
[Fact]
public async Task WorkspaceLoader_LoadAsync_UnqualifiedNameViaRecursiveMembershipImport_ResolvesWithoutWarning()
{
// Arrange — A is a nested package directly under TestNs; Foo lives one level further
// down, inside A's own Sub1 sub-package. Consumer uses a recursive membership import
// targeting A itself and references both A and Foo by short name.
var tempFile = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName() + ".sysml");
try
{
await File.WriteAllTextAsync(tempFile, """
package TestNs {
package A {
package Sub1 {
part def Foo;
}
}
}
package Consumer {
import TestNs::A::**;

part def UsesA {
part a : A;
part f : Foo;
}
}
""", TestContext.Current.CancellationToken);

// Act
var (stdlibTable, _) = StdlibProvider.GetSymbolTable();
var result = await WorkspaceLoader.LoadAsync([tempFile], stdlibTable);

// Assert — both "A" (the membership-import target itself) and "Foo" (A's nested
// descendant) resolve without warning.
Assert.NotNull(result.Workspace);
Assert.DoesNotContain(result.Diagnostics,
d => d.Severity == DemaConsulting.SysML2Tools.Parser.DiagnosticSeverity.Warning &&
(d.Message.Contains("'A'") || d.Message.Contains("Foo")));
}
finally
{
File.Delete(tempFile);
}
}

// Level 12c: Non-recursive wildcard import must NOT reach nested-namespace members
/// <summary>
/// A plain (non-recursive) wildcard import (<c>import X::*;</c>) must only bring X's own
/// direct members into scope — it must NOT reach members declared in namespaces nested
/// under X. This is the control case proving the recursive-descent fallback added for
/// <c>import X::*::**;</c> is correctly gated on <c>IsRecursive</c> and doesn't leak into
/// the plain wildcard-import path.
/// </summary>
[Fact]
public async Task WorkspaceLoader_LoadAsync_UnqualifiedNameViaPlainWildcardImport_DoesNotReachNestedNamespace()
{
// Arrange — Foo lives in the nested Sub1 sub-package, but Consumer only uses a
// plain (non-recursive) wildcard import targeting A.
var tempFile = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName() + ".sysml");
try
{
await File.WriteAllTextAsync(tempFile, """
package TestNs {
package A {
package Sub1 {
part def Foo;
}
}
}
package Consumer {
import TestNs::A::*;

part def Uses {
part f : Foo;
}
}
""", TestContext.Current.CancellationToken);

// Act
var (stdlibTable, _) = StdlibProvider.GetSymbolTable();
var result = await WorkspaceLoader.LoadAsync([tempFile], stdlibTable);

// Assert — "Foo" must NOT resolve; a non-recursive wildcard import doesn't reach
// into nested namespaces, so this must still produce an Unresolved reference warning.
Assert.NotNull(result.Workspace);
Assert.Contains(result.Diagnostics,
d => d.Severity == DemaConsulting.SysML2Tools.Parser.DiagnosticSeverity.Warning &&
d.Message.Contains("Foo"));
}
finally
{
File.Delete(tempFile);
}
}

// Level 13: Explicit named import resolves short name
/// <summary>
/// A part def that specializes a type using only its short name, where that type is
Expand Down
Loading