From e95febf27f36344c4c9a2e44193829ceba27676b Mon Sep 17 00:00:00 2001 From: Sheetal Nandi Date: Thu, 7 Aug 2025 14:05:11 -0700 Subject: [PATCH 1/6] File include reasons --- internal/compiler/fileInclude.go | 244 ++++++++++++++++++ internal/compiler/fileloader.go | 99 +++++-- internal/compiler/filesparser.go | 47 +++- internal/compiler/program.go | 79 ++++-- internal/execute/tsc.go | 24 +- internal/execute/watcher.go | 2 +- internal/project/configfileregistry.go | 2 +- internal/tsoptions/parsedcommandline.go | 20 +- internal/tsoptions/parsedcommandline_test.go | 20 +- internal/tsoptions/tsconfigparsing.go | 82 ++++-- .../configDir-template-with-commandline.js | 8 + .../tsc/extends/configDir-template.js | 8 + ...g-filename-for-buildinfo-on-commandline.js | 8 + 13 files changed, 546 insertions(+), 97 deletions(-) create mode 100644 internal/compiler/fileInclude.go diff --git a/internal/compiler/fileInclude.go b/internal/compiler/fileInclude.go new file mode 100644 index 0000000000..396514c6fb --- /dev/null +++ b/internal/compiler/fileInclude.go @@ -0,0 +1,244 @@ +package compiler + +import ( + "fmt" + "sync" + + "github.com/microsoft/typescript-go/internal/ast" + "github.com/microsoft/typescript-go/internal/core" + "github.com/microsoft/typescript-go/internal/diagnostics" + "github.com/microsoft/typescript-go/internal/module" + "github.com/microsoft/typescript-go/internal/tspath" +) + +type fileIncludeKind int + +const ( + // References from file + fileIncludeKindImport = iota + fileIncludeKindReferenceFile + fileIncludeKindTypeReferenceDirective + fileIncludeKindLibReferenceDirective + + fileIncludeKindRootFile + fileIncludeKindSourceFromProjectReference + fileIncludeKindOutputFromProjectReference + fileIncludeKindLibFile + fileIncludeKindAutomaticTypeDirectiveFile +) + +type fileIncludeReason struct { + kind fileIncludeKind + data any + + diag *ast.Diagnostic + diagOnce sync.Once +} + +type referencedFileData struct { + file tspath.Path + index int + synthetic *ast.Node + location *referenceFileLocation + locationOnce sync.Once +} + +type referenceFileLocation struct { + file *ast.SourceFile + node *ast.Node + ref *ast.FileReference + packageId module.PackageId +} + +func (r *referenceFileLocation) text() string { + if r.node != nil { + return r.node.Text() + } else { + return r.file.Text()[r.ref.Pos():r.ref.End()] + } +} + +type automaticTypeDirectiveFileData struct { + typeReference string + packageId module.PackageId +} + +func (r *fileIncludeReason) asIndex() int { + return r.data.(int) +} + +func (r *fileIncludeReason) asLibFileIndex() (int, bool) { + index, ok := r.data.(int) + return index, ok +} + +func (r *fileIncludeReason) isReferencedFile() bool { + return r.kind <= fileIncludeKindLibReferenceDirective +} + +func (r *fileIncludeReason) asReferencedFileData() *referencedFileData { + return r.data.(*referencedFileData) +} + +func (r *fileIncludeReason) asAutomaticTypeDirectiveFileData() *automaticTypeDirectiveFileData { + return r.data.(*automaticTypeDirectiveFileData) +} + +func (r *fileIncludeReason) toDiagnostic(program *Program, toFileName func(string) string) *ast.Diagnostic { + r.diagOnce.Do(func() { + if r.isReferencedFile() { + r.diag = r.toReferenceDiagnostic(program, toFileName) + return + } + switch r.kind { + case fileIncludeKindRootFile: + if program.opts.Config.ConfigFile != nil { + config := program.opts.Config + fileName := tspath.GetNormalizedAbsolutePath(config.FileNames()[r.asIndex()], program.GetCurrentDirectory()) + if matchedFileSpec := config.GetMatchedFileSpec(fileName); matchedFileSpec != "" { + r.diag = ast.NewCompilerDiagnostic(diagnostics.Part_of_files_list_in_tsconfig_json, matchedFileSpec, toFileName(fileName)) + } else if matchedIncludeSpec, isDefaultIncludeSpec := config.GetMatchedIncludeSpec(fileName); matchedIncludeSpec != "" { + if isDefaultIncludeSpec { + r.diag = ast.NewCompilerDiagnostic(diagnostics.Matched_by_default_include_pattern_Asterisk_Asterisk_Slash_Asterisk) + } else { + r.diag = ast.NewCompilerDiagnostic(diagnostics.Matched_by_include_pattern_0_in_1, matchedIncludeSpec, toFileName(config.ConfigName())) + } + } else { + r.diag = ast.NewCompilerDiagnostic(diagnostics.Root_file_specified_for_compilation) + } + } else { + r.diag = ast.NewCompilerDiagnostic(diagnostics.Root_file_specified_for_compilation) + } + case fileIncludeKindSourceFromProjectReference: + case fileIncludeKindOutputFromProjectReference: + diag := core.IfElse( + r.kind == fileIncludeKindOutputFromProjectReference, + diagnostics.Output_from_referenced_project_0_included_because_module_is_specified_as_none, + diagnostics.Source_from_referenced_project_0_included_because_module_is_specified_as_none, + ) + referencedResolvedRef := program.projectReferenceFileMapper.getResolvedProjectReferences()[r.asIndex()] + r.diag = ast.NewCompilerDiagnostic(diag, toFileName(referencedResolvedRef.ConfigName())) + case fileIncludeKindAutomaticTypeDirectiveFile: + data := r.asAutomaticTypeDirectiveFileData() + if program.Options().Types != nil { + if data.packageId.Name != "" { + r.diag = ast.NewCompilerDiagnostic(diagnostics.Entry_point_of_type_library_0_specified_in_compilerOptions_with_packageId_1, data.typeReference, data.packageId.String()) + } else { + r.diag = ast.NewCompilerDiagnostic(diagnostics.Entry_point_of_type_library_0_specified_in_compilerOptions, data.typeReference) + } + } else { + if data.packageId.Name != "" { + r.diag = ast.NewCompilerDiagnostic(diagnostics.Entry_point_for_implicit_type_library_0_with_packageId_1, data.typeReference, data.packageId.String()) + } else { + r.diag = ast.NewCompilerDiagnostic(diagnostics.Entry_point_for_implicit_type_library_0, data.typeReference) + } + } + case fileIncludeKindLibFile: + index, ok := r.asLibFileIndex() + if ok { + r.diag = ast.NewCompilerDiagnostic(diagnostics.Library_0_specified_in_compilerOptions, program.Options().Lib[index]) + } else { + target := program.Options().GetEmitScriptTarget().String() + if target != "" { + r.diag = ast.NewCompilerDiagnostic(diagnostics.Default_library_for_target_0, target) + } else { + r.diag = ast.NewCompilerDiagnostic(diagnostics.Default_library) + } + } + default: + panic(fmt.Sprintf("unknown reason: %v", r.kind)) + } + }) + return r.diag +} + +func (r *fileIncludeReason) toReferenceDiagnostic(program *Program, toFileName func(string) string) *ast.Diagnostic { + referenceLocation := r.getReferencedLocation(program) + referenceText := referenceLocation.text() + switch r.kind { + case fileIncludeKindImport: + if specifier, ok := program.importHelpersImportSpecifiers[referenceLocation.file.Path()]; ok && specifier == referenceLocation.node { + if referenceLocation.packageId.Name != "" { + return ast.NewCompilerDiagnostic(diagnostics.Imported_via_0_from_file_1_with_packageId_2_to_import_importHelpers_as_specified_in_compilerOptions, referenceText, toFileName(referenceLocation.file.FileName()), referenceLocation.packageId.String()) + } else { + return ast.NewCompilerDiagnostic(diagnostics.Imported_via_0_from_file_1_to_import_importHelpers_as_specified_in_compilerOptions, referenceText, toFileName(referenceLocation.file.FileName())) + } + } else if jsxSpecifier, ok := program.jsxRuntimeImportSpecifiers[referenceLocation.file.Path()]; ok && jsxSpecifier.specifier == referenceLocation.node { + if referenceLocation.packageId.Name != "" { + return ast.NewCompilerDiagnostic(diagnostics.Imported_via_0_from_file_1_with_packageId_2_to_import_jsx_and_jsxs_factory_functions, referenceText, toFileName(referenceLocation.file.FileName()), referenceLocation.packageId.String()) + } else { + return ast.NewCompilerDiagnostic(diagnostics.Imported_via_0_from_file_1_to_import_jsx_and_jsxs_factory_functions, referenceText, toFileName(referenceLocation.file.FileName())) + } + } else { + if referenceLocation.packageId.Name != "" { + return ast.NewCompilerDiagnostic(diagnostics.Imported_via_0_from_file_1_with_packageId_2, referenceText, toFileName(referenceLocation.file.FileName()), referenceLocation.packageId.String()) + } else { + return ast.NewCompilerDiagnostic(diagnostics.Imported_via_0_from_file_1, referenceText, toFileName(referenceLocation.file.FileName())) + } + } + case fileIncludeKindReferenceFile: + return ast.NewCompilerDiagnostic(diagnostics.Referenced_via_0_from_file_1, referenceText, toFileName(referenceLocation.file.FileName())) + case fileIncludeKindTypeReferenceDirective: + if referenceLocation.packageId.Name != "" { + return ast.NewCompilerDiagnostic(diagnostics.Type_library_referenced_via_0_from_file_1_with_packageId_2, referenceText, toFileName(referenceLocation.file.FileName()), referenceLocation.packageId.String()) + } else { + return ast.NewCompilerDiagnostic(diagnostics.Type_library_referenced_via_0_from_file_1, referenceText, toFileName(referenceLocation.file.FileName())) + } + case fileIncludeKindLibReferenceDirective: + return ast.NewCompilerDiagnostic(diagnostics.Library_referenced_via_0_from_file_1, referenceText, toFileName(referenceLocation.file.FileName())) + + default: + panic(fmt.Sprintf("unknown reason: %v", r.kind)) + } +} + +func (r *fileIncludeReason) getReferencedLocation(program *Program) *referenceFileLocation { + ref := r.asReferencedFileData() + ref.locationOnce.Do(func() { + file := program.GetSourceFileByPath(ref.file) + switch r.kind { + case fileIncludeKindImport: + var specifier *ast.Node + if ref.synthetic != nil { + specifier = ref.synthetic + } else if ref.index < len(file.Imports()) { + specifier = file.Imports()[ref.index] + } else { + augIndex := len(file.Imports()) + for _, imp := range file.ModuleAugmentations { + if imp.Kind == ast.KindStringLiteral { + if augIndex == ref.index { + specifier = imp + break + } + augIndex++ + } + } + } + resolution := program.GetResolvedModuleFromModuleSpecifier(file, specifier) + ref.location = &referenceFileLocation{ + file: file, + node: specifier, + packageId: resolution.PackageId, + } + case fileIncludeKindReferenceFile: + ref.location = &referenceFileLocation{ + file: file, + ref: file.ReferencedFiles[ref.index], + } + case fileIncludeKindTypeReferenceDirective: + ref.location = &referenceFileLocation{ + file: file, + ref: file.TypeReferenceDirectives[ref.index], + } + case fileIncludeKindLibReferenceDirective: + ref.location = &referenceFileLocation{ + file: file, + ref: file.LibReferenceDirectives[ref.index], + } + default: + panic(fmt.Sprintf("unknown reason: %v", r.kind)) + } + }) + return ref.location +} diff --git a/internal/compiler/fileloader.go b/internal/compiler/fileloader.go index fbb96adf93..356300ef51 100644 --- a/internal/compiler/fileloader.go +++ b/internal/compiler/fileloader.go @@ -54,6 +54,10 @@ type processedFiles struct { unsupportedExtensions []string sourceFilesFoundSearchingNodeModules collections.Set[tspath.Path] fileLoadDiagnostics *ast.DiagnosticsCollection + fileIncludeReasons map[tspath.Path][]*fileIncludeReason + // if file was included using source file and its output is actually part of program + // this contains mapping from output to source file + outputFileToProjectReferenceSource map[tspath.Path]string } type jsxRuntimeImportSpecifier struct { @@ -88,25 +92,23 @@ func processAllProgramFiles( } loader.addProjectReferenceTasks(singleThreaded) loader.resolver = module.NewResolver(loader.projectReferenceFileMapper.host, compilerOptions, opts.TypingsLocation, opts.ProjectName) - - var libs []string + for index, rootFile := range rootFiles { + loader.addRootTask(rootFile, false, &fileIncludeReason{kind: fileIncludeKindRootFile, data: index}) + } if len(rootFiles) > 0 && compilerOptions.NoLib.IsFalseOrUnknown() { if compilerOptions.Lib == nil { name := tsoptions.GetDefaultLibFileName(compilerOptions) - libs = append(libs, loader.pathForLibFile(name)) + loader.addRootTask(loader.pathForLibFile(name), true, &fileIncludeReason{kind: fileIncludeKindLibFile}) } else { - for _, lib := range compilerOptions.Lib { + for index, lib := range compilerOptions.Lib { if name, ok := tsoptions.GetLibFileName(lib); ok { - libs = append(libs, loader.pathForLibFile(name)) + loader.addRootTask(loader.pathForLibFile(name), true, &fileIncludeReason{kind: fileIncludeKindLibFile, data: index}) } // !!! error on unknown name } } } - loader.addRootTasks(rootFiles, false) - loader.addRootTasks(libs, true) - if len(rootFiles) > 0 { loader.addAutomaticTypeDirectiveTasks() } @@ -124,6 +126,8 @@ func processAllProgramFiles( libFiles := make([]*ast.SourceFile, 0, totalFileCount) // totalFileCount here since we append files to it later to construct the final list filesByPath := make(map[tspath.Path]*ast.SourceFile, totalFileCount) + fileIncludeReasons := make(map[tspath.Path][]*fileIncludeReason, totalFileCount) + outputFileToProjectReferenceSource := make(map[tspath.Path]string, totalFileCount) resolvedModules := make(map[tspath.Path]module.ModeAwareCache[*module.ResolvedModule], totalFileCount+1) typeResolutionsInFile := make(map[tspath.Path]module.ModeAwareCache[*module.ResolvedTypeReferenceDirective], totalFileCount) sourceFileMetaDatas := make(map[tspath.Path]ast.SourceFileMetaData, totalFileCount) @@ -135,7 +139,10 @@ func processAllProgramFiles( fileLoadDiagnostics := &ast.DiagnosticsCollection{} loader.filesParser.collect(&loader, loader.rootTasks, func(task *parseTask) { - if task.isRedirected { + if task.redirectedParseTask != nil { + if !opts.canUseProjectReferenceSource() { + outputFileToProjectReferenceSource[task.redirectedParseTask.path] = task.FileName() + } return } @@ -157,6 +164,7 @@ func processAllProgramFiles( } filesByPath[path] = file + fileIncludeReasons[path] = task.allIncludeReasons resolvedModules[path] = task.resolutionsInFile typeResolutionsInFile[path] = task.typeResolutionsInFile sourceFileMetaDatas[path] = task.metadata @@ -224,6 +232,8 @@ func processAllProgramFiles( sourceFilesFoundSearchingNodeModules: sourceFilesFoundSearchingNodeModules, libFiles: libFileSet, fileLoadDiagnostics: fileLoadDiagnostics, + fileIncludeReasons: fileIncludeReasons, + outputFileToProjectReferenceSource: outputFileToProjectReferenceSource, } } @@ -231,12 +241,15 @@ func (p *fileLoader) toPath(file string) tspath.Path { return tspath.ToPath(file, p.opts.Host.GetCurrentDirectory(), p.opts.Host.FS().UseCaseSensitiveFileNames()) } -func (p *fileLoader) addRootTasks(files []string, isLib bool) { - for _, fileName := range files { - absPath := tspath.GetNormalizedAbsolutePath(fileName, p.opts.Host.GetCurrentDirectory()) - if core.Tristate.IsTrue(p.opts.Config.CompilerOptions().AllowNonTsExtensions) || slices.Contains(p.supportedExtensions, tspath.TryGetExtensionFromPath(absPath)) { - p.rootTasks = append(p.rootTasks, &parseTask{normalizedFilePath: absPath, isLib: isLib, root: true}) - } +func (p *fileLoader) addRootTask(fileName string, isLib bool, includeReason *fileIncludeReason) { + absPath := tspath.GetNormalizedAbsolutePath(fileName, p.opts.Host.GetCurrentDirectory()) + if core.Tristate.IsTrue(p.opts.Config.CompilerOptions().AllowNonTsExtensions) || slices.Contains(p.supportedExtensions, tspath.TryGetExtensionFromPath(absPath)) { + p.rootTasks = append(p.rootTasks, &parseTask{ + normalizedFilePath: absPath, + isLib: isLib, + root: true, + includeReason: includeReason, + }) } } @@ -249,7 +262,11 @@ func (p *fileLoader) addAutomaticTypeDirectiveTasks() { containingDirectory = p.opts.Host.GetCurrentDirectory() } containingFileName := tspath.CombinePaths(containingDirectory, module.InferredTypesContainingFile) - p.rootTasks = append(p.rootTasks, &parseTask{normalizedFilePath: containingFileName, isLib: false, isForAutomaticTypeDirective: true}) + p.rootTasks = append(p.rootTasks, &parseTask{ + normalizedFilePath: containingFileName, + isLib: false, + isForAutomaticTypeDirective: true, + }) } func (p *fileLoader) resolveAutomaticTypeDirectives(containingFileName string) ( @@ -269,6 +286,10 @@ func (p *fileLoader) resolveAutomaticTypeDirectives(containingFileName string) ( fileName: resolved.ResolvedFileName, increaseDepth: resolved.IsExternalLibraryImport, elideOnDepth: false, + includeReason: &fileIncludeReason{ + kind: fileIncludeKindAutomaticTypeDirectiveFile, + data: &automaticTypeDirectiveFileData{name, resolved.PackageId}, + }, }) } } @@ -298,18 +319,32 @@ func (p *fileLoader) addProjectReferenceTasks(singleThreaded bool) { // when no module system is specified, allowing including all files for global symbol merging // !!! sheetal Do we really need it? if len(p.opts.Config.FileNames()) != 0 { - for _, resolved := range p.projectReferenceFileMapper.getResolvedProjectReferences() { + for index, resolved := range p.projectReferenceFileMapper.getResolvedProjectReferences() { if resolved == nil || resolved.CompilerOptions().GetEmitModuleKind() != core.ModuleKindNone { continue } if p.opts.canUseProjectReferenceSource() { for _, fileName := range resolved.FileNames() { - p.rootTasks = append(p.rootTasks, &parseTask{normalizedFilePath: fileName, isLib: false}) + p.rootTasks = append(p.rootTasks, &parseTask{ + normalizedFilePath: fileName, + isLib: false, + includeReason: &fileIncludeReason{ + kind: fileIncludeKindSourceFromProjectReference, + data: index, + }, + }) } } else { for outputDts := range resolved.GetOutputDeclarationFileNames() { if outputDts != "" { - p.rootTasks = append(p.rootTasks, &parseTask{normalizedFilePath: outputDts, isLib: false}) + p.rootTasks = append(p.rootTasks, &parseTask{ + normalizedFilePath: outputDts, + isLib: false, + includeReason: &fileIncludeReason{ + kind: fileIncludeKindOutputFromProjectReference, + data: index, + }, + }) } } } @@ -373,7 +408,7 @@ func (p *fileLoader) parseSourceFile(t *parseTask) *ast.SourceFile { return sourceFile } -func (p *fileLoader) resolveTripleslashPathReference(moduleName string, containingFile string) resolvedRef { +func (p *fileLoader) resolveTripleslashPathReference(moduleName string, containingFile string, index int) resolvedRef { basePath := tspath.GetDirectoryPath(containingFile) referencedFileName := moduleName @@ -382,6 +417,13 @@ func (p *fileLoader) resolveTripleslashPathReference(moduleName string, containi } return resolvedRef{ fileName: tspath.NormalizePath(referencedFileName), + includeReason: &fileIncludeReason{ + kind: fileIncludeKindReferenceFile, + data: &referencedFileData{ + file: p.toPath(containingFile), + index: index, + }, + }, } } @@ -393,7 +435,7 @@ func (p *fileLoader) resolveTypeReferenceDirectives(t *parseTask) { meta := t.metadata typeResolutionsInFile := make(module.ModeAwareCache[*module.ResolvedTypeReferenceDirective], len(file.TypeReferenceDirectives)) - for _, ref := range file.TypeReferenceDirectives { + for index, ref := range file.TypeReferenceDirectives { redirect := p.projectReferenceFileMapper.getRedirectForResolution(file) resolutionMode := getModeForTypeReferenceDirectiveInFile(ref, file, meta, module.GetCompilerOptionsWithRedirect(p.opts.Config.CompilerOptions(), redirect)) resolved := p.resolver.ResolveTypeReferenceDirective(ref.FileName, file.FileName(), resolutionMode, redirect) @@ -405,6 +447,13 @@ func (p *fileLoader) resolveTypeReferenceDirectives(t *parseTask) { increaseDepth: resolved.IsExternalLibraryImport, elideOnDepth: false, isFromExternalLibrary: resolved.IsExternalLibraryImport, + includeReason: &fileIncludeReason{ + kind: fileIncludeKindTypeReferenceDirective, + data: &referencedFileData{ + file: t.path, + index: index, + }, + }, }, false) } } @@ -496,6 +545,14 @@ func (p *fileLoader) resolveImportsAndModuleAugmentations(t *parseTask) { increaseDepth: resolvedModule.IsExternalLibraryImport, elideOnDepth: isJsFileFromNodeModules, isFromExternalLibrary: resolvedModule.IsExternalLibraryImport, + includeReason: &fileIncludeReason{ + kind: fileIncludeKindImport, + data: &referencedFileData{ + file: t.path, + index: importIndex, + synthetic: core.IfElse(importIndex < 0, entry, nil), + }, + }, }, false) } } diff --git a/internal/compiler/filesparser.go b/internal/compiler/filesparser.go index de3f2994c3..2988ec4b2e 100644 --- a/internal/compiler/filesparser.go +++ b/internal/compiler/filesparser.go @@ -17,11 +17,12 @@ type parseTask struct { path tspath.Path file *ast.SourceFile isLib bool - isRedirected bool + redirectedParseTask *parseTask subTasks []*parseTask loaded bool isForAutomaticTypeDirective bool root bool + includeReason *fileIncludeReason metadata ast.SourceFileMetaData resolutionsInFile module.ModeAwareCache[*module.ResolvedModule] @@ -35,6 +36,9 @@ type parseTask struct { // Track if this file is from an external library (node_modules) // This mirrors the TypeScript currentNodeModulesDepth > 0 check fromExternalLibrary bool + + loadedTask *parseTask + allIncludeReasons []*fileIncludeReason } func (t *parseTask) FileName() string { @@ -72,8 +76,8 @@ func (t *parseTask) load(loader *fileLoader) { t.file = file t.subTasks = make([]*parseTask, 0, len(file.ReferencedFiles)+len(file.Imports())+len(file.ModuleAugmentations)) - for _, ref := range file.ReferencedFiles { - resolvedPath := loader.resolveTripleslashPathReference(ref.FileName, file.FileName()) + for index, ref := range file.ReferencedFiles { + resolvedPath := loader.resolveTripleslashPathReference(ref.FileName, file.FileName(), index) t.addSubTask(resolvedPath, false) } @@ -81,9 +85,18 @@ func (t *parseTask) load(loader *fileLoader) { loader.resolveTypeReferenceDirectives(t) if compilerOptions.NoLib != core.TSTrue { - for _, lib := range file.LibReferenceDirectives { + for index, lib := range file.LibReferenceDirectives { if name, ok := tsoptions.GetLibFileName(lib.FileName); ok { - t.addSubTask(resolvedRef{fileName: loader.pathForLibFile(name)}, true) + t.addSubTask(resolvedRef{ + fileName: loader.pathForLibFile(name), + includeReason: &fileIncludeReason{ + kind: fileIncludeKindLibReferenceDirective, + data: &referencedFileData{ + file: t.path, + index: index, + }, + }, + }, true) } } } @@ -92,9 +105,14 @@ func (t *parseTask) load(loader *fileLoader) { } func (t *parseTask) redirect(loader *fileLoader, fileName string) { - t.isRedirected = true + t.redirectedParseTask = &parseTask{ + normalizedFilePath: tspath.NormalizePath(fileName), + isLib: t.isLib, + fromExternalLibrary: t.fromExternalLibrary, + includeReason: t.includeReason, + } // increaseDepth and elideOnDepth are not copied to redirects, otherwise their depth would be double counted. - t.subTasks = []*parseTask{{normalizedFilePath: tspath.NormalizePath(fileName), isLib: t.isLib, fromExternalLibrary: t.fromExternalLibrary}} + t.subTasks = []*parseTask{t.redirectedParseTask} } func (t *parseTask) loadAutomaticTypeDirectives(loader *fileLoader) { @@ -110,6 +128,7 @@ type resolvedRef struct { increaseDepth bool elideOnDepth bool isFromExternalLibrary bool + includeReason *fileIncludeReason } func (t *parseTask) addSubTask(ref resolvedRef, isLib bool) { @@ -120,6 +139,7 @@ func (t *parseTask) addSubTask(ref resolvedRef, isLib bool) { increaseDepth: ref.increaseDepth, elideOnDepth: ref.elideOnDepth, fromExternalLibrary: ref.isFromExternalLibrary, + includeReason: ref.includeReason, } t.subTasks = append(t.subTasks, subTask) } @@ -149,7 +169,7 @@ func (w *filesParser) start(loader *fileLoader, tasks []*parseTask, depth int, i loadedTask, loaded := w.tasksByFileName.LoadOrStore(task.FileName(), newTask) task = loadedTask.task if loaded { - tasks[i] = task + tasks[i].loadedTask = task // Add in the loaded task's external-ness. taskIsFromExternalLibrary = taskIsFromExternalLibrary || task.fromExternalLibrary } @@ -207,11 +227,18 @@ func (w *filesParser) collect(loader *fileLoader, tasks []*parseTask, iterate fu func (w *filesParser) collectWorker(loader *fileLoader, tasks []*parseTask, iterate func(*parseTask), seen collections.Set[*parseTask]) []tspath.Path { var results []tspath.Path for _, task := range tasks { + if task.redirectedParseTask == nil { + if task.loadedTask == nil { + task.allIncludeReasons = []*fileIncludeReason{task.includeReason} + } else { + task.loadedTask.allIncludeReasons = append(task.loadedTask.allIncludeReasons, task.includeReason) + task = task.loadedTask + } + } // ensure we only walk each task once - if !task.loaded || seen.Has(task) { + if !task.loaded || !seen.AddIfAbsent(task) { continue } - seen.Add(task) if subTasks := task.subTasks; len(subTasks) > 0 { w.collectWorker(loader, subTasks, iterate, seen) } diff --git a/internal/compiler/program.go b/internal/compiler/program.go index 4c409a319f..d4e3bf0473 100644 --- a/internal/compiler/program.go +++ b/internal/compiler/program.go @@ -3,6 +3,7 @@ package compiler import ( "context" "fmt" + "io" "maps" "slices" "strings" @@ -1464,6 +1465,65 @@ func (p *Program) GetSourceFiles() []*ast.SourceFile { return p.files } +func (p *Program) ExplainFiles(writer io.Writer) { + toRelativeFileName := func(fileName string) string { + return tspath.GetRelativePathFromDirectory(p.GetCurrentDirectory(), fileName, p.comparePathsOptions) + } + for _, file := range p.GetSourceFiles() { + fmt.Fprintln(writer, toRelativeFileName(file.FileName())) + for _, reason := range p.fileIncludeReasons[file.Path()] { + fmt.Fprintln(writer, " ", reason.toDiagnostic(p, toRelativeFileName).Message()) + } + for _, diag := range p.explainRedirectAndImpliedFormat(file, toRelativeFileName) { + fmt.Fprintln(writer, " ", diag.Message()) + } + } +} + +func (p *Program) explainRedirectAndImpliedFormat( + file *ast.SourceFile, + toFileName func(fileName string) string, +) []*ast.Diagnostic { + var result []*ast.Diagnostic + if source, ok := p.outputFileToProjectReferenceSource[file.Path()]; ok { + result = append(result, ast.NewCompilerDiagnostic( + diagnostics.File_is_output_of_project_reference_source_0, + toFileName(source), + )) + } + // !!! redirects + // if (file.redirectInfo) { + // (result ??= []).push(chainDiagnosticMessages( + // /*details*/ undefined, + // Diagnostics.File_redirects_to_file_0, + // toFileName(file.redirectInfo.redirectTarget, fileNameConvertor), + // )); + // } + if ast.IsExternalOrCommonJSModule(file) { + metaData := p.GetSourceFileMetaData(file.Path()) + switch p.GetImpliedNodeFormatForEmit(file) { + case core.ModuleKindESNext: + if metaData.PackageJsonType == "module" { + result = append(result, ast.NewCompilerDiagnostic( + diagnostics.File_is_ECMAScript_module_because_0_has_field_type_with_value_module, + toFileName(metaData.PackageJsonDirectory+"/package.json"), + )) + } + case core.ModuleKindCommonJS: + if metaData.PackageJsonType != "" { + result = append(result, ast.NewCompilerDiagnostic(diagnostics.File_is_CommonJS_module_because_0_has_field_type_whose_value_is_not_module, toFileName(metaData.PackageJsonDirectory+"/package.json"))) + } else if metaData.PackageJsonDirectory != "" { + if metaData.PackageJsonType == "" { + result = append(result, ast.NewCompilerDiagnostic(diagnostics.File_is_CommonJS_module_because_0_does_not_have_field_type, toFileName(metaData.PackageJsonDirectory+"/package.json"))) + } + } else { + result = append(result, ast.NewCompilerDiagnostic(diagnostics.File_is_CommonJS_module_because_package_json_was_not_found)) + } + } + } + return result +} + func (p *Program) GetLibFileFromReference(ref *ast.FileReference) *ast.SourceFile { path, ok := tsoptions.GetLibFileName(ref.FileName) if !ok { @@ -1499,25 +1559,6 @@ func (p *Program) IsSourceFileFromExternalLibrary(file *ast.SourceFile) bool { return p.sourceFilesFoundSearchingNodeModules.Has(file.Path()) } -type FileIncludeKind int - -const ( - FileIncludeKindRootFile FileIncludeKind = iota - FileIncludeKindSourceFromProjectReference - FileIncludeKindOutputFromProjectReference - FileIncludeKindImport - FileIncludeKindReferenceFile - FileIncludeKindTypeReferenceDirective - FileIncludeKindLibFile - FileIncludeKindLibReferenceDirective - FileIncludeKindAutomaticTypeDirectiveFile -) - -type FileIncludeReason struct { - Kind FileIncludeKind - Index int -} - // UnsupportedExtensions returns a list of all present "unsupported" extensions, // e.g. extensions that are not yet supported by the port. func (p *Program) UnsupportedExtensions() []string { diff --git a/internal/execute/tsc.go b/internal/execute/tsc.go index af97431c32..3ddf328173 100644 --- a/internal/execute/tsc.go +++ b/internal/execute/tsc.go @@ -305,7 +305,7 @@ func emitAndReportStatistics( buildInfoReadTime time.Duration, changesComputeTime time.Duration, ) ExitStatus { - result := emitFilesAndReportErrors(sys, programLike, reportDiagnostic) + result := emitFilesAndReportErrors(sys, programLike, program, reportDiagnostic) if result.status != ExitStatusSuccess { // compile exited early return result.status @@ -351,14 +351,15 @@ type compileAndEmitResult struct { func emitFilesAndReportErrors( sys System, - program compiler.ProgramLike, + programLike compiler.ProgramLike, + program *compiler.Program, reportDiagnostic diagnosticReporter, ) (result compileAndEmitResult) { ctx := context.Background() allDiagnostics := compiler.GetDiagnosticsOfAnyProgram( ctx, - program, + programLike, nil, false, func(ctx context.Context, file *ast.SourceFile) []*ast.Diagnostic { @@ -366,22 +367,22 @@ func emitFilesAndReportErrors( // and global diagnostics create checkers, which then bind all of the files. Do this binding // early so we can track the time. bindStart := sys.Now() - diags := program.GetBindDiagnostics(ctx, file) + diags := programLike.GetBindDiagnostics(ctx, file) result.bindTime = sys.Now().Sub(bindStart) return diags }, func(ctx context.Context, file *ast.SourceFile) []*ast.Diagnostic { checkStart := sys.Now() - diags := program.GetSemanticDiagnostics(ctx, file) + diags := programLike.GetSemanticDiagnostics(ctx, file) result.checkTime = sys.Now().Sub(checkStart) return diags }, ) emitResult := &compiler.EmitResult{EmitSkipped: true, Diagnostics: []*ast.Diagnostic{}} - if !program.Options().ListFilesOnly.IsTrue() { + if !programLike.Options().ListFilesOnly.IsTrue() { emitStart := sys.Now() - emitResult = program.Emit(ctx, compiler.EmitOptions{}) + emitResult = programLike.Emit(ctx, compiler.EmitOptions{}) result.emitTime = sys.Now().Sub(emitStart) } if emitResult != nil { @@ -400,7 +401,7 @@ func emitFilesAndReportErrors( listFiles(sys, program) } - createReportErrorSummary(sys, program.Options())(allDiagnostics) + createReportErrorSummary(sys, programLike.Options())(allDiagnostics) result.diagnostics = allDiagnostics result.emitResult = emitResult result.status = ExitStatusSuccess @@ -416,10 +417,11 @@ func showConfig(sys System, config *core.CompilerOptions) { _ = jsonutil.MarshalIndentWrite(sys.Writer(), config, "", " ") } -func listFiles(sys System, program compiler.ProgramLike) { +func listFiles(sys System, program *compiler.Program) { options := program.Options() - // !!! explainFiles - if options.ListFiles.IsTrue() || options.ListFilesOnly.IsTrue() { + if options.ExplainFiles.IsTrue() { + program.ExplainFiles(sys.Writer()) + } else if options.ListFiles.IsTrue() || options.ListFilesOnly.IsTrue() { for _, file := range program.GetSourceFiles() { fmt.Fprintln(sys.Writer(), file.FileName()) } diff --git a/internal/execute/watcher.go b/internal/execute/watcher.go index 223bdc73fc..8927f762f5 100644 --- a/internal/execute/watcher.go +++ b/internal/execute/watcher.go @@ -88,7 +88,7 @@ func (w *Watcher) DoCycle() { func (w *Watcher) compileAndEmit() { // !!! output/error reporting is currently the same as non-watch mode // diagnostics, emitResult, exitStatus := - emitFilesAndReportErrors(w.sys, w.program, w.reportDiagnostic) + emitFilesAndReportErrors(w.sys, w.program, w.program.GetProgram(), w.reportDiagnostic) } func (w *Watcher) hasErrorsInTsConfig() bool { diff --git a/internal/project/configfileregistry.go b/internal/project/configfileregistry.go index 26b9060ba9..fc4600e4af 100644 --- a/internal/project/configfileregistry.go +++ b/internal/project/configfileregistry.go @@ -101,7 +101,7 @@ func (c *ConfigFileRegistry) acquireConfig(fileName string, path tspath.Path, pr } switch entry.pendingReload { case PendingReloadFileNames: - entry.commandLine = tsoptions.ReloadFileNamesOfParsedCommandLine(entry.commandLine, c.Host.FS()) + entry.commandLine = entry.commandLine.ReloadFileNamesOfParsedCommandLine(c.Host.FS()) case PendingReloadFull: oldCommandLine := entry.commandLine entry.commandLine, _ = tsoptions.GetParsedCommandLineOfConfigFilePath(fileName, path, nil, c.Host, &c.ExtendedConfigCache) diff --git a/internal/tsoptions/parsedcommandline.go b/internal/tsoptions/parsedcommandline.go index 8d36f04811..a265f7dc87 100644 --- a/internal/tsoptions/parsedcommandline.go +++ b/internal/tsoptions/parsedcommandline.go @@ -270,10 +270,26 @@ func (p *ParsedCommandLine) MatchesFileName(fileName string) bool { return false } - return p.ConfigFile.configFileSpecs.matchesInclude(fileName, p.comparePathsOptions) + return p.ConfigFile.configFileSpecs.getMatchedIncludeSpec(fileName, p.comparePathsOptions) != "" } -func ReloadFileNamesOfParsedCommandLine(p *ParsedCommandLine, fs vfs.FS) *ParsedCommandLine { +func (p *ParsedCommandLine) GetMatchedFileSpec(fileName string) string { + return p.ConfigFile.configFileSpecs.getMatchedFileSpec(fileName, p.comparePathsOptions) +} + +func (p *ParsedCommandLine) GetMatchedIncludeSpec(fileName string) (string, bool) { + if len(p.ConfigFile.configFileSpecs.validatedIncludeSpecs) == 0 { + return "", false + } + + if p.ConfigFile.configFileSpecs.isDefaultIncludeSpec { + return p.ConfigFile.configFileSpecs.validatedIncludeSpecs[0], true + } + + return p.ConfigFile.configFileSpecs.getMatchedIncludeSpec(fileName, p.comparePathsOptions), false +} + +func (p *ParsedCommandLine) ReloadFileNamesOfParsedCommandLine(fs vfs.FS) *ParsedCommandLine { parsedConfig := *p.ParsedConfig parsedConfig.FileNames = getFileNamesFromConfigSpecs( *p.ConfigFile.configFileSpecs, diff --git a/internal/tsoptions/parsedcommandline_test.go b/internal/tsoptions/parsedcommandline_test.go index 81d324331a..0185e6abe0 100644 --- a/internal/tsoptions/parsedcommandline_test.go +++ b/internal/tsoptions/parsedcommandline_test.go @@ -105,7 +105,7 @@ func TestParsedCommandLine(t *testing.T) { "/dev/b.ts", }) - emptyParsedCommandLine := tsoptions.ReloadFileNamesOfParsedCommandLine(parsedCommandLine, noFilesFS) + emptyParsedCommandLine := parsedCommandLine.ReloadFileNamesOfParsedCommandLine(noFilesFS) assertMatches(t, emptyParsedCommandLine, noFiles, []string{ "/dev/a.ts", "/dev/b.ts", @@ -135,7 +135,7 @@ func TestParsedCommandLine(t *testing.T) { "/dev/b.ts", }) - emptyParsedCommandLine := tsoptions.ReloadFileNamesOfParsedCommandLine(parsedCommandLine, noFilesFS) + emptyParsedCommandLine := parsedCommandLine.ReloadFileNamesOfParsedCommandLine(noFilesFS) assertMatches(t, emptyParsedCommandLine, noFiles, []string{ "/dev/a.ts", "/dev/b.ts", @@ -159,7 +159,7 @@ func TestParsedCommandLine(t *testing.T) { assertMatches(t, parsedCommandLine, files, []string{}) - emptyParsedCommandLine := tsoptions.ReloadFileNamesOfParsedCommandLine(parsedCommandLine, noFilesFS) + emptyParsedCommandLine := parsedCommandLine.ReloadFileNamesOfParsedCommandLine(noFilesFS) assertMatches(t, emptyParsedCommandLine, noFiles, []string{}) }) @@ -185,7 +185,7 @@ func TestParsedCommandLine(t *testing.T) { "/dev/a.ts", }) - emptyParsedCommandLine := tsoptions.ReloadFileNamesOfParsedCommandLine(parsedCommandLine, noFilesFS) + emptyParsedCommandLine := parsedCommandLine.ReloadFileNamesOfParsedCommandLine(noFilesFS) assertMatches(t, emptyParsedCommandLine, noFiles, []string{ "/dev/a.ts", }) @@ -220,7 +220,7 @@ func TestParsedCommandLine(t *testing.T) { "/dev/z/aba.ts", }) - emptyParsedCommandLine := tsoptions.ReloadFileNamesOfParsedCommandLine(parsedCommandLine, noFilesFS) + emptyParsedCommandLine := parsedCommandLine.ReloadFileNamesOfParsedCommandLine(noFilesFS) assertMatches(t, emptyParsedCommandLine, noFiles, []string{ "/dev/z/a.ts", "/dev/z/aba.ts", @@ -251,7 +251,7 @@ func TestParsedCommandLine(t *testing.T) { }) // a.d.ts matches if a.ts is not already included - emptyParsedCommandLine := tsoptions.ReloadFileNamesOfParsedCommandLine(parsedCommandLine, noFilesFS) + emptyParsedCommandLine := parsedCommandLine.ReloadFileNamesOfParsedCommandLine(noFilesFS) assertMatches(t, emptyParsedCommandLine, noFiles, []string{ "/dev/a.ts", "/dev/a.d.ts", @@ -279,7 +279,7 @@ func TestParsedCommandLine(t *testing.T) { "/dev/x/b.ts", }) - emptyParsedCommandLine := tsoptions.ReloadFileNamesOfParsedCommandLine(parsedCommandLine, noFilesFS) + emptyParsedCommandLine := parsedCommandLine.ReloadFileNamesOfParsedCommandLine(noFilesFS) assertMatches(t, emptyParsedCommandLine, noFiles, []string{ "/dev/x/a.ts", "/dev/x/b.ts", @@ -302,7 +302,7 @@ func TestParsedCommandLine(t *testing.T) { assertMatches(t, parsedCommandLine, files, []string{}) - emptyParsedCommandLine := tsoptions.ReloadFileNamesOfParsedCommandLine(parsedCommandLine, noFilesFS) + emptyParsedCommandLine := parsedCommandLine.ReloadFileNamesOfParsedCommandLine(noFilesFS) assertMatches(t, emptyParsedCommandLine, noFiles, []string{}) }) @@ -328,7 +328,7 @@ func TestParsedCommandLine(t *testing.T) { "/dev/js/b.js", }) - emptyParsedCommandLine := tsoptions.ReloadFileNamesOfParsedCommandLine(parsedCommandLine, noFilesFS) + emptyParsedCommandLine := parsedCommandLine.ReloadFileNamesOfParsedCommandLine(noFilesFS) assertMatches(t, emptyParsedCommandLine, noFiles, []string{ "/dev/js/a.js", "/dev/js/b.js", @@ -357,7 +357,7 @@ func TestParsedCommandLine(t *testing.T) { "/dev/js/ab.min.js", }) - emptyParsedCommandLine := tsoptions.ReloadFileNamesOfParsedCommandLine(parsedCommandLine, noFilesFS) + emptyParsedCommandLine := parsedCommandLine.ReloadFileNamesOfParsedCommandLine(noFilesFS) assertMatches(t, emptyParsedCommandLine, noFiles, []string{ "/dev/js/d.min.js", "/dev/js/ab.min.js", diff --git a/internal/tsoptions/tsconfigparsing.go b/internal/tsoptions/tsconfigparsing.go index 8a90a3475b..7fdfcd4b94 100644 --- a/internal/tsoptions/tsconfigparsing.go +++ b/internal/tsoptions/tsconfigparsing.go @@ -91,11 +91,13 @@ type configFileSpecs struct { // Present to report errors (user specified specs), validatedIncludeSpecs are used for file name matching includeSpecs any // Present to report errors (user specified specs), validatedExcludeSpecs are used for file name matching - excludeSpecs any - validatedFilesSpec []string - validatedIncludeSpecs []string - validatedExcludeSpecs []string - isDefaultIncludeSpec bool + excludeSpecs any + validatedFilesSpec []string + validatedIncludeSpecs []string + validatedExcludeSpecs []string + validatedFilesSpecBeforeSubstitution []string + validatedIncludeSpecsBeforeSubstitution []string + isDefaultIncludeSpec bool } func (c *configFileSpecs) matchesExclude(fileName string, comparePathsOptions tspath.ComparePathsOptions) bool { @@ -115,20 +117,33 @@ func (c *configFileSpecs) matchesExclude(fileName string, comparePathsOptions ts return false } -func (c *configFileSpecs) matchesInclude(fileName string, comparePathsOptions tspath.ComparePathsOptions) bool { +func (c *configFileSpecs) getMatchedIncludeSpec(fileName string, comparePathsOptions tspath.ComparePathsOptions) string { if len(c.validatedIncludeSpecs) == 0 { - return false + return "" } - for _, spec := range c.validatedIncludeSpecs { + for index, spec := range c.validatedIncludeSpecs { includePattern := vfs.GetPatternFromSpec(spec, comparePathsOptions.CurrentDirectory, "files") if includePattern != "" { includeRegex := vfs.GetRegexFromPattern(includePattern, comparePathsOptions.UseCaseSensitiveFileNames) if match, err := includeRegex.MatchString(fileName); err == nil && match { - return true + return c.validatedIncludeSpecsBeforeSubstitution[index] } } } - return false + return "" +} + +func (c *configFileSpecs) getMatchedFileSpec(fileName string, comparePathsOptions tspath.ComparePathsOptions) string { + if len(c.validatedFilesSpec) == 0 { + return "" + } + filePath := tspath.ToPath(fileName, comparePathsOptions.CurrentDirectory, comparePathsOptions.UseCaseSensitiveFileNames) + for index, spec := range c.validatedFilesSpec { + if tspath.ToPath(spec, comparePathsOptions.CurrentDirectory, comparePathsOptions.UseCaseSensitiveFileNames) == filePath { + return c.validatedFilesSpecBeforeSubstitution[index] + } + } + return "" } type FileExtensionInfo struct { @@ -1199,31 +1214,39 @@ func parseJsonConfigFileContentWorker( isDefaultIncludeSpec = true } var validatedIncludeSpecs []string + var validatedIncludeSpecsBeforeSubstitution []string var validatedExcludeSpecs []string var validatedFilesSpec []string + var validatedFilesSpecBeforeSubstitution []string // The exclude spec list is converted into a regular expression, which allows us to quickly // test whether a file or directory should be excluded before recursively traversing the // file system. if includeSpecs.sliceValue != nil { var err []*ast.Diagnostic - validatedIncludeSpecs, err = validateSpecs(includeSpecs.sliceValue, true /*disallowTrailingRecursion*/, tsconfigToSourceFile(sourceFile), "include") + validatedIncludeSpecsBeforeSubstitution, err = validateSpecs(includeSpecs.sliceValue, true /*disallowTrailingRecursion*/, tsconfigToSourceFile(sourceFile), "include") errors = append(errors, err...) - substituteStringArrayWithConfigDirTemplate(validatedIncludeSpecs, basePathForFileNames) + if validatedIncludeSpecs = getSubstitutedStringArrayWithConfigDirTemplate(validatedIncludeSpecsBeforeSubstitution, basePathForFileNames); validatedIncludeSpecs == nil { + validatedIncludeSpecs = validatedIncludeSpecsBeforeSubstitution + } } if excludeSpecs.sliceValue != nil { var err []*ast.Diagnostic validatedExcludeSpecs, err = validateSpecs(excludeSpecs.sliceValue, false /*disallowTrailingRecursion*/, tsconfigToSourceFile(sourceFile), "exclude") errors = append(errors, err...) - substituteStringArrayWithConfigDirTemplate(validatedExcludeSpecs, basePathForFileNames) + if validatedExcludeSpecsWithSubstitution := getSubstitutedStringArrayWithConfigDirTemplate(validatedExcludeSpecs, basePathForFileNames); validatedExcludeSpecsWithSubstitution != nil { + validatedExcludeSpecs = validatedExcludeSpecsWithSubstitution + } } if fileSpecs.sliceValue != nil { fileSpecs := core.Filter(fileSpecs.sliceValue, func(spec any) bool { return reflect.TypeOf(spec).Kind() == reflect.String }) for _, spec := range fileSpecs { if spec, ok := spec.(string); ok { - validatedFilesSpec = append(validatedFilesSpec, spec) + validatedFilesSpecBeforeSubstitution = append(validatedFilesSpecBeforeSubstitution, spec) } } - substituteStringArrayWithConfigDirTemplate(validatedFilesSpec, basePathForFileNames) + if validatedFilesSpec = getSubstitutedStringArrayWithConfigDirTemplate(validatedFilesSpecBeforeSubstitution, basePathForFileNames); validatedFilesSpec == nil { + validatedFilesSpec = validatedFilesSpecBeforeSubstitution + } } configFileSpecs := configFileSpecs{ fileSpecs.sliceValue, @@ -1232,6 +1255,8 @@ func parseJsonConfigFileContentWorker( validatedFilesSpec, validatedIncludeSpecs, validatedExcludeSpecs, + validatedFilesSpecBeforeSubstitution, + validatedIncludeSpecsBeforeSubstitution, isDefaultIncludeSpec, } @@ -1426,12 +1451,20 @@ func getSubstitutedPathWithConfigDirTemplate(value string, basePath string) stri return tspath.GetNormalizedAbsolutePath(strings.Replace(value, configDirTemplate, "./", 1), basePath) } -func substituteStringArrayWithConfigDirTemplate(list []string, basePath string) { +func getSubstitutedStringArrayWithConfigDirTemplate(list []string, basePath string) []string { + var result []string for i, element := range list { if startsWithConfigDirTemplate(element) { - list[i] = getSubstitutedPathWithConfigDirTemplate(element, basePath) + if result == nil { + result = slices.Clone(list) + } + result[i] = getSubstitutedPathWithConfigDirTemplate(element, basePath) } } + if result != nil { + return result + } + return nil } func handleOptionConfigDirTemplateSubstitution(compilerOptions *core.CompilerOptions, basePath string) { @@ -1441,13 +1474,18 @@ func handleOptionConfigDirTemplateSubstitution(compilerOptions *core.CompilerOpt // !!! don't hardcode this; use options declarations? - for v := range compilerOptions.Paths.Values() { - substituteStringArrayWithConfigDirTemplate(v, basePath) + for k, v := range compilerOptions.Paths.Entries() { + if substitution := getSubstitutedStringArrayWithConfigDirTemplate(v, basePath); substitution != nil { + compilerOptions.Paths.Set(k, substitution) + } } - substituteStringArrayWithConfigDirTemplate(compilerOptions.RootDirs, basePath) - substituteStringArrayWithConfigDirTemplate(compilerOptions.TypeRoots, basePath) - + if rootDirs := getSubstitutedStringArrayWithConfigDirTemplate(compilerOptions.RootDirs, basePath); rootDirs != nil { + compilerOptions.RootDirs = rootDirs + } + if typeRoots := getSubstitutedStringArrayWithConfigDirTemplate(compilerOptions.TypeRoots, basePath); typeRoots != nil { + compilerOptions.TypeRoots = typeRoots + } if startsWithConfigDirTemplate(compilerOptions.GenerateCpuProfile) { compilerOptions.GenerateCpuProfile = getSubstitutedPathWithConfigDirTemplate(compilerOptions.GenerateCpuProfile, basePath) } diff --git a/testdata/baselines/reference/tsc/extends/configDir-template-with-commandline.js b/testdata/baselines/reference/tsc/extends/configDir-template-with-commandline.js index 95cdd5bd96..51d57078f4 100644 --- a/testdata/baselines/reference/tsc/extends/configDir-template-with-commandline.js +++ b/testdata/baselines/reference/tsc/extends/configDir-template-with-commandline.js @@ -62,6 +62,14 @@ Output:: 3 "compilerOptions": {    ~~~~~~~~~~~~~~~~~ +../../tslibs/TS/Lib/lib.d.ts + Default library for target 'ES5' +types/sometype.ts + Imported via @myscope/sometype from file 'main.ts' +main.ts + Part of 'files' list in tsconfig.json +src/secondary.ts + Matched by include pattern '${configDir}/src' in 'tsconfig.json' Found 2 errors in the same file, starting at: tsconfig.json:3 diff --git a/testdata/baselines/reference/tsc/extends/configDir-template.js b/testdata/baselines/reference/tsc/extends/configDir-template.js index 680bf58192..8faa5c8317 100644 --- a/testdata/baselines/reference/tsc/extends/configDir-template.js +++ b/testdata/baselines/reference/tsc/extends/configDir-template.js @@ -62,6 +62,14 @@ Output:: 3 "compilerOptions": {    ~~~~~~~~~~~~~~~~~ +../../tslibs/TS/Lib/lib.d.ts + Default library for target 'ES5' +types/sometype.ts + Imported via @myscope/sometype from file 'main.ts' +main.ts + Part of 'files' list in tsconfig.json +src/secondary.ts + Matched by include pattern '${configDir}/src' in 'tsconfig.json' Found 2 errors in the same file, starting at: tsconfig.json:3 diff --git a/testdata/baselines/reference/tsc/incremental/when-passing-filename-for-buildinfo-on-commandline.js b/testdata/baselines/reference/tsc/incremental/when-passing-filename-for-buildinfo-on-commandline.js index 11472a7f12..8cef762508 100644 --- a/testdata/baselines/reference/tsc/incremental/when-passing-filename-for-buildinfo-on-commandline.js +++ b/testdata/baselines/reference/tsc/incremental/when-passing-filename-for-buildinfo-on-commandline.js @@ -17,6 +17,10 @@ export const x = 10; tsgo --incremental --tsBuildInfoFile .tsbuildinfo --explainFiles ExitStatus:: Success Output:: +../../tslibs/TS/Lib/lib.d.ts + Default library for target 'ES5' +src/main.ts + Matched by include pattern 'src/**/*.ts' in 'tsconfig.json' //// [/home/src/tslibs/TS/Lib/lib.d.ts] *Lib* /// interface Boolean {} @@ -94,6 +98,10 @@ Edit [0]:: no change tsgo --incremental --tsBuildInfoFile .tsbuildinfo --explainFiles ExitStatus:: Success Output:: +../../tslibs/TS/Lib/lib.d.ts + Default library for target 'ES5' +src/main.ts + Matched by include pattern 'src/**/*.ts' in 'tsconfig.json' SemanticDiagnostics:: Signatures:: From 2418c517d0ed1c3271f97f5c801db2072d6e604b Mon Sep 17 00:00:00 2001 From: Sheetal Nandi Date: Thu, 7 Aug 2025 22:43:31 -0700 Subject: [PATCH 2/6] Preprocessing diagnsotics with file include reason explainations --- internal/ast/ast.go | 4 - internal/compiler/emitHost.go | 4 + internal/compiler/fileInclude.go | 334 ++++++++++++------ internal/compiler/fileloader.go | 23 +- internal/compiler/filesparser.go | 4 + internal/compiler/program.go | 113 ++++-- internal/modulespecifiers/specifiers.go | 4 +- internal/modulespecifiers/types.go | 2 +- internal/transformers/jsxtransforms/jsx.go | 2 +- .../tstransforms/importelision_test.go | 4 + internal/tsoptions/errors.go | 6 +- internal/tsoptions/tsconfigparsing.go | 46 ++- 12 files changed, 391 insertions(+), 155 deletions(-) diff --git a/internal/ast/ast.go b/internal/ast/ast.go index 87e9c79c53..831fb1b378 100644 --- a/internal/ast/ast.go +++ b/internal/ast/ast.go @@ -10374,10 +10374,6 @@ func (node *SourceFile) Path() tspath.Path { return node.parseOptions.Path } -func (node *SourceFile) OriginalFileName() string { - return node.FileName() // !!! redirect source files -} - func (node *SourceFile) Imports() []*LiteralLikeNode { return node.imports } diff --git a/internal/compiler/emitHost.go b/internal/compiler/emitHost.go index b2eeed388f..cf6f68dee6 100644 --- a/internal/compiler/emitHost.go +++ b/internal/compiler/emitHost.go @@ -74,6 +74,10 @@ func (host *emitHost) GetPackageJsonInfo(pkgJsonPath string) modulespecifiers.Pa return host.program.GetPackageJsonInfo(pkgJsonPath) } +func (host *emitHost) GetSourceOfProjectReferenceIfOutputIncluded(file ast.HasFileName) string { + return host.program.GetSourceOfProjectReferenceIfOutputIncluded(file) +} + func (host *emitHost) GetOutputAndProjectReference(path tspath.Path) *tsoptions.OutputDtsAndProjectReference { return host.program.GetOutputAndProjectReference(path) } diff --git a/internal/compiler/fileInclude.go b/internal/compiler/fileInclude.go index 396514c6fb..832a9b8eef 100644 --- a/internal/compiler/fileInclude.go +++ b/internal/compiler/fileInclude.go @@ -8,6 +8,7 @@ import ( "github.com/microsoft/typescript-go/internal/core" "github.com/microsoft/typescript-go/internal/diagnostics" "github.com/microsoft/typescript-go/internal/module" + "github.com/microsoft/typescript-go/internal/tsoptions" "github.com/microsoft/typescript-go/internal/tspath" ) @@ -31,8 +32,17 @@ type fileIncludeReason struct { kind fileIncludeKind data any + // Uses relative file name + relativeFileNameDiag *ast.Diagnostic + relativeFileNameDiagOnce sync.Once + + // Uses file name as is diag *ast.Diagnostic diagOnce sync.Once + + // related information + relatedInfo *ast.Diagnostic + relatedInfoOnce sync.Once } type referencedFileData struct { @@ -44,10 +54,11 @@ type referencedFileData struct { } type referenceFileLocation struct { - file *ast.SourceFile - node *ast.Node - ref *ast.FileReference - packageId module.PackageId + file *ast.SourceFile + node *ast.Node + ref *ast.FileReference + packageId module.PackageId + isSynthetic bool } func (r *referenceFileLocation) text() string { @@ -58,6 +69,14 @@ func (r *referenceFileLocation) text() string { } } +func (r *referenceFileLocation) diagnosticAt(message *diagnostics.Message, args ...any) *ast.Diagnostic { + if r.node != nil { + return tsoptions.CreateDiagnosticForNodeInSourceFile(r.file, r.node, message, args...) + } else { + return ast.NewDiagnostic(r.file, r.ref.TextRange, message, args...) + } +} + type automaticTypeDirectiveFileData struct { typeReference string packageId module.PackageId @@ -73,7 +92,7 @@ func (r *fileIncludeReason) asLibFileIndex() (int, bool) { } func (r *fileIncludeReason) isReferencedFile() bool { - return r.kind <= fileIncludeKindLibReferenceDirective + return r != nil && r.kind <= fileIncludeKindLibReferenceDirective } func (r *fileIncludeReason) asReferencedFileData() *referencedFileData { @@ -84,96 +103,158 @@ func (r *fileIncludeReason) asAutomaticTypeDirectiveFileData() *automaticTypeDir return r.data.(*automaticTypeDirectiveFileData) } -func (r *fileIncludeReason) toDiagnostic(program *Program, toFileName func(string) string) *ast.Diagnostic { - r.diagOnce.Do(func() { - if r.isReferencedFile() { - r.diag = r.toReferenceDiagnostic(program, toFileName) - return - } +func (r *fileIncludeReason) getReferencedLocation(program *Program) *referenceFileLocation { + ref := r.asReferencedFileData() + ref.locationOnce.Do(func() { + file := program.GetSourceFileByPath(ref.file) switch r.kind { - case fileIncludeKindRootFile: - if program.opts.Config.ConfigFile != nil { - config := program.opts.Config - fileName := tspath.GetNormalizedAbsolutePath(config.FileNames()[r.asIndex()], program.GetCurrentDirectory()) - if matchedFileSpec := config.GetMatchedFileSpec(fileName); matchedFileSpec != "" { - r.diag = ast.NewCompilerDiagnostic(diagnostics.Part_of_files_list_in_tsconfig_json, matchedFileSpec, toFileName(fileName)) - } else if matchedIncludeSpec, isDefaultIncludeSpec := config.GetMatchedIncludeSpec(fileName); matchedIncludeSpec != "" { - if isDefaultIncludeSpec { - r.diag = ast.NewCompilerDiagnostic(diagnostics.Matched_by_default_include_pattern_Asterisk_Asterisk_Slash_Asterisk) - } else { - r.diag = ast.NewCompilerDiagnostic(diagnostics.Matched_by_include_pattern_0_in_1, matchedIncludeSpec, toFileName(config.ConfigName())) + case fileIncludeKindImport: + var specifier *ast.Node + var isSynthetic bool + if ref.synthetic != nil { + specifier = ref.synthetic + isSynthetic = true + } else if ref.index < len(file.Imports()) { + specifier = file.Imports()[ref.index] + } else { + augIndex := len(file.Imports()) + for _, imp := range file.ModuleAugmentations { + if imp.Kind == ast.KindStringLiteral { + if augIndex == ref.index { + specifier = imp + break + } + augIndex++ } - } else { - r.diag = ast.NewCompilerDiagnostic(diagnostics.Root_file_specified_for_compilation) } - } else { - r.diag = ast.NewCompilerDiagnostic(diagnostics.Root_file_specified_for_compilation) } - case fileIncludeKindSourceFromProjectReference: - case fileIncludeKindOutputFromProjectReference: - diag := core.IfElse( - r.kind == fileIncludeKindOutputFromProjectReference, - diagnostics.Output_from_referenced_project_0_included_because_module_is_specified_as_none, - diagnostics.Source_from_referenced_project_0_included_because_module_is_specified_as_none, - ) - referencedResolvedRef := program.projectReferenceFileMapper.getResolvedProjectReferences()[r.asIndex()] - r.diag = ast.NewCompilerDiagnostic(diag, toFileName(referencedResolvedRef.ConfigName())) - case fileIncludeKindAutomaticTypeDirectiveFile: - data := r.asAutomaticTypeDirectiveFileData() - if program.Options().Types != nil { - if data.packageId.Name != "" { - r.diag = ast.NewCompilerDiagnostic(diagnostics.Entry_point_of_type_library_0_specified_in_compilerOptions_with_packageId_1, data.typeReference, data.packageId.String()) + resolution := program.GetResolvedModuleFromModuleSpecifier(file, specifier) + ref.location = &referenceFileLocation{ + file: file, + node: specifier, + packageId: resolution.PackageId, + isSynthetic: isSynthetic, + } + case fileIncludeKindReferenceFile: + ref.location = &referenceFileLocation{ + file: file, + ref: file.ReferencedFiles[ref.index], + } + case fileIncludeKindTypeReferenceDirective: + ref.location = &referenceFileLocation{ + file: file, + ref: file.TypeReferenceDirectives[ref.index], + } + case fileIncludeKindLibReferenceDirective: + ref.location = &referenceFileLocation{ + file: file, + ref: file.LibReferenceDirectives[ref.index], + } + default: + panic(fmt.Sprintf("unknown reason: %v", r.kind)) + } + }) + return ref.location +} + +func (r *fileIncludeReason) toDiagnostic(program *Program, relativeFileName bool) *ast.Diagnostic { + if relativeFileName { + r.relativeFileNameDiagOnce.Do(func() { + r.relativeFileNameDiag = r.computeDiagnostic(program, func(fileName string) string { + return tspath.GetRelativePathFromDirectory(program.GetCurrentDirectory(), fileName, program.comparePathsOptions) + }) + }) + return r.relativeFileNameDiag + } else { + r.diagOnce.Do(func() { + r.diag = r.computeDiagnostic(program, func(fileName string) string { return fileName }) + }) + return r.diag + } +} + +func (r *fileIncludeReason) computeDiagnostic(program *Program, toFileName func(string) string) *ast.Diagnostic { + if r.isReferencedFile() { + return r.computeReferenceFileDiagnostic(program, toFileName) + } + switch r.kind { + case fileIncludeKindRootFile: + if program.opts.Config.ConfigFile != nil { + config := program.opts.Config + fileName := tspath.GetNormalizedAbsolutePath(config.FileNames()[r.asIndex()], program.GetCurrentDirectory()) + if matchedFileSpec := config.GetMatchedFileSpec(fileName); matchedFileSpec != "" { + return ast.NewCompilerDiagnostic(diagnostics.Part_of_files_list_in_tsconfig_json, matchedFileSpec, toFileName(fileName)) + } else if matchedIncludeSpec, isDefaultIncludeSpec := config.GetMatchedIncludeSpec(fileName); matchedIncludeSpec != "" { + if isDefaultIncludeSpec { + return ast.NewCompilerDiagnostic(diagnostics.Matched_by_default_include_pattern_Asterisk_Asterisk_Slash_Asterisk) } else { - r.diag = ast.NewCompilerDiagnostic(diagnostics.Entry_point_of_type_library_0_specified_in_compilerOptions, data.typeReference) + return ast.NewCompilerDiagnostic(diagnostics.Matched_by_include_pattern_0_in_1, matchedIncludeSpec, toFileName(config.ConfigName())) } } else { - if data.packageId.Name != "" { - r.diag = ast.NewCompilerDiagnostic(diagnostics.Entry_point_for_implicit_type_library_0_with_packageId_1, data.typeReference, data.packageId.String()) - } else { - r.diag = ast.NewCompilerDiagnostic(diagnostics.Entry_point_for_implicit_type_library_0, data.typeReference) - } + return ast.NewCompilerDiagnostic(diagnostics.Root_file_specified_for_compilation) } - case fileIncludeKindLibFile: - index, ok := r.asLibFileIndex() - if ok { - r.diag = ast.NewCompilerDiagnostic(diagnostics.Library_0_specified_in_compilerOptions, program.Options().Lib[index]) + } else { + return ast.NewCompilerDiagnostic(diagnostics.Root_file_specified_for_compilation) + } + case fileIncludeKindSourceFromProjectReference, + fileIncludeKindOutputFromProjectReference: + diag := core.IfElse( + r.kind == fileIncludeKindOutputFromProjectReference, + diagnostics.Output_from_referenced_project_0_included_because_module_is_specified_as_none, + diagnostics.Source_from_referenced_project_0_included_because_module_is_specified_as_none, + ) + referencedResolvedRef := program.projectReferenceFileMapper.getResolvedProjectReferences()[r.asIndex()] + return ast.NewCompilerDiagnostic(diag, toFileName(referencedResolvedRef.ConfigName())) + case fileIncludeKindAutomaticTypeDirectiveFile: + data := r.asAutomaticTypeDirectiveFileData() + if program.Options().Types != nil { + if data.packageId.Name != "" { + return ast.NewCompilerDiagnostic(diagnostics.Entry_point_of_type_library_0_specified_in_compilerOptions_with_packageId_1, data.typeReference, data.packageId.String()) } else { - target := program.Options().GetEmitScriptTarget().String() - if target != "" { - r.diag = ast.NewCompilerDiagnostic(diagnostics.Default_library_for_target_0, target) - } else { - r.diag = ast.NewCompilerDiagnostic(diagnostics.Default_library) - } + return ast.NewCompilerDiagnostic(diagnostics.Entry_point_of_type_library_0_specified_in_compilerOptions, data.typeReference) + } + } else { + if data.packageId.Name != "" { + return ast.NewCompilerDiagnostic(diagnostics.Entry_point_for_implicit_type_library_0_with_packageId_1, data.typeReference, data.packageId.String()) + } else { + return ast.NewCompilerDiagnostic(diagnostics.Entry_point_for_implicit_type_library_0, data.typeReference) } - default: - panic(fmt.Sprintf("unknown reason: %v", r.kind)) } - }) - return r.diag + case fileIncludeKindLibFile: + if index, ok := r.asLibFileIndex(); ok { + return ast.NewCompilerDiagnostic(diagnostics.Library_0_specified_in_compilerOptions, program.Options().Lib[index]) + } else if target := program.Options().GetEmitScriptTarget().String(); target != "" { + return ast.NewCompilerDiagnostic(diagnostics.Default_library_for_target_0, target) + } else { + return ast.NewCompilerDiagnostic(diagnostics.Default_library) + } + default: + panic(fmt.Sprintf("unknown reason: %v", r.kind)) + } } -func (r *fileIncludeReason) toReferenceDiagnostic(program *Program, toFileName func(string) string) *ast.Diagnostic { +func (r *fileIncludeReason) computeReferenceFileDiagnostic(program *Program, toFileName func(string) string) *ast.Diagnostic { referenceLocation := r.getReferencedLocation(program) referenceText := referenceLocation.text() switch r.kind { case fileIncludeKindImport: - if specifier, ok := program.importHelpersImportSpecifiers[referenceLocation.file.Path()]; ok && specifier == referenceLocation.node { + if !referenceLocation.isSynthetic { if referenceLocation.packageId.Name != "" { - return ast.NewCompilerDiagnostic(diagnostics.Imported_via_0_from_file_1_with_packageId_2_to_import_importHelpers_as_specified_in_compilerOptions, referenceText, toFileName(referenceLocation.file.FileName()), referenceLocation.packageId.String()) + return ast.NewCompilerDiagnostic(diagnostics.Imported_via_0_from_file_1_with_packageId_2, referenceText, toFileName(referenceLocation.file.FileName()), referenceLocation.packageId.String()) } else { - return ast.NewCompilerDiagnostic(diagnostics.Imported_via_0_from_file_1_to_import_importHelpers_as_specified_in_compilerOptions, referenceText, toFileName(referenceLocation.file.FileName())) + return ast.NewCompilerDiagnostic(diagnostics.Imported_via_0_from_file_1, referenceText, toFileName(referenceLocation.file.FileName())) } - } else if jsxSpecifier, ok := program.jsxRuntimeImportSpecifiers[referenceLocation.file.Path()]; ok && jsxSpecifier.specifier == referenceLocation.node { + } else if specifier, ok := program.importHelpersImportSpecifiers[referenceLocation.file.Path()]; ok && specifier == referenceLocation.node { if referenceLocation.packageId.Name != "" { - return ast.NewCompilerDiagnostic(diagnostics.Imported_via_0_from_file_1_with_packageId_2_to_import_jsx_and_jsxs_factory_functions, referenceText, toFileName(referenceLocation.file.FileName()), referenceLocation.packageId.String()) + return ast.NewCompilerDiagnostic(diagnostics.Imported_via_0_from_file_1_with_packageId_2_to_import_importHelpers_as_specified_in_compilerOptions, referenceText, toFileName(referenceLocation.file.FileName()), referenceLocation.packageId.String()) } else { - return ast.NewCompilerDiagnostic(diagnostics.Imported_via_0_from_file_1_to_import_jsx_and_jsxs_factory_functions, referenceText, toFileName(referenceLocation.file.FileName())) + return ast.NewCompilerDiagnostic(diagnostics.Imported_via_0_from_file_1_to_import_importHelpers_as_specified_in_compilerOptions, referenceText, toFileName(referenceLocation.file.FileName())) } } else { if referenceLocation.packageId.Name != "" { - return ast.NewCompilerDiagnostic(diagnostics.Imported_via_0_from_file_1_with_packageId_2, referenceText, toFileName(referenceLocation.file.FileName()), referenceLocation.packageId.String()) + return ast.NewCompilerDiagnostic(diagnostics.Imported_via_0_from_file_1_with_packageId_2_to_import_jsx_and_jsxs_factory_functions, referenceText, toFileName(referenceLocation.file.FileName()), referenceLocation.packageId.String()) } else { - return ast.NewCompilerDiagnostic(diagnostics.Imported_via_0_from_file_1, referenceText, toFileName(referenceLocation.file.FileName())) + return ast.NewCompilerDiagnostic(diagnostics.Imported_via_0_from_file_1_to_import_jsx_and_jsxs_factory_functions, referenceText, toFileName(referenceLocation.file.FileName())) } } case fileIncludeKindReferenceFile: @@ -186,59 +267,86 @@ func (r *fileIncludeReason) toReferenceDiagnostic(program *Program, toFileName f } case fileIncludeKindLibReferenceDirective: return ast.NewCompilerDiagnostic(diagnostics.Library_referenced_via_0_from_file_1, referenceText, toFileName(referenceLocation.file.FileName())) - default: panic(fmt.Sprintf("unknown reason: %v", r.kind)) } } -func (r *fileIncludeReason) getReferencedLocation(program *Program) *referenceFileLocation { - ref := r.asReferencedFileData() - ref.locationOnce.Do(func() { - file := program.GetSourceFileByPath(ref.file) - switch r.kind { - case fileIncludeKindImport: - var specifier *ast.Node - if ref.synthetic != nil { - specifier = ref.synthetic - } else if ref.index < len(file.Imports()) { - specifier = file.Imports()[ref.index] - } else { - augIndex := len(file.Imports()) - for _, imp := range file.ModuleAugmentations { - if imp.Kind == ast.KindStringLiteral { - if augIndex == ref.index { - specifier = imp - break - } - augIndex++ - } - } +func (r *fileIncludeReason) toRelatedInfo(program *Program, getCompilerOptionsObjectLiteralSyntax func() *ast.ObjectLiteralExpression) *ast.Diagnostic { + r.relatedInfoOnce.Do(func() { + r.relatedInfo = r.computeRelatedInfo(program, getCompilerOptionsObjectLiteralSyntax) + }) + return r.relatedInfo +} + +func (r *fileIncludeReason) computeRelatedInfo(program *Program, getCompilerOptionsObjectLiteralSyntax func() *ast.ObjectLiteralExpression) *ast.Diagnostic { + if r.isReferencedFile() { + return r.computeReferenceFileRelatedInfo(program) + } + if program.opts.Config.ConfigFile == nil { + return nil + } + config := program.opts.Config + switch r.kind { + case fileIncludeKindRootFile: + fileName := tspath.GetNormalizedAbsolutePath(config.FileNames()[r.asIndex()], program.GetCurrentDirectory()) + if matchedFileSpec := config.GetMatchedFileSpec(fileName); matchedFileSpec != "" { + if filesNode := tsoptions.GetTsConfigPropArrayElementValue(config.ConfigFile.SourceFile, "files", matchedFileSpec); filesNode != nil { + return tsoptions.CreateDiagnosticForNodeInSourceFile(config.ConfigFile.SourceFile, filesNode.AsNode(), diagnostics.File_is_matched_by_files_list_specified_here) } - resolution := program.GetResolvedModuleFromModuleSpecifier(file, specifier) - ref.location = &referenceFileLocation{ - file: file, - node: specifier, - packageId: resolution.PackageId, + } else if matchedIncludeSpec, isDefaultIncludeSpec := config.GetMatchedIncludeSpec(fileName); matchedIncludeSpec != "" && !isDefaultIncludeSpec { + if includeNode := tsoptions.GetTsConfigPropArrayElementValue(config.ConfigFile.SourceFile, "include", matchedIncludeSpec); includeNode != nil { + return tsoptions.CreateDiagnosticForNodeInSourceFile(config.ConfigFile.SourceFile, includeNode.AsNode(), diagnostics.File_is_matched_by_include_pattern_specified_here) } - case fileIncludeKindReferenceFile: - ref.location = &referenceFileLocation{ - file: file, - ref: file.ReferencedFiles[ref.index], + } + case fileIncludeKindSourceFromProjectReference, + fileIncludeKindOutputFromProjectReference: + return tsoptions.CreateDiagnosticAtReferenceSyntax( + config, + r.asIndex(), + core.IfElse( + r.kind == fileIncludeKindOutputFromProjectReference, + diagnostics.File_is_output_from_referenced_project_specified_here, + diagnostics.File_is_source_from_referenced_project_specified_here, + )) + case fileIncludeKindAutomaticTypeDirectiveFile: + if program.Options().Types != nil { + data := r.asAutomaticTypeDirectiveFileData() + if typesSyntax := tsoptions.GetOptionsSyntaxByArrayElementValue(getCompilerOptionsObjectLiteralSyntax(), "types", data.typeReference); typesSyntax != nil { + return tsoptions.CreateDiagnosticForNodeInSourceFile(config.ConfigFile.SourceFile, typesSyntax.AsNode(), diagnostics.File_is_entry_point_of_type_library_specified_here) } - case fileIncludeKindTypeReferenceDirective: - ref.location = &referenceFileLocation{ - file: file, - ref: file.TypeReferenceDirectives[ref.index], + } + case fileIncludeKindLibFile: + if index, ok := r.asLibFileIndex(); ok { + if libSyntax := tsoptions.GetOptionsSyntaxByArrayElementValue(getCompilerOptionsObjectLiteralSyntax(), "lib", program.Options().Lib[index]); libSyntax != nil { + return tsoptions.CreateDiagnosticForNodeInSourceFile(config.ConfigFile.SourceFile, libSyntax.AsNode(), diagnostics.File_is_library_specified_here) } - case fileIncludeKindLibReferenceDirective: - ref.location = &referenceFileLocation{ - file: file, - ref: file.LibReferenceDirectives[ref.index], + } else if target := program.Options().GetEmitScriptTarget().String(); target != "" { + if targetValueSyntax := tsoptions.ForEachPropertyAssignment(getCompilerOptionsObjectLiteralSyntax(), "target", tsoptions.GetCallbackForFindingPropertyAssignmentByValue(target)); targetValueSyntax != nil { + return tsoptions.CreateDiagnosticForNodeInSourceFile(config.ConfigFile.SourceFile, targetValueSyntax.AsNode(), diagnostics.File_is_default_library_for_target_specified_here) } - default: - panic(fmt.Sprintf("unknown reason: %v", r.kind)) } - }) - return ref.location + default: + panic(fmt.Sprintf("unknown reason: %v", r.kind)) + } + return nil +} + +func (r *fileIncludeReason) computeReferenceFileRelatedInfo(program *Program) *ast.Diagnostic { + referenceLocation := r.getReferencedLocation(program) + if referenceLocation.isSynthetic { + return nil + } + switch r.kind { + case fileIncludeKindImport: + return referenceLocation.diagnosticAt(diagnostics.File_is_included_via_import_here) + case fileIncludeKindReferenceFile: + return referenceLocation.diagnosticAt(diagnostics.File_is_included_via_reference_here) + case fileIncludeKindTypeReferenceDirective: + return referenceLocation.diagnosticAt(diagnostics.File_is_included_via_type_library_reference_here) + case fileIncludeKindLibReferenceDirective: + return referenceLocation.diagnosticAt(diagnostics.File_is_included_via_library_reference_here) + default: + panic(fmt.Sprintf("unknown reason: %v", r.kind)) + } } diff --git a/internal/compiler/fileloader.go b/internal/compiler/fileloader.go index 356300ef51..a8f162622a 100644 --- a/internal/compiler/fileloader.go +++ b/internal/compiler/fileloader.go @@ -153,16 +153,35 @@ func processAllProgramFiles( file := task.file path := task.path if file == nil { + // !!! sheetal file preprocessing diagnostic explaining getSourceFileFromReferenceWorker missingFiles = append(missingFiles, task.normalizedFilePath) return } + + // !!! sheetal todo porting file case errors + // if _, ok := filesByPath[path]; ok { + // Check if it differs only in drive letters its ok to ignore that error: + // const checkedAbsolutePath = getNormalizedAbsolutePathWithoutRoot(checkedName, currentDirectory); + // const inputAbsolutePath = getNormalizedAbsolutePathWithoutRoot(fileName, currentDirectory); + // if (checkedAbsolutePath !== inputAbsolutePath) { + // reportFileNamesDifferOnlyInCasingError(fileName, file, reason); + // } + // } else if loader.comparePathsOptions.UseCaseSensitiveFileNames { + // pathIgnoreCase := tspath.ToPath(file.FileName(), loader.comparePathsOptions.CurrentDirectory, false) + // // for case-sensitsive file systems check if we've already seen some file with similar filename ignoring case + // if _, ok := filesByNameIgnoreCase[pathIgnoreCase]; ok { + // reportFileNamesDifferOnlyInCasingError(fileName, existingFile, reason); + // } else { + // filesByNameIgnoreCase[pathIgnoreCase] = file + // } + // } + if task.isLib { libFiles = append(libFiles, file) libFileSet.Add(path) } else { files = append(files, file) } - filesByPath[path] = file fileIncludeReasons[path] = task.allIncludeReasons resolvedModules[path] = task.resolutionsInFile @@ -455,6 +474,8 @@ func (p *fileLoader) resolveTypeReferenceDirectives(t *parseTask) { }, }, }, false) + } else { + // !! sheetal Cannot_find_type_definition_file_for_0 } } diff --git a/internal/compiler/filesparser.go b/internal/compiler/filesparser.go index 2988ec4b2e..07f7d57339 100644 --- a/internal/compiler/filesparser.go +++ b/internal/compiler/filesparser.go @@ -97,6 +97,10 @@ func (t *parseTask) load(loader *fileLoader) { }, }, }, true) + } else { + // !!! sheetal file preprocessing diagnostic explaining + // Cannot_find_lib_definition_for_0_Did_you_mean_1 + // filePreprocessingLibreferenceDiagnostic } } } diff --git a/internal/compiler/program.go b/internal/compiler/program.go index d4e3bf0473..e9ff9ea3b3 100644 --- a/internal/compiler/program.go +++ b/internal/compiler/program.go @@ -102,6 +102,17 @@ func (p *Program) GetRedirectTargets(path tspath.Path) []string { return nil // !!! TODO: project references support } +// gets the original file that was included in program +// this returns original source file name when including output of project reference +// otherwise same name +// Equivalent to originalFileName on SourceFile in Strada +func (p *Program) GetSourceOfProjectReferenceIfOutputIncluded(file ast.HasFileName) string { + if source, ok := p.outputFileToProjectReferenceSource[file.Path()]; ok { + return source + } + return file.FileName() +} + // GetOutputAndProjectReference implements checker.Program. func (p *Program) GetOutputAndProjectReference(path tspath.Path) *tsoptions.OutputDtsAndProjectReference { return p.projectReferenceFileMapper.getOutputAndProjectReference(path) @@ -411,7 +422,7 @@ func (p *Program) verifyCompilerOptions() { createOptionDiagnosticInObjectLiteralSyntax := func(objectLiteral *ast.ObjectLiteralExpression, onKey bool, key1 string, key2 string, message *diagnostics.Message, args ...any) *ast.Diagnostic { diag := tsoptions.ForEachPropertyAssignment(objectLiteral, key1, func(property *ast.PropertyAssignment) *ast.Diagnostic { - return tsoptions.CreateDiagnosticForNodeInSourceFileOrCompilerDiagnostic(sourceFile(), core.IfElse(onKey, property.Name(), property.Initializer), message, args...) + return tsoptions.CreateDiagnosticForNodeInSourceFile(sourceFile(), core.IfElse(onKey, property.Name(), property.Initializer), message, args...) }, key2) if diag != nil { p.programDiagnostics = append(p.programDiagnostics, diag) @@ -423,7 +434,7 @@ func (p *Program) verifyCompilerOptions() { compilerOptionsProperty := getCompilerOptionsPropertySyntax() var diag *ast.Diagnostic if compilerOptionsProperty != nil { - diag = tsoptions.CreateDiagnosticForNodeInSourceFileOrCompilerDiagnostic(sourceFile(), compilerOptionsProperty.Name(), message, args...) + diag = tsoptions.CreateDiagnosticForNodeInSourceFile(sourceFile(), compilerOptionsProperty.Name(), message, args...) } else { diag = ast.NewCompilerDiagnostic(message, args...) } @@ -555,9 +566,10 @@ func (p *Program) verifyCompilerOptions() { for _, file := range p.files { if sourceFileMayBeEmitted(file, p, false) && !rootPaths.Has(file.Path()) { - p.programDiagnostics = append(p.programDiagnostics, ast.NewDiagnostic( + p.programDiagnostics = append(p.programDiagnostics, p.createDiagnosticExplainingFile( + getCompilerOptionsObjectLiteralSyntax, file, - core.TextRange{}, + nil, diagnostics.File_0_is_not_listed_within_the_file_list_of_project_1_Projects_must_list_all_files_or_use_an_include_pattern, file.FileName(), configFilePath(), @@ -591,7 +603,7 @@ func (p *Program) verifyCompilerOptions() { if ast.IsArrayLiteralExpression(initializer) { elements := initializer.AsArrayLiteralExpression().Elements if elements != nil && len(elements.Nodes) > valueIndex { - diag := tsoptions.CreateDiagnosticForNodeInSourceFileOrCompilerDiagnostic(sourceFile(), elements.Nodes[valueIndex], message, args...) + diag := tsoptions.CreateDiagnosticForNodeInSourceFile(sourceFile(), elements.Nodes[valueIndex], message, args...) p.programDiagnostics = append(p.programDiagnostics, diag) return diag } @@ -677,6 +689,7 @@ func (p *Program) verifyCompilerOptions() { options.SourceRoot != "" || options.MapRoot != "" || (options.GetEmitDeclarations() && options.DeclarationDir != "") { + // !!! sheetal checkSourceFilesBelongToPath - for root Dir and configFile - explaining why file is in the program dir := p.CommonSourceDirectory() if options.OutDir != "" && dir == "" && core.Some(p.files, func(f *ast.SourceFile) bool { return tspath.GetRootLength(f.FileName()) > 1 }) { createDiagnosticForOptionName(diagnostics.Cannot_find_the_common_subdirectory_path_for_the_input_files, "outDir", "") @@ -831,19 +844,7 @@ func (p *Program) IsEmitBlocked(emitFileName string) bool { func (p *Program) verifyProjectReferences() { buildInfoFileName := core.IfElse(!p.Options().SuppressOutputPathCheck.IsTrue(), p.opts.Config.GetBuildInfoFileName(), "") createDiagnosticForReference := func(config *tsoptions.ParsedCommandLine, index int, message *diagnostics.Message, args ...any) { - var sourceFile *ast.SourceFile - if config.ConfigFile != nil { - sourceFile = config.ConfigFile.SourceFile - } - diag := tsoptions.ForEachTsConfigPropArray(sourceFile, "references", func(property *ast.PropertyAssignment) *ast.Diagnostic { - if ast.IsArrayLiteralExpression(property.Initializer) { - value := property.Initializer.AsArrayLiteralExpression().Elements.Nodes - if len(value) > index { - return tsoptions.CreateDiagnosticForNodeInSourceFileOrCompilerDiagnostic(sourceFile, value[index], message, args...) - } - } - return nil - }) + diag := tsoptions.CreateDiagnosticAtReferenceSyntax(config, index, message, args...) if diag == nil { diag = ast.NewCompilerDiagnostic(message, args...) } @@ -1472,7 +1473,7 @@ func (p *Program) ExplainFiles(writer io.Writer) { for _, file := range p.GetSourceFiles() { fmt.Fprintln(writer, toRelativeFileName(file.FileName())) for _, reason := range p.fileIncludeReasons[file.Path()] { - fmt.Fprintln(writer, " ", reason.toDiagnostic(p, toRelativeFileName).Message()) + fmt.Fprintln(writer, " ", reason.toDiagnostic(p, true).Message()) } for _, diag := range p.explainRedirectAndImpliedFormat(file, toRelativeFileName) { fmt.Fprintln(writer, " ", diag.Message()) @@ -1485,7 +1486,7 @@ func (p *Program) explainRedirectAndImpliedFormat( toFileName func(fileName string) string, ) []*ast.Diagnostic { var result []*ast.Diagnostic - if source, ok := p.outputFileToProjectReferenceSource[file.Path()]; ok { + if source := p.GetSourceOfProjectReferenceIfOutputIncluded(file); source != file.FileName() { result = append(result, ast.NewCompilerDiagnostic( diagnostics.File_is_output_of_project_reference_source_0, toFileName(source), @@ -1524,6 +1525,78 @@ func (p *Program) explainRedirectAndImpliedFormat( return result } +func (p *Program) createDiagnosticExplainingFile( + getCompilerOptionsObjectLiteralSyntax func() *ast.ObjectLiteralExpression, + file *ast.SourceFile, + diagnosticReason *fileIncludeReason, + diag *diagnostics.Message, + args ...any, +) *ast.Diagnostic { + var chain []*ast.Diagnostic + var relatedInfo []*ast.Diagnostic + var redirectInfo []*ast.Diagnostic + var preferredLocation *fileIncludeReason + var seenReasons collections.Set[*fileIncludeReason] + if diagnosticReason.isReferencedFile() && !diagnosticReason.getReferencedLocation(p).isSynthetic { + preferredLocation = diagnosticReason + } + + processRelatedInfo := func(includeReason *fileIncludeReason) { + if preferredLocation == nil && includeReason.isReferencedFile() && !includeReason.getReferencedLocation(p).isSynthetic { + preferredLocation = includeReason + } else { + info := includeReason.toRelatedInfo(p, getCompilerOptionsObjectLiteralSyntax) + if info != nil { + relatedInfo = append(relatedInfo, info) + } + } + } + processInclude := func(includeReason *fileIncludeReason) { + if !seenReasons.AddIfAbsent(includeReason) { + return + } + chain = append(chain, includeReason.toDiagnostic(p, false)) + processRelatedInfo(includeReason) + } + + // !!! todo sheetal caching + + if file != nil { + reasons := p.fileIncludeReasons[file.Path()] + chain = make([]*ast.Diagnostic, 0, len(reasons)) + for _, reason := range reasons { + processInclude(reason) + } + redirectInfo = p.explainRedirectAndImpliedFormat(file, func(fileName string) string { return fileName }) + } + if diagnosticReason != nil { + processInclude(diagnosticReason) + } + if chain != nil && (preferredLocation == nil || seenReasons.Len() != 1) { + fileReason := ast.NewCompilerDiagnostic(diagnostics.The_file_is_in_the_program_because_Colon) + fileReason.SetMessageChain(chain) + chain = []*ast.Diagnostic{fileReason} + } + if redirectInfo != nil { + chain = append(chain, redirectInfo...) + } + + var result *ast.Diagnostic + if preferredLocation != nil { + result = preferredLocation.getReferencedLocation(p).diagnosticAt(diag, args...) + } + if result == nil { + result = ast.NewCompilerDiagnostic(diag, args...) + } + if chain != nil { + result.SetMessageChain(chain) + } + if relatedInfo != nil { + result.SetRelatedInfo(relatedInfo) + } + return result +} + func (p *Program) GetLibFileFromReference(ref *ast.FileReference) *ast.SourceFile { path, ok := tsoptions.GetLibFileName(ref.FileName) if !ok { diff --git a/internal/modulespecifiers/specifiers.go b/internal/modulespecifiers/specifiers.go index cb377869f1..fb990f12e5 100644 --- a/internal/modulespecifiers/specifiers.go +++ b/internal/modulespecifiers/specifiers.go @@ -34,8 +34,8 @@ func GetModuleSpecifiers( return nil } modulePaths := getAllModulePathsWorker( - getInfo(importingSourceFile.FileName(), host), - moduleSourceFile.OriginalFileName(), + getInfo(host.GetSourceOfProjectReferenceIfOutputIncluded(importingSourceFile), host), + moduleSourceFile.FileName(), host, // compilerOptions, // options, diff --git a/internal/modulespecifiers/types.go b/internal/modulespecifiers/types.go index c6727aa022..309fc8ab6c 100644 --- a/internal/modulespecifiers/types.go +++ b/internal/modulespecifiers/types.go @@ -12,7 +12,6 @@ import ( type SourceFileForSpecifierGeneration interface { Path() tspath.Path FileName() string - OriginalFileName() string Imports() []*ast.StringLiteralLike IsJS() bool } @@ -55,6 +54,7 @@ type ModuleSpecifierGenerationHost interface { GetOutputAndProjectReference(path tspath.Path) *tsoptions.OutputDtsAndProjectReference GetRedirectTargets(path tspath.Path) []string + GetSourceOfProjectReferenceIfOutputIncluded(file ast.HasFileName) string FileExists(path string) bool diff --git a/internal/transformers/jsxtransforms/jsx.go b/internal/transformers/jsxtransforms/jsx.go index 54a2dcecf3..306f181f00 100644 --- a/internal/transformers/jsxtransforms/jsx.go +++ b/internal/transformers/jsxtransforms/jsx.go @@ -47,7 +47,7 @@ func (tx *JSXTransformer) getCurrentFileNameExpression() *ast.Node { }), nil, nil, - tx.Factory().NewStringLiteral(tx.currentSourceFile.OriginalFileName()), + tx.Factory().NewStringLiteral(tx.currentSourceFile.FileName()), ) tx.filenameDeclaration = d return d.AsVariableDeclaration().Name() diff --git a/internal/transformers/tstransforms/importelision_test.go b/internal/transformers/tstransforms/importelision_test.go index f7d8b9c043..aae14ac707 100644 --- a/internal/transformers/tstransforms/importelision_test.go +++ b/internal/transformers/tstransforms/importelision_test.go @@ -76,6 +76,10 @@ func (p *fakeProgram) GetRedirectTargets(path tspath.Path) []string { return nil } +func (p *fakeProgram) GetSourceOfProjectReferenceIfOutputIncluded(file ast.HasFileName) string { + return "" +} + func (p *fakeProgram) GetOutputAndProjectReference(path tspath.Path) *tsoptions.OutputDtsAndProjectReference { return nil } diff --git a/internal/tsoptions/errors.go b/internal/tsoptions/errors.go index 305693a9dd..ef56e1ddb3 100644 --- a/internal/tsoptions/errors.go +++ b/internal/tsoptions/errors.go @@ -78,9 +78,13 @@ func createUnknownOptionError( return CreateDiagnosticForNodeInSourceFileOrCompilerDiagnostic(sourceFile, node, unknownOptionDiagnostic, unknownOptionErrorText) } +func CreateDiagnosticForNodeInSourceFile(sourceFile *ast.SourceFile, node *ast.Node, message *diagnostics.Message, args ...any) *ast.Diagnostic { + return ast.NewDiagnostic(sourceFile, core.NewTextRange(scanner.SkipTrivia(sourceFile.Text(), node.Loc.Pos()), node.End()), message, args...) +} + func CreateDiagnosticForNodeInSourceFileOrCompilerDiagnostic(sourceFile *ast.SourceFile, node *ast.Node, message *diagnostics.Message, args ...any) *ast.Diagnostic { if sourceFile != nil && node != nil { - return ast.NewDiagnostic(sourceFile, core.NewTextRange(scanner.SkipTrivia(sourceFile.Text(), node.Loc.Pos()), node.End()), message, args...) + return CreateDiagnosticForNodeInSourceFile(sourceFile, node, message, args...) } return ast.NewCompilerDiagnostic(message, args...) } diff --git a/internal/tsoptions/tsconfigparsing.go b/internal/tsoptions/tsconfigparsing.go index 7fdfcd4b94..ca6757f048 100644 --- a/internal/tsoptions/tsconfigparsing.go +++ b/internal/tsoptions/tsconfigparsing.go @@ -15,7 +15,6 @@ import ( "github.com/microsoft/typescript-go/internal/jsnum" "github.com/microsoft/typescript-go/internal/module" "github.com/microsoft/typescript-go/internal/parser" - "github.com/microsoft/typescript-go/internal/scanner" "github.com/microsoft/typescript-go/internal/tspath" "github.com/microsoft/typescript-go/internal/vfs" ) @@ -223,7 +222,7 @@ func parseOwnConfigOfJsonSourceFile( propertySetErrors = append(propertySetErrors, err...) } else if option == nil { if keyText == "excludes" { - propertySetErrors = append(propertySetErrors, CreateDiagnosticForNodeInSourceFileOrCompilerDiagnostic(sourceFile, propertyAssignment.Name(), diagnostics.Unknown_option_excludes_Did_you_mean_exclude)) + propertySetErrors = append(propertySetErrors, CreateDiagnosticForNodeInSourceFile(sourceFile, propertyAssignment.Name(), diagnostics.Unknown_option_excludes_Did_you_mean_exclude)) } if core.Find(OptionsDeclarations, func(option *CommandLineOption) bool { return option.Name == keyText }) != nil { rootCompilerOptions = append(rootCompilerOptions, propertyAssignment.Name()) @@ -1186,7 +1185,7 @@ func parseJsonConfigFileContentWorker( } diagnosticMessage := diagnostics.The_files_list_in_config_file_0_is_empty nodeValue := ForEachTsConfigPropArray(sourceFile.SourceFile, "files", func(property *ast.PropertyAssignment) *ast.Node { return property.Initializer }) - errors = append(errors, ast.NewDiagnostic(sourceFile.SourceFile, core.NewTextRange(scanner.SkipTrivia(sourceFile.SourceFile.Text(), nodeValue.Pos()), nodeValue.End()), diagnosticMessage, fileName)) + errors = append(errors, CreateDiagnosticForNodeInSourceFile(sourceFile.SourceFile, nodeValue, diagnosticMessage, fileName)) } else { errors = append(errors, ast.NewCompilerDiagnostic(diagnostics.The_files_list_in_config_file_0_is_empty, configFileName)) } @@ -1336,7 +1335,7 @@ func shouldReportNoInputFiles(fileNames []string, canJsonReportNoInputFiles bool func validateSpecs(specs any, disallowTrailingRecursion bool, jsonSourceFile *ast.SourceFile, specKey string) ([]string, []*ast.Diagnostic) { createDiagnostic := func(message *diagnostics.Message, spec string) *ast.Diagnostic { - element := getTsConfigPropArrayElementValue(jsonSourceFile, specKey, spec) + element := GetTsConfigPropArrayElementValue(jsonSourceFile, specKey, spec) return CreateDiagnosticForNodeInSourceFileOrCompilerDiagnostic(jsonSourceFile, element.AsNode(), message, spec) } var errors []*ast.Diagnostic @@ -1402,15 +1401,11 @@ func invalidDotDotAfterRecursiveWildcard(s string) bool { // \/?$ # matches an optional trailing directory separator at the end of the string. const invalidTrailingRecursionPattern = `(?:^|\/)\*\*\/?$` -func getTsConfigPropArrayElementValue(tsConfigSourceFile *ast.SourceFile, propKey string, elementValue string) *ast.StringLiteral { +func GetTsConfigPropArrayElementValue(tsConfigSourceFile *ast.SourceFile, propKey string, elementValue string) *ast.StringLiteral { + callback := GetCallbackForFindingPropertyAssignmentByValue(elementValue) return ForEachTsConfigPropArray(tsConfigSourceFile, propKey, func(property *ast.PropertyAssignment) *ast.StringLiteral { - if ast.IsArrayLiteralExpression(property.Initializer) { - value := core.Find(property.Initializer.AsArrayLiteralExpression().Elements.Nodes, func(element *ast.Node) bool { - return ast.IsStringLiteral(element) && element.AsStringLiteral().Text == elementValue - }) - if value != nil { - return value.AsStringLiteral() - } + if value := callback(property); value != nil { + return value.AsStringLiteral() } return nil }) @@ -1423,6 +1418,33 @@ func ForEachTsConfigPropArray[T any](tsConfigSourceFile *ast.SourceFile, propKey return nil } +func CreateDiagnosticAtReferenceSyntax(config *ParsedCommandLine, index int, message *diagnostics.Message, args ...any) *ast.Diagnostic { + return ForEachTsConfigPropArray(config.ConfigFile.SourceFile, "references", func(property *ast.PropertyAssignment) *ast.Diagnostic { + if ast.IsArrayLiteralExpression(property.Initializer) { + value := property.Initializer.AsArrayLiteralExpression().Elements.Nodes + if len(value) > index { + return CreateDiagnosticForNodeInSourceFile(config.ConfigFile.SourceFile, value[index], message, args...) + } + } + return nil + }) +} + +func GetCallbackForFindingPropertyAssignmentByValue(value string) func(property *ast.PropertyAssignment) *ast.Node { + return func(property *ast.PropertyAssignment) *ast.Node { + if ast.IsArrayLiteralExpression(property.Initializer) { + return core.Find(property.Initializer.AsArrayLiteralExpression().Elements.Nodes, func(element *ast.Node) bool { + return ast.IsStringLiteral(element) && element.AsStringLiteral().Text == value + }) + } + return nil + } +} + +func GetOptionsSyntaxByArrayElementValue(objectLiteral *ast.ObjectLiteralExpression, propKey string, elementValue string) *ast.Node { + return ForEachPropertyAssignment(objectLiteral, propKey, GetCallbackForFindingPropertyAssignmentByValue(elementValue)) +} + func ForEachPropertyAssignment[T any](objectLiteral *ast.ObjectLiteralExpression, key string, callback func(property *ast.PropertyAssignment) *T, key2 ...string) *T { if objectLiteral != nil { for _, property := range objectLiteral.Properties.Nodes { From 8fa5ed10f2c20657610dea70e8ca1d8d480fd83a Mon Sep 17 00:00:00 2001 From: Sheetal Nandi Date: Mon, 11 Aug 2025 13:30:47 -0700 Subject: [PATCH 3/6] file processing diagnostics --- internal/compiler/fileInclude.go | 120 +++++++-------- internal/compiler/fileloader.go | 57 +++---- internal/compiler/filesparser.go | 35 +++-- internal/compiler/includeprocessor.go | 144 ++++++++++++++++++ internal/compiler/processingDiagnostic.go | 133 ++++++++++++++++ internal/compiler/program.go | 139 ++--------------- ...onWithSymlinks_preserveSymlinks.errors.txt | 5 +- ...hSymlinks_preserveSymlinks.errors.txt.diff | 5 +- ...MissingExports(module=commonjs).errors.txt | 17 +++ ...ngExports(module=commonjs).errors.txt.diff | 21 +++ ...thMissingExports(module=node16).errors.txt | 17 +++ ...singExports(module=node16).errors.txt.diff | 21 --- ...thMissingExports(module=node18).errors.txt | 17 +++ ...singExports(module=node18).errors.txt.diff | 21 --- ...MissingExports(module=nodenext).errors.txt | 17 +++ ...ngExports(module=nodenext).errors.txt.diff | 21 --- .../conformance/typingsLookup3.errors.txt | 15 ++ .../typingsLookup3.errors.txt.diff | 19 --- 18 files changed, 492 insertions(+), 332 deletions(-) create mode 100644 internal/compiler/includeprocessor.go create mode 100644 internal/compiler/processingDiagnostic.go create mode 100644 testdata/baselines/reference/submodule/compiler/tripleSlashTypesReferenceWithMissingExports(module=commonjs).errors.txt create mode 100644 testdata/baselines/reference/submodule/compiler/tripleSlashTypesReferenceWithMissingExports(module=commonjs).errors.txt.diff create mode 100644 testdata/baselines/reference/submodule/compiler/tripleSlashTypesReferenceWithMissingExports(module=node16).errors.txt delete mode 100644 testdata/baselines/reference/submodule/compiler/tripleSlashTypesReferenceWithMissingExports(module=node16).errors.txt.diff create mode 100644 testdata/baselines/reference/submodule/compiler/tripleSlashTypesReferenceWithMissingExports(module=node18).errors.txt delete mode 100644 testdata/baselines/reference/submodule/compiler/tripleSlashTypesReferenceWithMissingExports(module=node18).errors.txt.diff create mode 100644 testdata/baselines/reference/submodule/compiler/tripleSlashTypesReferenceWithMissingExports(module=nodenext).errors.txt delete mode 100644 testdata/baselines/reference/submodule/compiler/tripleSlashTypesReferenceWithMissingExports(module=nodenext).errors.txt.diff create mode 100644 testdata/baselines/reference/submodule/conformance/typingsLookup3.errors.txt delete mode 100644 testdata/baselines/reference/submodule/conformance/typingsLookup3.errors.txt.diff diff --git a/internal/compiler/fileInclude.go b/internal/compiler/fileInclude.go index 832a9b8eef..2e16f018df 100644 --- a/internal/compiler/fileInclude.go +++ b/internal/compiler/fileInclude.go @@ -39,18 +39,12 @@ type fileIncludeReason struct { // Uses file name as is diag *ast.Diagnostic diagOnce sync.Once - - // related information - relatedInfo *ast.Diagnostic - relatedInfoOnce sync.Once } type referencedFileData struct { - file tspath.Path - index int - synthetic *ast.Node - location *referenceFileLocation - locationOnce sync.Once + file tspath.Path + index int + synthetic *ast.Node } type referenceFileLocation struct { @@ -105,56 +99,53 @@ func (r *fileIncludeReason) asAutomaticTypeDirectiveFileData() *automaticTypeDir func (r *fileIncludeReason) getReferencedLocation(program *Program) *referenceFileLocation { ref := r.asReferencedFileData() - ref.locationOnce.Do(func() { - file := program.GetSourceFileByPath(ref.file) - switch r.kind { - case fileIncludeKindImport: - var specifier *ast.Node - var isSynthetic bool - if ref.synthetic != nil { - specifier = ref.synthetic - isSynthetic = true - } else if ref.index < len(file.Imports()) { - specifier = file.Imports()[ref.index] - } else { - augIndex := len(file.Imports()) - for _, imp := range file.ModuleAugmentations { - if imp.Kind == ast.KindStringLiteral { - if augIndex == ref.index { - specifier = imp - break - } - augIndex++ + file := program.GetSourceFileByPath(ref.file) + switch r.kind { + case fileIncludeKindImport: + var specifier *ast.Node + var isSynthetic bool + if ref.synthetic != nil { + specifier = ref.synthetic + isSynthetic = true + } else if ref.index < len(file.Imports()) { + specifier = file.Imports()[ref.index] + } else { + augIndex := len(file.Imports()) + for _, imp := range file.ModuleAugmentations { + if imp.Kind == ast.KindStringLiteral { + if augIndex == ref.index { + specifier = imp + break } + augIndex++ } } - resolution := program.GetResolvedModuleFromModuleSpecifier(file, specifier) - ref.location = &referenceFileLocation{ - file: file, - node: specifier, - packageId: resolution.PackageId, - isSynthetic: isSynthetic, - } - case fileIncludeKindReferenceFile: - ref.location = &referenceFileLocation{ - file: file, - ref: file.ReferencedFiles[ref.index], - } - case fileIncludeKindTypeReferenceDirective: - ref.location = &referenceFileLocation{ - file: file, - ref: file.TypeReferenceDirectives[ref.index], - } - case fileIncludeKindLibReferenceDirective: - ref.location = &referenceFileLocation{ - file: file, - ref: file.LibReferenceDirectives[ref.index], - } - default: - panic(fmt.Sprintf("unknown reason: %v", r.kind)) } - }) - return ref.location + resolution := program.GetResolvedModuleFromModuleSpecifier(file, specifier) + return &referenceFileLocation{ + file: file, + node: specifier, + packageId: resolution.PackageId, + isSynthetic: isSynthetic, + } + case fileIncludeKindReferenceFile: + return &referenceFileLocation{ + file: file, + ref: file.ReferencedFiles[ref.index], + } + case fileIncludeKindTypeReferenceDirective: + return &referenceFileLocation{ + file: file, + ref: file.TypeReferenceDirectives[ref.index], + } + case fileIncludeKindLibReferenceDirective: + return &referenceFileLocation{ + file: file, + ref: file.LibReferenceDirectives[ref.index], + } + default: + panic(fmt.Sprintf("unknown reason: %v", r.kind)) + } } func (r *fileIncludeReason) toDiagnostic(program *Program, relativeFileName bool) *ast.Diagnostic { @@ -234,7 +225,7 @@ func (r *fileIncludeReason) computeDiagnostic(program *Program, toFileName func( } func (r *fileIncludeReason) computeReferenceFileDiagnostic(program *Program, toFileName func(string) string) *ast.Diagnostic { - referenceLocation := r.getReferencedLocation(program) + referenceLocation := program.includeProcessor.getReferenceLocation(r, program) referenceText := referenceLocation.text() switch r.kind { case fileIncludeKindImport: @@ -272,14 +263,7 @@ func (r *fileIncludeReason) computeReferenceFileDiagnostic(program *Program, toF } } -func (r *fileIncludeReason) toRelatedInfo(program *Program, getCompilerOptionsObjectLiteralSyntax func() *ast.ObjectLiteralExpression) *ast.Diagnostic { - r.relatedInfoOnce.Do(func() { - r.relatedInfo = r.computeRelatedInfo(program, getCompilerOptionsObjectLiteralSyntax) - }) - return r.relatedInfo -} - -func (r *fileIncludeReason) computeRelatedInfo(program *Program, getCompilerOptionsObjectLiteralSyntax func() *ast.ObjectLiteralExpression) *ast.Diagnostic { +func (r *fileIncludeReason) toRelatedInfo(program *Program) *ast.Diagnostic { if r.isReferencedFile() { return r.computeReferenceFileRelatedInfo(program) } @@ -312,17 +296,17 @@ func (r *fileIncludeReason) computeRelatedInfo(program *Program, getCompilerOpti case fileIncludeKindAutomaticTypeDirectiveFile: if program.Options().Types != nil { data := r.asAutomaticTypeDirectiveFileData() - if typesSyntax := tsoptions.GetOptionsSyntaxByArrayElementValue(getCompilerOptionsObjectLiteralSyntax(), "types", data.typeReference); typesSyntax != nil { + if typesSyntax := tsoptions.GetOptionsSyntaxByArrayElementValue(program.includeProcessor.getCompilerOptionsObjectLiteralSyntax(program), "types", data.typeReference); typesSyntax != nil { return tsoptions.CreateDiagnosticForNodeInSourceFile(config.ConfigFile.SourceFile, typesSyntax.AsNode(), diagnostics.File_is_entry_point_of_type_library_specified_here) } } case fileIncludeKindLibFile: if index, ok := r.asLibFileIndex(); ok { - if libSyntax := tsoptions.GetOptionsSyntaxByArrayElementValue(getCompilerOptionsObjectLiteralSyntax(), "lib", program.Options().Lib[index]); libSyntax != nil { + if libSyntax := tsoptions.GetOptionsSyntaxByArrayElementValue(program.includeProcessor.getCompilerOptionsObjectLiteralSyntax(program), "lib", program.Options().Lib[index]); libSyntax != nil { return tsoptions.CreateDiagnosticForNodeInSourceFile(config.ConfigFile.SourceFile, libSyntax.AsNode(), diagnostics.File_is_library_specified_here) } } else if target := program.Options().GetEmitScriptTarget().String(); target != "" { - if targetValueSyntax := tsoptions.ForEachPropertyAssignment(getCompilerOptionsObjectLiteralSyntax(), "target", tsoptions.GetCallbackForFindingPropertyAssignmentByValue(target)); targetValueSyntax != nil { + if targetValueSyntax := tsoptions.ForEachPropertyAssignment(program.includeProcessor.getCompilerOptionsObjectLiteralSyntax(program), "target", tsoptions.GetCallbackForFindingPropertyAssignmentByValue(target)); targetValueSyntax != nil { return tsoptions.CreateDiagnosticForNodeInSourceFile(config.ConfigFile.SourceFile, targetValueSyntax.AsNode(), diagnostics.File_is_default_library_for_target_specified_here) } } @@ -333,7 +317,7 @@ func (r *fileIncludeReason) computeRelatedInfo(program *Program, getCompilerOpti } func (r *fileIncludeReason) computeReferenceFileRelatedInfo(program *Program) *ast.Diagnostic { - referenceLocation := r.getReferencedLocation(program) + referenceLocation := program.includeProcessor.getReferenceLocation(r, program) if referenceLocation.isSynthetic { return nil } diff --git a/internal/compiler/fileloader.go b/internal/compiler/fileloader.go index a8f162622a..1c45b6a0d7 100644 --- a/internal/compiler/fileloader.go +++ b/internal/compiler/fileloader.go @@ -22,8 +22,9 @@ type fileLoader struct { comparePathsOptions tspath.ComparePathsOptions supportedExtensions []string - filesParser *filesParser - rootTasks []*parseTask + filesParser *filesParser + rootTasks []*parseTask + includeProcessor *includeProcessor totalFileCount atomic.Int32 libFileCount atomic.Int32 @@ -53,8 +54,7 @@ type processedFiles struct { // List of present unsupported extensions unsupportedExtensions []string sourceFilesFoundSearchingNodeModules collections.Set[tspath.Path] - fileLoadDiagnostics *ast.DiagnosticsCollection - fileIncludeReasons map[tspath.Path][]*fileIncludeReason + includeProcessor *includeProcessor // if file was included using source file and its output is actually part of program // this contains mapping from output to source file outputFileToProjectReferenceSource map[tspath.Path]string @@ -89,6 +89,7 @@ func processAllProgramFiles( }, rootTasks: make([]*parseTask, 0, len(rootFiles)+len(compilerOptions.Lib)), supportedExtensions: core.Flatten(tsoptions.GetSupportedExtensionsWithJsonIfResolveJsonModule(compilerOptions, supportedExtensions)), + includeProcessor: &includeProcessor{}, } loader.addProjectReferenceTasks(singleThreaded) loader.resolver = module.NewResolver(loader.projectReferenceFileMapper.host, compilerOptions, opts.TypingsLocation, opts.ProjectName) @@ -126,7 +127,7 @@ func processAllProgramFiles( libFiles := make([]*ast.SourceFile, 0, totalFileCount) // totalFileCount here since we append files to it later to construct the final list filesByPath := make(map[tspath.Path]*ast.SourceFile, totalFileCount) - fileIncludeReasons := make(map[tspath.Path][]*fileIncludeReason, totalFileCount) + loader.includeProcessor.fileIncludeReasons = make(map[tspath.Path][]*fileIncludeReason, totalFileCount) outputFileToProjectReferenceSource := make(map[tspath.Path]string, totalFileCount) resolvedModules := make(map[tspath.Path]module.ModeAwareCache[*module.ResolvedModule], totalFileCount+1) typeResolutionsInFile := make(map[tspath.Path]module.ModeAwareCache[*module.ResolvedTypeReferenceDirective], totalFileCount) @@ -136,7 +137,6 @@ func processAllProgramFiles( var unsupportedExtensions []string var sourceFilesFoundSearchingNodeModules collections.Set[tspath.Path] var libFileSet collections.Set[tspath.Path] - fileLoadDiagnostics := &ast.DiagnosticsCollection{} loader.filesParser.collect(&loader, loader.rootTasks, func(task *parseTask) { if task.redirectedParseTask != nil { @@ -183,7 +183,6 @@ func processAllProgramFiles( files = append(files, file) } filesByPath[path] = file - fileIncludeReasons[path] = task.allIncludeReasons resolvedModules[path] = task.resolutionsInFile typeResolutionsInFile[path] = task.typeResolutionsInFile sourceFileMetaDatas[path] = task.metadata @@ -212,28 +211,8 @@ func processAllProgramFiles( allFiles := append(libFiles, files...) - for _, resolutions := range resolvedModules { - for _, resolvedModule := range resolutions { - for _, diag := range resolvedModule.ResolutionDiagnostics { - fileLoadDiagnostics.Add(diag) - } - } - } - for _, typeResolutions := range typeResolutionsInFile { - for _, resolvedTypeRef := range typeResolutions { - for _, diag := range resolvedTypeRef.ResolutionDiagnostics { - fileLoadDiagnostics.Add(diag) - } - } - } - loader.pathForLibFileResolutions.Range(func(key tspath.Path, value module.ModeAwareCache[*module.ResolvedModule]) bool { resolvedModules[key] = value - for _, resolvedModule := range value { - for _, diag := range resolvedModule.ResolutionDiagnostics { - fileLoadDiagnostics.Add(diag) - } - } return true }) @@ -250,8 +229,7 @@ func processAllProgramFiles( unsupportedExtensions: unsupportedExtensions, sourceFilesFoundSearchingNodeModules: sourceFilesFoundSearchingNodeModules, libFiles: libFileSet, - fileLoadDiagnostics: fileLoadDiagnostics, - fileIncludeReasons: fileIncludeReasons, + includeProcessor: loader.includeProcessor, outputFileToProjectReferenceSource: outputFileToProjectReferenceSource, } } @@ -459,23 +437,26 @@ func (p *fileLoader) resolveTypeReferenceDirectives(t *parseTask) { resolutionMode := getModeForTypeReferenceDirectiveInFile(ref, file, meta, module.GetCompilerOptionsWithRedirect(p.opts.Config.CompilerOptions(), redirect)) resolved := p.resolver.ResolveTypeReferenceDirective(ref.FileName, file.FileName(), resolutionMode, redirect) typeResolutionsInFile[module.ModeAwareCacheKey{Name: ref.FileName, Mode: resolutionMode}] = resolved - + includeReason := &fileIncludeReason{ + kind: fileIncludeKindTypeReferenceDirective, + data: &referencedFileData{ + file: t.path, + index: index, + }, + } if resolved.IsResolved() { t.addSubTask(resolvedRef{ fileName: resolved.ResolvedFileName, increaseDepth: resolved.IsExternalLibraryImport, elideOnDepth: false, isFromExternalLibrary: resolved.IsExternalLibraryImport, - includeReason: &fileIncludeReason{ - kind: fileIncludeKindTypeReferenceDirective, - data: &referencedFileData{ - file: t.path, - index: index, - }, - }, + includeReason: includeReason, }, false) } else { - // !! sheetal Cannot_find_type_definition_file_for_0 + p.includeProcessor.addProcessingDiagnostic(&processingDiagnostic{ + kind: processingDiagnosticKindUnknownReference, + data: includeReason, + }) } } diff --git a/internal/compiler/filesparser.go b/internal/compiler/filesparser.go index 07f7d57339..7df753b6c5 100644 --- a/internal/compiler/filesparser.go +++ b/internal/compiler/filesparser.go @@ -86,21 +86,23 @@ func (t *parseTask) load(loader *fileLoader) { if compilerOptions.NoLib != core.TSTrue { for index, lib := range file.LibReferenceDirectives { + includeReason := &fileIncludeReason{ + kind: fileIncludeKindLibReferenceDirective, + data: &referencedFileData{ + file: t.path, + index: index, + }, + } if name, ok := tsoptions.GetLibFileName(lib.FileName); ok { t.addSubTask(resolvedRef{ - fileName: loader.pathForLibFile(name), - includeReason: &fileIncludeReason{ - kind: fileIncludeKindLibReferenceDirective, - data: &referencedFileData{ - file: t.path, - index: index, - }, - }, + fileName: loader.pathForLibFile(name), + includeReason: includeReason, }, true) } else { - // !!! sheetal file preprocessing diagnostic explaining - // Cannot_find_lib_definition_for_0_Did_you_mean_1 - // filePreprocessingLibreferenceDiagnostic + loader.includeProcessor.addProcessingDiagnostic(&processingDiagnostic{ + kind: processingDiagnosticKindUnknownReference, + data: includeReason, + }) } } } @@ -232,12 +234,15 @@ func (w *filesParser) collectWorker(loader *fileLoader, tasks []*parseTask, iter var results []tspath.Path for _, task := range tasks { if task.redirectedParseTask == nil { - if task.loadedTask == nil { - task.allIncludeReasons = []*fileIncludeReason{task.includeReason} - } else { - task.loadedTask.allIncludeReasons = append(task.loadedTask.allIncludeReasons, task.includeReason) + includeReason := task.includeReason + if task.loadedTask != nil { task = task.loadedTask } + if existing, ok := loader.includeProcessor.fileIncludeReasons[task.path]; ok { + loader.includeProcessor.fileIncludeReasons[task.path] = append(existing, includeReason) + } else { + loader.includeProcessor.fileIncludeReasons[task.path] = []*fileIncludeReason{includeReason} + } } // ensure we only walk each task once if !task.loaded || !seen.AddIfAbsent(task) { diff --git a/internal/compiler/includeprocessor.go b/internal/compiler/includeprocessor.go new file mode 100644 index 0000000000..d865d74fbb --- /dev/null +++ b/internal/compiler/includeprocessor.go @@ -0,0 +1,144 @@ +package compiler + +import ( + "sync" + + "github.com/microsoft/typescript-go/internal/ast" + "github.com/microsoft/typescript-go/internal/collections" + "github.com/microsoft/typescript-go/internal/core" + "github.com/microsoft/typescript-go/internal/diagnostics" + "github.com/microsoft/typescript-go/internal/tsoptions" + "github.com/microsoft/typescript-go/internal/tspath" +) + +type includeProcessor struct { + fileIncludeReasons map[tspath.Path][]*fileIncludeReason + processingDiagnostics []*processingDiagnostic + + reasonToReferenceLocation collections.SyncMap[*fileIncludeReason, *referenceFileLocation] + includeReasonToRelatedInfo collections.SyncMap[*fileIncludeReason, *ast.Diagnostic] + redirectAndFileFormat collections.SyncMap[tspath.Path, []*ast.Diagnostic] + computedDiagnostics *ast.DiagnosticsCollection + computedDiagnosticsOnce sync.Once + compilerOptionsSyntax *ast.ObjectLiteralExpression + compilerOptionsSyntaxOnce sync.Once +} + +func updateFileIncludeProcessor(p *Program) { + p.includeProcessor = &includeProcessor{ + fileIncludeReasons: p.includeProcessor.fileIncludeReasons, + processingDiagnostics: p.includeProcessor.processingDiagnostics, + } +} + +func (i *includeProcessor) getDiagnostics(p *Program) *ast.DiagnosticsCollection { + i.computedDiagnosticsOnce.Do(func() { + i.computedDiagnostics = &ast.DiagnosticsCollection{} + for _, d := range i.processingDiagnostics { + i.computedDiagnostics.Add(d.toDiagnostic(p)) + } + for _, resolutions := range p.resolvedModules { + for _, resolvedModule := range resolutions { + for _, diag := range resolvedModule.ResolutionDiagnostics { + i.computedDiagnostics.Add(diag) + } + } + } + for _, typeResolutions := range p.typeResolutionsInFile { + for _, resolvedTypeRef := range typeResolutions { + for _, diag := range resolvedTypeRef.ResolutionDiagnostics { + i.computedDiagnostics.Add(diag) + } + } + } + }) + return i.computedDiagnostics +} + +func (i *includeProcessor) addProcessingDiagnostic(d ...*processingDiagnostic) { + i.processingDiagnostics = append(i.processingDiagnostics, d...) +} + +func (i *includeProcessor) getReferenceLocation(r *fileIncludeReason, program *Program) *referenceFileLocation { + if existing, ok := i.reasonToReferenceLocation.Load(r); ok { + return existing + } + + loc, _ := i.reasonToReferenceLocation.LoadOrStore(r, r.getReferencedLocation(program)) + return loc +} + +func (i *includeProcessor) getCompilerOptionsObjectLiteralSyntax(program *Program) *ast.ObjectLiteralExpression { + i.compilerOptionsSyntaxOnce.Do(func() { + configFile := program.opts.Config.ConfigFile + if configFile != nil { + if compilerOptionsProperty := tsoptions.ForEachTsConfigPropArray(configFile.SourceFile, "compilerOptions", core.Identity); compilerOptionsProperty != nil && + compilerOptionsProperty.Initializer != nil && + ast.IsObjectLiteralExpression(compilerOptionsProperty.Initializer) { + i.compilerOptionsSyntax = compilerOptionsProperty.Initializer.AsObjectLiteralExpression() + } + } else { + i.compilerOptionsSyntax = nil + } + }) + return i.compilerOptionsSyntax +} + +func (i *includeProcessor) getRelatedInfo(r *fileIncludeReason, program *Program) *ast.Diagnostic { + if existing, ok := i.includeReasonToRelatedInfo.Load(r); ok { + return existing + } + + relatedInfo, _ := i.includeReasonToRelatedInfo.LoadOrStore(r, r.toRelatedInfo(program)) + return relatedInfo +} + +func (i *includeProcessor) explainRedirectAndImpliedFormat( + program *Program, + file *ast.SourceFile, + toFileName func(fileName string) string, +) []*ast.Diagnostic { + if existing, ok := i.redirectAndFileFormat.Load(file.Path()); ok { + return existing + } + var result []*ast.Diagnostic + if source := program.GetSourceOfProjectReferenceIfOutputIncluded(file); source != file.FileName() { + result = append(result, ast.NewCompilerDiagnostic( + diagnostics.File_is_output_of_project_reference_source_0, + toFileName(source), + )) + } + // !!! redirects + // if (file.redirectInfo) { + // (result ??= []).push(chainDiagnosticMessages( + // /*details*/ undefined, + // Diagnostics.File_redirects_to_file_0, + // toFileName(file.redirectInfo.redirectTarget, fileNameConvertor), + // )); + // } + if ast.IsExternalOrCommonJSModule(file) { + metaData := program.GetSourceFileMetaData(file.Path()) + switch program.GetImpliedNodeFormatForEmit(file) { + case core.ModuleKindESNext: + if metaData.PackageJsonType == "module" { + result = append(result, ast.NewCompilerDiagnostic( + diagnostics.File_is_ECMAScript_module_because_0_has_field_type_with_value_module, + toFileName(metaData.PackageJsonDirectory+"/package.json"), + )) + } + case core.ModuleKindCommonJS: + if metaData.PackageJsonType != "" { + result = append(result, ast.NewCompilerDiagnostic(diagnostics.File_is_CommonJS_module_because_0_has_field_type_whose_value_is_not_module, toFileName(metaData.PackageJsonDirectory+"/package.json"))) + } else if metaData.PackageJsonDirectory != "" { + if metaData.PackageJsonType == "" { + result = append(result, ast.NewCompilerDiagnostic(diagnostics.File_is_CommonJS_module_because_0_does_not_have_field_type, toFileName(metaData.PackageJsonDirectory+"/package.json"))) + } + } else { + result = append(result, ast.NewCompilerDiagnostic(diagnostics.File_is_CommonJS_module_because_package_json_was_not_found)) + } + } + } + + result, _ = i.redirectAndFileFormat.LoadOrStore(file.Path(), result) + return result +} diff --git a/internal/compiler/processingDiagnostic.go b/internal/compiler/processingDiagnostic.go new file mode 100644 index 0000000000..4fde4a9caf --- /dev/null +++ b/internal/compiler/processingDiagnostic.go @@ -0,0 +1,133 @@ +package compiler + +import ( + "strings" + + "github.com/microsoft/typescript-go/internal/ast" + "github.com/microsoft/typescript-go/internal/collections" + "github.com/microsoft/typescript-go/internal/core" + "github.com/microsoft/typescript-go/internal/diagnostics" + "github.com/microsoft/typescript-go/internal/tsoptions" + "github.com/microsoft/typescript-go/internal/tspath" +) + +type processingDiagnosticKind int + +const ( + processingDiagnosticKindUnknownReference processingDiagnosticKind = iota + processingDiagnosticKindExplainingFileInclude +) + +type processingDiagnostic struct { + kind processingDiagnosticKind + data any +} + +func (d *processingDiagnostic) asFileIncludeReason() *fileIncludeReason { + return d.data.(*fileIncludeReason) +} + +type includeExplainingDiagnostic struct { + file tspath.Path + diagnosticReason *fileIncludeReason + message *diagnostics.Message + args []any +} + +func (d *processingDiagnostic) asIncludeExplainingDiagnostic() *includeExplainingDiagnostic { + return d.data.(*includeExplainingDiagnostic) +} + +func (d *processingDiagnostic) toDiagnostic(program *Program) *ast.Diagnostic { + switch d.kind { + case processingDiagnosticKindUnknownReference: + ref := d.asFileIncludeReason() + loc := ref.getReferencedLocation(program) + switch ref.kind { + case fileIncludeKindTypeReferenceDirective: + return loc.diagnosticAt(diagnostics.Cannot_find_type_definition_file_for_0, loc.ref.FileName) + case fileIncludeKindLibReferenceDirective: + libName := tspath.ToFileNameLowerCase(loc.ref.FileName) + unqualifiedLibName := strings.TrimSuffix(strings.TrimPrefix(libName, "lib."), ".d.ts") + suggestion := core.GetSpellingSuggestion(unqualifiedLibName, tsoptions.Libs, core.Identity) + return loc.diagnosticAt(core.IfElse( + suggestion != "", + diagnostics.Cannot_find_lib_definition_for_0_Did_you_mean_1, + diagnostics.Cannot_find_lib_definition_for_0, + ), libName, suggestion) + default: + panic("unknown include kind") + } + case processingDiagnosticKindExplainingFileInclude: + return d.createDiagnosticExplainingFile(program) + default: + panic("unknown processingDiagnosticKind") + } +} + +func (d *processingDiagnostic) createDiagnosticExplainingFile(program *Program) *ast.Diagnostic { + diag := d.asIncludeExplainingDiagnostic() + var chain []*ast.Diagnostic + var relatedInfo []*ast.Diagnostic + var redirectInfo []*ast.Diagnostic + var preferredLocation *fileIncludeReason + var seenReasons collections.Set[*fileIncludeReason] + if diag.diagnosticReason.isReferencedFile() && !program.includeProcessor.getReferenceLocation(diag.diagnosticReason, program).isSynthetic { + preferredLocation = diag.diagnosticReason + } + + processRelatedInfo := func(includeReason *fileIncludeReason) { + if preferredLocation == nil && includeReason.isReferencedFile() && !program.includeProcessor.getReferenceLocation(includeReason, program).isSynthetic { + preferredLocation = includeReason + } else { + info := program.includeProcessor.getRelatedInfo(includeReason, program) + if info != nil { + relatedInfo = append(relatedInfo, info) + } + } + } + processInclude := func(includeReason *fileIncludeReason) { + if !seenReasons.AddIfAbsent(includeReason) { + return + } + chain = append(chain, includeReason.toDiagnostic(program, false)) + processRelatedInfo(includeReason) + } + + // !!! todo sheetal caching + + if diag.file != "" { + reasons := program.includeProcessor.fileIncludeReasons[diag.file] + chain = make([]*ast.Diagnostic, 0, len(reasons)) + for _, reason := range reasons { + processInclude(reason) + } + redirectInfo = program.includeProcessor.explainRedirectAndImpliedFormat(program, program.GetSourceFileByPath(diag.file), func(fileName string) string { return fileName }) + } + if diag.diagnosticReason != nil { + processInclude(diag.diagnosticReason) + } + if chain != nil && (preferredLocation == nil || seenReasons.Len() != 1) { + fileReason := ast.NewCompilerDiagnostic(diagnostics.The_file_is_in_the_program_because_Colon) + fileReason.SetMessageChain(chain) + chain = []*ast.Diagnostic{fileReason} + } + if redirectInfo != nil { + chain = append(chain, redirectInfo...) + } + + var result *ast.Diagnostic + if preferredLocation != nil { + result = program.includeProcessor.getReferenceLocation(preferredLocation, program).diagnosticAt(diag.message, diag.args...) + } + if result == nil { + result = ast.NewCompilerDiagnostic(diag.message, diag.args...) + } + if chain != nil { + result.SetMessageChain(chain) + } + if relatedInfo != nil { + result.SetRelatedInfo(relatedInfo) + } + return result +} diff --git a/internal/compiler/program.go b/internal/compiler/program.go index e9ff9ea3b3..b3033a18f5 100644 --- a/internal/compiler/program.go +++ b/internal/compiler/program.go @@ -227,6 +227,7 @@ func (p *Program) UpdateProgram(changedFilePath tspath.Path) (*Program, bool) { result.files[index] = newFile result.filesByPath = maps.Clone(result.filesByPath) result.filesByPath[newFile.Path()] = newFile + updateFileIncludeProcessor(result) return result, true } @@ -374,7 +375,7 @@ func (p *Program) GetSuggestionDiagnostics(ctx context.Context, sourceFile *ast. } func (p *Program) GetProgramDiagnostics() []*ast.Diagnostic { - return SortAndDeduplicateDiagnostics(slices.Concat(p.programDiagnostics, p.fileLoadDiagnostics.GetDiagnostics())) + return SortAndDeduplicateDiagnostics(slices.Concat(p.programDiagnostics, p.includeProcessor.getDiagnostics(p).GetDiagnostics())) } func (p *Program) getSourceFilesToEmit(targetSourceFile *ast.SourceFile, forceDtsEmit bool) []*ast.SourceFile { @@ -566,14 +567,14 @@ func (p *Program) verifyCompilerOptions() { for _, file := range p.files { if sourceFileMayBeEmitted(file, p, false) && !rootPaths.Has(file.Path()) { - p.programDiagnostics = append(p.programDiagnostics, p.createDiagnosticExplainingFile( - getCompilerOptionsObjectLiteralSyntax, - file, - nil, - diagnostics.File_0_is_not_listed_within_the_file_list_of_project_1_Projects_must_list_all_files_or_use_an_include_pattern, - file.FileName(), - configFilePath(), - )) + p.includeProcessor.addProcessingDiagnostic(&processingDiagnostic{ + kind: processingDiagnosticKindExplainingFileInclude, + data: &includeExplainingDiagnostic{ + file: file.Path(), + message: diagnostics.File_0_is_not_listed_within_the_file_list_of_project_1_Projects_must_list_all_files_or_use_an_include_pattern, + args: []any{file.FileName(), configFilePath()}, + }, + }) } } } @@ -1472,131 +1473,15 @@ func (p *Program) ExplainFiles(writer io.Writer) { } for _, file := range p.GetSourceFiles() { fmt.Fprintln(writer, toRelativeFileName(file.FileName())) - for _, reason := range p.fileIncludeReasons[file.Path()] { + for _, reason := range p.includeProcessor.fileIncludeReasons[file.Path()] { fmt.Fprintln(writer, " ", reason.toDiagnostic(p, true).Message()) } - for _, diag := range p.explainRedirectAndImpliedFormat(file, toRelativeFileName) { + for _, diag := range p.includeProcessor.explainRedirectAndImpliedFormat(p, file, toRelativeFileName) { fmt.Fprintln(writer, " ", diag.Message()) } } } -func (p *Program) explainRedirectAndImpliedFormat( - file *ast.SourceFile, - toFileName func(fileName string) string, -) []*ast.Diagnostic { - var result []*ast.Diagnostic - if source := p.GetSourceOfProjectReferenceIfOutputIncluded(file); source != file.FileName() { - result = append(result, ast.NewCompilerDiagnostic( - diagnostics.File_is_output_of_project_reference_source_0, - toFileName(source), - )) - } - // !!! redirects - // if (file.redirectInfo) { - // (result ??= []).push(chainDiagnosticMessages( - // /*details*/ undefined, - // Diagnostics.File_redirects_to_file_0, - // toFileName(file.redirectInfo.redirectTarget, fileNameConvertor), - // )); - // } - if ast.IsExternalOrCommonJSModule(file) { - metaData := p.GetSourceFileMetaData(file.Path()) - switch p.GetImpliedNodeFormatForEmit(file) { - case core.ModuleKindESNext: - if metaData.PackageJsonType == "module" { - result = append(result, ast.NewCompilerDiagnostic( - diagnostics.File_is_ECMAScript_module_because_0_has_field_type_with_value_module, - toFileName(metaData.PackageJsonDirectory+"/package.json"), - )) - } - case core.ModuleKindCommonJS: - if metaData.PackageJsonType != "" { - result = append(result, ast.NewCompilerDiagnostic(diagnostics.File_is_CommonJS_module_because_0_has_field_type_whose_value_is_not_module, toFileName(metaData.PackageJsonDirectory+"/package.json"))) - } else if metaData.PackageJsonDirectory != "" { - if metaData.PackageJsonType == "" { - result = append(result, ast.NewCompilerDiagnostic(diagnostics.File_is_CommonJS_module_because_0_does_not_have_field_type, toFileName(metaData.PackageJsonDirectory+"/package.json"))) - } - } else { - result = append(result, ast.NewCompilerDiagnostic(diagnostics.File_is_CommonJS_module_because_package_json_was_not_found)) - } - } - } - return result -} - -func (p *Program) createDiagnosticExplainingFile( - getCompilerOptionsObjectLiteralSyntax func() *ast.ObjectLiteralExpression, - file *ast.SourceFile, - diagnosticReason *fileIncludeReason, - diag *diagnostics.Message, - args ...any, -) *ast.Diagnostic { - var chain []*ast.Diagnostic - var relatedInfo []*ast.Diagnostic - var redirectInfo []*ast.Diagnostic - var preferredLocation *fileIncludeReason - var seenReasons collections.Set[*fileIncludeReason] - if diagnosticReason.isReferencedFile() && !diagnosticReason.getReferencedLocation(p).isSynthetic { - preferredLocation = diagnosticReason - } - - processRelatedInfo := func(includeReason *fileIncludeReason) { - if preferredLocation == nil && includeReason.isReferencedFile() && !includeReason.getReferencedLocation(p).isSynthetic { - preferredLocation = includeReason - } else { - info := includeReason.toRelatedInfo(p, getCompilerOptionsObjectLiteralSyntax) - if info != nil { - relatedInfo = append(relatedInfo, info) - } - } - } - processInclude := func(includeReason *fileIncludeReason) { - if !seenReasons.AddIfAbsent(includeReason) { - return - } - chain = append(chain, includeReason.toDiagnostic(p, false)) - processRelatedInfo(includeReason) - } - - // !!! todo sheetal caching - - if file != nil { - reasons := p.fileIncludeReasons[file.Path()] - chain = make([]*ast.Diagnostic, 0, len(reasons)) - for _, reason := range reasons { - processInclude(reason) - } - redirectInfo = p.explainRedirectAndImpliedFormat(file, func(fileName string) string { return fileName }) - } - if diagnosticReason != nil { - processInclude(diagnosticReason) - } - if chain != nil && (preferredLocation == nil || seenReasons.Len() != 1) { - fileReason := ast.NewCompilerDiagnostic(diagnostics.The_file_is_in_the_program_because_Colon) - fileReason.SetMessageChain(chain) - chain = []*ast.Diagnostic{fileReason} - } - if redirectInfo != nil { - chain = append(chain, redirectInfo...) - } - - var result *ast.Diagnostic - if preferredLocation != nil { - result = preferredLocation.getReferencedLocation(p).diagnosticAt(diag, args...) - } - if result == nil { - result = ast.NewCompilerDiagnostic(diag, args...) - } - if chain != nil { - result.SetMessageChain(chain) - } - if relatedInfo != nil { - result.SetRelatedInfo(relatedInfo) - } - return result -} - func (p *Program) GetLibFileFromReference(ref *ast.FileReference) *ast.SourceFile { path, ok := tsoptions.GetLibFileName(ref.FileName) if !ok { diff --git a/testdata/baselines/reference/submodule/compiler/moduleResolutionWithSymlinks_preserveSymlinks.errors.txt b/testdata/baselines/reference/submodule/compiler/moduleResolutionWithSymlinks_preserveSymlinks.errors.txt index bde23ab460..15ac745a8f 100644 --- a/testdata/baselines/reference/submodule/compiler/moduleResolutionWithSymlinks_preserveSymlinks.errors.txt +++ b/testdata/baselines/reference/submodule/compiler/moduleResolutionWithSymlinks_preserveSymlinks.errors.txt @@ -1,10 +1,13 @@ +/app/app.ts(2,23): error TS2688: Cannot find type definition file for 'linked'. /app/app.ts(4,25): error TS2307: Cannot find module 'linked' or its corresponding type declarations. /app/app.ts(5,25): error TS2307: Cannot find module 'linked2' or its corresponding type declarations. -==== /app/app.ts (2 errors) ==== +==== /app/app.ts (3 errors) ==== // We shouldn't resolve symlinks for references either. See the trace. /// + ~~~~~~ +!!! error TS2688: Cannot find type definition file for 'linked'. import { C as C1 } from "linked"; ~~~~~~~~ diff --git a/testdata/baselines/reference/submodule/compiler/moduleResolutionWithSymlinks_preserveSymlinks.errors.txt.diff b/testdata/baselines/reference/submodule/compiler/moduleResolutionWithSymlinks_preserveSymlinks.errors.txt.diff index ddd0f79e76..4a31f74d95 100644 --- a/testdata/baselines/reference/submodule/compiler/moduleResolutionWithSymlinks_preserveSymlinks.errors.txt.diff +++ b/testdata/baselines/reference/submodule/compiler/moduleResolutionWithSymlinks_preserveSymlinks.errors.txt.diff @@ -6,13 +6,16 @@ - - -==== /app/app.ts (1 errors) ==== ++/app/app.ts(2,23): error TS2688: Cannot find type definition file for 'linked'. +/app/app.ts(4,25): error TS2307: Cannot find module 'linked' or its corresponding type declarations. +/app/app.ts(5,25): error TS2307: Cannot find module 'linked2' or its corresponding type declarations. + + -+==== /app/app.ts (2 errors) ==== ++==== /app/app.ts (3 errors) ==== // We shouldn't resolve symlinks for references either. See the trace. /// ++ ~~~~~~ ++!!! error TS2688: Cannot find type definition file for 'linked'. import { C as C1 } from "linked"; + ~~~~~~~~ diff --git a/testdata/baselines/reference/submodule/compiler/tripleSlashTypesReferenceWithMissingExports(module=commonjs).errors.txt b/testdata/baselines/reference/submodule/compiler/tripleSlashTypesReferenceWithMissingExports(module=commonjs).errors.txt new file mode 100644 index 0000000000..9062d29947 --- /dev/null +++ b/testdata/baselines/reference/submodule/compiler/tripleSlashTypesReferenceWithMissingExports(module=commonjs).errors.txt @@ -0,0 +1,17 @@ +usage.ts(1,23): error TS2688: Cannot find type definition file for 'pkg'. + + +==== node_modules/pkg/index.d.ts (0 errors) ==== + interface GlobalThing { a: number } +==== node_modules/pkg/package.json (0 errors) ==== + { + "name": "pkg", + "types": "index.d.ts", + "exports": "some-other-thing.js" + } +==== usage.ts (1 errors) ==== + /// + ~~~ +!!! error TS2688: Cannot find type definition file for 'pkg'. + + const a: GlobalThing = { a: 0 }; \ No newline at end of file diff --git a/testdata/baselines/reference/submodule/compiler/tripleSlashTypesReferenceWithMissingExports(module=commonjs).errors.txt.diff b/testdata/baselines/reference/submodule/compiler/tripleSlashTypesReferenceWithMissingExports(module=commonjs).errors.txt.diff new file mode 100644 index 0000000000..d1bf09c663 --- /dev/null +++ b/testdata/baselines/reference/submodule/compiler/tripleSlashTypesReferenceWithMissingExports(module=commonjs).errors.txt.diff @@ -0,0 +1,21 @@ +--- old.tripleSlashTypesReferenceWithMissingExports(module=commonjs).errors.txt ++++ new.tripleSlashTypesReferenceWithMissingExports(module=commonjs).errors.txt +@@= skipped -0, +0 lines =@@ +- ++usage.ts(1,23): error TS2688: Cannot find type definition file for 'pkg'. ++ ++ ++==== node_modules/pkg/index.d.ts (0 errors) ==== ++ interface GlobalThing { a: number } ++==== node_modules/pkg/package.json (0 errors) ==== ++ { ++ "name": "pkg", ++ "types": "index.d.ts", ++ "exports": "some-other-thing.js" ++ } ++==== usage.ts (1 errors) ==== ++ /// ++ ~~~ ++!!! error TS2688: Cannot find type definition file for 'pkg'. ++ ++ const a: GlobalThing = { a: 0 }; \ No newline at end of file diff --git a/testdata/baselines/reference/submodule/compiler/tripleSlashTypesReferenceWithMissingExports(module=node16).errors.txt b/testdata/baselines/reference/submodule/compiler/tripleSlashTypesReferenceWithMissingExports(module=node16).errors.txt new file mode 100644 index 0000000000..9062d29947 --- /dev/null +++ b/testdata/baselines/reference/submodule/compiler/tripleSlashTypesReferenceWithMissingExports(module=node16).errors.txt @@ -0,0 +1,17 @@ +usage.ts(1,23): error TS2688: Cannot find type definition file for 'pkg'. + + +==== node_modules/pkg/index.d.ts (0 errors) ==== + interface GlobalThing { a: number } +==== node_modules/pkg/package.json (0 errors) ==== + { + "name": "pkg", + "types": "index.d.ts", + "exports": "some-other-thing.js" + } +==== usage.ts (1 errors) ==== + /// + ~~~ +!!! error TS2688: Cannot find type definition file for 'pkg'. + + const a: GlobalThing = { a: 0 }; \ No newline at end of file diff --git a/testdata/baselines/reference/submodule/compiler/tripleSlashTypesReferenceWithMissingExports(module=node16).errors.txt.diff b/testdata/baselines/reference/submodule/compiler/tripleSlashTypesReferenceWithMissingExports(module=node16).errors.txt.diff deleted file mode 100644 index f38980ad8b..0000000000 --- a/testdata/baselines/reference/submodule/compiler/tripleSlashTypesReferenceWithMissingExports(module=node16).errors.txt.diff +++ /dev/null @@ -1,21 +0,0 @@ ---- old.tripleSlashTypesReferenceWithMissingExports(module=node16).errors.txt -+++ new.tripleSlashTypesReferenceWithMissingExports(module=node16).errors.txt -@@= skipped -0, +0 lines =@@ --usage.ts(1,23): error TS2688: Cannot find type definition file for 'pkg'. -- -- --==== node_modules/pkg/index.d.ts (0 errors) ==== -- interface GlobalThing { a: number } --==== node_modules/pkg/package.json (0 errors) ==== -- { -- "name": "pkg", -- "types": "index.d.ts", -- "exports": "some-other-thing.js" -- } --==== usage.ts (1 errors) ==== -- /// -- ~~~ --!!! error TS2688: Cannot find type definition file for 'pkg'. -- -- const a: GlobalThing = { a: 0 }; -+ \ No newline at end of file diff --git a/testdata/baselines/reference/submodule/compiler/tripleSlashTypesReferenceWithMissingExports(module=node18).errors.txt b/testdata/baselines/reference/submodule/compiler/tripleSlashTypesReferenceWithMissingExports(module=node18).errors.txt new file mode 100644 index 0000000000..9062d29947 --- /dev/null +++ b/testdata/baselines/reference/submodule/compiler/tripleSlashTypesReferenceWithMissingExports(module=node18).errors.txt @@ -0,0 +1,17 @@ +usage.ts(1,23): error TS2688: Cannot find type definition file for 'pkg'. + + +==== node_modules/pkg/index.d.ts (0 errors) ==== + interface GlobalThing { a: number } +==== node_modules/pkg/package.json (0 errors) ==== + { + "name": "pkg", + "types": "index.d.ts", + "exports": "some-other-thing.js" + } +==== usage.ts (1 errors) ==== + /// + ~~~ +!!! error TS2688: Cannot find type definition file for 'pkg'. + + const a: GlobalThing = { a: 0 }; \ No newline at end of file diff --git a/testdata/baselines/reference/submodule/compiler/tripleSlashTypesReferenceWithMissingExports(module=node18).errors.txt.diff b/testdata/baselines/reference/submodule/compiler/tripleSlashTypesReferenceWithMissingExports(module=node18).errors.txt.diff deleted file mode 100644 index 8cbb190eb4..0000000000 --- a/testdata/baselines/reference/submodule/compiler/tripleSlashTypesReferenceWithMissingExports(module=node18).errors.txt.diff +++ /dev/null @@ -1,21 +0,0 @@ ---- old.tripleSlashTypesReferenceWithMissingExports(module=node18).errors.txt -+++ new.tripleSlashTypesReferenceWithMissingExports(module=node18).errors.txt -@@= skipped -0, +0 lines =@@ --usage.ts(1,23): error TS2688: Cannot find type definition file for 'pkg'. -- -- --==== node_modules/pkg/index.d.ts (0 errors) ==== -- interface GlobalThing { a: number } --==== node_modules/pkg/package.json (0 errors) ==== -- { -- "name": "pkg", -- "types": "index.d.ts", -- "exports": "some-other-thing.js" -- } --==== usage.ts (1 errors) ==== -- /// -- ~~~ --!!! error TS2688: Cannot find type definition file for 'pkg'. -- -- const a: GlobalThing = { a: 0 }; -+ \ No newline at end of file diff --git a/testdata/baselines/reference/submodule/compiler/tripleSlashTypesReferenceWithMissingExports(module=nodenext).errors.txt b/testdata/baselines/reference/submodule/compiler/tripleSlashTypesReferenceWithMissingExports(module=nodenext).errors.txt new file mode 100644 index 0000000000..9062d29947 --- /dev/null +++ b/testdata/baselines/reference/submodule/compiler/tripleSlashTypesReferenceWithMissingExports(module=nodenext).errors.txt @@ -0,0 +1,17 @@ +usage.ts(1,23): error TS2688: Cannot find type definition file for 'pkg'. + + +==== node_modules/pkg/index.d.ts (0 errors) ==== + interface GlobalThing { a: number } +==== node_modules/pkg/package.json (0 errors) ==== + { + "name": "pkg", + "types": "index.d.ts", + "exports": "some-other-thing.js" + } +==== usage.ts (1 errors) ==== + /// + ~~~ +!!! error TS2688: Cannot find type definition file for 'pkg'. + + const a: GlobalThing = { a: 0 }; \ No newline at end of file diff --git a/testdata/baselines/reference/submodule/compiler/tripleSlashTypesReferenceWithMissingExports(module=nodenext).errors.txt.diff b/testdata/baselines/reference/submodule/compiler/tripleSlashTypesReferenceWithMissingExports(module=nodenext).errors.txt.diff deleted file mode 100644 index 02e1361b29..0000000000 --- a/testdata/baselines/reference/submodule/compiler/tripleSlashTypesReferenceWithMissingExports(module=nodenext).errors.txt.diff +++ /dev/null @@ -1,21 +0,0 @@ ---- old.tripleSlashTypesReferenceWithMissingExports(module=nodenext).errors.txt -+++ new.tripleSlashTypesReferenceWithMissingExports(module=nodenext).errors.txt -@@= skipped -0, +0 lines =@@ --usage.ts(1,23): error TS2688: Cannot find type definition file for 'pkg'. -- -- --==== node_modules/pkg/index.d.ts (0 errors) ==== -- interface GlobalThing { a: number } --==== node_modules/pkg/package.json (0 errors) ==== -- { -- "name": "pkg", -- "types": "index.d.ts", -- "exports": "some-other-thing.js" -- } --==== usage.ts (1 errors) ==== -- /// -- ~~~ --!!! error TS2688: Cannot find type definition file for 'pkg'. -- -- const a: GlobalThing = { a: 0 }; -+ \ No newline at end of file diff --git a/testdata/baselines/reference/submodule/conformance/typingsLookup3.errors.txt b/testdata/baselines/reference/submodule/conformance/typingsLookup3.errors.txt new file mode 100644 index 0000000000..5073cb5493 --- /dev/null +++ b/testdata/baselines/reference/submodule/conformance/typingsLookup3.errors.txt @@ -0,0 +1,15 @@ +/a.ts(1,23): error TS2688: Cannot find type definition file for 'JqUeRy'. + + +==== /tsconfig.json (0 errors) ==== + { "files": "a.ts" } + +==== /a.ts (1 errors) ==== + /// + ~~~~~~ +!!! error TS2688: Cannot find type definition file for 'JqUeRy'. + $.x; + +==== /node_modules/@types/jquery/index.d.ts (0 errors) ==== + declare var $: { x: any }; + \ No newline at end of file diff --git a/testdata/baselines/reference/submodule/conformance/typingsLookup3.errors.txt.diff b/testdata/baselines/reference/submodule/conformance/typingsLookup3.errors.txt.diff deleted file mode 100644 index 8c57b37ae7..0000000000 --- a/testdata/baselines/reference/submodule/conformance/typingsLookup3.errors.txt.diff +++ /dev/null @@ -1,19 +0,0 @@ ---- old.typingsLookup3.errors.txt -+++ new.typingsLookup3.errors.txt -@@= skipped -0, +0 lines =@@ --/a.ts(1,23): error TS2688: Cannot find type definition file for 'JqUeRy'. -- -- --==== /tsconfig.json (0 errors) ==== -- { "files": "a.ts" } -- --==== /a.ts (1 errors) ==== -- /// -- ~~~~~~ --!!! error TS2688: Cannot find type definition file for 'JqUeRy'. -- $.x; -- --==== /node_modules/@types/jquery/index.d.ts (0 errors) ==== -- declare var $: { x: any }; -- -+ \ No newline at end of file From dcb0d6fd1d487e18414228cb2e669da2d1c1380e Mon Sep 17 00:00:00 2001 From: Sheetal Nandi Date: Tue, 12 Aug 2025 12:21:04 -0700 Subject: [PATCH 4/6] rename --- internal/compiler/program.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/internal/compiler/program.go b/internal/compiler/program.go index b3033a18f5..a0f82619d1 100644 --- a/internal/compiler/program.go +++ b/internal/compiler/program.go @@ -1467,17 +1467,17 @@ func (p *Program) GetSourceFiles() []*ast.SourceFile { return p.files } -func (p *Program) ExplainFiles(writer io.Writer) { +func (p *Program) ExplainFiles(w io.Writer) { toRelativeFileName := func(fileName string) string { return tspath.GetRelativePathFromDirectory(p.GetCurrentDirectory(), fileName, p.comparePathsOptions) } for _, file := range p.GetSourceFiles() { - fmt.Fprintln(writer, toRelativeFileName(file.FileName())) + fmt.Fprintln(w, toRelativeFileName(file.FileName())) for _, reason := range p.includeProcessor.fileIncludeReasons[file.Path()] { - fmt.Fprintln(writer, " ", reason.toDiagnostic(p, true).Message()) + fmt.Fprintln(w, " ", reason.toDiagnostic(p, true).Message()) } for _, diag := range p.includeProcessor.explainRedirectAndImpliedFormat(p, file, toRelativeFileName) { - fmt.Fprintln(writer, " ", diag.Message()) + fmt.Fprintln(w, " ", diag.Message()) } } } From 1b30a3cabf22ba08650e858a5f4ea1b61a79fc58 Mon Sep 17 00:00:00 2001 From: Sheetal Nandi Date: Tue, 12 Aug 2025 13:14:06 -0700 Subject: [PATCH 5/6] remove unncessary field root --- internal/compiler/fileloader.go | 1 - internal/compiler/filesparser.go | 7 +++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/internal/compiler/fileloader.go b/internal/compiler/fileloader.go index 6363035c94..1df4c404ec 100644 --- a/internal/compiler/fileloader.go +++ b/internal/compiler/fileloader.go @@ -266,7 +266,6 @@ func (p *fileLoader) addRootTask(fileName string, libFile *LibFile, includeReaso p.rootTasks = append(p.rootTasks, &parseTask{ normalizedFilePath: absPath, libFile: libFile, - root: true, includeReason: includeReason, }) } diff --git a/internal/compiler/filesparser.go b/internal/compiler/filesparser.go index f895322969..82d5dc90b5 100644 --- a/internal/compiler/filesparser.go +++ b/internal/compiler/filesparser.go @@ -21,7 +21,6 @@ type parseTask struct { subTasks []*parseTask loaded bool isForAutomaticTypeDirective bool - root bool includeReason *fileIncludeReason metadata ast.SourceFileMetaData @@ -51,6 +50,10 @@ func (t *parseTask) Path() tspath.Path { return t.path } +func (t *parseTask) isRoot() bool { + return t.includeReason.kind == fileIncludeKindRootFile || t.includeReason.kind == fileIncludeKindLibFile +} + func (t *parseTask) load(loader *fileLoader) { t.loaded = true t.path = loader.toPath(t.normalizedFilePath) @@ -201,7 +204,7 @@ func (w *filesParser) start(loader *fileLoader, tasks []*parseTask, depth int, i startSubtasks = true } - if !task.root && taskIsFromExternalLibrary && !loadedTask.fromExternalLibrary { + if !task.isRoot() && taskIsFromExternalLibrary && !loadedTask.fromExternalLibrary { // If we're seeing this task now as an external library, // reprocess its subtasks to ensure they are also marked as external. loadedTask.fromExternalLibrary = true From 2006b365c21aa6076a40a384293b705b0c815c7b Mon Sep 17 00:00:00 2001 From: Sheetal Nandi Date: Tue, 12 Aug 2025 13:28:05 -0700 Subject: [PATCH 6/6] Fix crash --- internal/compiler/filesparser.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/internal/compiler/filesparser.go b/internal/compiler/filesparser.go index 82d5dc90b5..e4f35280da 100644 --- a/internal/compiler/filesparser.go +++ b/internal/compiler/filesparser.go @@ -51,7 +51,8 @@ func (t *parseTask) Path() tspath.Path { } func (t *parseTask) isRoot() bool { - return t.includeReason.kind == fileIncludeKindRootFile || t.includeReason.kind == fileIncludeKindLibFile + // Intentionally not checking t.includeReason != nil to ensure we can catch cases for missing include reason + return !t.isForAutomaticTypeDirective && (t.includeReason.kind == fileIncludeKindRootFile || t.includeReason.kind == fileIncludeKindLibFile) } func (t *parseTask) load(loader *fileLoader) {