Skip to content

Incremental port #1322

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 36 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
29ee285
Refactor
sheetalkamat Jun 20, 2025
62f6eb3
Add stub for incremental
sheetalkamat Jun 20, 2025
d0a9571
Incremental
sheetalkamat Jul 3, 2025
8301ea5
Make buildinfo tests portable and readable
sheetalkamat Jun 26, 2025
c737dd2
Handle casing with compiler options serialization
sheetalkamat Jun 26, 2025
778dc6c
make common js default impolied file format
sheetalkamat Jun 26, 2025
ea85af3
More changes
sheetalkamat Jun 27, 2025
84d07c8
More changes
sheetalkamat Jun 27, 2025
fae0baf
fix test
sheetalkamat Jun 27, 2025
b52d094
more change
sheetalkamat Jun 27, 2025
f3b2563
Parallel!!!
sheetalkamat Jul 3, 2025
70476c6
Tests
sheetalkamat Jun 30, 2025
19f5ecb
Watch changes
sheetalkamat Jun 30, 2025
08b7e8c
Verify diagnostic and options serializing
sheetalkamat Jun 30, 2025
d2834c2
Tests for reference map
sheetalkamat Jun 30, 2025
fd9aa38
Lint
sheetalkamat Jul 1, 2025
53a9bac
Use fake lib contents to make it deterministic for baselining as well…
sheetalkamat Jul 1, 2025
dca1bdb
Fix race with latest dts file
sheetalkamat Jul 1, 2025
b681785
Fix more tests
sheetalkamat Jul 1, 2025
6757fe3
change to test
sheetalkamat Jul 1, 2025
8fccf4b
Lint
sheetalkamat Jul 1, 2025
67f9911
Support for edits in tsc tests
sheetalkamat Jul 1, 2025
80a75d5
Fix race with default libs
sheetalkamat Jul 1, 2025
8430b07
Always collect references for proper program update
sheetalkamat Jul 1, 2025
b79ef45
Add baseline for semantic diagnostics refresh to check for correctness
sheetalkamat Jul 1, 2025
a715d9b
No need for scenario name in test name
sheetalkamat Jul 1, 2025
5e01867
Baseline signature compute
sheetalkamat Jul 1, 2025
41f58d9
Test incremental edit
sheetalkamat Jul 1, 2025
2f8abf6
Fix modified edit baseline
sheetalkamat Jul 1, 2025
32b3907
Use semantic diagnosics from old snapshot to compare
sheetalkamat Jul 1, 2025
728c2f7
Fix incremental updates
sheetalkamat Jul 1, 2025
7e374a2
More tests and updates
sheetalkamat Jul 2, 2025
767b853
Unify tsc runner even more
sheetalkamat Jul 2, 2025
62f4c15
Test fs refactor
sheetalkamat Jul 2, 2025
ac20ac4
rename test baselines
sheetalkamat Jul 2, 2025
04b68cb
Incremental correctness
sheetalkamat Jul 2, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion cmd/tsgo/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,5 +20,6 @@ func runMain() int {
return runAPI(args[1:])
}
}
return int(execute.CommandLine(newSystem(), nil, args))
status, _, _ := execute.CommandLine(newSystem(), args, false)
return int(status)
}
29 changes: 29 additions & 0 deletions internal/ast/diagnostic.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ type Diagnostic struct {
relatedInformation []*Diagnostic
reportsUnnecessary bool
reportsDeprecated bool
skippedOnNoEmit bool
}

func (d *Diagnostic) File() *SourceFile { return d.file }
Expand All @@ -35,10 +36,12 @@ func (d *Diagnostic) MessageChain() []*Diagnostic { return d.messageChain
func (d *Diagnostic) RelatedInformation() []*Diagnostic { return d.relatedInformation }
func (d *Diagnostic) ReportsUnnecessary() bool { return d.reportsUnnecessary }
func (d *Diagnostic) ReportsDeprecated() bool { return d.reportsDeprecated }
func (d *Diagnostic) SkippedOnNoEmit() bool { return d.skippedOnNoEmit }

func (d *Diagnostic) SetFile(file *SourceFile) { d.file = file }
func (d *Diagnostic) SetLocation(loc core.TextRange) { d.loc = loc }
func (d *Diagnostic) SetCategory(category diagnostics.Category) { d.category = category }
func (d *Diagnostic) SetSkippedOnNoEmit() { d.skippedOnNoEmit = true }

func (d *Diagnostic) SetMessageChain(messageChain []*Diagnostic) *Diagnostic {
d.messageChain = messageChain
Expand Down Expand Up @@ -69,6 +72,32 @@ func (d *Diagnostic) Clone() *Diagnostic {
return &result
}

func NewDiagnosticWith(
file *SourceFile,
loc core.TextRange,
code int32,
category diagnostics.Category,
message string,
messageChain []*Diagnostic,
relatedInformation []*Diagnostic,
reportsUnnecessary bool,
reportsDeprecated bool,
skippedOnNoEmit bool,
) *Diagnostic {
return &Diagnostic{
file: file,
loc: loc,
code: code,
category: category,
message: message,
messageChain: messageChain,
relatedInformation: relatedInformation,
reportsUnnecessary: reportsUnnecessary,
reportsDeprecated: reportsDeprecated,
skippedOnNoEmit: skippedOnNoEmit,
}
}

func NewDiagnostic(file *SourceFile, loc core.TextRange, message *diagnostics.Message, args ...any) *Diagnostic {
return &Diagnostic{
file: file,
Expand Down
4 changes: 4 additions & 0 deletions internal/ast/utilities.go
Original file line number Diff line number Diff line change
Expand Up @@ -1644,6 +1644,10 @@ func IsModuleAugmentationExternal(node *Node) bool {
return false
}

func IsModuleWithStringLiteralName(node *Node) bool {
return IsModuleDeclaration(node) && node.Name().Kind == KindStringLiteral
}

func GetContainingClass(node *Node) *Node {
return FindAncestor(node.Parent, IsClassLike)
}
Expand Down
23 changes: 21 additions & 2 deletions internal/checker/checker.go
Original file line number Diff line number Diff line change
Expand Up @@ -856,6 +856,8 @@ type Checker struct {
skipDirectInferenceNodes collections.Set[*ast.Node]
ctx context.Context
packagesMap map[string]bool
ambientModulesOnce sync.Once
ambientModules []*ast.Symbol
}

func NewChecker(program Program) *Checker {
Expand Down Expand Up @@ -2104,7 +2106,7 @@ func (c *Checker) getSymbol(symbols ast.SymbolTable, name string, meaning ast.Sy
}

func (c *Checker) CheckSourceFile(ctx context.Context, sourceFile *ast.SourceFile) {
if SkipTypeChecking(sourceFile, c.compilerOptions, c.program) {
if SkipTypeChecking(sourceFile, c.compilerOptions, c.program, false) {
return
}
c.checkSourceFile(ctx, sourceFile)
Expand Down Expand Up @@ -10088,7 +10090,7 @@ func (c *Checker) checkCollisionWithRequireExportsInGeneratedCode(node *ast.Node
parent := ast.GetDeclarationContainer(node)
if ast.IsSourceFile(parent) && ast.IsExternalOrCommonJSModule(parent.AsSourceFile()) {
// If the declaration happens to be in external module, report error that require and exports are reserved keywords
c.error(name, diagnostics.Duplicate_identifier_0_Compiler_reserves_name_1_in_top_level_scope_of_a_module, scanner.DeclarationNameToString(name), scanner.DeclarationNameToString(name))
c.errorSkippedOnNoEmit(name, diagnostics.Duplicate_identifier_0_Compiler_reserves_name_1_in_top_level_scope_of_a_module, scanner.DeclarationNameToString(name), scanner.DeclarationNameToString(name))
}
}

Expand Down Expand Up @@ -13340,6 +13342,12 @@ func (c *Checker) error(location *ast.Node, message *diagnostics.Message, args .
return diagnostic
}

func (c *Checker) errorSkippedOnNoEmit(location *ast.Node, message *diagnostics.Message, args ...any) *ast.Diagnostic {
diagnostic := c.error(location, message, args...)
diagnostic.SetSkippedOnNoEmit()
return diagnostic
}

func (c *Checker) errorOrSuggestion(isError bool, location *ast.Node, message *diagnostics.Message, args ...any) {
c.addErrorOrSuggestion(isError, NewDiagnosticForNode(location, message, args...))
}
Expand Down Expand Up @@ -14836,6 +14844,17 @@ func (c *Checker) tryFindAmbientModule(moduleName string, withAugmentations bool
return symbol
}

func (c *Checker) GetAmbientModules() []*ast.Symbol {
c.ambientModulesOnce.Do(func() {
for sym, global := range c.globals {
if strings.HasPrefix(sym, "\"") && strings.HasSuffix(sym, "\"") {
c.ambientModules = append(c.ambientModules, global)
}
}
})
return c.ambientModules
}

func (c *Checker) resolveExternalModuleSymbol(moduleSymbol *ast.Symbol, dontResolveAlias bool) *ast.Symbol {
if moduleSymbol != nil {
exportEquals := c.resolveSymbolEx(moduleSymbol.Exports[ast.InternalSymbolNameExportEquals], dontResolveAlias)
Expand Down
2 changes: 1 addition & 1 deletion internal/checker/checker_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ func TestCheckSrcCompiler(t *testing.T) {
Config: parsed,
Host: host,
})
p.CheckSourceFiles(t.Context())
p.CheckSourceFiles(t.Context(), nil)
}

func BenchmarkNewChecker(b *testing.B) {
Expand Down
13 changes: 12 additions & 1 deletion internal/checker/grammarchecks.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,17 @@ func (c *Checker) grammarErrorOnNode(node *ast.Node, message *diagnostics.Messag
return false
}

func (c *Checker) grammarErrorOnNodeSkippedOnNoEmit(node *ast.Node, message *diagnostics.Message, args ...any) bool {
sourceFile := ast.GetSourceFileOfNode(node)
if !c.hasParseDiagnostics(sourceFile) {
d := NewDiagnosticForNode(node, message, args...)
d.SetSkippedOnNoEmit()
c.diagnostics.Add(d)
return true
}
return false
}

func (c *Checker) checkGrammarRegularExpressionLiteral(_ *ast.RegularExpressionLiteral) bool {
// !!!
// Unclear if this is needed until regular expression parsing is more thoroughly implemented.
Expand Down Expand Up @@ -1648,7 +1659,7 @@ func (c *Checker) checkGrammarVariableDeclaration(node *ast.VariableDeclaration)
func (c *Checker) checkGrammarForEsModuleMarkerInBindingName(name *ast.Node) bool {
if ast.IsIdentifier(name) {
if name.Text() == "__esModule" {
return c.grammarErrorOnNode(name, diagnostics.Identifier_expected_esModule_is_reserved_as_an_exported_marker_when_transforming_ECMAScript_modules)
return c.grammarErrorOnNodeSkippedOnNoEmit(name, diagnostics.Identifier_expected_esModule_is_reserved_as_an_exported_marker_when_transforming_ECMAScript_modules)
}
} else {
for _, element := range name.AsBindingPattern().Elements.Nodes {
Expand Down
6 changes: 1 addition & 5 deletions internal/checker/symbolaccessibility.go
Original file line number Diff line number Diff line change
Expand Up @@ -105,11 +105,7 @@ func (ch *Checker) IsAnySymbolAccessible(symbols []*ast.Symbol, enclosingDeclara
}

func hasNonGlobalAugmentationExternalModuleSymbol(declaration *ast.Node) bool {
return isModuleWithStringLiteralName(declaration) || (declaration.Kind == ast.KindSourceFile && ast.IsExternalOrCommonJSModule(declaration.AsSourceFile()))
}

func isModuleWithStringLiteralName(node *ast.Node) bool {
return ast.IsModuleDeclaration(node) && node.Name().Kind == ast.KindStringLiteral
return ast.IsModuleWithStringLiteralName(declaration) || (declaration.Kind == ast.KindSourceFile && ast.IsExternalOrCommonJSModule(declaration.AsSourceFile()))
}

func getQualifiedLeftMeaning(rightMeaning ast.SymbolFlags) ast.SymbolFlags {
Expand Down
4 changes: 2 additions & 2 deletions internal/checker/utilities.go
Original file line number Diff line number Diff line change
Expand Up @@ -1467,8 +1467,8 @@ func forEachYieldExpression(body *ast.Node, visitor func(expr *ast.Node)) {
traverse(body)
}

func SkipTypeChecking(sourceFile *ast.SourceFile, options *core.CompilerOptions, host Program) bool {
return options.NoCheck.IsTrue() ||
func SkipTypeChecking(sourceFile *ast.SourceFile, options *core.CompilerOptions, host Program, ignoreNoCheck bool) bool {
return (!ignoreNoCheck && options.NoCheck.IsTrue()) ||
options.SkipLibCheck.IsTrue() && sourceFile.IsDeclarationFile ||
options.SkipDefaultLibCheck.IsTrue() && host.IsSourceFileDefaultLibrary(sourceFile.Path()) ||
host.IsSourceFromProjectReference(sourceFile.Path()) ||
Expand Down
68 changes: 68 additions & 0 deletions internal/collections/manytomanymap.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
package collections

type ManyToManyMap[K comparable, V comparable] struct {
keyToValueSet map[K]*Set[V]
valueToKeySet map[V]*Set[K]
}

func (m *ManyToManyMap[K, V]) GetKeys(value V) (*Set[K], bool) {
keys, present := m.valueToKeySet[value]
return keys, present
}

func (m *ManyToManyMap[K, V]) GetValues(key K) (*Set[V], bool) {
values, present := m.keyToValueSet[key]
return values, present
}

func (m *ManyToManyMap[K, V]) Len() int {
return len(m.keyToValueSet)
}

func (m *ManyToManyMap[K, V]) Keys() map[K]*Set[V] {
return m.keyToValueSet
}

func (m *ManyToManyMap[K, V]) Add(key K, valueSet *Set[V]) {
existingValueSet, hasExisting := m.keyToValueSet[key]
if m.keyToValueSet == nil {
m.keyToValueSet = make(map[K]*Set[V])
}
m.keyToValueSet[key] = valueSet
for value := range valueSet.Keys() {
if !hasExisting || !existingValueSet.Has(value) {
// Add to valueToKeySet
if m.valueToKeySet == nil {
m.valueToKeySet = make(map[V]*Set[K])
}
addToMapOfSet(m.valueToKeySet, value, key)
}
}

if hasExisting {
for value := range existingValueSet.Keys() {
if !valueSet.Has(value) {
// Remove from valueToKeySet
defer deleteFromMapOfSet(m.valueToKeySet, value, key)
}
}
}
}

func addToMapOfSet[K comparable, V comparable](mapKSetV map[K]*Set[V], key K, value V) {
set, exists := mapKSetV[key]
if !exists {
set = &Set[V]{}
mapKSetV[key] = set
}
set.Add(value)
}

func deleteFromMapOfSet[K comparable, V comparable](mapKSetV map[K]*Set[V], key K, value V) {
if set, exists := mapKSetV[key]; exists {
set.Delete(value)
if set.Len() == 0 {
delete(mapKSetV, key)
}
}
}
28 changes: 28 additions & 0 deletions internal/collections/set.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package collections

import "maps"

type Set[T comparable] struct {
M map[T]struct{}
}
Expand Down Expand Up @@ -48,6 +50,32 @@ func (s *Set[T]) AddIfAbsent(key T) bool {
return true
}

func (s *Set[T]) Clone() *Set[T] {
if s == nil {
return nil
}
clone := &Set[T]{M: maps.Clone(s.M)}
return clone
}

func (s *Set[T]) Equals(other *Set[T]) bool {
if s == other {
return true
}
if s == nil || other == nil {
return false
}
if s.Len() != other.Len() {
return false
}
for key := range s.M {
if !other.Has(key) {
return false
}
}
return true
}

func NewSetFromItems[T comparable](items ...T) *Set[T] {
s := &Set[T]{}
for _, item := range items {
Expand Down
16 changes: 16 additions & 0 deletions internal/collections/syncset.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,19 @@ func (s *SyncSet[T]) Add(key T) {
func (s *SyncSet[T]) Delete(key T) {
s.m.Delete(key)
}

func (s *SyncSet[T]) Range(fn func(key T) bool) {
s.m.Range(func(key T, value struct{}) bool {
return fn(key)
})
}

func (s *SyncSet[T]) ToArray() []T {
var arr []T
arr = make([]T, 0, s.m.Size())
s.m.Range(func(key T, value struct{}) bool {
arr = append(arr, key)
return true
})
return arr
}
2 changes: 1 addition & 1 deletion internal/compiler/checkerpool.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ func (p *checkerPool) GetChecker(ctx context.Context) (*checker.Checker, func())

func (p *checkerPool) createCheckers() {
p.createCheckersOnce.Do(func() {
wg := core.NewWorkGroup(p.program.singleThreaded())
wg := core.NewWorkGroup(p.program.SingleThreaded())
for i := range p.checkerCount {
wg.Queue(func() {
p.checkers[i] = checker.NewChecker(p.program)
Expand Down
10 changes: 1 addition & 9 deletions internal/compiler/emitHost.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,6 @@ import (
"github.com/microsoft/typescript-go/internal/tspath"
)

type WriteFileData struct {
SourceMapUrlPos int
// BuildInfo BuildInfo
Diagnostics []*ast.Diagnostic
DiffersOnlyInMap bool
SkippedDtsWrite bool
}

// NOTE: EmitHost operations must be thread-safe
type EmitHost interface {
printer.EmitHost
Expand Down Expand Up @@ -120,7 +112,7 @@ func (host *emitHost) IsEmitBlocked(file string) bool {
return false
}

func (host *emitHost) WriteFile(fileName string, text string, writeByteOrderMark bool, _ []*ast.SourceFile, _ *printer.WriteFileData) error {
func (host *emitHost) WriteFile(fileName string, text string, writeByteOrderMark bool) error {
return host.program.Host().FS().WriteFile(fileName, text, writeByteOrderMark)
}

Expand Down
Loading
Loading