Add error related to file not in rootDir and file not part of included root files at the references

This commit is contained in:
Sheetal Nandi 2019-08-09 11:15:20 -07:00
parent fd515b519e
commit 8ac45805c0
13 changed files with 444 additions and 143 deletions

View file

@ -552,6 +552,12 @@ namespace ts {
allDiagnostics?: Diagnostic[];
}
interface RefFile extends TextRange {
kind: RefFileKind;
index: number;
file: SourceFile;
}
/**
* Determines if program structure is upto date or needs to be recreated
*/
@ -717,6 +723,8 @@ namespace ts {
let noDiagnosticsTypeChecker: TypeChecker;
let classifiableNames: UnderscoreEscapedMap<true>;
const ambientModuleNameToUnmodifiedFileName = createMap<string>();
// Todo:: Use this to report why file was included in --extendedDiagnostics
let refFileMap: MultiMap<ts.RefFile> | undefined;
const cachedSemanticDiagnosticsForFile: DiagnosticCache<Diagnostic> = {};
const cachedDeclarationDiagnosticsForFile: DiagnosticCache<DiagnosticWithLocation> = {};
@ -913,6 +921,7 @@ namespace ts {
getSourceFileByPath,
getSourceFiles: () => files,
getMissingFilePaths: () => missingFilePaths!, // TODO: GH#18217
getRefFileMap: () => refFileMap,
getCompilerOptions: () => options,
getSyntacticDiagnostics,
getOptionsDiagnostics,
@ -1391,6 +1400,7 @@ namespace ts {
}
missingFilePaths = oldProgram.getMissingFilePaths();
refFileMap = oldProgram.getRefFileMap();
// update fileName -> file mapping
for (const newSourceFile of newSourceFiles) {
@ -2179,25 +2189,24 @@ namespace ts {
}
/** This has side effects through `findSourceFile`. */
function processSourceFile(fileName: string, isDefaultLib: boolean, ignoreNoDefaultLib: boolean, packageId: PackageId | undefined, refFile?: SourceFile, refPos?: number, refEnd?: number): void {
getSourceFileFromReferenceWorker(fileName,
fileName => findSourceFile(fileName, toPath(fileName), isDefaultLib, ignoreNoDefaultLib, refFile!, refPos!, refEnd!, packageId), // TODO: GH#18217
(diagnostic, ...args) => {
fileProcessingDiagnostics.add(refFile !== undefined && refEnd !== undefined && refPos !== undefined
? createFileDiagnostic(refFile, refPos, refEnd - refPos, diagnostic, ...args)
: createCompilerDiagnostic(diagnostic, ...args));
},
refFile);
function processSourceFile(fileName: string, isDefaultLib: boolean, ignoreNoDefaultLib: boolean, packageId: PackageId | undefined, refFile?: RefFile): void {
getSourceFileFromReferenceWorker(
fileName,
fileName => findSourceFile(fileName, toPath(fileName), isDefaultLib, ignoreNoDefaultLib, refFile, packageId), // TODO: GH#18217
(diagnostic, ...args) => fileProcessingDiagnostics.add(
createRefFileDiagnostic(refFile, diagnostic, ...args)
),
refFile && refFile.file
);
}
function reportFileNamesDifferOnlyInCasingError(fileName: string, existingFileName: string, refFile: SourceFile, refPos: number, refEnd: number): void {
if (refFile !== undefined && refPos !== undefined && refEnd !== undefined) {
fileProcessingDiagnostics.add(createFileDiagnostic(refFile, refPos, refEnd - refPos,
Diagnostics.File_name_0_differs_from_already_included_file_name_1_only_in_casing, fileName, existingFileName));
}
else {
fileProcessingDiagnostics.add(createCompilerDiagnostic(Diagnostics.File_name_0_differs_from_already_included_file_name_1_only_in_casing, fileName, existingFileName));
}
function reportFileNamesDifferOnlyInCasingError(fileName: string, existingFileName: string, refFile: RefFile | undefined): void {
fileProcessingDiagnostics.add(createRefFileDiagnostic(
refFile,
Diagnostics.File_name_0_differs_from_already_included_file_name_1_only_in_casing,
fileName,
existingFileName
));
}
function createRedirectSourceFile(redirectTarget: SourceFile, unredirected: SourceFile, fileName: string, path: Path, resolvedPath: Path, originalFileName: string): SourceFile {
@ -2222,7 +2231,7 @@ namespace ts {
}
// Get source file from normalized fileName
function findSourceFile(fileName: string, path: Path, isDefaultLib: boolean, ignoreNoDefaultLib: boolean, refFile: SourceFile, refPos: number, refEnd: number, packageId: PackageId | undefined): SourceFile | undefined {
function findSourceFile(fileName: string, path: Path, isDefaultLib: boolean, ignoreNoDefaultLib: boolean, refFile: RefFile | undefined, packageId: PackageId | undefined): SourceFile | undefined {
const originalFileName = fileName;
if (filesByName.has(path)) {
const file = filesByName.get(path);
@ -2239,7 +2248,7 @@ namespace ts {
const checkedAbsolutePath = getNormalizedAbsolutePathWithoutRoot(checkedName, currentDirectory);
const inputAbsolutePath = getNormalizedAbsolutePathWithoutRoot(inputName, currentDirectory);
if (checkedAbsolutePath !== inputAbsolutePath) {
reportFileNamesDifferOnlyInCasingError(inputName, checkedName, refFile, refPos, refEnd);
reportFileNamesDifferOnlyInCasingError(inputName, checkedName, refFile);
}
}
@ -2266,6 +2275,7 @@ namespace ts {
}
}
addFileToRefFileMap(file || undefined, refFile);
return file || undefined;
}
@ -2289,15 +2299,17 @@ namespace ts {
}
// We haven't looked for this file, do so now and cache result
const file = host.getSourceFile(fileName, options.target!, hostErrorMessage => { // TODO: GH#18217
if (refFile !== undefined && refPos !== undefined && refEnd !== undefined) {
fileProcessingDiagnostics.add(createFileDiagnostic(refFile, refPos, refEnd - refPos,
Diagnostics.Cannot_read_file_0_Colon_1, fileName, hostErrorMessage));
}
else {
fileProcessingDiagnostics.add(createCompilerDiagnostic(Diagnostics.Cannot_read_file_0_Colon_1, fileName, hostErrorMessage));
}
}, shouldCreateNewSourceFile);
const file = host.getSourceFile(
fileName,
options.target!,
hostErrorMessage => fileProcessingDiagnostics.add(createRefFileDiagnostic(
refFile,
Diagnostics.Cannot_read_file_0_Colon_1,
fileName,
hostErrorMessage
)),
shouldCreateNewSourceFile
);
if (packageId) {
const packageIdKey = packageIdToString(packageId);
@ -2331,7 +2343,7 @@ namespace ts {
// for case-sensitive file systems check if we've already seen some file with similar filename ignoring case
const existingFile = filesByNameIgnoreCase!.get(pathLowerCase);
if (existingFile) {
reportFileNamesDifferOnlyInCasingError(fileName, existingFile.fileName, refFile, refPos, refEnd);
reportFileNamesDifferOnlyInCasingError(fileName, existingFile.fileName, refFile);
}
else {
filesByNameIgnoreCase!.set(pathLowerCase, file);
@ -2359,10 +2371,20 @@ namespace ts {
processingOtherFiles!.push(file);
}
}
addFileToRefFileMap(file, refFile);
return file;
}
function addFileToRefFileMap(file: SourceFile | undefined, refFile: RefFile | undefined) {
if (refFile && file) {
(refFileMap || (refFileMap = createMultiMap())).add(file.path, {
kind: refFile.kind,
index: refFile.index,
file: refFile.file.path
});
}
}
function addFileToFilesByName(file: SourceFile | undefined, path: Path, redirectedPath: Path | undefined) {
if (redirectedPath) {
filesByName.set(redirectedPath, file);
@ -2479,9 +2501,21 @@ namespace ts {
}
function processReferencedFiles(file: SourceFile, isDefaultLib: boolean) {
forEach(file.referencedFiles, ref => {
forEach(file.referencedFiles, (ref, index) => {
const referencedFileName = resolveTripleslashReference(ref.fileName, file.originalFileName);
processSourceFile(referencedFileName, isDefaultLib, /*ignoreNoDefaultLib*/ false, /*packageId*/ undefined, file, ref.pos, ref.end);
processSourceFile(
referencedFileName,
isDefaultLib,
/*ignoreNoDefaultLib*/ false,
/*packageId*/ undefined,
{
kind: RefFileKind.ReferenceFile,
index,
file,
pos: ref.pos,
end: ref.end
}
);
});
}
@ -2500,12 +2534,25 @@ namespace ts {
// store resolved type directive on the file
const fileName = ref.fileName.toLocaleLowerCase();
setResolvedTypeReferenceDirective(file, fileName, resolvedTypeReferenceDirective);
processTypeReferenceDirective(fileName, resolvedTypeReferenceDirective, file, ref.pos, ref.end);
processTypeReferenceDirective(
fileName,
resolvedTypeReferenceDirective,
{
kind: RefFileKind.TypeReferenceDirective,
index: i,
file,
pos: ref.pos,
end: ref.end
}
);
}
}
function processTypeReferenceDirective(typeReferenceDirective: string, resolvedTypeReferenceDirective?: ResolvedTypeReferenceDirective,
refFile?: SourceFile, refPos?: number, refEnd?: number): void {
function processTypeReferenceDirective(
typeReferenceDirective: string,
resolvedTypeReferenceDirective?: ResolvedTypeReferenceDirective,
refFile?: RefFile
): void {
// If we already found this library as a primary reference - nothing to do
const previousResolution = resolvedTypeReferenceDirectives.get(typeReferenceDirective);
@ -2518,7 +2565,7 @@ namespace ts {
if (resolvedTypeReferenceDirective.primary) {
// resolved from the primary path
processSourceFile(resolvedTypeReferenceDirective.resolvedFileName!, /*isDefaultLib*/ false, /*ignoreNoDefaultLib*/ false, resolvedTypeReferenceDirective.packageId, refFile, refPos, refEnd); // TODO: GH#18217
processSourceFile(resolvedTypeReferenceDirective.resolvedFileName!, /*isDefaultLib*/ false, /*ignoreNoDefaultLib*/ false, resolvedTypeReferenceDirective.packageId, refFile); // TODO: GH#18217
}
else {
// If we already resolved to this file, it must have been a secondary reference. Check file contents
@ -2528,12 +2575,15 @@ namespace ts {
if (resolvedTypeReferenceDirective.resolvedFileName !== previousResolution.resolvedFileName) {
const otherFileText = host.readFile(resolvedTypeReferenceDirective.resolvedFileName!);
if (otherFileText !== getSourceFile(previousResolution.resolvedFileName!)!.text) {
fileProcessingDiagnostics.add(createDiagnostic(refFile!, refPos!, refEnd!, // TODO: GH#18217
Diagnostics.Conflicting_definitions_for_0_found_at_1_and_2_Consider_installing_a_specific_version_of_this_library_to_resolve_the_conflict,
typeReferenceDirective,
resolvedTypeReferenceDirective.resolvedFileName,
previousResolution.resolvedFileName
));
fileProcessingDiagnostics.add(
createRefFileDiagnostic(
refFile,
Diagnostics.Conflicting_definitions_for_0_found_at_1_and_2_Consider_installing_a_specific_version_of_this_library_to_resolve_the_conflict,
typeReferenceDirective,
resolvedTypeReferenceDirective.resolvedFileName,
previousResolution.resolvedFileName
)
);
}
}
// don't overwrite previous resolution result
@ -2541,14 +2591,18 @@ namespace ts {
}
else {
// First resolution of this library
processSourceFile(resolvedTypeReferenceDirective.resolvedFileName!, /*isDefaultLib*/ false, /*ignoreNoDefaultLib*/ false, resolvedTypeReferenceDirective.packageId, refFile, refPos, refEnd);
processSourceFile(resolvedTypeReferenceDirective.resolvedFileName!, /*isDefaultLib*/ false, /*ignoreNoDefaultLib*/ false, resolvedTypeReferenceDirective.packageId, refFile);
}
}
if (resolvedTypeReferenceDirective.isExternalLibraryImport) currentNodeModulesDepth--;
}
else {
fileProcessingDiagnostics.add(createDiagnostic(refFile!, refPos!, refEnd!, Diagnostics.Cannot_find_type_definition_file_for_0, typeReferenceDirective)); // TODO: GH#18217
fileProcessingDiagnostics.add(createRefFileDiagnostic(
refFile,
Diagnostics.Cannot_find_type_definition_file_for_0,
typeReferenceDirective
));
}
if (saveResolution) {
@ -2568,17 +2622,24 @@ namespace ts {
const unqualifiedLibName = removeSuffix(removePrefix(libName, "lib."), ".d.ts");
const suggestion = getSpellingSuggestion(unqualifiedLibName, libs, identity);
const message = suggestion ? Diagnostics.Cannot_find_lib_definition_for_0_Did_you_mean_1 : Diagnostics.Cannot_find_lib_definition_for_0;
fileProcessingDiagnostics.add(createDiagnostic(file, libReference.pos, libReference.end, message, libName, suggestion));
fileProcessingDiagnostics.add(createFileDiagnostic(
file,
libReference.pos,
libReference.end - libReference.pos,
message,
libName,
suggestion
));
}
});
}
function createDiagnostic(refFile: SourceFile, refPos: number, refEnd: number, message: DiagnosticMessage, ...args: any[]): Diagnostic {
if (refFile === undefined || refPos === undefined || refEnd === undefined) {
function createRefFileDiagnostic(refFile: RefFile | undefined, message: DiagnosticMessage, ...args: any[]): Diagnostic {
if (!refFile) {
return createCompilerDiagnostic(message, ...args);
}
else {
return createFileDiagnostic(refFile, refPos, refEnd - refPos, message, ...args);
return createFileDiagnostic(refFile.file, refFile.pos, refFile.end - refFile.pos, message, ...args);
}
}
@ -2632,7 +2693,20 @@ namespace ts {
else if (shouldAddFile) {
const path = toPath(resolvedFileName);
const pos = skipTrivia(file.text, file.imports[i].pos);
findSourceFile(resolvedFileName, path, /*isDefaultLib*/ false, /*ignoreNoDefaultLib*/ false, file, pos, file.imports[i].end, resolution.packageId);
findSourceFile(
resolvedFileName,
path,
/*isDefaultLib*/ false,
/*ignoreNoDefaultLib*/ false,
{
kind: RefFileKind.Import,
index: i,
file,
pos,
end: file.imports[i].end
},
resolution.packageId
);
}
if (isFromNodeModulesSearch) {
@ -2654,12 +2728,20 @@ namespace ts {
function checkSourceFilesBelongToPath(sourceFiles: ReadonlyArray<SourceFile>, rootDirectory: string): boolean {
let allFilesBelongToPath = true;
const absoluteRootDirectoryPath = host.getCanonicalFileName(getNormalizedAbsolutePath(rootDirectory, currentDirectory));
let rootPaths: Map<true> | undefined;
for (const sourceFile of sourceFiles) {
if (!sourceFile.isDeclarationFile) {
const absoluteSourceFilePath = host.getCanonicalFileName(getNormalizedAbsolutePath(sourceFile.fileName, currentDirectory));
if (absoluteSourceFilePath.indexOf(absoluteRootDirectoryPath) !== 0) {
programDiagnostics.add(createCompilerDiagnostic(Diagnostics.File_0_is_not_under_rootDir_1_rootDir_is_expected_to_contain_all_source_files, sourceFile.fileName, rootDirectory));
if (!rootPaths) rootPaths = arrayToSet(rootNames, toPath);
addProgramDiagnosticAtRefPath(
sourceFile,
rootPaths,
Diagnostics.File_0_is_not_under_rootDir_1_rootDir_is_expected_to_contain_all_source_files,
sourceFile.fileName,
rootDirectory
);
allFilesBelongToPath = false;
}
}
@ -2771,12 +2853,18 @@ namespace ts {
// List of collected files is complete; validate exhautiveness if this is a project with a file list
if (options.composite) {
const rootPaths = rootNames.map(toPath);
const rootPaths = arrayToSet(rootNames, toPath);
for (const file of files) {
// Ignore file that is not emitted
if (!sourceFileMayBeEmitted(file, options, isSourceFileFromExternalLibrary, getResolvedProjectReferenceToRedirect)) continue;
if (rootPaths.indexOf(file.path) === -1) {
programDiagnostics.add(createCompilerDiagnostic(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, options.configFilePath || ""));
if (!rootPaths.has(file.path)) {
addProgramDiagnosticAtRefPath(
file,
rootPaths,
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,
options.configFilePath || ""
);
}
}
}
@ -2984,6 +3072,35 @@ namespace ts {
}
}
function addProgramDiagnosticAtRefPath(file: SourceFile, rootPaths: Map<true>, message: DiagnosticMessage, ...args: (string | number | undefined)[]) {
const refPaths = refFileMap && refFileMap.get(file.path);
const refPathToReportErrorOn = forEach(refPaths, refPath => rootPaths.has(refPath.file) ? refPath : undefined) ||
elementAt(refPaths, 0);
if (refPathToReportErrorOn) {
const refFile = Debug.assertDefined(getSourceFileByPath(refPathToReportErrorOn.file));
const { kind, index } = refPathToReportErrorOn;
let pos: number, end: number;
switch (kind) {
case RefFileKind.Import:
pos = skipTrivia(refFile.text, refFile.imports[index].pos);
end = refFile.imports[index].end;
break;
case RefFileKind.ReferenceFile:
({ pos, end } = refFile.referencedFiles[index]);
break;
case RefFileKind.TypeReferenceDirective:
({ pos, end } = refFile.typeReferenceDirectives[index]);
break;
default:
return Debug.assertNever(kind);
}
programDiagnostics.add(createFileDiagnostic(refFile, pos, end - pos, message, ...args));
}
else {
programDiagnostics.add(createCompilerDiagnostic(message, ...args));
}
}
function verifyProjectReferences() {
const buildInfoPath = !options.noEmit && !options.suppressOutputPathCheck ? getOutputPathForBuildInfo(options) : undefined;
forEachProjectReference(projectReferences, resolvedProjectReferences, (resolvedRef, index, parent) => {

View file

@ -2914,6 +2914,20 @@ namespace ts {
throwIfCancellationRequested(): void;
}
/*@internal*/
export enum RefFileKind {
Import,
ReferenceFile,
TypeReferenceDirective
}
/*@internal*/
export interface RefFile {
kind: RefFileKind;
index: number;
file: Path;
}
// TODO: This should implement TypeCheckerHost but that's an internal type.
export interface Program extends ScriptReferenceHost {
@ -2933,6 +2947,8 @@ namespace ts {
*/
/* @internal */
getMissingFilePaths(): ReadonlyArray<Path>;
/* @internal */
getRefFileMap(): MultiMap<RefFile> | undefined;
/**
* Emits the JavaScript and declaration files. If targetSourceFile is not specified, then

View file

@ -192,7 +192,7 @@ namespace ts {
export function arrayToSet<T>(array: ReadonlyArray<T>, makeKey: (value: T) => string | undefined): Map<true>;
export function arrayToSet<T>(array: ReadonlyArray<T>, makeKey: (value: T) => __String | undefined): UnderscoreEscapedMap<true>;
export function arrayToSet(array: ReadonlyArray<any>, makeKey?: (value: any) => string | __String | undefined): Map<true> | UnderscoreEscapedMap<true> {
return arrayToMap<any, true>(array, makeKey || (s => s), () => true);
return arrayToMap<any, true>(array, makeKey || (s => s), returnTrue);
}
export function cloneMap(map: SymbolTable): SymbolTable;

View file

@ -193,7 +193,7 @@ namespace ts {
};
testProjectReferences(spec, "/primary/tsconfig.json", program => {
const errs = program.getOptionsDiagnostics();
const errs = program.getSemanticDiagnostics(program.getSourceFile("/primary/a.ts"));
assertHasError("Reports an error about b.ts not being in the list", errs, Diagnostics.File_0_is_not_listed_within_the_file_list_of_project_1_Projects_must_list_all_files_or_use_an_include_pattern);
});
});
@ -343,8 +343,9 @@ namespace ts {
}
};
testProjectReferences(spec, "/alpha/tsconfig.json", (program) => {
assertHasError("Issues an error about the rootDir", program.getOptionsDiagnostics(), Diagnostics.File_0_is_not_under_rootDir_1_rootDir_is_expected_to_contain_all_source_files);
assertHasError("Issues an error about the fileList", program.getOptionsDiagnostics(), Diagnostics.File_0_is_not_listed_within_the_file_list_of_project_1_Projects_must_list_all_files_or_use_an_include_pattern);
const semanticDiagnostics = program.getSemanticDiagnostics(program.getSourceFile("/alpha/src/a.ts"));
assertHasError("Issues an error about the rootDir", semanticDiagnostics, Diagnostics.File_0_is_not_under_rootDir_1_rootDir_is_expected_to_contain_all_source_files);
assertHasError("Issues an error about the fileList", semanticDiagnostics, Diagnostics.File_0_is_not_listed_within_the_file_list_of_project_1_Projects_must_list_all_files_or_use_an_include_pattern);
});
});
});

View file

@ -42,7 +42,7 @@ namespace ts {
interface VerifyBuild {
modifyDiskLayout: (fs: vfs.FileSystem) => void;
expectedExitStatus: ExitStatus;
expectedDiagnostics: fakes.ExpectedDiagnostic[];
expectedDiagnostics: (fs: vfs.FileSystem) => fakes.ExpectedDiagnostic[];
expectedOutputs: readonly string[];
notExpectedOutputs: readonly string[];
}
@ -54,7 +54,7 @@ namespace ts {
const builder = createSolutionBuilder(host, ["/src/tsconfig.json"], { verbose: true });
const exitStatus = builder.build();
assert.equal(exitStatus, expectedExitStatus);
host.assertDiagnosticMessages(...expectedDiagnostics);
host.assertDiagnosticMessages(...expectedDiagnostics(fs));
verifyOutputsPresent(fs, expectedOutputs);
verifyOutputsAbsent(fs, notExpectedOutputs);
}
@ -63,7 +63,7 @@ namespace ts {
verifyBuild({
modifyDiskLayout: noop,
expectedExitStatus: ExitStatus.Success,
expectedDiagnostics: [
expectedDiagnostics: () => [
getExpectedDiagnosticForProjectsInBuild("src/core/tsconfig.json", "src/animals/tsconfig.json", "src/zoo/tsconfig.json", "src/tsconfig.json"),
[Diagnostics.Project_0_is_out_of_date_because_output_file_1_does_not_exist, "src/core/tsconfig.json", "src/lib/core/utilities.js"],
[Diagnostics.Building_project_0, "/src/core/tsconfig.json"],
@ -91,7 +91,7 @@ namespace ts {
]`
),
expectedExitStatus: ExitStatus.ProjectReferenceCycle_OutputsSkupped,
expectedDiagnostics: [
expectedDiagnostics: () => [
getExpectedDiagnosticForProjectsInBuild("src/animals/tsconfig.json", "src/zoo/tsconfig.json", "src/core/tsconfig.json", "src/tsconfig.json"),
errorDiagnostic([
Diagnostics.Project_references_may_not_form_a_circular_graph_Cycle_detected_Colon_0,
@ -117,16 +117,38 @@ namespace ts {
`
),
expectedExitStatus: ExitStatus.DiagnosticsPresent_OutputsSkipped,
expectedDiagnostics: [
expectedDiagnostics: fs => [
getExpectedDiagnosticForProjectsInBuild("src/core/tsconfig.json", "src/animals/tsconfig.json", "src/zoo/tsconfig.json", "src/tsconfig.json"),
[Diagnostics.Project_0_is_out_of_date_because_output_file_1_does_not_exist, "src/core/tsconfig.json", "src/lib/core/utilities.js"],
[Diagnostics.Building_project_0, "/src/core/tsconfig.json"],
errorDiagnostic([Diagnostics.File_0_is_not_under_rootDir_1_rootDir_is_expected_to_contain_all_source_files, "/src/animals/animal.ts", "/src/core"]),
errorDiagnostic([Diagnostics.File_0_is_not_under_rootDir_1_rootDir_is_expected_to_contain_all_source_files, "/src/animals/dog.ts", "/src/core"]),
errorDiagnostic([Diagnostics.File_0_is_not_under_rootDir_1_rootDir_is_expected_to_contain_all_source_files, "/src/animals/index.ts", "/src/core"]),
errorDiagnostic([Diagnostics.File_0_is_not_listed_within_the_file_list_of_project_1_Projects_must_list_all_files_or_use_an_include_pattern, "/src/animals/animal.ts", "/src/core/tsconfig.json"]),
errorDiagnostic([Diagnostics.File_0_is_not_listed_within_the_file_list_of_project_1_Projects_must_list_all_files_or_use_an_include_pattern, "/src/animals/dog.ts", "/src/core/tsconfig.json"]),
errorDiagnostic([Diagnostics.File_0_is_not_listed_within_the_file_list_of_project_1_Projects_must_list_all_files_or_use_an_include_pattern, "/src/animals/index.ts", "/src/core/tsconfig.json"]),
{
message: [Diagnostics.File_0_is_not_under_rootDir_1_rootDir_is_expected_to_contain_all_source_files, "/src/animals/animal.ts", "/src/core"],
location: expectedLocationIndexOf(fs, "/src/animals/index.ts", `'./animal'`),
},
{
message: [Diagnostics.File_0_is_not_listed_within_the_file_list_of_project_1_Projects_must_list_all_files_or_use_an_include_pattern, "/src/animals/animal.ts", "/src/core/tsconfig.json"],
location: expectedLocationIndexOf(fs, "/src/animals/index.ts", `'./animal'`),
},
{
message: [Diagnostics.File_0_is_not_under_rootDir_1_rootDir_is_expected_to_contain_all_source_files, "/src/animals/dog.ts", "/src/core"],
location: expectedLocationIndexOf(fs, "/src/animals/index.ts", `'./dog'`),
},
{
message: [Diagnostics.File_0_is_not_listed_within_the_file_list_of_project_1_Projects_must_list_all_files_or_use_an_include_pattern, "/src/animals/dog.ts", "/src/core/tsconfig.json"],
location: expectedLocationIndexOf(fs, "/src/animals/index.ts", `'./dog'`),
},
{
message: [Diagnostics._0_is_declared_but_its_value_is_never_read, "A"],
location: expectedLocationIndexOf(fs, "/src/core/utilities.ts", `import * as A from '../animals';`),
},
{
message: [Diagnostics.File_0_is_not_under_rootDir_1_rootDir_is_expected_to_contain_all_source_files, "/src/animals/index.ts", "/src/core"],
location: expectedLocationIndexOf(fs, "/src/core/utilities.ts", `'../animals'`),
},
{
message: [Diagnostics.File_0_is_not_listed_within_the_file_list_of_project_1_Projects_must_list_all_files_or_use_an_include_pattern, "/src/animals/index.ts", "/src/core/tsconfig.json"],
location: expectedLocationIndexOf(fs, "/src/core/utilities.ts", `'../animals'`),
},
[Diagnostics.Project_0_can_t_be_built_because_its_dependency_1_has_errors, "src/animals/tsconfig.json", "src/core"],
[Diagnostics.Skipping_build_of_project_0_because_its_dependency_1_has_errors, "/src/animals/tsconfig.json", "/src/core"],
[Diagnostics.Project_0_can_t_be_built_because_its_dependency_1_was_not_built, "src/zoo/tsconfig.json", "src/animals"],

View file

@ -17,11 +17,7 @@ namespace ts {
builder.build();
host.assertDiagnosticMessages({
message: [Diagnostics.The_files_list_in_config_file_0_is_empty, "/src/no-references/tsconfig.json"],
location: {
file: "/src/no-references/tsconfig.json",
start: lastIndexOf(fs, "/src/no-references/tsconfig.json", "[]"),
length: 2
}
location: expectedLocationLastIndexOf(fs, "/src/no-references/tsconfig.json", "[]"),
});
// Check for outputs to not be written.

View file

@ -62,6 +62,22 @@ namespace ts {
return content.lastIndexOf(searchStr);
}
export function expectedLocationIndexOf(fs: vfs.FileSystem, file: string, searchStr: string): fakes.ExpectedDiagnosticLocation {
return {
file,
start: indexOf(fs, file, searchStr),
length: searchStr.length
};
}
export function expectedLocationLastIndexOf(fs: vfs.FileSystem, file: string, searchStr: string): fakes.ExpectedDiagnosticLocation {
return {
file,
start: lastIndexOf(fs, file, searchStr),
length: searchStr.length
};
}
export function getTime() {
let currentTime = 100;
return { tick, time, touch };

View file

@ -45,11 +45,7 @@ namespace ts {
[Diagnostics.Building_project_0, "/src/src/main/tsconfig.json"],
{
message: [Diagnostics.Cannot_write_file_0_because_it_will_overwrite_tsbuildinfo_file_generated_by_referenced_project_1, "/src/dist/tsconfig.tsbuildinfo", "/src/src/other"],
location: {
file: "/src/src/main/tsconfig.json",
start: indexOf(fs, "/src/src/main/tsconfig.json", `{ "path": "../other" }`),
length: `{ "path": "../other" }`.length
}
location: expectedLocationIndexOf(fs, "/src/src/main/tsconfig.json", `{ "path": "../other" }`),
}
);
verifyOutputsPresent(fs, allExpectedOutputs);
@ -84,11 +80,7 @@ namespace ts {
[Diagnostics.Building_project_0, "/src/src/main/tsconfig.json"],
{
message: [Diagnostics.Cannot_write_file_0_because_it_will_overwrite_tsbuildinfo_file_generated_by_referenced_project_1, "/src/dist/tsconfig.tsbuildinfo", "/src/src/other"],
location: {
file: "/src/src/main/tsconfig.json",
start: indexOf(fs, "/src/src/main/tsconfig.json", `{"path":"../other"}`),
length: `{"path":"../other"}`.length
}
location: expectedLocationIndexOf(fs, "/src/src/main/tsconfig.json", `{"path":"../other"}`),
}
);
verifyOutputsPresent(fs, allExpectedOutputs);

View file

@ -30,11 +30,14 @@ namespace ts {
it("with resolveJsonModule and include only", () => {
verifyProjectWithResolveJsonModule(
"/src/tsconfig_withInclude.json",
errorDiagnostic([
Diagnostics.File_0_is_not_listed_within_the_file_list_of_project_1_Projects_must_list_all_files_or_use_an_include_pattern,
"/src/src/hello.json",
"/src/tsconfig_withInclude.json"
])
{
message: [
Diagnostics.File_0_is_not_listed_within_the_file_list_of_project_1_Projects_must_list_all_files_or_use_an_include_pattern,
"/src/src/hello.json",
"/src/tsconfig_withInclude.json"
],
location: expectedLocationIndexOf(projFs, "/src/src/index.ts", `"./hello.json"`)
}
);
});

View file

@ -452,11 +452,7 @@ namespace ts {
[Diagnostics.Building_project_0, "/src/logic/tsconfig.json"],
{
message: [Diagnostics.Property_0_does_not_exist_on_type_1, "muitply", `typeof import("/src/core/index")`],
location: {
file: "/src/logic/index.ts",
start: indexOf(fs, "/src/logic/index.ts", "muitply"),
length: "muitply".length
}
location: expectedLocationIndexOf(fs, "/src/logic/index.ts", "muitply"),
},
[Diagnostics.Project_0_can_t_be_built_because_its_dependency_1_has_errors, "src/tests/tsconfig.json", "src/logic"],
[Diagnostics.Skipping_build_of_project_0_because_its_dependency_1_has_errors, "/src/tests/tsconfig.json", "/src/logic"]

View file

@ -1295,6 +1295,62 @@ export function someFn() { }`);
]);
});
it("updates with bad reference", () => {
const host = createTsBuildWatchSystem([
...allFilesExceptBase,
baseConfig,
{ path: libFile.path, content: libContent }
], { currentDirectory: projectLocation });
host.writeFile(coreFiles[1].path, `import * as A from '../animals';
${coreFiles[1].content}`);
createSolutionBuilderWithWatch(host, ["tsconfig.json"], { verbose: true, watch: true });
const errors = [
`animals/index.ts(1,20): error TS6059: File '/user/username/projects/demo/animals/animal.ts' is not under 'rootDir' '/user/username/projects/demo/core'. 'rootDir' is expected to contain all source files.\n`,
`animals/index.ts(1,20): error TS6307: File '/user/username/projects/demo/animals/animal.ts' is not listed within the file list of project '/user/username/projects/demo/core/tsconfig.json'. Projects must list all files or use an 'include' pattern.\n`,
`animals/index.ts(4,32): error TS6059: File '/user/username/projects/demo/animals/dog.ts' is not under 'rootDir' '/user/username/projects/demo/core'. 'rootDir' is expected to contain all source files.\n`,
`animals/index.ts(4,32): error TS6307: File '/user/username/projects/demo/animals/dog.ts' is not listed within the file list of project '/user/username/projects/demo/core/tsconfig.json'. Projects must list all files or use an 'include' pattern.\n`,
`core/utilities.ts(1,1): error TS6133: 'A' is declared but its value is never read.\n`,
`core/utilities.ts(1,20): error TS6059: File '/user/username/projects/demo/animals/index.ts' is not under 'rootDir' '/user/username/projects/demo/core'. 'rootDir' is expected to contain all source files.\n`,
`core/utilities.ts(1,20): error TS6307: File '/user/username/projects/demo/animals/index.ts' is not listed within the file list of project '/user/username/projects/demo/core/tsconfig.json'. Projects must list all files or use an 'include' pattern.\n`
].map(hostOutputDiagnostic);
checkOutputErrors(host, [
startingCompilationInWatchMode(),
hostOutputLog(`Projects in this build: \r\n * core/tsconfig.json\r\n * animals/tsconfig.json\r\n * zoo/tsconfig.json\r\n * tsconfig.json\n\n`),
hostOutputLog(`Project 'core/tsconfig.json' is out of date because output file 'lib/core/utilities.js' does not exist\n\n`),
hostOutputLog(`Building project '/user/username/projects/demo/core/tsconfig.json'...\n\n`),
...errors,
hostOutputLog(`Project 'animals/tsconfig.json' can't be built because its dependency 'core' has errors\n\n`),
hostOutputLog(`Skipping build of project '/user/username/projects/demo/animals/tsconfig.json' because its dependency '/user/username/projects/demo/core' has errors\n\n`),
hostOutputLog(`Project 'zoo/tsconfig.json' can't be built because its dependency 'animals' was not built\n\n`),
hostOutputLog(`Skipping build of project '/user/username/projects/demo/zoo/tsconfig.json' because its dependency '/user/username/projects/demo/animals' was not built\n\n`),
foundErrorsWatching(errors)
]);
verifyWatches(host);
// Make changes
host.writeFile(coreFiles[1].path, `
import * as A from '../animals';
${coreFiles[1].content}`);
const newErrors = [
`animals/index.ts(1,20): error TS6059: File '/user/username/projects/demo/animals/animal.ts' is not under 'rootDir' '/user/username/projects/demo/core'. 'rootDir' is expected to contain all source files.\n`,
`animals/index.ts(1,20): error TS6307: File '/user/username/projects/demo/animals/animal.ts' is not listed within the file list of project '/user/username/projects/demo/core/tsconfig.json'. Projects must list all files or use an 'include' pattern.\n`,
`animals/index.ts(4,32): error TS6059: File '/user/username/projects/demo/animals/dog.ts' is not under 'rootDir' '/user/username/projects/demo/core'. 'rootDir' is expected to contain all source files.\n`,
`animals/index.ts(4,32): error TS6307: File '/user/username/projects/demo/animals/dog.ts' is not listed within the file list of project '/user/username/projects/demo/core/tsconfig.json'. Projects must list all files or use an 'include' pattern.\n`,
`core/utilities.ts(2,1): error TS6133: 'A' is declared but its value is never read.\n`,
`core/utilities.ts(2,20): error TS6059: File '/user/username/projects/demo/animals/index.ts' is not under 'rootDir' '/user/username/projects/demo/core'. 'rootDir' is expected to contain all source files.\n`,
`core/utilities.ts(2,20): error TS6307: File '/user/username/projects/demo/animals/index.ts' is not listed within the file list of project '/user/username/projects/demo/core/tsconfig.json'. Projects must list all files or use an 'include' pattern.\n`
].map(hostOutputDiagnostic);
host.checkTimeoutQueueLengthAndRun(1); // build core
host.checkTimeoutQueueLength(0);
checkOutputErrors(host, [
fileChangeDetected(),
hostOutputLog(`Project 'core/tsconfig.json' is out of date because output file 'lib/core/utilities.js' does not exist\n\n`),
hostOutputLog(`Building project '/user/username/projects/demo/core/tsconfig.json'...\n\n`),
...newErrors,
foundErrorsWatching(newErrors)
]);
});
function subProjectFiles(subProject: string, fileNames: readonly string[]): File[] {
return fileNames.map(file => projectFile(`${subProject}/${file}`));
}

View file

@ -54,30 +54,51 @@ namespace ts.tscWatch {
const elapsedRegex = /^Elapsed:: [0-9]+ms/;
const buildVerboseLogRegEx = /^.+ \- /;
function checkOutputErrors(
export enum HostOutputKind {
Log,
Diagnostic,
WatchDiagnostic
}
export interface HostOutputLog {
kind: HostOutputKind.Log;
expected: string;
caption?: string;
}
export interface HostOutputDiagnostic {
kind: HostOutputKind.Diagnostic;
diagnostic: Diagnostic | string;
}
export interface HostOutputWatchDiagnostic {
kind: HostOutputKind.WatchDiagnostic;
diagnostic: Diagnostic | string;
}
export type HostOutput = HostOutputLog | HostOutputDiagnostic | HostOutputWatchDiagnostic;
export function checkOutputErrors(
host: WatchedSystem,
logsBeforeWatchDiagnostic: string[] | undefined,
preErrorsWatchDiagnostic: Diagnostic | undefined,
logsBeforeErrors: string[] | undefined,
errors: ReadonlyArray<Diagnostic> | ReadonlyArray<string>,
disableConsoleClears?: boolean | undefined,
...postErrorsWatchDiagnostics: Diagnostic[] | string[]
expected: readonly HostOutput[],
disableConsoleClears?: boolean | undefined
) {
let screenClears = 0;
const outputs = host.getOutput();
const expectedOutputCount = (preErrorsWatchDiagnostic ? 1 : 0) +
errors.length +
postErrorsWatchDiagnostics.length +
(logsBeforeWatchDiagnostic ? logsBeforeWatchDiagnostic.length : 0) +
(logsBeforeErrors ? logsBeforeErrors.length : 0);
assert.equal(outputs.length, expectedOutputCount, JSON.stringify(outputs));
assert.equal(outputs.length, expected.length, JSON.stringify(outputs));
let index = 0;
forEach(logsBeforeWatchDiagnostic, log => assertLog("logsBeforeWatchDiagnostic", log));
if (preErrorsWatchDiagnostic) assertWatchDiagnostic(preErrorsWatchDiagnostic);
forEach(logsBeforeErrors, log => assertLog("logBeforeError", log));
// Verify errors
forEach(errors, assertDiagnostic);
forEach(postErrorsWatchDiagnostics, assertWatchDiagnostic);
forEach(expected, expected => {
switch (expected.kind) {
case HostOutputKind.Log:
return assertLog(expected);
case HostOutputKind.Diagnostic:
return assertDiagnostic(expected);
case HostOutputKind.WatchDiagnostic:
return assertWatchDiagnostic(expected);
default:
return Debug.assertNever(expected);
}
});
assert.equal(host.screenClears.length, screenClears, "Expected number of screen clears");
host.clearOutput();
@ -85,7 +106,7 @@ namespace ts.tscWatch {
return !!(diagnostic as Diagnostic).messageText;
}
function assertDiagnostic(diagnostic: Diagnostic | string) {
function assertDiagnostic({ diagnostic }: HostOutputDiagnostic) {
const expected = isDiagnostic(diagnostic) ? formatDiagnostic(diagnostic, host) : diagnostic;
assert.equal(outputs[index], expected, getOutputAtFailedMessage("Diagnostic", expected));
index++;
@ -95,13 +116,13 @@ namespace ts.tscWatch {
return log.replace(elapsedRegex, "").replace(buildVerboseLogRegEx, "");
}
function assertLog(caption: string, expected: string) {
function assertLog({ caption, expected }: HostOutputLog) {
const actual = outputs[index];
assert.equal(getCleanLogString(actual), getCleanLogString(expected), getOutputAtFailedMessage(caption, expected));
assert.equal(getCleanLogString(actual), getCleanLogString(expected), getOutputAtFailedMessage(caption || "Log", expected));
index++;
}
function assertWatchDiagnostic(diagnostic: Diagnostic | string) {
function assertWatchDiagnostic({ diagnostic }: HostOutputWatchDiagnostic) {
if (isString(diagnostic)) {
assert.equal(outputs[index], diagnostic, getOutputAtFailedMessage("Diagnostic", diagnostic));
}
@ -128,54 +149,82 @@ namespace ts.tscWatch {
}
}
function createErrorsFoundCompilerDiagnostic(errors: ReadonlyArray<Diagnostic> | ReadonlyArray<string>) {
return errors.length === 1
? createCompilerDiagnostic(Diagnostics.Found_1_error_Watching_for_file_changes)
: createCompilerDiagnostic(Diagnostics.Found_0_errors_Watching_for_file_changes, errors.length);
export function hostOutputLog(expected: string, caption?: string): HostOutputLog {
return { kind: HostOutputKind.Log, expected, caption };
}
export function hostOutputDiagnostic(diagnostic: Diagnostic | string): HostOutputDiagnostic {
return { kind: HostOutputKind.Diagnostic, diagnostic };
}
export function hostOutputWatchDiagnostic(diagnostic: Diagnostic | string): HostOutputWatchDiagnostic {
return { kind: HostOutputKind.WatchDiagnostic, diagnostic };
}
export function startingCompilationInWatchMode() {
return hostOutputWatchDiagnostic(createCompilerDiagnostic(Diagnostics.Starting_compilation_in_watch_mode));
}
export function foundErrorsWatching(errors: readonly any[]) {
return hostOutputWatchDiagnostic(errors.length === 1 ?
createCompilerDiagnostic(Diagnostics.Found_1_error_Watching_for_file_changes) :
createCompilerDiagnostic(Diagnostics.Found_0_errors_Watching_for_file_changes, errors.length)
);
}
export function fileChangeDetected() {
return hostOutputWatchDiagnostic(createCompilerDiagnostic(Diagnostics.File_change_detected_Starting_incremental_compilation));
}
export function checkOutputErrorsInitial(host: WatchedSystem, errors: ReadonlyArray<Diagnostic> | ReadonlyArray<string>, disableConsoleClears?: boolean, logsBeforeErrors?: string[]) {
checkOutputErrors(
host,
/*logsBeforeWatchDiagnostic*/ undefined,
createCompilerDiagnostic(Diagnostics.Starting_compilation_in_watch_mode),
logsBeforeErrors,
errors,
disableConsoleClears,
createErrorsFoundCompilerDiagnostic(errors));
[
startingCompilationInWatchMode(),
...map(logsBeforeErrors || emptyArray, expected => hostOutputLog(expected, "logBeforeError")),
...map(errors, hostOutputDiagnostic),
foundErrorsWatching(errors)
],
disableConsoleClears
);
}
export function checkOutputErrorsIncremental(host: WatchedSystem, errors: ReadonlyArray<Diagnostic> | ReadonlyArray<string>, disableConsoleClears?: boolean, logsBeforeWatchDiagnostic?: string[], logsBeforeErrors?: string[]) {
checkOutputErrors(
host,
logsBeforeWatchDiagnostic,
createCompilerDiagnostic(Diagnostics.File_change_detected_Starting_incremental_compilation),
logsBeforeErrors,
errors,
disableConsoleClears,
createErrorsFoundCompilerDiagnostic(errors));
[
...map(logsBeforeWatchDiagnostic || emptyArray, expected => hostOutputLog(expected, "logsBeforeWatchDiagnostic")),
fileChangeDetected(),
...map(logsBeforeErrors || emptyArray, expected => hostOutputLog(expected, "logBeforeError")),
...map(errors, hostOutputDiagnostic),
foundErrorsWatching(errors)
],
disableConsoleClears
);
}
export function checkOutputErrorsIncrementalWithExit(host: WatchedSystem, errors: ReadonlyArray<Diagnostic> | ReadonlyArray<string>, expectedExitCode: ExitStatus, disableConsoleClears?: boolean, logsBeforeWatchDiagnostic?: string[], logsBeforeErrors?: string[]) {
checkOutputErrors(
host,
logsBeforeWatchDiagnostic,
createCompilerDiagnostic(Diagnostics.File_change_detected_Starting_incremental_compilation),
logsBeforeErrors,
errors,
disableConsoleClears);
[
...map(logsBeforeWatchDiagnostic || emptyArray, expected => hostOutputLog(expected, "logsBeforeWatchDiagnostic")),
fileChangeDetected(),
...map(logsBeforeErrors || emptyArray, expected => hostOutputLog(expected, "logBeforeError")),
...map(errors, hostOutputDiagnostic),
],
disableConsoleClears
);
assert.equal(host.exitCode, expectedExitCode);
}
export function checkNormalBuildErrors(host: WatchedSystem, errors: ReadonlyArray<Diagnostic> | ReadonlyArray<string>, reportErrorSummary?: boolean) {
checkOutputErrors(
host,
/*logsBeforeWatchDiagnostic*/ undefined,
/*preErrorsWatchDiagnostic*/ undefined,
/*logsBeforeErrors*/ undefined,
errors,
/*disableConsoleClears*/ undefined,
...(reportErrorSummary ? [getErrorSummaryText(errors.length, host.newLine)] : emptyArray)
[
...map(errors, hostOutputDiagnostic),
...map(
reportErrorSummary ?
[getErrorSummaryText(errors.length, host.newLine)] :
emptyArray,
hostOutputWatchDiagnostic
)
]
);
}

View file

@ -1335,5 +1335,42 @@ exports.a = 1;
checkProgramActualFiles(watch(), [aFile.path, bFile.path, libFile.path]);
}
});
it("reports errors correctly with file not in rootDir", () => {
const currentDirectory = "/user/username/projects/myproject";
const aFile: File = {
path: `${currentDirectory}/a.ts`,
content: `import { x } from "../b";`
};
const bFile: File = {
path: `/user/username/projects/b.ts`,
content: `export const x = 10;`
};
const configFile: File = {
path: `${currentDirectory}/tsconfig.json`,
content: JSON.stringify({
compilerOptions: {
rootDir: ".",
outDir: "lib"
}
})
};
const files = [aFile, bFile, libFile, configFile];
const host = createWatchedSystem(files, { currentDirectory });
const watch = createWatchOfConfigFile("tsconfig.json", host);
checkOutputErrorsInitial(host, [
getDiagnosticOfFileFromProgram(watch(), aFile.path, aFile.content.indexOf(`"../b"`), `"../b"`.length, Diagnostics.File_0_is_not_under_rootDir_1_rootDir_is_expected_to_contain_all_source_files, bFile.path, currentDirectory)
]);
const aContent = `
${aFile.content}`;
host.writeFile(aFile.path, aContent);
host.runQueuedTimeoutCallbacks();
checkOutputErrorsIncremental(host, [
getDiagnosticOfFileFromProgram(watch(), aFile.path, aContent.indexOf(`"../b"`), `"../b"`.length, Diagnostics.File_0_is_not_under_rootDir_1_rootDir_is_expected_to_contain_all_source_files, bFile.path, currentDirectory)
]);
});
});
}