1
0
Fork 0

Complete Phase E5 tier-6 Adw/Soup/Gst compile-gate compliance

Relaxes the generated-filename PascalCase validator's uppercase-run cap
(>3 -> >4) so legitimate acronym type names (PluginAPIFlags, AuthNTLM)
stop crashing generation.

Fixes four compounding cross-module correctness bugs the larger tier-6
GIR set exposed at scale:
- The duplicate-of-dependency drop only checked simple-name collision,
  wrongly dropping genuinely distinct C types that merely share a
  post-namespace-stripping Swift name (Gst.Object/GstObject vs
  GObject.Object/GObject, plus three tier-4/5 cases: Gdk.AppLaunchContext,
  Gdk.Gravity, Gdk.Rectangle). Now also requires matching cType.
- Qualifying a cross-module reference as "GObject.X" broke wherever a
  raw C struct also named GObject was in scope (every C target's import),
  since Swift resolved the module name to the shadowing struct. GObject's
  Support.swift now exports collision-free GLibObject/GLibValueArray
  aliases used instead.
- Missing `override` keyword: added ancestor-method-selector detection
  (name + parameter labels, same-module only, since none of these members
  are open) so a subclass narrowing an ancestor's return type compiles.
- The pre-existing cross-module inherited-member dedup pass keyed
  ancestors by bare Swift name; its cycle guard falsely self-terminated
  once two classes shared a name, missing real inherited members (e.g.
  GstObject's own ref()/unref() were never recognized as duplicating
  GObject.Object's, producing an illegal redeclaration). Rewired to walk
  by unambiguous GIR name via new ClassPlan.girName/parentGIRName fields.

Also fixes bitfield/enum-typed global constants (wraps the raw literal
in Type(rawValue:)) and a void-returning ref function
(gst_atomic_queue_ref, unlike GstBuffer/GObject's T*-returning
convention) via new RecordPlan.copyReturnsVoid.

Adds tier-6 smoke tests (Gst/Soup/Adw version calls against the real
linked libraries) and refreshes the tier-4/5 skip baselines for the
duplicate-detection fix's legitimate coverage growth. Zero skip-baseline
drift on tiers 1-6; 220/220 unit tests and all tier smoke suites pass.
This commit is contained in:
Brendan Szymanski 2026-07-20 21:59:10 -04:00
parent c20015be48
commit 525aefa7aa
9 changed files with 545 additions and 121 deletions

View file

@ -53,6 +53,8 @@ struct NamingTests {
#expect(isValidGeneratedFileName("DNDEvent.swift")) // acronym type name
#expect(!isValidGeneratedFileName("Align.txt")) // wrong extension
#expect(!isValidGeneratedFileName("MarshalBOOLEAN.swift")) // uppercase run, PascalCase start
#expect(isValidGeneratedFileName("PluginAPIFlags.swift")) // 4-run acronym, Gst
#expect(isValidGeneratedFileName("AuthNTLM.swift")) // 4-run acronym, Soup
}
// MARK: - File merging

View file

@ -203,16 +203,22 @@ struct TypeRegistryTests {
@Test("A type dropped as a duplicate of a dependency is detected regardless of the referencing namespace")
func detectsCrossModuleDroppedType() {
// Dep declares Rect; Mid (which depends on Dep) redeclares the same
// simple name the planner drops Mid.Rect as a duplicate. A
// reference into Mid.Rect from any namespace, not just Mid's own,
// must be recognized as dangling.
// simple name *and the same underlying C struct* a genuine
// redundant forwarding declaration (e.g. a compat header
// re-exporting a dependency's type under its own namespace) the
// planner drops Mid.Rect as a duplicate. A reference into Mid.Rect
// from any namespace, not just Mid's own, must be recognized as
// dangling. A same-name but *different*-cType pair (two genuinely
// distinct C structs that merely share a post-namespace-stripping
// Swift name, e.g. `GstObject` vs `GObject`) is deliberately NOT
// a match here see `ambiguousCrossModuleNameKeepsBothTypes`.
let dep = Repository(namespaces: [
Namespace(name: "Dep", version: "1.0",
records: [Record(name: "Rect", cType: "DepRect", getTypeFunction: "dep_rect_get_type")])
records: [Record(name: "Rect", cType: "Rect", getTypeFunction: "dep_rect_get_type")])
])
let mid = Repository(namespaces: [
Namespace(name: "Mid", version: "1.0",
records: [Record(name: "Rect", cType: "MidRect", getTypeFunction: "mid_rect_get_type")])
records: [Record(name: "Rect", cType: "Rect", getTypeFunction: "mid_rect_get_type")])
])
let registry = TypeRegistry(
repositories: ["Dep": dep, "Mid": mid],
@ -233,6 +239,32 @@ struct TypeRegistryTests {
}
}
@Test("A same-name but different-cType type across a dependency is not a duplicate — both are kept and disambiguated")
func ambiguousCrossModuleNameKeepsBothTypes() throws {
// Mirrors `Gst.Object` (`GstObject`) vs `GObject.Object` (`GObject`):
// Base declares `Object`, and Sub (which depends on Base) also
// declares its own distinct `Object` with a different cType.
// this must NOT be treated as a duplicate: both are real, distinct
// C structs that merely share a post-namespace-stripping Swift
// name, so both must be generated and cross-references qualified.
let base = Repository(namespaces: [
Namespace(name: "Base", version: "1.0",
classes: [Class(name: "Object", cType: "BaseObject", parent: nil, getTypeFunction: "base_object_get_type")])
])
let sub = Repository(namespaces: [
Namespace(name: "Sub", version: "1.0",
classes: [Class(name: "Object", cType: "SubObject", parent: "Base.Object", getTypeFunction: "sub_object_get_type")])
])
let registry = TypeRegistry(
repositories: ["Base": base, "Sub": sub],
directDependencies: ["Sub": ["Base"]]
)
#expect(registry.droppedShadow("Sub.Object") == nil)
#expect(registry.droppedShadow("Base.Object") == nil)
let subObject = try #require(registry.resolve(girName: "Sub.Object"))
#expect(registry.swiftTypeName(for: subObject, in: "Down") == "Sub.Object")
}
@Test("Manually excluded namespaces are treated as foreign")
func manualNamespacesAreForeign() {
let repo = Repository(namespaces: [