diff --git a/internal/ast/utilities.go b/internal/ast/utilities.go index 7b122a98e7..d0cdf33854 100644 --- a/internal/ast/utilities.go +++ b/internal/ast/utilities.go @@ -1123,7 +1123,7 @@ func IsDeclaration(node *Node) bool { // True if `name` is the name of a declaration node func IsDeclarationName(name *Node) bool { - return !IsSourceFile(name) && !IsBindingPattern(name) && IsDeclaration(name.Parent) + return !IsSourceFile(name) && !IsBindingPattern(name) && IsDeclaration(name.Parent) && name.Parent.Name() == name } // Like 'isDeclarationName', but returns true for LHS of `import { x as y }` or `export { x as y }`. diff --git a/internal/compiler/checker.go b/internal/compiler/checker.go index 81a6ad8316..53ea89794d 100644 --- a/internal/compiler/checker.go +++ b/internal/compiler/checker.go @@ -8073,7 +8073,7 @@ func (c *Checker) isInPropertyInitializerOrClassStaticBlock(node *ast.Node) bool } return ast.FindAncestorQuit default: - if isExpressionNode(node) { + if IsExpressionNode(node) { return ast.FindAncestorFalse } return ast.FindAncestorQuit @@ -22736,6 +22736,14 @@ func (c *Checker) getActualTypeVariable(t *Type) *Type { return t } +func (c *Checker) GetSymbolAtLocation(node *ast.Node) *ast.Symbol { + // !!! + // const node = getParseTreeNode(nodeIn); + + // set ignoreErrors: true because any lookups invoked by the API shouldn't cause any new errors + return c.getSymbolAtLocation(node, true /*ignoreErrors*/) +} + func (c *Checker) getSymbolAtLocation(node *ast.Node, ignoreErrors bool) *ast.Symbol { if ast.IsSourceFile(node) { if ast.IsExternalModule(node.AsSourceFile()) { @@ -22963,7 +22971,7 @@ func (c *Checker) getSymbolOfNameOrPropertyAccessExpression(name *ast.Node) *ast } } - if isExpressionNode(name) { + if IsExpressionNode(name) { if ast.NodeIsMissing(name) { // Missing entity name. return nil @@ -23066,7 +23074,7 @@ func (c *Checker) getTypeOfNode(node *ast.Node) *Type { return typeFromTypeNode } - if isExpressionNode(node) { + if IsExpressionNode(node) { return c.getRegularTypeOfExpression(node) } diff --git a/internal/compiler/checker_test.go b/internal/compiler/checker_test.go index bd07c962db..35c77b98db 100644 --- a/internal/compiler/checker_test.go +++ b/internal/compiler/checker_test.go @@ -33,14 +33,14 @@ foo.bar;` } p := NewProgram(opts) p.bindSourceFiles() - c := p.getTypeChecker() + c := p.GetTypeChecker() file := p.filesByPath["/foo.ts"] interfaceId := file.Statements.Nodes[0].Name() varId := file.Statements.Nodes[1].AsVariableStatement().DeclarationList.AsVariableDeclarationList().Declarations.Nodes[0].Name() propAccess := file.Statements.Nodes[2].AsExpressionStatement().Expression nodes := []*ast.Node{interfaceId, varId, propAccess} for _, node := range nodes { - symbol := c.getSymbolAtLocation(node, true /*ignoreErrors*/) + symbol := c.GetSymbolAtLocation(node) if symbol == nil { t.Fatalf("Expected symbol to be non-nil") } diff --git a/internal/compiler/grammarchecks.go b/internal/compiler/grammarchecks.go index b6da6ca8c8..a84ec14589 100644 --- a/internal/compiler/grammarchecks.go +++ b/internal/compiler/grammarchecks.go @@ -97,7 +97,7 @@ func (c *Checker) checkGrammarPrivateIdentifierExpression(privId *ast.PrivateIde } if !ast.IsForInStatement(privId.Parent) { - if !isExpressionNode(privIdAsNode) { + if !IsExpressionNode(privIdAsNode) { return c.grammarErrorOnNode(privIdAsNode, diagnostics.Private_identifiers_are_only_allowed_in_class_bodies_and_may_only_be_used_as_part_of_a_class_member_declaration_property_access_or_on_the_left_hand_side_of_an_in_expression) } diff --git a/internal/compiler/printer.go b/internal/compiler/printer.go index 8a0d84afa3..cfe73f5f41 100644 --- a/internal/compiler/printer.go +++ b/internal/compiler/printer.go @@ -27,6 +27,10 @@ func (c *Checker) getTypePrecedence(t *Type) ast.TypePrecedence { return ast.TypePrecedenceNonArray } +func (c *Checker) SymbolToString(s *ast.Symbol) string { + return c.symbolToString(s) +} + func (c *Checker) symbolToString(s *ast.Symbol) string { if s.ValueDeclaration != nil { name := ast.GetNameOfDeclaration(s.ValueDeclaration) @@ -571,7 +575,7 @@ func (c *Checker) getTextAndTypeOfNode(node *ast.Node) (string, *Type, bool) { } } } - if isExpressionNode(node) && !isRightSideOfQualifiedNameOrPropertyAccess(node) { + if IsExpressionNode(node) && !isRightSideOfQualifiedNameOrPropertyAccess(node) { return scanner.GetTextOfNode(node), c.getTypeOfExpression(node), false } return "", nil, false diff --git a/internal/compiler/program.go b/internal/compiler/program.go index 6e6f36fa2c..f718bd4a5e 100644 --- a/internal/compiler/program.go +++ b/internal/compiler/program.go @@ -360,7 +360,7 @@ func (p *Program) GetSemanticDiagnostics(sourceFile *ast.SourceFile) []*ast.Diag } func (p *Program) GetGlobalDiagnostics() []*ast.Diagnostic { - return sortAndDeduplicateDiagnostics(p.getTypeChecker().GetGlobalDiagnostics()) + return sortAndDeduplicateDiagnostics(p.GetTypeChecker().GetGlobalDiagnostics()) } func (p *Program) TypeCount() int { @@ -370,7 +370,7 @@ func (p *Program) TypeCount() int { return int(p.checker.typeCount) } -func (p *Program) getTypeChecker() *Checker { +func (p *Program) GetTypeChecker() *Checker { if p.checker == nil { p.checker = NewChecker(p) } @@ -386,7 +386,7 @@ func (p *Program) getBindDiagnosticsForFile(sourceFile *ast.SourceFile) []*ast.D } func (p *Program) getSemanticDiagnosticsForFile(sourceFile *ast.SourceFile) []*ast.Diagnostic { - return core.Concatenate(sourceFile.BindDiagnostics(), p.getTypeChecker().GetDiagnostics(sourceFile)) + return core.Concatenate(sourceFile.BindDiagnostics(), p.GetTypeChecker().GetDiagnostics(sourceFile)) } func (p *Program) getDiagnosticsHelper(sourceFile *ast.SourceFile, ensureBound bool, getDiagnostics func(*ast.SourceFile) []*ast.Diagnostic) []*ast.Diagnostic { @@ -414,7 +414,7 @@ type NodeCount struct { func (p *Program) PrintSourceFileWithTypes() { for _, file := range p.files { if tspath.GetBaseFileName(file.FileName()) == "main.ts" { - fmt.Print(p.getTypeChecker().sourceFileWithTypes(file)) + fmt.Print(p.GetTypeChecker().sourceFileWithTypes(file)) } } } @@ -785,3 +785,8 @@ func (p *Program) Emit(options *EmitOptions) *EmitResult { } return result } + +func (p *Program) GetSourceFile(filename string) *ast.SourceFile { + path := tspath.ToPath(filename, p.host.GetCurrentDirectory(), p.host.FS().UseCaseSensitiveFileNames()) + return p.filesByPath[path] +} diff --git a/internal/compiler/utilities.go b/internal/compiler/utilities.go index d5bf949a4e..3bfaf1dd3a 100644 --- a/internal/compiler/utilities.go +++ b/internal/compiler/utilities.go @@ -993,7 +993,7 @@ func isValidTypeOnlyAliasUseSite(useSite *ast.Node) bool { ast.IsPartOfTypeQuery(useSite) || isIdentifierInNonEmittingHeritageClause(useSite) || isPartOfPossiblyValidTypeOrAbstractComputedPropertyName(useSite) || - !(isExpressionNode(useSite) || isShorthandPropertyNameUseSite(useSite)) + !(IsExpressionNode(useSite) || isShorthandPropertyNameUseSite(useSite)) } func isIdentifierInNonEmittingHeritageClause(node *ast.Node) bool { @@ -1062,7 +1062,7 @@ func nodeCanBeDecorated(useLegacyDecorators bool, node *ast.Node, parent *ast.No return false } -func isExpressionNode(node *ast.Node) bool { +func IsExpressionNode(node *ast.Node) bool { switch node.Kind { case ast.KindSuperKeyword, ast.KindNullKeyword, ast.KindTrueKeyword, ast.KindFalseKeyword, ast.KindRegularExpressionLiteral, ast.KindArrayLiteralExpression, ast.KindObjectLiteralExpression, ast.KindPropertyAccessExpression, ast.KindElementAccessExpression, @@ -1154,7 +1154,7 @@ func isInExpressionContext(node *ast.Node) bool { case ast.KindSatisfiesExpression: return parent.AsSatisfiesExpression().Expression == node default: - return isExpressionNode(parent) + return IsExpressionNode(parent) } } diff --git a/internal/testutil/baseline/baseline.go b/internal/testutil/baseline/baseline.go index 6b18e386c1..5df817bddf 100644 --- a/internal/testutil/baseline/baseline.go +++ b/internal/testutil/baseline/baseline.go @@ -15,11 +15,11 @@ type Options struct { const NoContent = "" -func Run(t testing.TB, fileName string, actual string, opts Options) { +func Run(t *testing.T, fileName string, actual string, opts Options) { writeComparison(t, actual, fileName, opts) } -func writeComparison(t testing.TB, actual string, relativeFileName string, opts Options) { +func writeComparison(t *testing.T, actual string, relativeFileName string, opts Options) { if actual == "" { panic("The generated content was \"\". Return 'baseline.NoContent' if no baselining is required.") } diff --git a/internal/testutil/baseline/error_baseline.go b/internal/testutil/baseline/error_baseline.go index 4699d48300..244bf9502a 100644 --- a/internal/testutil/baseline/error_baseline.go +++ b/internal/testutil/baseline/error_baseline.go @@ -19,12 +19,6 @@ import ( // IO const harnessNewLine = "\r\n" -var ( - lineDelimiter = regexp.MustCompile("\r?\n") - nonWhitespace = regexp.MustCompile(`\S`) - tsExtension = regexp.MustCompile(`\.tsx?$`) -) - var formatOpts = &compiler.DiagnosticsFormattingOptions{ NewLine: harnessNewLine, } @@ -40,7 +34,7 @@ var ( diagnosticsLocationPattern = regexp.MustCompile(`(?i)(lib.*\.d\.ts):\d+:\d+`) ) -func DoErrorBaseline(t testing.TB, baselinePath string, inputFiles []*TestFile, errors []*ast.Diagnostic, pretty bool) { +func DoErrorBaseline(t *testing.T, baselinePath string, inputFiles []*TestFile, errors []*ast.Diagnostic, pretty bool) { baselinePath = tsExtension.ReplaceAllString(baselinePath, ".errors.txt") var errorBaseline string if len(errors) > 0 { @@ -61,7 +55,7 @@ func minimalDiagnosticsToString(diagnostics []*ast.Diagnostic, pretty bool) stri return output.String() } -func getErrorBaseline(t testing.TB, inputFiles []*TestFile, diagnostics []*ast.Diagnostic, pretty bool) string { +func getErrorBaseline(t *testing.T, inputFiles []*TestFile, diagnostics []*ast.Diagnostic, pretty bool) string { t.Helper() outputLines := iterateErrorBaseline(t, inputFiles, diagnostics, pretty) @@ -77,7 +71,7 @@ func getErrorBaseline(t testing.TB, inputFiles []*TestFile, diagnostics []*ast.D return strings.Join(outputLines, "") } -func iterateErrorBaseline(t testing.TB, inputFiles []*TestFile, inputDiagnostics []*ast.Diagnostic, pretty bool) []string { +func iterateErrorBaseline(t *testing.T, inputFiles []*TestFile, inputDiagnostics []*ast.Diagnostic, pretty bool) []string { t.Helper() diagnostics := slices.Clone(inputDiagnostics) slices.SortFunc(diagnostics, compiler.CompareDiagnostics) @@ -225,7 +219,6 @@ func iterateErrorBaseline(t testing.TB, inputFiles []*TestFile, inputDiagnostics // Verify we didn't miss any errors in this file assert.Check(t, cmp.Equal(markedErrorCount, len(fileErrors)), "count of errors in "+inputFile.unitName) _, isDupe := dupeCase[sanitizeTestFilePath(inputFile.unitName)] - checkDuplicatedFileName(inputFile.unitName, dupeCase) result = append(result, outputLines.String()) if isDupe { // Case-duplicated files on a case-insensitive build will have errors reported in both the dupe and the original @@ -253,19 +246,6 @@ func iterateErrorBaseline(t testing.TB, inputFiles []*TestFile, inputDiagnostics return result } -func checkDuplicatedFileName(resultName string, dupeCase map[string]int) string { - resultName = sanitizeTestFilePath(resultName) - if _, ok := dupeCase[resultName]; ok { - // A different baseline filename should be manufactured if the names differ only in case, for windows compat - count := 1 + dupeCase[resultName] - dupeCase[resultName] = count - resultName = fmt.Sprintf("%s.dupe%d", resultName, count) - } else { - dupeCase[resultName] = 0 - } - return resultName -} - func flattenDiagnosticMessage(d *ast.Diagnostic, newLine string) string { var output strings.Builder compiler.WriteFlattenedDiagnosticMessage(&output, d, newLine) diff --git a/internal/testutil/baseline/symbol_baseline.go b/internal/testutil/baseline/symbol_baseline.go new file mode 100644 index 0000000000..6df70bd7c6 --- /dev/null +++ b/internal/testutil/baseline/symbol_baseline.go @@ -0,0 +1,306 @@ +package baseline + +import ( + "fmt" + "regexp" + "slices" + "strconv" + "strings" + "testing" + + "github.com/microsoft/typescript-go/internal/ast" + "github.com/microsoft/typescript-go/internal/compiler" + "github.com/microsoft/typescript-go/internal/core" + "github.com/microsoft/typescript-go/internal/scanner" + "github.com/microsoft/typescript-go/internal/tspath" +) + +var ( + codeLinesRegexp = regexp.MustCompile("[\r\u2028\u2029]|\r?\n") + bracketLineRegex = regexp.MustCompile(`^\s*[{|}]\s*$`) + lineEndRegex = regexp.MustCompile(`\r?\n`) +) + +func DoTypeAndSymbolBaseline( + t *testing.T, + baselinePath string, + header string, + program *compiler.Program, + allFiles []*TestFile, + opts Options, + skipTypeBaselines bool, + skipSymbolBaselines bool, + hasErrorBaseline bool, +) { + // The full walker simulates the types that you would get from doing a full + // compile. The pull walker simulates the types you get when you just do + // a type query for a random node (like how the LS would do it). Most of the + // time, these will be the same. However, occasionally, they can be different. + // Specifically, when the compiler internally depends on symbol IDs to order + // things, then we may see different results because symbols can be created in a + // different order with 'pull' operations, and thus can produce slightly differing + // output. + // + // For example, with a full type check, we may see a type displayed as: number | string + // But with a pull type check, we may see it as: string | number + // + // These types are equivalent, but depend on what order the compiler observed + // certain parts of the program. + + fullWalker := newTypeWriterWalker(program, hasErrorBaseline) + + t.Run("type", func(t *testing.T) { + checkBaselines(t, baselinePath, allFiles, fullWalker, header, opts, false /*isSymbolBaseline*/) + }) + t.Run("symbol", func(t *testing.T) { + checkBaselines(t, baselinePath, allFiles, fullWalker, header, opts, true /*isSymbolBaseline*/) + }) +} + +func checkBaselines( + t *testing.T, + baselinePath string, + allFiles []*TestFile, + fullWalker *typeWriterWalker, + header string, + opts Options, + isSymbolBaseline bool, +) { + fullExtension := core.IfElse(isSymbolBaseline, ".symbols", ".types") + outputFileName := tspath.RemoveFileExtension(baselinePath) + fullBaseline := generateBaseline(allFiles, fullWalker, header, isSymbolBaseline) + Run(t, outputFileName+fullExtension, fullBaseline, opts) +} + +func generateBaseline( + allFiles []*TestFile, + fullWalker *typeWriterWalker, + header string, + isSymbolBaseline bool, +) string { + var result strings.Builder + // !!! Perf baseline + var perfLines []string + // prePerformanceValues := getPerformanceBaselineValues() + baselines := iterateBaseline(allFiles, fullWalker, isSymbolBaseline) + for _, value := range baselines { + result.WriteString(value) + } + // postPerformanceValues := getPerformanceBaselineValues() + + if !isSymbolBaseline { + // !!! Perf baselines + // const perfStats: [name: string, reportThreshold: number, beforeValue: number, afterValue: number][] = []; + // perfStats.push(["Strict subtype cache", 1000, prePerformanceValues.strictSubtype, postPerformanceValues.strictSubtype]); + // perfStats.push(["Subtype cache", 1000, prePerformanceValues.subtype, postPerformanceValues.subtype]); + // perfStats.push(["Identity cache", 1000, prePerformanceValues.identity, postPerformanceValues.identity]); + // perfStats.push(["Assignability cache", 1000, prePerformanceValues.assignability, postPerformanceValues.assignability]); + // perfStats.push(["Type Count", 1000, prePerformanceValues.typeCount, postPerformanceValues.typeCount]); + // perfStats.push(["Instantiation count", 1500, prePerformanceValues.instantiation, postPerformanceValues.instantiation]); + // perfStats.push(["Symbol count", 45000, prePerformanceValues.symbol, postPerformanceValues.symbol]); + + // if (perfStats.some(([, threshold, , postValue]) => postValue >= threshold)) { + // perfLines.push(`=== Performance Stats ===`); + // for (const [name, threshold, preValue, postValue] of perfStats) { + // if (postValue >= threshold) { + // const preString = valueToString(preValue); + // const postString = valueToString(postValue); + // if (preString === postString) { + // perfLines.push(`${name}: ${preString}`); + // } + // else { + // perfLines.push(`${name}: ${preString} -> ${postString}`); + // } + // } + // } + // perfLines.push(""); + // perfLines.push(""); + // } + } + + if result.Len() > 0 { + return fmt.Sprintf("//// [%s] ////\r\n\r\n%s%s", header, strings.Join(perfLines, "\n"), result.String()) + } + return result.String() +} + +func iterateBaseline(allFiles []*TestFile, fullWalker *typeWriterWalker, isSymbolBaseline bool) []string { + var baselines []string + + for _, file := range allFiles { + unitName := file.unitName + var typeLines strings.Builder + typeLines.WriteString("=== " + unitName + " ===\r\n") + codeLines := codeLinesRegexp.Split(file.content, -1) + var results []*typeWriterResult + if isSymbolBaseline { + results = fullWalker.getSymbols(unitName) + } else { + results = fullWalker.getTypes(unitName) + } + lastIndexWritten := -1 + for _, result := range results { + if isSymbolBaseline && result.symbol == "" { + return baselines + } + if lastIndexWritten == -1 { + typeLines.WriteString(strings.Join(codeLines[:result.line+1], "\r\n")) + typeLines.WriteString("\r\n") + } else if lastIndexWritten != result.line { + if !(lastIndexWritten+1 < len(codeLines) && + (bracketLineRegex.MatchString(codeLines[lastIndexWritten+1]) || strings.TrimSpace(codeLines[lastIndexWritten+1]) == "")) { + typeLines.WriteString("\r\n") + } + typeLines.WriteString(strings.Join(codeLines[lastIndexWritten+1:result.line+1], "\r\n")) + typeLines.WriteString("\r\n") + } + lastIndexWritten = result.line + typeOrSymbolString := core.IfElse(isSymbolBaseline, result.symbol, result.typ) + lineText := lineDelimiter.ReplaceAllString(result.sourceText, "") + typeLines.WriteString(">") + fmt.Fprintf(&typeLines, "%s : %s", lineText, typeOrSymbolString) + typeLines.WriteString("\r\n") + if result.underline != "" { + typeLines.WriteString(">") + for range len(lineText) { + typeLines.WriteString(" ") + } + typeLines.WriteString(" : ") + typeLines.WriteString(result.underline) + typeLines.WriteString("\r\n") + } + } + + if lastIndexWritten+1 < len(codeLines) { + if !(lastIndexWritten+1 < len(codeLines) && + (bracketLineRegex.MatchString(codeLines[lastIndexWritten+1]) || strings.TrimSpace(codeLines[lastIndexWritten+1]) == "")) { + typeLines.WriteString("\r\n") + } + typeLines.WriteString(strings.Join(codeLines[lastIndexWritten+1:], "\r\n")) + } + typeLines.WriteString("\r\n") + + baselines = append( + baselines, + removeTestPathPrefixes(typeLines.String(), false /*retainTrailingDirectorySeparator*/), + ) + } + + return baselines +} + +type typeWriterWalker struct { + program *compiler.Program + checker *compiler.Checker + hadErrorBaseline bool + currentSourceFile *ast.SourceFile + declarationTextCache map[*ast.Node]string +} + +func newTypeWriterWalker(program *compiler.Program, hadErrorBaseline bool) *typeWriterWalker { + return &typeWriterWalker{ + checker: program.GetTypeChecker(), + program: program, + hadErrorBaseline: hadErrorBaseline, + declarationTextCache: make(map[*ast.Node]string), + } +} + +type typeWriterResult struct { + line int + sourceText string + symbol string + typ string + underline string // !!! +} + +func (walker *typeWriterWalker) getTypes(filename string) []*typeWriterResult { + sourceFile := walker.program.GetSourceFile(filename) + walker.currentSourceFile = sourceFile + return walker.visitNode(sourceFile.AsNode(), false /*isSymbolWalk*/) +} + +func (walker *typeWriterWalker) getSymbols(filename string) []*typeWriterResult { + sourceFile := walker.program.GetSourceFile(filename) + walker.currentSourceFile = sourceFile + return walker.visitNode(sourceFile.AsNode(), true /*isSymbolWalk*/) +} + +func (walker *typeWriterWalker) visitNode(node *ast.Node, isSymbolWalk bool) []*typeWriterResult { + nodes := forEachASTNode(node) + var results []*typeWriterResult + for _, n := range nodes { + if compiler.IsExpressionNode(n) || n.Kind == ast.KindIdentifier || ast.IsDeclarationName(n) { + result := walker.writeTypeOrSymbol(n, isSymbolWalk) + if result != nil { + results = append(results, result) + } + } + } + return results +} + +func forEachASTNode(node *ast.Node) []*ast.Node { + var result []*ast.Node + work := []*ast.Node{node} + for len(work) > 0 { + elem := work[len(work)-1] + work = work[:len(work)-1] + result = append(result, elem) + + var resChildren []*ast.Node + elem.ForEachChild(func(child *ast.Node) bool { + resChildren = append(resChildren, child) + return false + }) + slices.Reverse(resChildren) + work = append(work, resChildren...) + } + return result +} + +func (walker *typeWriterWalker) writeTypeOrSymbol(node *ast.Node, isSymbolWalk bool) *typeWriterResult { + actualPos := scanner.SkipTrivia(walker.currentSourceFile.Text, node.Pos()) + line, _ := scanner.GetLineAndCharacterOfPosition(walker.currentSourceFile, actualPos) + sourceText := scanner.GetSourceTextOfNodeFromSourceFile(walker.currentSourceFile, node, false /*includeTrivia*/) + + if !isSymbolWalk { + // !!! Types baseline + } + + symbol := walker.checker.GetSymbolAtLocation(node) + if symbol == nil { + return nil + } + + var symbolString strings.Builder + symbolString.WriteString("Symbol(" + walker.checker.SymbolToString(symbol)) + count := 0 + for _, declaration := range symbol.Declarations { + if count >= 5 { + fmt.Fprintf(&symbolString, " ... and %d more", len(symbol.Declarations)-count) + break + } + count++ + symbolString.WriteString(", ") + if declText, ok := walker.declarationTextCache[declaration]; ok { + symbolString.WriteString(declText) + continue + } + + declSourceFile := ast.GetSourceFileOfNode(declaration) + declLine, declChar := scanner.GetLineAndCharacterOfPosition(declSourceFile, declaration.Pos()) + fileName := tspath.GetBaseFileName(declSourceFile.FileName()) + isLibFile := isDefaultLibraryFile(fileName) + lineStr := strconv.Itoa(declLine) + charStr := strconv.Itoa(declChar) + declText := fmt.Sprintf("Decl(%s, %s, %s)", fileName, core.IfElse(isLibFile, "--", lineStr), core.IfElse(isLibFile, "--", charStr)) + symbolString.WriteString(declText) + } + symbolString.WriteString(")") + return &typeWriterResult{ + line: line, + sourceText: sourceText, + symbol: symbolString.String(), + } +} diff --git a/internal/testutil/baseline/util.go b/internal/testutil/baseline/util.go index bd23c4ce2e..2ba94a6654 100644 --- a/internal/testutil/baseline/util.go +++ b/internal/testutil/baseline/util.go @@ -8,6 +8,9 @@ import ( ) var ( + lineDelimiter = regexp.MustCompile("\r?\n") + nonWhitespace = regexp.MustCompile(`\S`) + tsExtension = regexp.MustCompile(`\.tsx?$`) testPathPrefix = regexp.MustCompile(`(?:(file:\/{3})|\/)\.(?:ts|lib|src)\/`) testPathCharacters = regexp.MustCompile(`[\^<>:"|?*%]`) testPathDotDot = regexp.MustCompile(`\.\.\/`)