fix(compiler): report errors properly in G3 in certain conditions (#20041)

Condition: static analysis error, given:
- noResolve:true
- generateCodeForLibraries: false
- CompilerHost.getSourceFile throws on non existent files

All of these are true in G3.
PR Close #20041
This commit is contained in:
Tobias Bosch 2017-10-30 16:03:22 -07:00 committed by Matias Niemelä
parent 951bd33b09
commit 54480f7dfc
2 changed files with 46 additions and 25 deletions

View File

@ -456,13 +456,13 @@ class AngularCompilerProgram implements Program {
}; };
let rootNames = this.rootNames; let rootNames = [...this.rootNames];
if (this.options.generateCodeForLibraries !== false) { if (this.options.generateCodeForLibraries !== false) {
// if we should generateCodeForLibraries, never include // if we should generateCodeForLibraries, never include
// generated files in the program as otherwise we will // generated files in the program as otherwise we will
// ovewrite them and typescript will report the error // ovewrite them and typescript will report the error
// TS5055: Cannot write file ... because it would overwrite input file. // TS5055: Cannot write file ... because it would overwrite input file.
rootNames = this.rootNames.filter(fn => !GENERATED_FILES.test(fn)); rootNames = rootNames.filter(fn => !GENERATED_FILES.test(fn));
} }
if (this.options.noResolve) { if (this.options.noResolve) {
this.rootNames.forEach(rootName => { this.rootNames.forEach(rootName => {

View File

@ -78,6 +78,15 @@ describe('ng program', () => {
return {emitResult, program}; return {emitResult, program};
} }
function resolveFiles(rootNames: string[]) {
const preOptions = testSupport.createCompilerOptions();
const preHost = ts.createCompilerHost(preOptions);
// don't resolve symlinks
preHost.realpath = (f) => f;
const preProgram = ts.createProgram(rootNames, preOptions, preHost);
return preProgram.getSourceFiles().map(sf => sf.fileName);
}
describe('reuse of old program', () => { describe('reuse of old program', () => {
it('should reuse generated code for libraries from old programs', () => { it('should reuse generated code for libraries from old programs', () => {
compileLib('lib'); compileLib('lib');
@ -373,13 +382,7 @@ describe('ng program', () => {
testSupport.writeFiles({ testSupport.writeFiles({
'src/main.ts': createModuleAndCompSource('main'), 'src/main.ts': createModuleAndCompSource('main'),
}); });
const preOptions = testSupport.createCompilerOptions(); const allRootNames = resolveFiles([path.resolve(testSupport.basePath, 'src/main.ts')]);
const preHost = ts.createCompilerHost(preOptions);
// don't resolve symlinks
preHost.realpath = (f) => f;
const preProgram =
ts.createProgram([path.resolve(testSupport.basePath, 'src/main.ts')], preOptions, preHost);
const allRootNames = preProgram.getSourceFiles().map(sf => sf.fileName);
// now do the actual test with noResolve // now do the actual test with noResolve
const program = compile(undefined, {noResolve: true}, allRootNames); const program = compile(undefined, {noResolve: true}, allRootNames);
@ -932,23 +935,41 @@ describe('ng program', () => {
}); });
}); });
it('should be able to use a program with structural errors as oldProgram', () => { it('should be able report structural errors with noResolve:true and generateCodeForLibraries:false ' +
testSupport.write('src/index.ts', fileWithStructuralError); 'even if getSourceFile throws for non existent files',
() => {
testSupport.write('src/index.ts', fileWithGoodContent);
const options = testSupport.createCompilerOptions(); // compile angular and produce .ngsummary.json / ngfactory.d.ts files
const host = ng.createCompilerHost({options}); compile();
const program1 = ng.createProgram(
{rootNames: [path.resolve(testSupport.basePath, 'src/index.ts')], options, host});
expect(program1.getNgStructuralDiagnostics().length).toBe(1);
testSupport.write('src/index.ts', fileWithGoodContent); testSupport.write('src/ok.ts', fileWithGoodContent);
const program2 = ng.createProgram({ testSupport.write('src/error.ts', fileWithStructuralError);
rootNames: [path.resolve(testSupport.basePath, 'src/index.ts')],
options, // Make sure the ok.ts file is before the error.ts file,
host, // so we added a .ngfactory.ts file for it.
oldProgram: program1 const allRootNames = resolveFiles(
}); ['src/ok.ts', 'src/error.ts'].map(fn => path.resolve(testSupport.basePath, fn)));
expectNoDiagnosticsInProgram(options, program2);
}); const options = testSupport.createCompilerOptions({
noResolve: true,
generateCodeForLibraries: false,
});
const host = ng.createCompilerHost({options});
const originalGetSourceFile = host.getSourceFile;
host.getSourceFile =
(fileName: string, languageVersion: ts.ScriptTarget,
onError?: ((message: string) => void) | undefined): ts.SourceFile => {
// We should never try to load .ngfactory.ts files
if (fileName.match(/\.ngfactory\.ts$/)) {
throw new Error(`Non existent ngfactory file: ` + fileName);
}
return originalGetSourceFile.call(host, fileName, languageVersion, onError);
};
const program = ng.createProgram({rootNames: allRootNames, options, host});
const structuralErrors = program.getNgStructuralDiagnostics();
expect(structuralErrors.length).toBe(1);
expect(structuralErrors[0].messageText).toContain('Function calls are not supported.');
});
}); });
}); });