ttsc 0.15.4 → 0.15.6

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.
@@ -0,0 +1,181 @@
1
+ package main
2
+
3
+ import (
4
+ "go/token"
5
+ "go/types"
6
+ "reflect"
7
+ "testing"
8
+ )
9
+
10
+ // TestLinknameRe pins the two //go:linkname target shapes the reachability scan
11
+ // must recognize — a package-level symbol and a (pointer-)receiver method — plus
12
+ // slashed (nested) package suffixes, and rejects non-typescript-go targets.
13
+ func TestLinknameRe(t *testing.T) {
14
+ cases := []struct {
15
+ line string
16
+ wantPkg string
17
+ wantName string
18
+ wantHit bool
19
+ }{
20
+ {"//go:linkname checkerGetMinArgumentCount github.com/microsoft/typescript-go/internal/checker.(*Checker).getMinArgumentCount", "checker", "getMinArgumentCount", true},
21
+ {"//go:linkname x github.com/microsoft/typescript-go/internal/checker.(Checker).foo", "checker", "foo", true},
22
+ {"//go:linkname GetSourceFileOfNode github.com/microsoft/typescript-go/internal/ast.getSourceFileOfNode", "ast", "getSourceFileOfNode", true},
23
+ {"//go:linkname From github.com/microsoft/typescript-go/internal/vfs/cachedvfs.From", "vfs/cachedvfs", "From", true},
24
+ {"//go:linkname FS github.com/microsoft/typescript-go/internal/vfs/osvfs.FS", "vfs/osvfs", "FS", true},
25
+ {"//go:linkname foo runtime.bar", "", "", false}, // not a tsgo internal target
26
+ {"// just a comment", "", "", false},
27
+ }
28
+ for _, c := range cases {
29
+ m := linknameRe.FindStringSubmatch(c.line)
30
+ if !c.wantHit {
31
+ if m != nil {
32
+ t.Errorf("expected no match for %q, got %v", c.line, m)
33
+ }
34
+ continue
35
+ }
36
+ if m == nil {
37
+ t.Errorf("expected match for %q, got none", c.line)
38
+ continue
39
+ }
40
+ if m[1] != c.wantPkg || m[2] != c.wantName {
41
+ t.Errorf("for %q: got pkg=%q name=%q, want pkg=%q name=%q", c.line, m[1], m[2], c.wantPkg, c.wantName)
42
+ }
43
+ }
44
+ }
45
+
46
+ // TestDedupe locks the determinism guarantee: one entry per kind|pkg|symbol,
47
+ // keeping the lexicographically smallest detail, ordered by pkg, kind, symbol.
48
+ func TestDedupe(t *testing.T) {
49
+ in := []finding{
50
+ {"FUNC", "checker", "B", "detail-z"},
51
+ {"FUNC", "checker", "B", "detail-a"}, // duplicate key, smaller detail wins
52
+ {"FUNC", "ast", "A", "d2"},
53
+ {"ENUM", "ast", "A", "d1"},
54
+ }
55
+ got := dedupe(in)
56
+ want := []finding{
57
+ {"ENUM", "ast", "A", "d1"},
58
+ {"FUNC", "ast", "A", "d2"},
59
+ {"FUNC", "checker", "B", "detail-a"},
60
+ }
61
+ if !reflect.DeepEqual(got, want) {
62
+ t.Errorf("dedupe mismatch:\n got %+v\nwant %+v", got, want)
63
+ }
64
+ }
65
+
66
+ // TestTierOf pins the kind→tier mapping the gate and report depend on.
67
+ func TestTierOf(t *testing.T) {
68
+ cases := map[string]int{"ENUM": 1, "FUNC": 2, "ESCAPE": 3, "ENUM?": 4, "UNEXPORTED": 4}
69
+ for kind, want := range cases {
70
+ if got := tierOf(kind); got != want {
71
+ t.Errorf("tierOf(%q) = %d, want %d", kind, got, want)
72
+ }
73
+ }
74
+ }
75
+
76
+ // TestCommonPrefix covers the helper feeding the abbreviation fallback.
77
+ func TestCommonPrefix(t *testing.T) {
78
+ cases := []struct {
79
+ in []string
80
+ want string
81
+ }{
82
+ {[]string{"OEKParentheses", "OEKTypeAssertions", "OEKSatisfies"}, "OEK"},
83
+ {[]string{"TypeFlagsAny", "TypeFlagsObject"}, "TypeFlags"},
84
+ {[]string{"Foo"}, "Foo"},
85
+ {[]string{"abc", "xyz"}, ""},
86
+ {nil, ""},
87
+ }
88
+ for _, c := range cases {
89
+ if got := commonPrefix(c.in); got != c.want {
90
+ t.Errorf("commonPrefix(%v) = %q, want %q", c.in, got, c.want)
91
+ }
92
+ }
93
+ }
94
+
95
+ // TestAttachUntypedConsts is the crux: it pins both the type-name attribution
96
+ // (longest wins) and the abbreviation fallback that closes the untyped+
97
+ // unprefixed blind spot (OuterExpressionKinds' OEKExcludeJSDocTypeAssertion),
98
+ // plus the negative twins that must NOT over-attach.
99
+ func TestAttachUntypedConsts(t *testing.T) {
100
+ t.Run("abbreviation fallback (OEK blind spot)", func(t *testing.T) {
101
+ enums := []string{"OuterExpressionKinds", "Kind"}
102
+ typed := map[string][]string{
103
+ "OuterExpressionKinds": {"OEKParentheses", "OEKTypeAssertions", "OEKNonNullAssertions", "OEKSatisfies"},
104
+ "Kind": {"KindCallExpression", "KindIdentifier"},
105
+ }
106
+ untyped := []string{"OEKExcludeJSDocTypeAssertion", "KindFoo", "Unrelated"}
107
+ got := attachUntypedConsts(enums, typed, untyped)
108
+ want := map[string][]string{
109
+ "OuterExpressionKinds": {"OEKExcludeJSDocTypeAssertion"},
110
+ "Kind": {"KindFoo"},
111
+ }
112
+ if !reflect.DeepEqual(got, want) {
113
+ t.Errorf("got %v, want %v", got, want)
114
+ }
115
+ })
116
+
117
+ t.Run("longest type-name prefix wins", func(t *testing.T) {
118
+ enums := []string{"Foo", "FooBar"}
119
+ got := attachUntypedConsts(enums, nil, []string{"FooBarBaz", "FooQux"})
120
+ want := map[string][]string{"FooBar": {"FooBarBaz"}, "Foo": {"FooQux"}}
121
+ if !reflect.DeepEqual(got, want) {
122
+ t.Errorf("got %v, want %v", got, want)
123
+ }
124
+ })
125
+
126
+ t.Run("no abbreviation candidate when members are type-name-prefixed", func(t *testing.T) {
127
+ enums := []string{"NodeFlags"}
128
+ typed := map[string][]string{"NodeFlags": {"NodeFlagsNone", "NodeFlagsConst"}}
129
+ // "NodeFlagsExtra" attaches via the type name; "NFXfoo" must NOT attach
130
+ // (no spurious 2/3-char "NF"/"Nod" abbreviation candidate is created).
131
+ got := attachUntypedConsts(enums, typed, []string{"NodeFlagsExtra", "NFXfoo"})
132
+ want := map[string][]string{"NodeFlags": {"NodeFlagsExtra"}}
133
+ if !reflect.DeepEqual(got, want) {
134
+ t.Errorf("got %v, want %v", got, want)
135
+ }
136
+ })
137
+
138
+ t.Run("lowercase boundary is not a member", func(t *testing.T) {
139
+ got := attachUntypedConsts([]string{"Modifier"}, nil, []string{"Modifierabc"})
140
+ if len(got) != 0 {
141
+ t.Errorf("expected no attachment, got %v", got)
142
+ }
143
+ })
144
+
145
+ t.Run("short shared prefix (<3) is not used as an abbreviation", func(t *testing.T) {
146
+ // Tristate members share only "TS" (2 chars); an unrelated "TSConfig" const
147
+ // must not be pulled in by an over-broad abbreviation.
148
+ enums := []string{"Tristate"}
149
+ typed := map[string][]string{"Tristate": {"TSUnknown", "TSFalse", "TSTrue"}}
150
+ got := attachUntypedConsts(enums, typed, []string{"TSConfig"})
151
+ if len(got) != 0 {
152
+ t.Errorf("expected no attachment, got %v", got)
153
+ }
154
+ })
155
+ }
156
+
157
+ // TestIsReachable covers the structural recursion plus the decisive case: an
158
+ // internal named type is reachable only when the shim exposes it.
159
+ func TestIsReachable(t *testing.T) {
160
+ fresh := func() map[types.Type]bool { return map[types.Type]bool{} }
161
+ r := reachable{}
162
+
163
+ if !isReachable(types.Typ[types.Int], r, fresh()) {
164
+ t.Error("basic int should be reachable")
165
+ }
166
+ if !isReachable(types.NewPointer(types.NewSlice(types.Typ[types.String])), r, fresh()) {
167
+ t.Error("pointer-to-slice-of-basic should be reachable")
168
+ }
169
+
170
+ pkg := types.NewPackage("github.com/microsoft/typescript-go/internal/checker", "checker")
171
+ obj := types.NewTypeName(token.NoPos, pkg, "Foo", nil)
172
+ named := types.NewNamed(obj, types.NewStruct(nil, nil), nil)
173
+ if isReachable(named, reachable{}, fresh()) {
174
+ t.Error("internal checker.Foo must be UNreachable when the shim does not expose it")
175
+ }
176
+ exposed := reachable{}
177
+ exposed.add("checker", "Foo")
178
+ if !isReachable(named, exposed, fresh()) {
179
+ t.Error("internal checker.Foo must be reachable once the shim exposes it")
180
+ }
181
+ }