fix(compiler): throw an error for invalid provider (#13544)

Closes #8870
This commit is contained in:
Dzmitry Shylovich 2016-12-28 01:02:28 +03:00 committed by Hans
parent 174334dec3
commit 445ed43b9a
2 changed files with 30 additions and 9 deletions

View File

@ -14,9 +14,8 @@ import {assertArrayOfStrings, assertInterpolationSymbols} from './assertions';
import * as cpl from './compile_metadata'; import * as cpl from './compile_metadata';
import {DirectiveNormalizer} from './directive_normalizer'; import {DirectiveNormalizer} from './directive_normalizer';
import {DirectiveResolver} from './directive_resolver'; import {DirectiveResolver} from './directive_resolver';
import {ListWrapper, StringMapWrapper} from './facade/collection'; import {stringify} from './facade/lang';
import {isBlank, isPresent, stringify} from './facade/lang'; import {Identifiers, resolveIdentifier} from './identifiers';
import {Identifiers, createIdentifierToken, resolveIdentifier} from './identifiers';
import {CompilerInjectable} from './injectable'; import {CompilerInjectable} from './injectable';
import {hasLifecycleHook} from './lifecycle_reflector'; import {hasLifecycleHook} from './lifecycle_reflector';
import {NgModuleResolver} from './ng_module_resolver'; import {NgModuleResolver} from './ng_module_resolver';
@ -25,7 +24,7 @@ import {ComponentStillLoadingError, LIFECYCLE_HOOKS_VALUES, ReflectorReader, ref
import {ElementSchemaRegistry} from './schema/element_schema_registry'; import {ElementSchemaRegistry} from './schema/element_schema_registry';
import {SummaryResolver} from './summary_resolver'; import {SummaryResolver} from './summary_resolver';
import {getUrlScheme} from './url_resolver'; import {getUrlScheme} from './url_resolver';
import {MODULE_SUFFIX, SyncAsyncResult, SyntaxError, ValueTransformer, visitValue} from './util'; import {MODULE_SUFFIX, SyntaxError, ValueTransformer, visitValue} from './util';
export type ErrorCollector = (error: any, type?: any) => void; export type ErrorCollector = (error: any, type?: any) => void;
export const ERROR_COLLECTOR_TOKEN = new OpaqueToken('ErrorCollector'); export const ERROR_COLLECTOR_TOKEN = new OpaqueToken('ErrorCollector');
@ -70,7 +69,7 @@ export class CompileMetadataResolver {
} }
} }
clearCache() { clearCache(): void {
this._directiveCache.clear(); this._directiveCache.clear();
this._nonNormalizedDirectiveCache.clear(); this._nonNormalizedDirectiveCache.clear();
this._summaryCache.clear(); this._summaryCache.clear();
@ -337,14 +336,14 @@ export class CompileMetadataResolver {
} }
let providers: cpl.CompileProviderMetadata[] = []; let providers: cpl.CompileProviderMetadata[] = [];
if (isPresent(dirMeta.providers)) { if (dirMeta.providers != null) {
providers = this._getProvidersMetadata( providers = this._getProvidersMetadata(
dirMeta.providers, entryComponentMetadata, dirMeta.providers, entryComponentMetadata,
`providers for "${stringifyType(directiveType)}"`, [], directiveType); `providers for "${stringifyType(directiveType)}"`, [], directiveType);
} }
let queries: cpl.CompileQueryMetadata[] = []; let queries: cpl.CompileQueryMetadata[] = [];
let viewQueries: cpl.CompileQueryMetadata[] = []; let viewQueries: cpl.CompileQueryMetadata[] = [];
if (isPresent(dirMeta.queries)) { if (dirMeta.queries != null) {
queries = this._getQueriesMetadata(dirMeta.queries, false, directiveType); queries = this._getQueriesMetadata(dirMeta.queries, false, directiveType);
viewQueries = this._getQueriesMetadata(dirMeta.queries, true, directiveType); viewQueries = this._getQueriesMetadata(dirMeta.queries, true, directiveType);
} }
@ -807,14 +806,14 @@ export class CompileMetadataResolver {
token = paramEntry.attributeName; token = paramEntry.attributeName;
} else if (paramEntry instanceof Inject) { } else if (paramEntry instanceof Inject) {
token = paramEntry.token; token = paramEntry.token;
} else if (isValidType(paramEntry) && isBlank(token)) { } else if (isValidType(paramEntry) && token == null) {
token = paramEntry; token = paramEntry;
} }
}); });
} else { } else {
token = param; token = param;
} }
if (isBlank(token)) { if (token == null) {
hasUnknownDeps = true; hasUnknownDeps = true;
return null; return null;
} }
@ -864,6 +863,7 @@ export class CompileMetadataResolver {
provider = resolveForwardRef(provider); provider = resolveForwardRef(provider);
let providerMeta: cpl.ProviderMeta; let providerMeta: cpl.ProviderMeta;
if (provider && typeof provider == 'object' && provider.hasOwnProperty('provide')) { if (provider && typeof provider == 'object' && provider.hasOwnProperty('provide')) {
this._validateProvider(provider);
providerMeta = new cpl.ProviderMeta(provider.provide, provider); providerMeta = new cpl.ProviderMeta(provider.provide, provider);
} else if (isValidType(provider)) { } else if (isValidType(provider)) {
providerMeta = new cpl.ProviderMeta(provider, {useClass: provider}); providerMeta = new cpl.ProviderMeta(provider, {useClass: provider});
@ -897,6 +897,16 @@ export class CompileMetadataResolver {
return compileProviders; return compileProviders;
} }
private _validateProvider(provider: any): void {
if (provider.hasOwnProperty('useClass') && provider.useClass == null) {
this._reportError(new SyntaxError(
`Invalid provider for ${stringifyType(provider.provide)}. useClass cannot be ${provider.useClass}.
Usually it happens when:
1. There's a circular dependency (might be caused by using index.ts (barrel) files).
2. Class was used before it was declared. Use forwardRef in this case.`));
}
}
private _getEntryComponentsFromProvider(provider: cpl.ProviderMeta, type?: any): private _getEntryComponentsFromProvider(provider: cpl.ProviderMeta, type?: any):
cpl.CompileEntryComponentMetadata[] { cpl.CompileEntryComponentMetadata[] {
const components: cpl.CompileEntryComponentMetadata[] = []; const components: cpl.CompileEntryComponentMetadata[] = [];

View File

@ -222,6 +222,17 @@ export function main() {
SyntaxError, `Can't resolve all parameters for NonAnnotatedService: (?).`); SyntaxError, `Can't resolve all parameters for NonAnnotatedService: (?).`);
})); }));
it('should throw with descriptive error message when encounter invalid provider',
inject([CompileMetadataResolver], (resolver: CompileMetadataResolver) => {
@NgModule({providers: [{provide: SimpleService, useClass: undefined}]})
class SomeModule {
}
expect(() => resolver.loadNgModuleDirectiveAndPipeMetadata(SomeModule, true))
.toThrowError(
SyntaxError, /Invalid provider for SimpleService. useClass cannot be undefined./);
}));
it('should throw with descriptive error message when one of providers is not present', it('should throw with descriptive error message when one of providers is not present',
inject([CompileMetadataResolver], (resolver: CompileMetadataResolver) => { inject([CompileMetadataResolver], (resolver: CompileMetadataResolver) => {
@NgModule({declarations: [MyBrokenComp3]}) @NgModule({declarations: [MyBrokenComp3]})