ttsc 0.18.4 → 0.19.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (137) hide show
  1. package/README.md +5 -6
  2. package/cmd/graphbench/main.go +1 -1
  3. package/cmd/platform/main.go +1 -48
  4. package/cmd/ttsc/build_branches_test.go +5 -1
  5. package/cmd/ttsc/main.go +0 -50
  6. package/cmd/ttsc/main_alias_branches_test.go +6 -12
  7. package/cmd/ttscgraph/dump.go +13 -3
  8. package/cmd/ttscserver/main.go +5 -3
  9. package/driver/lsp.go +13 -1
  10. package/driver/plugins.go +35 -0
  11. package/driver/program.go +7 -8
  12. package/driver/rewrite.go +381 -21
  13. package/go.mod +1 -0
  14. package/internal/graph/build.go +297 -3
  15. package/internal/graph/build_records_a_node_per_top_level_declaration_test.go +16 -2
  16. package/internal/graph/diagnostics.go +5 -4
  17. package/internal/graph/dump.go +60 -4
  18. package/internal/graph/dump_is_a_function_of_the_source_test.go +71 -0
  19. package/internal/graph/dump_serializes_the_full_graph_test.go +16 -4
  20. package/internal/graph/edges.go +122 -11
  21. package/internal/graph/exports.go +74 -17
  22. package/internal/graph/graph.go +33 -1
  23. package/internal/graph/node_identity_is_position_invariant_test.go +4 -2
  24. package/internal/graph/resolve.go +24 -0
  25. package/internal/graphsymbols/provider.go +50 -14
  26. package/internal/lspserver/lsp_native_plugin_source.go +77 -23
  27. package/internal/lspserver/lsp_plugin.go +19 -2
  28. package/internal/lspserver/lsp_proxy.go +244 -52
  29. package/internal/lspserver/lsp_server.go +35 -27
  30. package/internal/lspserver/lsp_symbols.go +25 -16
  31. package/lib/TtscCompiler.d.ts +10 -0
  32. package/lib/TtscCompiler.js +24 -6
  33. package/lib/TtscCompiler.js.map +1 -1
  34. package/lib/TtscService.js +9 -7
  35. package/lib/TtscService.js.map +1 -1
  36. package/lib/compiler/internal/compileProjectInMemory.js +1 -0
  37. package/lib/compiler/internal/compileProjectInMemory.js.map +1 -1
  38. package/lib/compiler/internal/project/createNativeProjectContextArgs.d.ts +3 -0
  39. package/lib/compiler/internal/project/createNativeProjectContextArgs.js +14 -0
  40. package/lib/compiler/internal/project/createNativeProjectContextArgs.js.map +1 -0
  41. package/lib/compiler/internal/project/readProjectConfig.js +53 -4
  42. package/lib/compiler/internal/project/readProjectConfig.js.map +1 -1
  43. package/lib/compiler/internal/project/resolveProjectConfig.d.ts +9 -2
  44. package/lib/compiler/internal/project/resolveProjectConfig.js +45 -16
  45. package/lib/compiler/internal/project/resolveProjectConfig.js.map +1 -1
  46. package/lib/compiler/internal/residentTransformProcess.d.ts +16 -5
  47. package/lib/compiler/internal/residentTransformProcess.js +68 -20
  48. package/lib/compiler/internal/residentTransformProcess.js.map +1 -1
  49. package/lib/compiler/internal/runBuild.d.ts +11 -0
  50. package/lib/compiler/internal/runBuild.js +44 -8
  51. package/lib/compiler/internal/runBuild.js.map +1 -1
  52. package/lib/compiler/internal/runSingleFileEmit.js +7 -4
  53. package/lib/compiler/internal/runSingleFileEmit.js.map +1 -1
  54. package/lib/compiler/internal/sharedHostHelpers.d.ts +16 -0
  55. package/lib/compiler/internal/sharedHostHelpers.js +23 -0
  56. package/lib/compiler/internal/sharedHostHelpers.js.map +1 -1
  57. package/lib/compiler/internal/startResidentTransform.js +18 -1
  58. package/lib/compiler/internal/startResidentTransform.js.map +1 -1
  59. package/lib/compiler/internal/transformProjectInMemory.js +34 -8
  60. package/lib/compiler/internal/transformProjectInMemory.js.map +1 -1
  61. package/lib/flags/parser.d.ts +17 -0
  62. package/lib/flags/parser.js +11 -1
  63. package/lib/flags/parser.js.map +1 -1
  64. package/lib/flags/schema.js +7 -0
  65. package/lib/flags/schema.js.map +1 -1
  66. package/lib/launcher/internal/prepareExecution.js +21 -2
  67. package/lib/launcher/internal/prepareExecution.js.map +1 -1
  68. package/lib/launcher/internal/runTtsc.js +13 -53
  69. package/lib/launcher/internal/runTtsc.js.map +1 -1
  70. package/lib/launcher/internal/runTtscserver.js +11 -0
  71. package/lib/launcher/internal/runTtscserver.js.map +1 -1
  72. package/lib/launcher/internal/runTtsx.js +27 -11
  73. package/lib/launcher/internal/runTtsx.js.map +1 -1
  74. package/lib/launcher/internal/runtimeHooks.d.ts +81 -0
  75. package/lib/launcher/internal/runtimeHooks.js +632 -43
  76. package/lib/launcher/internal/runtimeHooks.js.map +1 -1
  77. package/lib/launcher/internal/servedSourceMap.d.ts +25 -0
  78. package/lib/launcher/internal/servedSourceMap.js +164 -0
  79. package/lib/launcher/internal/servedSourceMap.js.map +1 -0
  80. package/lib/plugin/internal/buildSourcePlugin.d.ts +146 -2
  81. package/lib/plugin/internal/buildSourcePlugin.js +651 -113
  82. package/lib/plugin/internal/buildSourcePlugin.js.map +1 -1
  83. package/lib/plugin/internal/loadProjectPlugins.d.ts +7 -0
  84. package/lib/plugin/internal/loadProjectPlugins.js +33 -10
  85. package/lib/plugin/internal/loadProjectPlugins.js.map +1 -1
  86. package/lib/structures/ITtscCompilerContext.d.ts +16 -2
  87. package/lib/structures/ITtscPluginCapabilities.d.ts +8 -0
  88. package/lib/structures/ITtscPluginFactoryContext.d.ts +14 -0
  89. package/lib/structures/internal/ITtscParsedProjectConfig.d.ts +3 -0
  90. package/lib/structures/internal/ITtscProjectIdentity.d.ts +17 -0
  91. package/lib/structures/internal/ITtscProjectIdentity.js +3 -0
  92. package/lib/structures/internal/ITtscProjectIdentity.js.map +1 -0
  93. package/lib/structures/internal/TtscCommonOptions.d.ts +8 -0
  94. package/package.json +11 -8
  95. package/shim/ast/lint.go +10 -0
  96. package/shim/ast/shim.go +44 -0
  97. package/shim/checker/enums_gen.go +19 -0
  98. package/shim/checker/shim.go +130 -0
  99. package/shim/diagnosticwriter/lint.go +30 -3
  100. package/shim/scanner/regexp.go +27 -0
  101. package/src/TtscCompiler.ts +25 -6
  102. package/src/TtscService.ts +15 -7
  103. package/src/compiler/internal/compileProjectInMemory.ts +1 -0
  104. package/src/compiler/internal/project/createNativeProjectContextArgs.ts +15 -0
  105. package/src/compiler/internal/project/readProjectConfig.ts +61 -5
  106. package/src/compiler/internal/project/resolveProjectConfig.ts +51 -21
  107. package/src/compiler/internal/residentTransformProcess.ts +95 -21
  108. package/src/compiler/internal/runBuild.ts +76 -9
  109. package/src/compiler/internal/runSingleFileEmit.ts +7 -4
  110. package/src/compiler/internal/sharedHostHelpers.ts +24 -0
  111. package/src/compiler/internal/startResidentTransform.ts +21 -1
  112. package/src/compiler/internal/transformProjectInMemory.ts +49 -6
  113. package/src/flags/parser.ts +28 -1
  114. package/src/flags/schema.ts +8 -0
  115. package/src/launcher/internal/prepareExecution.ts +22 -2
  116. package/src/launcher/internal/runTtsc.ts +13 -61
  117. package/src/launcher/internal/runTtscserver.ts +14 -0
  118. package/src/launcher/internal/runTtsx.ts +28 -13
  119. package/src/launcher/internal/runtimeHooks.ts +771 -53
  120. package/src/launcher/internal/servedSourceMap.ts +183 -0
  121. package/src/plugin/internal/buildSourcePlugin.ts +859 -122
  122. package/src/plugin/internal/loadProjectPlugins.ts +36 -6
  123. package/src/structures/ITtscCompilerContext.ts +17 -2
  124. package/src/structures/ITtscPluginCapabilities.ts +9 -0
  125. package/src/structures/ITtscPluginFactoryContext.ts +15 -0
  126. package/src/structures/internal/ITtscParsedProjectConfig.ts +3 -0
  127. package/src/structures/internal/ITtscProjectIdentity.ts +17 -0
  128. package/src/structures/internal/TtscCommonOptions.ts +8 -0
  129. package/tools/shim_audit/baseline.json +16 -7
  130. package/tools/shim_audit/main.go +777 -71
  131. package/tools/shim_audit/main_test.go +28 -1
  132. package/tools/shim_audit/producer_exemptions_require_rationale_test.go +42 -0
  133. package/tools/shim_audit/producer_surface_includes_exposed_methods_test.go +154 -0
  134. package/tools/shim_audit/producer_surface_rejects_unproducible_reference_test.go +142 -0
  135. package/utility/host.go +46 -20
  136. package/utility/serve.go +1 -1
  137. package/shim/checker/signature_api_completeness.go +0 -58
@@ -5,6 +5,8 @@ import (
5
5
  "go/types"
6
6
  "reflect"
7
7
  "testing"
8
+
9
+ "golang.org/x/tools/go/packages"
8
10
  )
9
11
 
10
12
  // TestLinknameRe pins the two //go:linkname target shapes the reachability scan
@@ -65,7 +67,7 @@ func TestDedupe(t *testing.T) {
65
67
 
66
68
  // TestTierOf pins the kind→tier mapping the gate and report depend on.
67
69
  func TestTierOf(t *testing.T) {
68
- cases := map[string]int{"ENUM": 1, "FUNC": 2, "ESCAPE": 3, "ENUM?": 4, "UNEXPORTED": 4}
70
+ cases := map[string]int{"ENUM": 1, "FUNC": 2, "PRODUCER": 2, "ESCAPE": 3, "ENUM?": 4, "UNEXPORTED": 4}
69
71
  for kind, want := range cases {
70
72
  if got := tierOf(kind); got != want {
71
73
  t.Errorf("tierOf(%q) = %d, want %d", kind, got, want)
@@ -73,6 +75,31 @@ func TestTierOf(t *testing.T) {
73
75
  }
74
76
  }
75
77
 
78
+ // TestIndexInternalPackages proves dependency-only internal type packages stay
79
+ // available to the named producer-contract scanner and load failures stay fatal.
80
+ func TestIndexInternalPackages(t *testing.T) {
81
+ dependencyTypes := types.NewPackage(internalPrefix+"lsp/lsproto", "lsproto")
82
+ dependency := &packages.Package{
83
+ PkgPath: internalPrefix + "lsp/lsproto",
84
+ Types: dependencyTypes,
85
+ }
86
+ root := &packages.Package{
87
+ PkgPath: internalPrefix + "lsp",
88
+ Types: types.NewPackage(internalPrefix+"lsp", "lsp"),
89
+ Imports: map[string]*packages.Package{dependency.PkgPath: dependency},
90
+ }
91
+ indexed, errored := indexInternalPackages([]*packages.Package{root})
92
+ if indexed["lsp"] != root || indexed["lsp/lsproto"] != dependency || len(errored) != 0 {
93
+ t.Fatalf("indexed=%+v errored=%+v, want root and dependency", indexed, errored)
94
+ }
95
+
96
+ dependency.Types = nil
97
+ indexed, errored = indexInternalPackages([]*packages.Package{root})
98
+ if indexed["lsp/lsproto"] != nil || errored["lsp/lsproto"] == "" {
99
+ t.Fatalf("failed dependency indexed=%+v errored=%+v, want closed failure", indexed, errored)
100
+ }
101
+ }
102
+
76
103
  // TestCommonPrefix covers the helper feeding the abbreviation fallback.
77
104
  func TestCommonPrefix(t *testing.T) {
78
105
  cases := []struct {
@@ -0,0 +1,42 @@
1
+ package main
2
+
3
+ import "testing"
4
+
5
+ // Verifies producer exemptions require a non-empty rationale.
6
+ //
7
+ // Producer gaps use a separate zero-tolerance path so an ordinary accepted gap
8
+ // or an empty placeholder cannot silently classify compiler state as a root.
9
+ //
10
+ // 1. Evaluate the same producer gap with no exemption and an empty exemption.
11
+ // 2. Supply a reasoned public-root exemption and accept the unrelated function gap.
12
+ // 3. Confirm only the deliberate classification passes both gate classes.
13
+ func TestProducerExemptionsRequireRationale(t *testing.T) {
14
+ surface := newProducerSurface()
15
+ surface.add(flowConsume, flowType{pkg: "fixture", name: "Root"}, "fixture.UseRoot")
16
+ functionGap := finding{kind: "FUNC", pkg: "fixture", symbol: "Reachable"}
17
+
18
+ absentProducer := evaluateProducerSurface(surface, nil)
19
+ absent := evaluateBaseline(append(absentProducer.gaps, functionGap), baselineFile{}, absentProducer.usedRoots)
20
+ if len(absent.producerGaps) != 1 || len(absent.newGaps) != 1 {
21
+ t.Fatalf("absent exemption evaluation = %+v", absent)
22
+ }
23
+
24
+ emptyBaseline := baselineFile{
25
+ ProducerExemptions: map[string]string{"fixture.Root": " "},
26
+ }
27
+ emptyProducer := evaluateProducerSurface(surface, emptyBaseline.ProducerExemptions)
28
+ empty := evaluateBaseline(append(emptyProducer.gaps, functionGap), emptyBaseline, emptyProducer.usedRoots)
29
+ if len(empty.producerGaps) != 1 || len(empty.invalidReasons) != 1 {
30
+ t.Fatalf("empty exemption evaluation = %+v", empty)
31
+ }
32
+
33
+ reasonedBaseline := baselineFile{
34
+ Accepted: []string{"FUNC|fixture|Reachable"},
35
+ ProducerExemptions: map[string]string{"fixture.Root": "Caller-owned configuration object."},
36
+ }
37
+ reasonedProducer := evaluateProducerSurface(surface, reasonedBaseline.ProducerExemptions)
38
+ reasoned := evaluateBaseline(append(reasonedProducer.gaps, functionGap), reasonedBaseline, reasonedProducer.usedRoots)
39
+ if len(reasoned.producerGaps) != 0 || len(reasoned.newGaps) != 0 || len(reasoned.invalidReasons) != 0 {
40
+ t.Fatalf("reasoned exemption evaluation = %+v", reasoned)
41
+ }
42
+ }
@@ -0,0 +1,154 @@
1
+ package main
2
+
3
+ import (
4
+ "go/token"
5
+ "go/types"
6
+ "testing"
7
+
8
+ "golang.org/x/tools/go/packages"
9
+ )
10
+
11
+ // Verifies producer closure includes exported methods and canonical type aliases.
12
+ //
13
+ // Shim aliases publish upstream method sets without source declarations, while
14
+ // aliases such as Expression and Node can spell the same compiler object twice.
15
+ //
16
+ // 1. Build a rooted exposed owner with consumer and producer methods.
17
+ // 2. Remove the producer and confirm direct and named-callback inputs become a gap.
18
+ // 3. Confirm an alias and canonical producer close the same object edge.
19
+ // 4. Remove the receiver root and confirm its result no longer counts.
20
+ // 5. Restore it explicitly, then prove a rootless self-cycle still fails.
21
+ // 6. Prove reachable receivers do not legitimize argument/result cycles.
22
+ func TestProducerSurfaceIncludesExposedMethods(t *testing.T) {
23
+ build := func(includeProducer bool) (reachable, map[string]*packages.Package) {
24
+ pkg := types.NewPackage(internalPrefix+"fixture", "fixture")
25
+ tokenName := types.NewTypeName(token.NoPos, pkg, "Token", nil)
26
+ tokenType := types.NewNamed(tokenName, types.NewStruct(nil, nil), nil)
27
+ pkg.Scope().Insert(tokenName)
28
+ ownerName := types.NewTypeName(token.NoPos, pkg, "Owner", nil)
29
+ ownerType := types.NewNamed(ownerName, types.NewStruct(nil, nil), nil)
30
+ pkg.Scope().Insert(ownerName)
31
+ receiver := types.NewVar(token.NoPos, pkg, "owner", types.NewPointer(ownerType))
32
+ consume := types.NewSignatureType(
33
+ receiver,
34
+ nil,
35
+ nil,
36
+ types.NewTuple(types.NewVar(token.NoPos, pkg, "value", types.NewPointer(tokenType))),
37
+ types.NewTuple(),
38
+ false,
39
+ )
40
+ ownerType.AddMethod(types.NewFunc(token.NoPos, pkg, "Consume", consume))
41
+ factoryName := types.NewTypeName(token.NoPos, pkg, "TokenFactory", nil)
42
+ factorySignature := types.NewSignatureType(
43
+ nil,
44
+ nil,
45
+ nil,
46
+ types.NewTuple(),
47
+ types.NewTuple(types.NewVar(token.NoPos, pkg, "value", types.NewPointer(tokenType))),
48
+ false,
49
+ )
50
+ factoryType := types.NewNamed(factoryName, factorySignature, nil)
51
+ pkg.Scope().Insert(factoryName)
52
+ register := types.NewSignatureType(
53
+ receiver,
54
+ nil,
55
+ nil,
56
+ types.NewTuple(types.NewVar(token.NoPos, pkg, "factory", factoryType)),
57
+ types.NewTuple(),
58
+ false,
59
+ )
60
+ ownerType.AddMethod(types.NewFunc(token.NoPos, pkg, "Register", register))
61
+ if includeProducer {
62
+ produce := types.NewSignatureType(
63
+ receiver,
64
+ nil,
65
+ nil,
66
+ types.NewTuple(),
67
+ types.NewTuple(types.NewVar(token.NoPos, pkg, "value", types.NewPointer(tokenType))),
68
+ false,
69
+ )
70
+ ownerType.AddMethod(types.NewFunc(token.NoPos, pkg, "Produce", produce))
71
+ }
72
+ exposed := reachable{}
73
+ exposed.add("fixture", "Owner")
74
+ return exposed, map[string]*packages.Package{"fixture": {Types: pkg}}
75
+ }
76
+
77
+ completeReachable, completeInner := build(true)
78
+ complete := newProducerSurface()
79
+ complete.add(flowProduce, flowType{pkg: "fixture", name: "Owner"}, "fixture.NewOwner")
80
+ addExposedMethodFlow(completeReachable, completeInner, &complete)
81
+ if findings := evaluateProducerSurface(canonicalizeProducerSurface(complete, completeInner), nil).gaps; len(findings) != 0 {
82
+ t.Fatalf("complete method surface findings = %+v", findings)
83
+ }
84
+
85
+ missingReachable, missingInner := build(false)
86
+ missing := newProducerSurface()
87
+ missing.add(flowProduce, flowType{pkg: "fixture", name: "Owner"}, "fixture.NewOwner")
88
+ addExposedMethodFlow(missingReachable, missingInner, &missing)
89
+ canonicalMissing := canonicalizeProducerSurface(missing, missingInner)
90
+ findings := evaluateProducerSurface(canonicalMissing, nil).gaps
91
+ if len(findings) != 1 || findings[0].pkg != "fixture" || findings[0].symbol != "Token" {
92
+ t.Fatalf("missing method producer findings = %+v, want fixture.Token", findings)
93
+ }
94
+
95
+ aliasPkg := missingInner["fixture"].Types
96
+ aliasName := types.NewTypeName(token.NoPos, aliasPkg, "TokenAlias", nil)
97
+ types.NewAlias(aliasName, aliasPkg.Scope().Lookup("Token").Type())
98
+ aliasPkg.Scope().Insert(aliasName)
99
+ aliases := newProducerSurface()
100
+ aliases.add(flowConsume, flowType{pkg: "fixture", name: "TokenAlias"}, "fixture.ConsumeAlias")
101
+ aliases.add(flowProduce, flowType{pkg: "fixture", name: "Token"}, "fixture.ProduceToken")
102
+ if findings := evaluateProducerSurface(canonicalizeProducerSurface(aliases, missingInner), nil).gaps; len(findings) != 0 {
103
+ t.Fatalf("canonical alias surface findings = %+v", findings)
104
+ }
105
+
106
+ rootless := newProducerSurface()
107
+ token := flowType{pkg: "fixture", name: "Token"}
108
+ owner := flowType{pkg: "fixture", name: "Owner"}
109
+ rootless.add(flowConsume, token, "fixture.UseToken")
110
+ rootless.operations = append(rootless.operations, operationFlow{
111
+ receiver: &owner,
112
+ consumed: map[flowType]map[string]bool{},
113
+ produced: map[flowType]map[string]bool{token: {"fixture.Owner.Produce": true}},
114
+ })
115
+ if findings := evaluateProducerSurface(rootless, nil).gaps; len(findings) != 1 || findings[0].symbol != "Token" {
116
+ t.Fatalf("rootless receiver findings = %+v, want fixture.Token", findings)
117
+ }
118
+ rooted := evaluateProducerSurface(rootless, map[string]string{"fixture.Owner": "Caller-owned root."})
119
+ if len(rooted.gaps) != 0 || !rooted.usedRoots["fixture.Owner"] {
120
+ t.Fatalf("rooted receiver evaluation = %+v, want no gaps and used Owner root", rooted)
121
+ }
122
+
123
+ cycle := newProducerSurface()
124
+ cycle.add(flowConsume, owner, "fixture.UseOwner")
125
+ cycle.operations = append(cycle.operations, operationFlow{
126
+ receiver: &owner,
127
+ consumed: map[flowType]map[string]bool{},
128
+ produced: map[flowType]map[string]bool{owner: {"fixture.Owner.Clone": true}},
129
+ })
130
+ if findings := evaluateProducerSurface(cycle, nil).gaps; len(findings) != 1 || findings[0].symbol != "Owner" {
131
+ t.Fatalf("rootless self-cycle findings = %+v, want fixture.Owner", findings)
132
+ }
133
+
134
+ methodCycle := newProducerSurface()
135
+ methodCycle.add(flowProduce, owner, "fixture.NewOwner")
136
+ left := flowType{pkg: "fixture", name: "Left"}
137
+ right := flowType{pkg: "fixture", name: "Right"}
138
+ methodCycle.operations = append(methodCycle.operations,
139
+ operationFlow{
140
+ receiver: &owner,
141
+ consumed: map[flowType]map[string]bool{left: {"fixture.Owner.RightFromLeft": true}},
142
+ produced: map[flowType]map[string]bool{right: {"fixture.Owner.RightFromLeft": true}},
143
+ },
144
+ operationFlow{
145
+ receiver: &owner,
146
+ consumed: map[flowType]map[string]bool{right: {"fixture.Owner.LeftFromRight": true}},
147
+ produced: map[flowType]map[string]bool{left: {"fixture.Owner.LeftFromRight": true}},
148
+ },
149
+ )
150
+ methodFindings := evaluateProducerSurface(methodCycle, nil).gaps
151
+ if len(methodFindings) != 2 || methodFindings[0].symbol != "Left" || methodFindings[1].symbol != "Right" {
152
+ t.Fatalf("rootless method argument cycle findings = %+v, want Left and Right", methodFindings)
153
+ }
154
+ }
@@ -0,0 +1,142 @@
1
+ package main
2
+
3
+ import (
4
+ "go/token"
5
+ "go/types"
6
+ "strings"
7
+ "testing"
8
+
9
+ "golang.org/x/tools/go/packages"
10
+ )
11
+
12
+ // Verifies producer closure rejects a consumed reference after its only producer is removed.
13
+ //
14
+ // The regression is type-flow specific: callback inputs are producers, callback
15
+ // results are consumers, named contracts retain that variance, and non-pointer
16
+ // values must not enter the object graph.
17
+ //
18
+ // 1. Scan a generic surface containing returns, callbacks, and a source method.
19
+ // 2. Confirm only the intentionally input-only object is initially unreachable.
20
+ // 3. Remove a package producer and confirm its generic type-keyed gap appears.
21
+ // 4. Remove a method consumer's producer and confirm that gap appears too.
22
+ // 5. Repeat through recursive named callbacks and containers without hanging.
23
+ // 6. Prove package-operation cycles fail until an independent root is added.
24
+ func TestProducerSurfaceRejectsUnproducibleReference(t *testing.T) {
25
+ source := `package fixture
26
+ import inner "github.com/microsoft/typescript-go/internal/fixture"
27
+ func ConsumeToken(value *inner.Token) {}
28
+ func ProduceToken() []*inner.Token { return nil }
29
+ func Register(handler func(*inner.Event) *inner.Reply) {}
30
+ func ProduceReply() *inner.Reply { return nil }
31
+ func RegisterInterface(handler interface { Handle(*inner.Notice) *inner.Ack }) {}
32
+ func ProduceAck() *inner.Ack { return nil }
33
+ type Owner struct{}
34
+ func (*Owner) ConsumeMethod(value *inner.MethodToken) {}
35
+ func ProduceMethodToken() *inner.MethodToken { return nil }
36
+ func ConsumeOrphan(value *inner.Orphan) {}
37
+ func IgnorePointedContainer(value *[]inner.ContainerValue) {}
38
+ func IgnoreValue(value inner.Mode) {}
39
+ func hidden(value *inner.Hidden) {}
40
+ `
41
+ scanWithInner := func(input string, inner map[string]*packages.Package) []finding {
42
+ surface := newProducerSurface()
43
+ definitions, err := scanLocalFlowDefinitions([]byte(input), "fixture.go")
44
+ if err != nil {
45
+ t.Fatal(err)
46
+ }
47
+ if err := scanProducerFile([]byte(input), "fixture.go", "fixture", definitions, inner, &surface); err != nil {
48
+ t.Fatal(err)
49
+ }
50
+ return evaluateProducerSurface(surface, nil).gaps
51
+ }
52
+ scan := func(input string) []finding { return scanWithInner(input, nil) }
53
+
54
+ complete := scan(source)
55
+ if len(complete) != 1 || complete[0].symbol != "Orphan" {
56
+ t.Fatalf("complete surface findings = %+v, want only input-only Orphan", complete)
57
+ }
58
+
59
+ mutated := scan(strings.Replace(source, "func ProduceToken() []*inner.Token { return nil }\n", "", 1))
60
+ if len(mutated) != 2 || mutated[0].symbol != "Orphan" || mutated[1].symbol != "Token" {
61
+ t.Fatalf("mutated surface findings = %+v, want Orphan and Token", mutated)
62
+ }
63
+ for _, finding := range mutated {
64
+ if finding.kind != "PRODUCER" || strings.Contains(finding.detail, "Signature") {
65
+ t.Fatalf("finding is not generic producer evidence: %+v", finding)
66
+ }
67
+ }
68
+
69
+ mutatedMethod := scan(strings.Replace(source, "func ProduceMethodToken() *inner.MethodToken { return nil }\n", "", 1))
70
+ if len(mutatedMethod) != 2 || mutatedMethod[0].symbol != "MethodToken" || mutatedMethod[1].symbol != "Orphan" {
71
+ t.Fatalf("source-method mutation findings = %+v, want MethodToken and Orphan", mutatedMethod)
72
+ }
73
+
74
+ namedSource := `package fixture
75
+ import inner "github.com/microsoft/typescript-go/internal/fixture"
76
+ type Factory func() *inner.FactoryToken
77
+ type Batch []*inner.BatchToken
78
+ type PointerBatch []*inner.PointerBatchToken
79
+ type Recursive func(Recursive)
80
+ func RegisterFactory(factory Factory) {}
81
+ func ConsumeBatch(batch Batch) {}
82
+ func ConsumePointerBatch(batch *PointerBatch) {}
83
+ func RegisterRecursive(callback Recursive) {}
84
+ func ProduceFactoryToken() *inner.FactoryToken { return nil }
85
+ func ProduceBatchToken() *inner.BatchToken { return nil }
86
+ func ProducePointerBatchToken() *inner.PointerBatchToken { return nil }
87
+ `
88
+ if findings := scan(namedSource); len(findings) != 0 {
89
+ t.Fatalf("named callback/container findings = %+v, want none", findings)
90
+ }
91
+ namedMutated := strings.Replace(namedSource, "func ProduceFactoryToken() *inner.FactoryToken { return nil }\n", "", 1)
92
+ namedMutated = strings.Replace(namedMutated, "func ProduceBatchToken() *inner.BatchToken { return nil }\n", "", 1)
93
+ namedMutated = strings.Replace(namedMutated, "func ProducePointerBatchToken() *inner.PointerBatchToken { return nil }\n", "", 1)
94
+ namedFindings := scan(namedMutated)
95
+ if len(namedFindings) != 3 || namedFindings[0].symbol != "BatchToken" || namedFindings[1].symbol != "FactoryToken" || namedFindings[2].symbol != "PointerBatchToken" {
96
+ t.Fatalf("named callback/container mutation findings = %+v, want BatchToken, FactoryToken, and PointerBatchToken", namedFindings)
97
+ }
98
+
99
+ upstream := types.NewPackage(internalPrefix+"fixture", "fixture")
100
+ upstreamTokenName := types.NewTypeName(token.NoPos, upstream, "UpstreamToken", nil)
101
+ upstreamToken := types.NewNamed(upstreamTokenName, types.NewStruct(nil, nil), nil)
102
+ upstream.Scope().Insert(upstreamTokenName)
103
+ upstreamFactoryName := types.NewTypeName(token.NoPos, upstream, "UpstreamFactory", nil)
104
+ types.NewNamed(upstreamFactoryName, types.NewSignatureType(
105
+ nil,
106
+ nil,
107
+ nil,
108
+ types.NewTuple(),
109
+ types.NewTuple(types.NewVar(token.NoPos, upstream, "value", types.NewPointer(upstreamToken))),
110
+ false,
111
+ ), nil)
112
+ upstream.Scope().Insert(upstreamFactoryName)
113
+ upstreamInner := map[string]*packages.Package{"fixture": {Types: upstream}}
114
+ upstreamSource := `package fixture
115
+ import inner "github.com/microsoft/typescript-go/internal/fixture"
116
+ func RegisterUpstreamFactory(factory inner.UpstreamFactory) {}
117
+ func ProduceUpstreamToken() *inner.UpstreamToken { return nil }
118
+ `
119
+ if findings := scanWithInner(upstreamSource, upstreamInner); len(findings) != 0 {
120
+ t.Fatalf("upstream named callback findings = %+v, want none", findings)
121
+ }
122
+ upstreamMutated := strings.Replace(upstreamSource, "func ProduceUpstreamToken() *inner.UpstreamToken { return nil }\n", "", 1)
123
+ upstreamFindings := scanWithInner(upstreamMutated, upstreamInner)
124
+ if len(upstreamFindings) != 1 || upstreamFindings[0].symbol != "UpstreamToken" {
125
+ t.Fatalf("upstream named callback mutation findings = %+v, want UpstreamToken", upstreamFindings)
126
+ }
127
+
128
+ cycleSource := `package fixture
129
+ import inner "github.com/microsoft/typescript-go/internal/fixture"
130
+ func AFromB(value *inner.B) *inner.A { return nil }
131
+ func BFromA(value *inner.A) *inner.B { return nil }
132
+ func UseA(value *inner.A) {}
133
+ `
134
+ cycleFindings := scan(cycleSource)
135
+ if len(cycleFindings) != 2 || cycleFindings[0].symbol != "A" || cycleFindings[1].symbol != "B" {
136
+ t.Fatalf("rootless operation cycle findings = %+v, want A and B", cycleFindings)
137
+ }
138
+ rootedChain := strings.Replace(cycleSource, "func UseA(value *inner.A) {}\n", "func NewA() *inner.A { return nil }\nfunc UseA(value *inner.A) {}\n", 1)
139
+ if findings := scan(rootedChain); len(findings) != 0 {
140
+ t.Fatalf("rooted operation chain findings = %+v, want none", findings)
141
+ }
142
+ }
package/utility/host.go CHANGED
@@ -4,6 +4,7 @@ import (
4
4
  "encoding/json"
5
5
  "flag"
6
6
  "fmt"
7
+ "io"
7
8
  "os"
8
9
  "path/filepath"
9
10
  "strings"
@@ -29,6 +30,8 @@ type hostOptions struct {
29
30
  singleThreaded bool
30
31
  checkers int
31
32
  tsgoArgs []string
33
+ stdout io.Writer
34
+ stderr io.Writer
32
35
  // fs overrides the filesystem the program loads from. Only the resident serve
33
36
  // host sets it (to an OverlayFS), so build/check/transform leave it nil and
34
37
  // LoadProgram falls back to the default filesystem.
@@ -46,7 +49,12 @@ type transformResult struct {
46
49
  // RunCheck validates the project and linked plugin configuration without
47
50
  // emitting output.
48
51
  func RunCheck(args []string) int {
49
- opts, ok := parseHostOptions("check", args)
52
+ return RunCheckWithIO(args, os.Stdout, os.Stderr)
53
+ }
54
+
55
+ // RunCheckWithIO runs check with invocation-owned output streams.
56
+ func RunCheckWithIO(args []string, stdout, stderr io.Writer) int {
57
+ opts, ok := parseHostOptions("check", args, stdout, stderr)
50
58
  if !ok {
51
59
  return 2
52
60
  }
@@ -57,7 +65,7 @@ func RunCheck(args []string) int {
57
65
  }
58
66
  defer prog.Close()
59
67
  if err := prog.ApplyLinkedPlugins(); err != nil {
60
- fmt.Fprintln(os.Stderr, err)
68
+ fmt.Fprintln(opts.stderr, err)
61
69
  return 2
62
70
  }
63
71
  return 0
@@ -65,7 +73,12 @@ func RunCheck(args []string) int {
65
73
 
66
74
  // RunBuild hosts linked transform packages inside one compiler emit.
67
75
  func RunBuild(args []string) int {
68
- opts, ok := parseHostOptions("build", args)
76
+ return RunBuildWithIO(args, os.Stdout, os.Stderr)
77
+ }
78
+
79
+ // RunBuildWithIO runs build with invocation-owned output streams.
80
+ func RunBuildWithIO(args []string, stdout, stderr io.Writer) int {
81
+ opts, ok := parseHostOptions("build", args, stdout, stderr)
69
82
  if !ok {
70
83
  return 2
71
84
  }
@@ -81,21 +94,21 @@ func RunBuild(args []string) int {
81
94
  opts.quiet = false // --verbose overrides the default --quiet=true
82
95
  }
83
96
  if !opts.quiet {
84
- fmt.Fprintf(os.Stdout, "// ttsc utility: plugins=%d emit=%v\n", len(entries), !opts.noEmit)
97
+ fmt.Fprintf(opts.stdout, "// ttsc utility: plugins=%d emit=%v\n", len(entries), !opts.noEmit)
85
98
  }
86
99
  res, eDiags, err := prog.EmitAllRaw(makeSourcePreambleWriteFile(prog))
87
100
  if err != nil {
88
- fmt.Fprintf(os.Stderr, "ttsc utility: emit failed: %v\n", err)
101
+ fmt.Fprintf(opts.stderr, "ttsc utility: emit failed: %v\n", err)
89
102
  return 3
90
103
  }
91
104
  for _, d := range eDiags {
92
- fmt.Fprintln(os.Stderr, " -", d.String())
105
+ fmt.Fprintln(opts.stderr, " -", d.String())
93
106
  }
94
107
  if driver.CountErrors(eDiags) > 0 {
95
108
  return 2
96
109
  }
97
110
  if res != nil && !opts.quiet {
98
- fmt.Fprintf(os.Stdout, "// ttsc utility: emitted=%d files\n", len(res.EmittedFiles))
111
+ fmt.Fprintf(opts.stdout, "// ttsc utility: emitted=%d files\n", len(res.EmittedFiles))
99
112
  }
100
113
  return 0
101
114
  }
@@ -103,7 +116,12 @@ func RunBuild(args []string) int {
103
116
  // RunTransform returns the project TypeScript text after linked source
104
117
  // mutations.
105
118
  func RunTransform(args []string) int {
106
- opts, ok := parseHostOptions("transform", args)
119
+ return RunTransformWithIO(args, os.Stdout, os.Stderr)
120
+ }
121
+
122
+ // RunTransformWithIO runs transform with invocation-owned output streams.
123
+ func RunTransformWithIO(args []string, stdout, stderr io.Writer) int {
124
+ opts, ok := parseHostOptions("transform", args, stdout, stderr)
107
125
  if !ok {
108
126
  return 2
109
127
  }
@@ -113,7 +131,7 @@ func RunTransform(args []string) int {
113
131
  }
114
132
  defer prog.Close()
115
133
  if err := prog.ApplyLinkedPlugins(); err != nil {
116
- fmt.Fprintln(os.Stderr, err)
134
+ fmt.Fprintln(opts.stderr, err)
117
135
  return 2
118
136
  }
119
137
  printer := shimprinter.NewPrinter(shimprinter.PrinterOptions{}, shimprinter.PrintHandlers{}, nil)
@@ -123,7 +141,7 @@ func RunTransform(args []string) int {
123
141
  out.TypeScript[apiOutputKey(opts.cwd, file.FileName())] = text
124
142
  }
125
143
  data, _ := json.Marshal(out)
126
- fmt.Fprintln(os.Stdout, string(data))
144
+ fmt.Fprintln(opts.stdout, string(data))
127
145
  return 0
128
146
  }
129
147
 
@@ -131,9 +149,15 @@ func RunTransform(args []string) int {
131
149
  // Unknown flags forwarded from the JS launcher are stripped by filterHostArgs
132
150
  // before flag.FlagSet sees them, so spurious "flag provided but not defined"
133
151
  // errors are avoided. Returns (zero, false) on any parse or validation error.
134
- func parseHostOptions(command string, args []string) (hostOptions, bool) {
152
+ func parseHostOptions(command string, args []string, stdout, stderr io.Writer) (hostOptions, bool) {
153
+ if stdout == nil {
154
+ stdout = io.Discard
155
+ }
156
+ if stderr == nil {
157
+ stderr = io.Discard
158
+ }
135
159
  fs := flag.NewFlagSet(command, flag.ContinueOnError)
136
- fs.SetOutput(os.Stderr)
160
+ fs.SetOutput(stderr)
137
161
  cwd := fs.String("cwd", "", "project directory")
138
162
  emit := fs.Bool("emit", false, "force emit")
139
163
  noEmit := fs.Bool("noEmit", false, "force no emit")
@@ -151,12 +175,12 @@ func parseHostOptions(command string, args []string) (hostOptions, bool) {
151
175
  var tsgoArgs []string
152
176
  if *tsgoArgsRaw != "" {
153
177
  if err := json.Unmarshal([]byte(*tsgoArgsRaw), &tsgoArgs); err != nil {
154
- fmt.Fprintf(os.Stderr, "ttsc utility: invalid --tsgo-args: %v\n", err)
178
+ fmt.Fprintf(stderr, "ttsc utility: invalid --tsgo-args: %v\n", err)
155
179
  return hostOptions{}, false
156
180
  }
157
181
  }
158
182
  if *emit && *noEmit {
159
- fmt.Fprintln(os.Stderr, "ttsc utility: --emit and --noEmit are mutually exclusive")
183
+ fmt.Fprintln(stderr, "ttsc utility: --emit and --noEmit are mutually exclusive")
160
184
  return hostOptions{}, false
161
185
  }
162
186
  resolvedCwd := *cwd
@@ -164,14 +188,14 @@ func parseHostOptions(command string, args []string) (hostOptions, bool) {
164
188
  var err error
165
189
  resolvedCwd, err = os.Getwd()
166
190
  if err != nil {
167
- fmt.Fprintf(os.Stderr, "ttsc utility: cwd: %v\n", err)
191
+ fmt.Fprintf(stderr, "ttsc utility: cwd: %v\n", err)
168
192
  return hostOptions{}, false
169
193
  }
170
194
  }
171
195
  if !filepath.IsAbs(resolvedCwd) {
172
196
  abs, err := filepath.Abs(resolvedCwd)
173
197
  if err != nil {
174
- fmt.Fprintf(os.Stderr, "ttsc utility: cwd: %v\n", err)
198
+ fmt.Fprintf(stderr, "ttsc utility: cwd: %v\n", err)
175
199
  return hostOptions{}, false
176
200
  }
177
201
  resolvedCwd = abs
@@ -188,6 +212,8 @@ func parseHostOptions(command string, args []string) (hostOptions, bool) {
188
212
  singleThreaded: *singleThreaded,
189
213
  checkers: *checkers,
190
214
  tsgoArgs: tsgoArgs,
215
+ stdout: stdout,
216
+ stderr: stderr,
191
217
  }, true
192
218
  }
193
219
 
@@ -243,7 +269,7 @@ func flagName(arg string) (string, bool) {
243
269
  func loadUtilityProgram(opts hostOptions) (*driver.Program, []driver.PluginEntry, bool) {
244
270
  entries, err := parsePluginEntries(opts.pluginsJSON)
245
271
  if err != nil {
246
- fmt.Fprintln(os.Stderr, err)
272
+ fmt.Fprintln(opts.stderr, err)
247
273
  return nil, nil, false
248
274
  }
249
275
  restoreEnv := setLinkedPluginManifest(opts.pluginsJSON)
@@ -259,15 +285,15 @@ func loadUtilityProgram(opts hostOptions) (*driver.Program, []driver.PluginEntry
259
285
  FS: opts.fs,
260
286
  })
261
287
  if err != nil {
262
- fmt.Fprintf(os.Stderr, "ttsc utility: %v\n", err)
288
+ fmt.Fprintf(opts.stderr, "ttsc utility: %v\n", err)
263
289
  return nil, nil, false
264
290
  }
265
291
  if len(diags) > 0 {
266
- driver.WritePrettyDiagnostics(os.Stderr, diags, opts.cwd)
292
+ driver.WritePrettyDiagnostics(opts.stderr, diags, opts.cwd)
267
293
  return nil, nil, false
268
294
  }
269
295
  if diags := prog.Diagnostics(); len(diags) > 0 {
270
- driver.WritePrettyDiagnostics(os.Stderr, diags, opts.cwd)
296
+ driver.WritePrettyDiagnostics(opts.stderr, diags, opts.cwd)
271
297
  _ = prog.Close()
272
298
  return nil, nil, false
273
299
  }
package/utility/serve.go CHANGED
@@ -56,7 +56,7 @@ type serveUpdateResponse struct {
56
56
  // in and out are explicit so the request loop is testable; the utility-host
57
57
  // command wires them to os.Stdin and os.Stdout.
58
58
  func RunServe(in io.Reader, out io.Writer, args []string) int {
59
- opts, ok := parseHostOptions("serve", args)
59
+ opts, ok := parseHostOptions("serve", args, out, os.Stderr)
60
60
  if !ok {
61
61
  return 2
62
62
  }
@@ -1,58 +0,0 @@
1
- package checker
2
-
3
- // Compile-time guard: the signature-introspection surface that typia's
4
- // plain.classify from/new construction-strategy detection depends on must
5
- // compose end-to-end through the shim.
6
- //
7
- // 0.15.5 exposed a CONSUMER of a *Signature (Checker_getMinArgumentCount)
8
- // without the PRODUCER that yields one (Checker_getSignaturesOfType): a
9
- // *Signature was nameable (the `Signature` alias) but unobtainable. The closure
10
- // auditor checks that every escaping type can be NAMED, not that it can be
11
- // PRODUCED, so it did not catch the gap and the feature stayed blocked.
12
- //
13
- // This lives in a NORMAL (non-_test) file on purpose. The shim/checker package
14
- // is a nested Go module that no CI job runs `go test` against, so a _test.go
15
- // guard would never be compiled and would silently stop guarding. As ordinary
16
- // package code it is type-checked by every build that links the shim (typia's
17
- // native plugin, ttsc's lint engine), so dropping or renaming any leg below
18
- // turns those builds red. It never runs (assigned to blank, never called).
19
- var _ = func(c *Checker, instanceType *Type) {
20
- // class instance type -> class symbol -> constructor (static) type, which
21
- // carries both the construct signatures and the static `from` member.
22
- classSymbol := Type_getTypeNameSymbol(instanceType)
23
- ctorType := Checker_getTypeOfSymbol(c, classSymbol)
24
-
25
- // `new C(seed)` strategy: construct signatures -> arity (min args +
26
- // parameter count, together disambiguating `()` from `(x?)`) -> return type
27
- // -> the seed parameter's type. The seed is Signature_parameters[0]'s type,
28
- // EXCEPT a rest-ONLY parameter `(...xs: S[])` — when
29
- // `hasRestParameter && parameterCount == 1` — whose seed is the rest ELEMENT
30
- // (getRestTypeOfSignature). A leading-required + rest-tail `(s, ...r)` still
31
- // has hasRestParameter true but its seed is the first parameter S.
32
- for _, sig := range Checker_getSignaturesOfType(c, ctorType, SignatureKindConstruct) {
33
- _ = Checker_getMinArgumentCount(c, sig)
34
- _ = Signature_parameterCount(sig)
35
- _ = Checker_getReturnTypeOfSignature(c, sig)
36
- if Signature_hasRestParameter(sig) {
37
- _ = Checker_getRestTypeOfSignature(c, sig)
38
- }
39
- for _, p := range Signature_parameters(sig) {
40
- _ = Checker_getTypeOfSymbol(c, p)
41
- }
42
- }
43
-
44
- // `C.from(seed)` strategy: static `from` member -> its call signatures, read
45
- // identically (arity, return type, seed parameter / rest element type).
46
- fromType := Checker_getTypeOfPropertyOfType(c, ctorType, "from")
47
- for _, sig := range Checker_getSignaturesOfType(c, fromType, SignatureKindCall) {
48
- _ = Checker_getMinArgumentCount(c, sig)
49
- _ = Signature_parameterCount(sig)
50
- _ = Checker_getReturnTypeOfSignature(c, sig)
51
- if Signature_hasRestParameter(sig) {
52
- _ = Checker_getRestTypeOfSignature(c, sig)
53
- }
54
- for _, p := range Signature_parameters(sig) {
55
- _ = Checker_getTypeOfSymbol(c, p)
56
- }
57
- }
58
- }