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 {DirectiveNormalizer} from './directive_normalizer';
import {DirectiveResolver} from './directive_resolver';
import {ListWrapper, StringMapWrapper} from './facade/collection';
import {isBlank, isPresent, stringify} from './facade/lang';
import {Identifiers, createIdentifierToken, resolveIdentifier} from './identifiers';
import {stringify} from './facade/lang';
import {Identifiers, resolveIdentifier} from './identifiers';
import {CompilerInjectable} from './injectable';
import {hasLifecycleHook} from './lifecycle_reflector';
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 {SummaryResolver} from './summary_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 const ERROR_COLLECTOR_TOKEN = new OpaqueToken('ErrorCollector');
@ -70,7 +69,7 @@ export class CompileMetadataResolver {
}
}
clearCache() {
clearCache(): void {
this._directiveCache.clear();
this._nonNormalizedDirectiveCache.clear();
this._summaryCache.clear();
@ -337,14 +336,14 @@ export class CompileMetadataResolver {
}
let providers: cpl.CompileProviderMetadata[] = [];
if (isPresent(dirMeta.providers)) {
if (dirMeta.providers != null) {
providers = this._getProvidersMetadata(
dirMeta.providers, entryComponentMetadata,
`providers for "${stringifyType(directiveType)}"`, [], directiveType);
}
let queries: cpl.CompileQueryMetadata[] = [];
let viewQueries: cpl.CompileQueryMetadata[] = [];
if (isPresent(dirMeta.queries)) {
if (dirMeta.queries != null) {
queries = this._getQueriesMetadata(dirMeta.queries, false, directiveType);
viewQueries = this._getQueriesMetadata(dirMeta.queries, true, directiveType);
}
@ -807,14 +806,14 @@ export class CompileMetadataResolver {
token = paramEntry.attributeName;
} else if (paramEntry instanceof Inject) {
token = paramEntry.token;
} else if (isValidType(paramEntry) && isBlank(token)) {
} else if (isValidType(paramEntry) && token == null) {
token = paramEntry;
}
});
} else {
token = param;
}
if (isBlank(token)) {
if (token == null) {
hasUnknownDeps = true;
return null;
}
@ -864,6 +863,7 @@ export class CompileMetadataResolver {
provider = resolveForwardRef(provider);
let providerMeta: cpl.ProviderMeta;
if (provider && typeof provider == 'object' && provider.hasOwnProperty('provide')) {
this._validateProvider(provider);
providerMeta = new cpl.ProviderMeta(provider.provide, provider);
} else if (isValidType(provider)) {
providerMeta = new cpl.ProviderMeta(provider, {useClass: provider});
@ -897,6 +897,16 @@ export class CompileMetadataResolver {
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):
cpl.CompileEntryComponentMetadata[] {
const components: cpl.CompileEntryComponentMetadata[] = [];

View File

@ -222,6 +222,17 @@ export function main() {
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',
inject([CompileMetadataResolver], (resolver: CompileMetadataResolver) => {
@NgModule({declarations: [MyBrokenComp3]})