@ttsc/lint 0.33.1 → 0.34.1

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 (78) hide show
  1. package/go.mod +2 -2
  2. package/linthost/ast_helpers.go +2 -2
  3. package/linthost/base_chain_walk_crosses_generic_boundary_test.go +3 -3
  4. package/linthost/check_observations.go +14 -14
  5. package/linthost/check_observations_retains_failed_program_and_raw_inputs_test.go +3 -2
  6. package/linthost/command_check_preserves_compiler_argument_cwd_test.go +2 -2
  7. package/linthost/comment_scan_handles_malformed_parser_recovery_test.go +2 -1
  8. package/linthost/comment_scan_uses_parser_lexical_ranges_across_source_modes_test.go +2 -1
  9. package/linthost/compile.go +5 -8
  10. package/linthost/config.go +4 -2
  11. package/linthost/config_loader_sources_escape_every_literal_percent_test.go +2 -1
  12. package/linthost/contributor_diagnostic_ranges_are_bounded_test.go +19 -19
  13. package/linthost/contributor_range_fix_bounds_diagnostic_independently_from_edit_test.go +2 -2
  14. package/linthost/contributor_report_related_reaches_finding_test.go +1 -1
  15. package/linthost/engine.go +1 -1
  16. package/linthost/engine_quarantines_panicking_rule_per_file_test.go +4 -4
  17. package/linthost/engine_run_parallel_matches_serial_finding_set_test.go +1 -1
  18. package/linthost/fix.go +1 -1
  19. package/linthost/helpers_test.go +5 -4
  20. package/linthost/host.go +130 -52
  21. package/linthost/imported_tsx_source_joins_the_read_scope_test.go +1 -1
  22. package/linthost/input_observation.go +24 -27
  23. package/linthost/invalid_contributor_range_cannot_panic_inline_directive_filtering_test.go +1 -1
  24. package/linthost/lint_corpus_filename_separators_share_materialized_source_identity_test.go +1 -1
  25. package/linthost/load_config_resolver_compact_globs_preserve_entry_policy_test.go +2 -2
  26. package/linthost/load_program_keeps_checker_pool_for_type_aware_rules_test.go +4 -4
  27. package/linthost/lsp.go +10 -5
  28. package/linthost/no_floating_promises_javascript_generic_defaults_test.go +1 -1
  29. package/linthost/no_floating_promises_rejects_inapplicable_generic_receiver_branches_test.go +1 -1
  30. package/linthost/project_engine.go +1 -1
  31. package/linthost/project_rule_sources_apply_only_global_ignores_test.go +1 -1
  32. package/linthost/project_rule_sources_include_imported_sibling_source_test.go +2 -2
  33. package/linthost/project_source_files_follow_tsconfig_selection_test.go +2 -2
  34. package/linthost/project_source_selection_preserves_explicit_bundled_roots_test.go +6 -5
  35. package/linthost/resident_whole_corpus_matches_cold_load_test.go +1 -1
  36. package/linthost/rules_boundaries.go +7 -7
  37. package/linthost/rules_boundaries_dependencies.go +9 -9
  38. package/linthost/rules_format_declaration_header.go +11 -4
  39. package/linthost/rules_gap.go +1 -1
  40. package/linthost/rules_jsdoc.go +1 -1
  41. package/linthost/rules_logic.go +2 -2
  42. package/linthost/rules_nextjs.go +1 -1
  43. package/linthost/rules_no_loop_func.go +4 -4
  44. package/linthost/rules_no_promise_executor_return.go +1 -1
  45. package/linthost/rules_promise.go +16 -16
  46. package/linthost/rules_react_perf.go +1 -1
  47. package/linthost/rules_react_refresh.go +2 -2
  48. package/linthost/rules_security.go +4 -4
  49. package/linthost/rules_storybook.go +1 -1
  50. package/linthost/rules_testing_library.go +1 -1
  51. package/linthost/rules_ts_no_base_to_string.go +1 -1
  52. package/linthost/rules_ts_no_deprecated.go +3 -3
  53. package/linthost/rules_ts_no_unnecessary_qualifier.go +4 -4
  54. package/linthost/rules_ts_no_unnecessary_type_arguments.go +2 -2
  55. package/linthost/rules_ts_no_unsafe_assignment.go +1 -1
  56. package/linthost/rules_ts_no_unsafe_enum_comparison.go +1 -1
  57. package/linthost/rules_ts_prefer_readonly.go +1 -1
  58. package/linthost/rules_ts_switch_exhaustiveness_check.go +2 -2
  59. package/linthost/rules_ts_unbound_method.go +1 -1
  60. package/linthost/rules_unicorn_consistent_destructuring.go +2 -2
  61. package/linthost/rules_unicorn_consistent_existence_index_check.go +2 -2
  62. package/linthost/rules_unicorn_consistent_function_scoping.go +7 -7
  63. package/linthost/rules_unicorn_filename_case.go +1 -1
  64. package/linthost/rules_unicorn_isolated_functions.go +3 -3
  65. package/linthost/rules_unicorn_no_typeof_undefined.go +1 -1
  66. package/linthost/rules_unicorn_no_unnecessary_polyfills.go +1 -1
  67. package/linthost/rules_unicorn_no_unused_properties.go +3 -3
  68. package/linthost/rules_unicorn_prefer_number_properties.go +1 -1
  69. package/linthost/rules_unicorn_prefer_simple_condition_first.go +1 -1
  70. package/linthost/rules_unicorn_prevent_abbreviations.go +7 -7
  71. package/linthost/rules_var.go +1 -1
  72. package/linthost/solid_helpers_test.go +1 -1
  73. package/linthost/type_alias_reference_node_uses_borrowed_builder_context_test.go +2 -2
  74. package/linthost/type_instantiation_mappers_reach_runtime_endpoints_test.go +3 -3
  75. package/linthost/user_source_files_exclude_imported_declarations_and_javascript_test.go +2 -2
  76. package/linthost/user_source_files_span_program_typescript_sources_test.go +1 -1
  77. package/linthost/windows_project_source_selection_preserves_physical_aliases_test.go +5 -4
  78. package/package.json +2 -2
package/go.mod CHANGED
@@ -32,9 +32,9 @@ require (
32
32
  )
33
33
 
34
34
  require (
35
- github.com/go-json-experiment/json v0.0.0-20260601182631-00ed12fed2a6 // indirect
35
+ github.com/go-json-experiment/json v0.0.0-20260623181947-01eb4420fa68 // indirect
36
36
  github.com/klauspost/cpuid/v2 v2.2.10 // indirect
37
- github.com/microsoft/typescript-go v0.0.0-20260610182825-7fc57c005063 // indirect
37
+ github.com/microsoft/typescript-go v0.0.0-20260708042240-2bd066d87f5b // indirect
38
38
  github.com/zeebo/xxh3 v1.1.0 // indirect
39
39
  golang.org/x/sync v0.21.0 // indirect
40
40
  golang.org/x/sys v0.46.0 // indirect
@@ -596,8 +596,8 @@ func canonicalValueSymbol(ctx *Context, identifier *shimast.Node) *shimast.Symbo
596
596
  if symbol == nil {
597
597
  return nil
598
598
  }
599
- if symbol.Flags&shimast.SymbolFlagsExportValue != 0 && symbol.ExportSymbol != nil {
600
- symbol = symbol.ExportSymbol
599
+ if shimast.SymbolFlagsOf(symbol)&shimast.SymbolFlagsExportValue != 0 && shimast.SymbolExportSymbolOf(symbol) != nil {
600
+ symbol = shimast.SymbolExportSymbolOf(symbol)
601
601
  }
602
602
  return ctx.Checker.GetMergedSymbol(symbol)
603
603
  }
@@ -111,7 +111,7 @@ func classSymbol(t *testing.T, prog *program, name string) *shimast.Symbol {
111
111
  continue
112
112
  }
113
113
  for _, stmt := range file.Statements.Nodes {
114
- if sym := stmt.Symbol(); sym != nil && sym.Name == name {
114
+ if sym := stmt.Symbol(); sym != nil && shimast.SymbolNameOf(sym) == name {
115
115
  return sym
116
116
  }
117
117
  }
@@ -137,7 +137,7 @@ func collectAncestorNames(c *shimchecker.Checker, start *shimchecker.Type, bridg
137
137
  }
138
138
  seen[t] = true
139
139
  if sym := shimchecker.Type_getTypeNameSymbol(t); sym != nil {
140
- found[sym.Name] = true
140
+ found[shimast.SymbolNameOf(sym)] = true
141
141
  }
142
142
  if t.ObjectFlags()&shimchecker.ObjectFlagsClassOrInterface == 0 {
143
143
  return // not safe to feed to getBaseTypes
@@ -153,7 +153,7 @@ func collectAncestorNames(c *shimchecker.Checker, start *shimchecker.Type, bridg
153
153
  // base is a generic Reference: record the boundary name, then cross it
154
154
  // only when bridging is allowed.
155
155
  if sym := shimchecker.Type_getTypeNameSymbol(base); sym != nil {
156
- found[sym.Name] = true
156
+ found[shimast.SymbolNameOf(sym)] = true
157
157
  if bridge {
158
158
  visit(shimchecker.Checker_getDeclaredTypeOfSymbol(c, sym))
159
159
  }
@@ -60,22 +60,22 @@ func (p *program) checkGraph() *lintCheckGraph {
60
60
  graph := &lintCheckGraph{Edges: map[string][]string{}, Globals: []string{}, Configs: []string{}, ResolutionInputs: []string{}, InputObservations: map[string]transformInputObservation{}, InputHashes: map[string]*string{}, InputRealpaths: map[string]*string{}, InputProofFailures: map[string]string{}, UseCaseSensitiveFileNames: p.tsProgram.UseCaseSensitiveFileNames()}
61
61
  realized := map[string]bool{}
62
62
  for _, file := range p.tsProgram.SourceFiles() {
63
- if file == nil || strings.HasPrefix(file.FileName(), "bundled:///") {
63
+ if file == nil || strings.HasPrefix(string(file.FileName()), "bundled:///") {
64
64
  continue
65
65
  }
66
- key := lintInputKey(p.cwd, file.FileName())
67
- realized[file.FileName()] = true
66
+ key := lintInputKey(p.cwd, string(file.FileName()))
67
+ realized[string(file.FileName())] = true
68
68
  targets := []string{}
69
- // The shim returns canonical tspath.Path values encoded as strings, not
70
- // filenames. Preserve that exact identity and recover this Program's
71
- // resident source spelling; unresolved candidates are not realized edges.
69
+ // The shim returns canonical tspath.PathKey values, not filenames. Preserve
70
+ // that exact key and recover this Program's resident source spelling;
71
+ // unresolved candidates are not realized edges.
72
72
  for _, referenced := range shimcompiler.GetReferencedFilePaths(p.tsProgram, file) {
73
- source := p.tsProgram.GetSourceFileByPath(shimtspath.Path(referenced))
74
- if source == nil || source == file || strings.HasPrefix(source.FileName(), "bundled:///") {
73
+ source := p.tsProgram.GetSourceFileByPath(shimtspath.PathKey(referenced))
74
+ if source == nil || source == file || strings.HasPrefix(string(source.FileName()), "bundled:///") {
75
75
  continue
76
76
  }
77
- realized[source.FileName()] = true
78
- targets = append(targets, lintInputKey(p.cwd, source.FileName()))
77
+ realized[string(source.FileName())] = true
78
+ targets = append(targets, lintInputKey(p.cwd, string(source.FileName())))
79
79
  }
80
80
  sort.Strings(targets)
81
81
  graph.Edges[key] = targets
@@ -86,12 +86,12 @@ func (p *program) checkGraph() *lintCheckGraph {
86
86
  sort.Strings(graph.Globals)
87
87
  if p.parsed != nil && p.parsed.ConfigFile != nil {
88
88
  if file := p.parsed.ConfigFile.SourceFile; file != nil {
89
- realized[file.FileName()] = true
90
- graph.Configs = append(graph.Configs, lintInputKey(p.cwd, file.FileName()))
89
+ realized[string(file.FileName())] = true
90
+ graph.Configs = append(graph.Configs, lintInputKey(p.cwd, string(file.FileName())))
91
91
  }
92
92
  for _, file := range p.parsed.ExtendedSourceFiles() {
93
- realized[file] = true
94
- graph.Configs = append(graph.Configs, lintInputKey(p.cwd, file))
93
+ realized[string(file)] = true
94
+ graph.Configs = append(graph.Configs, lintInputKey(p.cwd, string(file)))
95
95
  }
96
96
  }
97
97
  // Conservatively universalize actual compiler predicates. This needs no
@@ -11,6 +11,7 @@ import (
11
11
  "reflect"
12
12
  "testing"
13
13
 
14
+ shimtspath "github.com/microsoft/typescript-go/shim/tspath"
14
15
  publicrule "github.com/samchon/ttsc/packages/lint/rule"
15
16
  )
16
17
 
@@ -125,7 +126,7 @@ func TestCheckObservationsRetainsFailedProgramAndRawInputs(t *testing.T) {
125
126
  if err != nil || !bytes.Equal(actual, raw) {
126
127
  t.Fatalf("raw reader changed bytes: %x %v", actual, err)
127
128
  }
128
- decoded, ok := prog.inputObserver.ReadFile(document)
129
+ decoded, ok := prog.inputObserver.ReadFile(shimtspath.RootedFilePathFromAbsolute(document))
129
130
  if !ok || decoded != "# Before\n" {
130
131
  t.Fatalf("compiler text changed its decoder contract: %q %v", decoded, ok)
131
132
  }
@@ -191,7 +192,7 @@ func TestCheckObservationsRetainsFailedProgramAndRawInputs(t *testing.T) {
191
192
  if directory.Kind != "directory" || directory.Version != 1 || directory.Digest != hex.EncodeToString(directorySum[:]) || !directory.IdentityStable {
192
193
  t.Fatalf("native listing lost authored membership proof: %#v", directory)
193
194
  }
194
- compilerListing := prog.inputObserver.GetAccessibleEntries(tree)
195
+ compilerListing := prog.inputObserver.GetAccessibleEntries(shimtspath.RootedDirectoryPathFromAbsolute(tree))
195
196
  if !reflect.DeepEqual(compilerListing.Files, []string{"a"}) || !reflect.DeepEqual(compilerListing.Directories, []string{"skip"}) {
196
197
  t.Fatalf("compiler listing changed: %#v", compilerListing)
197
198
  }
@@ -55,7 +55,7 @@ func TestCommandCheckPreservesCompilerArgumentCwd(t *testing.T) {
55
55
  defer prog.close()
56
56
  normalized := func(value string) string { return filepath.ToSlash(filepath.Clean(value)) }
57
57
  options := prog.parsed.CompilerOptions()
58
- if normalized(options.OutDir) != normalized(c.out) || normalized(options.RootDir) != normalized(b) || normalized(options.ConfigFilePath) != normalized(filepath.Join(b, "tsconfig.json")) {
58
+ if normalized(string(options.OutDir)) != normalized(c.out) || normalized(string(options.RootDir)) != normalized(b) || normalized(string(options.ConfigFilePath)) != normalized(filepath.Join(b, "tsconfig.json")) {
59
59
  t.Fatalf("wrong native options: out=%q root=%q config=%q", options.OutDir, options.RootDir, options.ConfigFilePath)
60
60
  }
61
61
  expectedRoot, err := os.Stat(b)
@@ -66,7 +66,7 @@ func TestCommandCheckPreservesCompilerArgumentCwd(t *testing.T) {
66
66
  if err != nil {
67
67
  t.Fatal(err)
68
68
  }
69
- if prog.cwd != b || normalized(prog.tsProgram.GetCurrentDirectory()) != normalized(b) || prog.findSourceFile(filepath.Join(b, "main.ts")) == nil || prog.findSourceFile(filepath.Join(a, "main.ts")) != nil || !os.SameFile(expectedRoot, physicalRoot) {
69
+ if prog.cwd != b || normalized(string(prog.tsProgram.GetCurrentDirectory())) != normalized(b) || prog.findSourceFile(filepath.Join(b, "main.ts")) == nil || prog.findSourceFile(filepath.Join(a, "main.ts")) != nil || !os.SameFile(expectedRoot, physicalRoot) {
70
70
  t.Fatalf("compiler argument base replaced Program/rule identity: cwd=%q TypeScriptCwd=%q physicalRoot=%q expectedB=%q Bsource=%t Asource=%t", prog.cwd, prog.tsProgram.GetCurrentDirectory(), prog.identity.PhysicalProjectRoot, b, prog.findSourceFile(filepath.Join(b, "main.ts")) != nil, prog.findSourceFile(filepath.Join(a, "main.ts")) != nil)
71
71
  }
72
72
  })
@@ -6,6 +6,7 @@ import (
6
6
  shimast "github.com/microsoft/typescript-go/shim/ast"
7
7
  shimcore "github.com/microsoft/typescript-go/shim/core"
8
8
  shimparser "github.com/microsoft/typescript-go/shim/parser"
9
+ shimtspath "github.com/microsoft/typescript-go/shim/tspath"
9
10
  )
10
11
 
11
12
  // TestCommentScanHandlesMalformedParserRecovery verifies incomplete source is
@@ -27,7 +28,7 @@ import (
27
28
  func TestCommentScanHandlesMalformedParserRecovery(t *testing.T) {
28
29
  cases := []struct {
29
30
  name string
30
- fileName string
31
+ fileName shimtspath.RootedFilePath
31
32
  kind shimcore.ScriptKind
32
33
  source string
33
34
  comments []string
@@ -9,6 +9,7 @@ import (
9
9
  shimast "github.com/microsoft/typescript-go/shim/ast"
10
10
  shimcore "github.com/microsoft/typescript-go/shim/core"
11
11
  shimparser "github.com/microsoft/typescript-go/shim/parser"
12
+ shimtspath "github.com/microsoft/typescript-go/shim/tspath"
12
13
  )
13
14
 
14
15
  // TestCommentScanUsesParserLexicalRangesAcrossSourceModes verifies exact,
@@ -58,7 +59,7 @@ func TestCommentScanUsesParserLexicalRangesAcrossSourceModes(t *testing.T) {
58
59
 
59
60
  cases := []struct {
60
61
  name string
61
- fileName string
62
+ fileName shimtspath.RootedFilePath
62
63
  kind shimcore.ScriptKind
63
64
  source string
64
65
  comments []string
@@ -233,17 +233,14 @@ func RunTransformWithIO(args []string, stdout, stderr io.Writer) int {
233
233
  }
234
234
 
235
235
  var captured string
236
- capture := func(name, text string, _ *shimcompiler.WriteFileData) error {
237
- if !isJavaScriptOutput(name) {
236
+ capture := func(name shimtspath.RootedFilePath, text string, _ *shimcompiler.WriteFileData) error {
237
+ if !isJavaScriptOutput(string(name)) {
238
238
  return nil
239
239
  }
240
240
  captured = text
241
241
  return nil
242
242
  }
243
- result := prog.tsProgram.Emit(context.Background(), shimcompiler.EmitOptions{
244
- TargetSourceFile: target,
245
- WriteFile: shimcompiler.WriteFile(capture),
246
- })
243
+ result := prog.tsProgram.Emit(context.Background(), shimcompiler.TargetedEmitOptions(target, shimcompiler.WriteFile(capture)))
247
244
  if result == nil {
248
245
  fmt.Fprintln(stderr, "@ttsc/lint transform: Emit returned nil")
249
246
  return 3
@@ -512,8 +509,8 @@ func runProject(opts *subcommandOpts) (code int) {
512
509
  }
513
510
 
514
511
  result := prog.tsProgram.Emit(context.Background(), shimcompiler.EmitOptions{
515
- WriteFile: shimcompiler.WriteFile(func(fileName, text string, data *shimcompiler.WriteFileData) error {
516
- return defaultWriteFile(fileName, text)
512
+ WriteFile: shimcompiler.WriteFile(func(fileName shimtspath.RootedFilePath, text string, data *shimcompiler.WriteFileData) error {
513
+ return defaultWriteFile(string(fileName), text)
517
514
  }),
518
515
  })
519
516
  if result == nil {
@@ -3959,8 +3959,10 @@ func fileURL(location string) string {
3959
3959
  if volume != "" {
3960
3960
  volumePath := filepath.ToSlash(volume)
3961
3961
  rest := strings.TrimPrefix(filepath.ToSlash(location[len(volume):]), "/")
3962
- if strings.HasPrefix(volumePath, "//?/UNC") {
3963
- return uncFileURL(rest)
3962
+ if unc, ok := strings.CutPrefix(volumePath, "//?/UNC"); ok {
3963
+ // Go 1.26 reports `\\?\UNC` as the volume and Go 1.27 reports
3964
+ // `\\?\UNC\server\share`; the server and share follow the prefix either way.
3965
+ return uncFileURL(strings.TrimPrefix(unc+"/"+rest, "/"))
3964
3966
  }
3965
3967
  if strings.HasPrefix(volumePath, "//?/") {
3966
3968
  pathname := "/" + strings.TrimPrefix(volumePath, "//?/")
@@ -7,6 +7,7 @@ import (
7
7
  shimast "github.com/microsoft/typescript-go/shim/ast"
8
8
  shimcore "github.com/microsoft/typescript-go/shim/core"
9
9
  shimparser "github.com/microsoft/typescript-go/shim/parser"
10
+ shimtspath "github.com/microsoft/typescript-go/shim/tspath"
10
11
  )
11
12
 
12
13
  // TestConfigLoaderSourcesEscapeEveryLiteralPercent verifies generated loader syntax
@@ -67,7 +68,7 @@ func TestConfigLoaderSourcesEscapeEveryLiteralPercent(t *testing.T) {
67
68
  )
68
69
  }
69
70
  parsed := shimparser.ParseSourceFile(
70
- shimast.SourceFileParseOptions{FileName: "/virtual/" + generated.name + map[shimcore.ScriptKind]string{shimcore.ScriptKindJS: ".js", shimcore.ScriptKindTS: ".ts"}[generated.kind]},
71
+ shimast.SourceFileParseOptions{FileName: shimtspath.RootedFilePathFromAbsolute("/virtual/" + generated.name + map[shimcore.ScriptKind]string{shimcore.ScriptKindJS: ".js", shimcore.ScriptKindTS: ".ts"}[generated.kind])},
71
72
  generated.source,
72
73
  generated.kind,
73
74
  )
@@ -31,12 +31,12 @@ func TestContributorDiagnosticRangesAreBounded(t *testing.T) {
31
31
  }
32
32
  contributor := &boundedDiagnosticRangeContributor{
33
33
  spans: map[string][2]int{
34
- files[0].FileName(): {-7, 5},
35
- files[1].FileName(): {8, 3},
36
- files[2].FileName(): {999, 1200},
37
- files[3].FileName(): {len(files[3].Text()), len(files[3].Text())},
38
- files[4].FileName(): {-4, 12},
39
- files[5].FileName(): {6, 11},
34
+ string(files[0].FileName()): {-7, 5},
35
+ string(files[1].FileName()): {8, 3},
36
+ string(files[2].FileName()): {999, 1200},
37
+ string(files[3].FileName()): {len(files[3].Text()), len(files[3].Text())},
38
+ string(files[4].FileName()): {-4, 12},
39
+ string(files[5].FileName()): {6, 11},
40
40
  },
41
41
  }
42
42
  metadata, err := inspectContributor(contributor)
@@ -56,23 +56,23 @@ func TestContributorDiagnosticRangesAreBounded(t *testing.T) {
56
56
  t.Fatalf("findings = %d, want %d: %+v", got, want, findings)
57
57
  }
58
58
  expected := map[string][2]int{
59
- files[0].FileName(): {0, 5},
60
- files[1].FileName(): {8, 9},
61
- files[2].FileName(): {len(files[2].Text()), len(files[2].Text())},
62
- files[3].FileName(): {len(files[3].Text()), len(files[3].Text())},
63
- files[4].FileName(): {0, 0},
64
- files[5].FileName(): {6, 11},
59
+ string(files[0].FileName()): {0, 5},
60
+ string(files[1].FileName()): {8, 9},
61
+ string(files[2].FileName()): {len(files[2].Text()), len(files[2].Text())},
62
+ string(files[3].FileName()): {len(files[3].Text()), len(files[3].Text())},
63
+ string(files[4].FileName()): {0, 0},
64
+ string(files[5].FileName()): {6, 11},
65
65
  }
66
66
  seen := map[string]bool{}
67
67
  for _, finding := range findings {
68
- want, ok := expected[finding.File.FileName()]
68
+ want, ok := expected[string(finding.File.FileName())]
69
69
  if !ok {
70
70
  t.Fatalf("unexpected finding file: %+v", finding)
71
71
  }
72
- if seen[finding.File.FileName()] || finding.Message != "explicit contributor range" {
72
+ if seen[string(finding.File.FileName())] || finding.Message != "explicit contributor range" {
73
73
  t.Fatalf("duplicate file or lost contributor message: %+v", finding)
74
74
  }
75
- seen[finding.File.FileName()] = true
75
+ seen[string(finding.File.FileName())] = true
76
76
  if finding.Pos != want[0] || finding.End != want[1] {
77
77
  t.Fatalf("range for %s = [%d,%d), want [%d,%d)",
78
78
  finding.File.FileName(), finding.Pos, finding.End, want[0], want[1])
@@ -86,8 +86,8 @@ func TestContributorDiagnosticRangesAreBounded(t *testing.T) {
86
86
 
87
87
  diagnostic := shimdw.NewLintDiagnostic(
88
88
  finding.File,
89
- contributor.spans[finding.File.FileName()][0],
90
- contributor.spans[finding.File.FileName()][1],
89
+ contributor.spans[string(finding.File.FileName())][0],
90
+ contributor.spans[string(finding.File.FileName())][1],
91
91
  9501,
92
92
  shimdw.LintCategoryError,
93
93
  "bounded contributor diagnostic",
@@ -118,8 +118,8 @@ func (*boundedDiagnosticRangeContributor) Visits() []shimast.Kind {
118
118
  return []shimast.Kind{shimast.KindSourceFile}
119
119
  }
120
120
  func (r *boundedDiagnosticRangeContributor) Check(ctx *publicrule.Context, _ *shimast.Node) {
121
- span := r.spans[ctx.File.FileName()]
122
- if ctx.File.FileName() == r.fixFile {
121
+ span := r.spans[string(ctx.File.FileName())]
122
+ if string(ctx.File.FileName()) == r.fixFile {
123
123
  ctx.ReportRangeFix(span[0], span[1], "explicit contributor range", publicrule.TextEdit{
124
124
  Pos: 0,
125
125
  End: 5,
@@ -20,8 +20,8 @@ import (
20
20
  func TestContributorRangeFixBoundsDiagnosticIndependentlyFromEdit(t *testing.T) {
21
21
  file := parseTSFile(t, "/virtual/range-fix.ts", "const value = 1;\n")
22
22
  contributor := &boundedDiagnosticRangeContributor{
23
- spans: map[string][2]int{file.FileName(): {999, -5}},
24
- fixFile: file.FileName(),
23
+ spans: map[string][2]int{string(file.FileName()): {999, -5}},
24
+ fixFile: string(file.FileName()),
25
25
  }
26
26
  metadata, err := inspectContributor(contributor)
27
27
  if err != nil {
@@ -72,7 +72,7 @@ func TestContributorReportRelatedReachesFinding(t *testing.T) {
72
72
  if entry.Message != "defined over here" {
73
73
  t.Fatalf("render lost the message: %q", entry.Message)
74
74
  }
75
- if want := fileURL(file.FileName()); entry.Location.URI != want {
75
+ if want := fileURL(string(file.FileName())); entry.Location.URI != want {
76
76
  t.Fatalf("related location must carry the finding's own file URI: want %q got %q", want, entry.Location.URI)
77
77
  }
78
78
  if entry.Location.Range.Start == entry.Location.Range.End {
@@ -1368,7 +1368,7 @@ func (e *Engine) runFile(
1368
1368
  ) []*Finding {
1369
1369
  var collected []*Finding
1370
1370
  collect := func(f *Finding) { collected = append(collected, f) }
1371
- resolved := e.config.ResolveRules(file.FileName())
1371
+ resolved := e.config.ResolveRules(string(file.FileName()))
1372
1372
  if resolved.Ignored {
1373
1373
  return collected
1374
1374
  }
@@ -75,10 +75,10 @@ delta;
75
75
 
76
76
  for _, file := range []*shimast.SourceFile{first, second} {
77
77
  name := file.FileName()
78
- if got, want := bomb.checks[name], 1; got != want {
78
+ if got, want := bomb.checks[string(name)], 1; got != want {
79
79
  t.Fatalf("panicking rule checks for %s = %d, want %d", name, got, want)
80
80
  }
81
- if got, want := sibling.checks[name], 6; got != want {
81
+ if got, want := sibling.checks[string(name)], 6; got != want {
82
82
  t.Fatalf("sibling rule checks for %s = %d, want %d", name, got, want)
83
83
  }
84
84
  }
@@ -93,7 +93,7 @@ func (*fileQuarantinePanickingRule) Visits() []shimast.Kind {
93
93
  return []shimast.Kind{shimast.KindIdentifier, shimast.KindNumericLiteral}
94
94
  }
95
95
  func (r *fileQuarantinePanickingRule) Check(ctx *Context, _ *shimast.Node) {
96
- r.checks[ctx.File.FileName()]++
96
+ r.checks[string(ctx.File.FileName())]++
97
97
  panic("synthetic repeated panic")
98
98
  }
99
99
 
@@ -106,5 +106,5 @@ func (*fileQuarantineSiblingRule) Visits() []shimast.Kind {
106
106
  return []shimast.Kind{shimast.KindIdentifier, shimast.KindNumericLiteral}
107
107
  }
108
108
  func (r *fileQuarantineSiblingRule) Check(ctx *Context, _ *shimast.Node) {
109
- r.checks[ctx.File.FileName()]++
109
+ r.checks[string(ctx.File.FileName())]++
110
110
  }
@@ -77,7 +77,7 @@ func parallelFindingFingerprint(findings []*Finding) string {
77
77
  for _, f := range findings {
78
78
  name := ""
79
79
  if f.File != nil {
80
- name = f.File.FileName()
80
+ name = string(f.File.FileName())
81
81
  }
82
82
  rows = append(rows, name+"|"+f.Rule+"|"+strconv.Itoa(f.Pos))
83
83
  }
package/linthost/fix.go CHANGED
@@ -205,7 +205,7 @@ func applyFindingFixes(cwd string, findings []*Finding) (int, error) {
205
205
  if finding == nil || finding.File == nil || len(finding.Fix) == 0 {
206
206
  continue
207
207
  }
208
- path := finding.File.FileName()
208
+ path := string(finding.File.FileName())
209
209
  if path == "" {
210
210
  continue
211
211
  }
@@ -19,6 +19,7 @@ import (
19
19
  shimcore "github.com/microsoft/typescript-go/shim/core"
20
20
  shimparser "github.com/microsoft/typescript-go/shim/parser"
21
21
  shimscanner "github.com/microsoft/typescript-go/shim/scanner"
22
+ shimtspath "github.com/microsoft/typescript-go/shim/tspath"
22
23
  )
23
24
 
24
25
  var ruleExpectationPattern = regexp.MustCompile(`//\s*expect:\s*([@\w/-]+)\s+(error|warn)\s*$`)
@@ -61,9 +62,9 @@ func parseTS(t *testing.T, source string) *shimast.SourceFile {
61
62
  func parseTSFile(t *testing.T, fileName, source string) *shimast.SourceFile {
62
63
  t.Helper()
63
64
  opts := shimast.SourceFileParseOptions{
64
- // The tsgo parser asserts on normalized absolute paths; Windows
65
- // t.TempDir() callers would otherwise panic it with backslashes.
66
- FileName: filepath.ToSlash(fileName),
65
+ // RootedFilePathFromAbsolute normalizes separators, so Windows t.TempDir()
66
+ // callers hand the parser the same rooted spelling as POSIX ones.
67
+ FileName: shimtspath.RootedFilePathFromAbsolute(fileName),
67
68
  }
68
69
  file := shimparser.ParseSourceFile(opts, source, shimcore.ScriptKindTS)
69
70
  if file == nil {
@@ -84,7 +85,7 @@ func parseTSXFile(t *testing.T, fileName, source string) *shimast.SourceFile {
84
85
  t.Helper()
85
86
  opts := shimast.SourceFileParseOptions{
86
87
  // Same normalization as parseTSFile — see the comment there.
87
- FileName: filepath.ToSlash(fileName),
88
+ FileName: shimtspath.RootedFilePathFromAbsolute(fileName),
88
89
  }
89
90
  file := shimparser.ParseSourceFile(opts, source, shimcore.ScriptKindTSX)
90
91
  if file == nil {