Skip to content

Cross-file qualified references through a package's short name fail to resolve (global scope indexes declaredName only) #778

Description

@liberallogic

Cross-file qualified references through a package's short name fail to resolve (global scope indexes declaredName only)

Summary

A qualified name whose first segment is the short name (<...>) of a top-level package defined in another file fails to resolve. The same reference resolves when written with the package's declaredName, and short names resolve correctly in every same-resource position. The asymmetry comes from the global-scope index being built from declaredName only, while the local scope provider correctly matches both memberName and memberShortName.

Environment

  • Reproduced on the pilot as packaged in jupyter-sysml-kernel 0.59.0 (SysMLInteractive / IResourceValidator.validate(..., CheckMode.ALL, ...)).
  • Source references below are from tag 2026-05. The relevant code path is unchanged there (see Root cause), so the behavior is expected to persist on the latest release.

Steps to reproduce

Load each case as one or more .sysml files in the same resource set and validate.

T1 — package short name as first segment, same file → OK

package <P> Alpha { part def Foo; }
package Beta { part x : P::Foo; }

T2 — member short name as a non-first segment → OK

package Alpha { part def <F> Foo; }
package Beta { part x : Alpha::F; }

T3 — member short name, unqualified, same namespace → OK

package Alpha { part def <F> Foo; part x : F; }

T4 — package short name as first segment, references another file → FAILS

// a.sysml
package <P> Alpha { part def Foo; }
// b.sysml
package Beta { part x : P::Foo; }

Actual result

Only T4 fails:

Couldn't resolve reference to Type 'P::Foo'.
An occurrence, item or part must be typed by occurrence definitions.   // cascading, from the now-untyped feature

Writing the same reference with the declared name — part x : Alpha::Foo; — resolves. Importing by short name across files (private import P::*;) fails the same way; private import Alpha::*; works.

Expected result

T4 resolves, consistent with T1–T3. A package's short name is a valid memberShortName of its namespace, and per the KerML name-resolution rules a namespace provides names for its members via both memberNames and memberShortNames. There is no stated reason why short-name qualification should work within a resource but not across resources.

Root cause

The local scope provider already handles short names. KerMLScope.xtend checks each membership under both names:

// org.omg.kerml.xtext/.../scoping/KerMLScope.xtend
var memberName = ... else mem.memberName
if (checkElementName(memberName, qn, ...)) return true
...
var memberShortName = ... else mem.memberShortName
if (checkElementName(memberShortName, qn, ...)) return true

That is why T1–T3 (and non-first segments in general) work.

For a cross-resource reference, however, the first segment is resolved through the default global index, not by model traversal:

// org.omg.kerml.xtext/.../scoping/KerMLGlobalScope.xtend  (getSingleElement)
val rootName = QualifiedName.create(qualifiedName.firstSegment)
val root = outer.getSingleElement(rootName)   // outer == default global scope (the exported index)
...
// subsequent segments are resolved by scopeFor(root as Namespace) — which DOES honor short names

The exported index is produced by KerMLQualifiedNameProvider, which derives qualified names from declaredName only:

// org.omg.kerml.xtext/.../naming/KerMLQualifiedNameProvider.java
private Function<EObject, String> resolver =
        SimpleAttributeResolver.newResolver(String.class, "declaredName");

So a top-level element is indexed under its declaredName-based qualified name but never under its declaredShortName. When the first segment of a cross-resource reference is a short name, outer.getSingleElement(rootName) returns null, and resolution stops before the (short-name-aware) local scope is ever consulted.

This is long-standing and deliberate — introduced in the 2022-12 baseline (commit ST6RI-628, "Updated KerMLQualifiedNameProvider to use declaredName for global scope name resolution") and unchanged through 2026-05.

Impact

Multi-file models that reference each other's top-level packages by short name do not validate. A common pattern is a project split into several files, each declaring a package with a short name (package <ABC> SomeLongName { … }) and referring to the others by that short name (ABC::Something). Under the pilot every such cross-file reference fails, and each failure cascades into secondary rule violations (e.g. An occurrence, item or part must be typed by occurrence definitions., A requirement must be typed by one requirement definition.) on the now-untyped features. Rewriting every short-name qualifier to the corresponding declaredName makes the whole model validate with zero errors, which confirms the single root cause.

Suggested fix

Make cross-resource resolution consistent with local resolution by exposing short names to the global index. Options:

  1. In KerMLQualifiedNameProvider, also produce a declaredShortName-based qualified name (e.g. override getFullyQualifiedNames / contribute an additional IEObjectDescription), so a top-level element is indexed under both its name and its short name; or
  2. In KerMLGlobalScope.getSingleElement, fall back to looking up the first segment by short name when the declaredName lookup misses.

The local scope already honors memberShortName, so the change is isolated to the export/index side.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions