diff --git a/packages/compiler-cli/src/diagnostics/symbols.ts b/packages/compiler-cli/src/diagnostics/symbols.ts index a0d4be0671bbc6..731b4eb2a3af6b 100644 --- a/packages/compiler-cli/src/diagnostics/symbols.ts +++ b/packages/compiler-cli/src/diagnostics/symbols.ts @@ -322,7 +322,7 @@ export interface SymbolQuery { /** * Return the type symbol for the given static symbol. */ - getTypeSymbol(type: StaticSymbol): Symbol; + getTypeSymbol(type: StaticSymbol): Symbol|undefined; /** * Return the members that are in the context of a type's template reference. diff --git a/packages/compiler-cli/src/diagnostics/typescript_symbols.ts b/packages/compiler-cli/src/diagnostics/typescript_symbols.ts index da82db0f1833fa..9d5199be2b9e5e 100644 --- a/packages/compiler-cli/src/diagnostics/typescript_symbols.ts +++ b/packages/compiler-cli/src/diagnostics/typescript_symbols.ts @@ -159,10 +159,10 @@ class TypeScriptSymbolQuery implements SymbolQuery { } } - getTypeSymbol(type: StaticSymbol): Symbol { + getTypeSymbol(type: StaticSymbol): Symbol|undefined { const context: TypeContext = {node: this.source, program: this.program, checker: this.checker}; - const typeSymbol = findClassSymbolInContext(type, context) !; - return new SymbolWrapper(typeSymbol, context); + const typeSymbol = findClassSymbolInContext(type, context); + return typeSymbol && new SymbolWrapper(typeSymbol, context); } createSymbolTable(symbols: SymbolDeclaration[]): SymbolTable { diff --git a/packages/compiler-cli/test/diagnostics/symbol_query_spec.ts b/packages/compiler-cli/test/diagnostics/symbol_query_spec.ts new file mode 100644 index 00000000000000..cf7d7697e65310 --- /dev/null +++ b/packages/compiler-cli/test/diagnostics/symbol_query_spec.ts @@ -0,0 +1,108 @@ +/** + * @license + * Copyright Google Inc. All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + +import {StaticSymbol} from '@angular/compiler'; +import {AngularCompilerOptions, CompilerHost} from '@angular/compiler-cli'; +import {EmittingCompilerHost, MockAotCompilerHost, MockCompilerHost, MockData, MockDirectory, MockMetadataBundlerHost, arrayToMockDir, arrayToMockMap, isSource, settings, setup, toMockFileArray} from '@angular/compiler/test/aot/test_util'; +import * as ts from 'typescript'; + +import {Symbol, SymbolQuery, SymbolTable} from '../../src/diagnostics/symbols'; +import {getSymbolQuery} from '../../src/diagnostics/typescript_symbols'; +import {Directory} from '../mocks'; + +import {DiagnosticContext, MockLanguageServiceHost} from './mocks'; + +function emptyPipes(): SymbolTable { + return { + size: 0, + get(key: string) { return undefined; }, + has(key: string) { return false; }, + values(): Symbol[]{return [];} + }; +} + +describe('symbol query', () => { + let program: ts.Program; + let checker: ts.TypeChecker; + let sourceFile: ts.SourceFile; + let query: SymbolQuery; + let context: DiagnosticContext; + beforeEach(() => { + const registry = ts.createDocumentRegistry(false, '/src'); + const host = new MockLanguageServiceHost( + ['/quickstart/app/app.component.ts'], QUICKSTART, '/quickstart'); + const service = ts.createLanguageService(host, registry); + program = service.getProgram(); + checker = program.getTypeChecker(); + sourceFile = program.getSourceFile('/quickstart/app/app.component.ts'); + const options: AngularCompilerOptions = Object.create(host.getCompilationSettings()); + options.genDir = '/dist'; + options.basePath = '/quickstart'; + const aotHost = new CompilerHost(program, options, host, {verboseInvalidExpression: true}); + context = new DiagnosticContext(service, program, checker, aotHost); + query = getSymbolQuery(program, checker, sourceFile, emptyPipes) + }); + + it('should be able to get undefined for an unknown symbol', () => { + const unknownType = context.getStaticSymbol('/unkonwn/file.ts', 'UnknownType'); + const symbol = query.getTypeSymbol(unknownType); + expect(symbol).toBeUndefined(); + }); +}); + +function appComponentSource(template: string): string { + return ` + import {Component} from '@angular/core'; + + export interface Person { + name: string; + address: Address; + } + + export interface Address { + street: string; + city: string; + state: string; + zip: string; + } + + @Component({ + template: '${template}' + }) + export class AppComponent { + name = 'Angular'; + person: Person; + people: Person[]; + maybePerson?: Person; + + getName(): string { return this.name; } + getPerson(): Person { return this.person; } + getMaybePerson(): Person | undefined { this.maybePerson; } + } + `; +} + +const QUICKSTART: Directory = { + quickstart: { + app: { + 'app.component.ts': appComponentSource('

Hello {{name}}

'), + 'app.module.ts': ` + import { NgModule } from '@angular/core'; + import { toString } from './utils'; + + import { AppComponent } from './app.component'; + + @NgModule({ + declarations: [ AppComponent ], + bootstrap: [ AppComponent ] + }) + export class AppModule { } + ` + } + } +};