fix: Improve error message on missing dependency

This commit is contained in:
Misko Hevery 2015-05-18 17:19:54 -07:00
parent 7501ad11ca
commit 2ccc65d7fd
16 changed files with 85 additions and 48 deletions

View File

@ -1,4 +1,4 @@
import {CONST} from 'angular2/src/facade/lang'; import {CONST, stringify} from 'angular2/src/facade/lang';
import {DependencyAnnotation} from 'angular2/src/di/annotations_impl'; import {DependencyAnnotation} from 'angular2/src/di/annotations_impl';
/** /**
@ -41,6 +41,7 @@ export class Attribute extends DependencyAnnotation {
// account. // account.
return this; return this;
} }
toString() { return `@Attribute(${stringify(this.attributeName)})`; }
} }
/** /**
@ -53,4 +54,5 @@ export class Attribute extends DependencyAnnotation {
@CONST() @CONST()
export class Query extends DependencyAnnotation { export class Query extends DependencyAnnotation {
constructor(public directive: any) { super(); } constructor(public directive: any) { super(); }
toString() { return `@Query(${stringify(this.directive)})`; }
} }

View File

@ -9,6 +9,9 @@ export class Visibility extends DependencyAnnotation {
} }
get includeSelf(): boolean { return isBlank(this._includeSelf) ? false : this._includeSelf; } get includeSelf(): boolean { return isBlank(this._includeSelf) ? false : this._includeSelf; }
toString() {
return `@Visibility(depth: ${this.depth}, crossComponentBoundaries: ${this.crossComponentBoundaries}, includeSelf: ${this.includeSelf}})`;
}
} }
/** /**
@ -51,6 +54,7 @@ export class Visibility extends DependencyAnnotation {
@CONST() @CONST()
export class Self extends Visibility { export class Self extends Visibility {
constructor() { super(0, false, true); } constructor() { super(0, false, true); }
toString() { return `@Self()`; }
} }
// make constants after switching to ts2dart // make constants after switching to ts2dart
@ -102,6 +106,7 @@ export var self = new Self();
@CONST() @CONST()
export class Parent extends Visibility { export class Parent extends Visibility {
constructor({self}: {self?: boolean} = {}) { super(1, false, self); } constructor({self}: {self?: boolean} = {}) { super(1, false, self); }
toString() { return `@Parent(self: ${this.includeSelf}})`; }
} }
/** /**
@ -164,6 +169,7 @@ export class Parent extends Visibility {
@CONST() @CONST()
export class Ancestor extends Visibility { export class Ancestor extends Visibility {
constructor({self}: {self?: boolean} = {}) { super(999999, false, self); } constructor({self}: {self?: boolean} = {}) { super(999999, false, self); }
toString() { return `@Ancestor(self: ${this.includeSelf}})`; }
} }
/** /**
@ -203,4 +209,5 @@ export class Ancestor extends Visibility {
@CONST() @CONST()
export class Unbounded extends Visibility { export class Unbounded extends Visibility {
constructor({self}: {self?: boolean} = {}) { super(999999, true, self); } constructor({self}: {self?: boolean} = {}) { super(999999, true, self); }
toString() { return `@Unbounded(self: ${this.includeSelf}})`; }
} }

View File

@ -1,4 +1,4 @@
import {CONST} from "angular2/src/facade/lang"; import {CONST, stringify} from "angular2/src/facade/lang";
/** /**
* A parameter annotation that specifies a dependency. * A parameter annotation that specifies a dependency.
@ -15,6 +15,7 @@ import {CONST} from "angular2/src/facade/lang";
@CONST() @CONST()
export class Inject { export class Inject {
constructor(public token) {} constructor(public token) {}
toString() { return `@Inject(${stringify(this.token)})`; }
} }
/** /**
@ -33,6 +34,7 @@ export class Inject {
@CONST() @CONST()
export class InjectPromise { export class InjectPromise {
constructor(public token) {} constructor(public token) {}
toString() { return `@InjectPromise(${stringify(this.token)})`; }
} }
/** /**
@ -51,6 +53,7 @@ export class InjectPromise {
@CONST() @CONST()
export class InjectLazy { export class InjectLazy {
constructor(public token) {} constructor(public token) {}
toString() { return `@InjectLazy(${stringify(this.token)})`; }
} }
/** /**
@ -69,6 +72,7 @@ export class InjectLazy {
*/ */
@CONST() @CONST()
export class Optional { export class Optional {
toString() { return `@Optional()`; }
} }
/** /**

View File

@ -437,22 +437,27 @@ export class BindingBuilder {
} }
} }
function _constructDependencies(factoryFunction: Function, dependencies: List<any>) { function _constructDependencies(factoryFunction: Function,
return isBlank(dependencies) ? dependencies: List<any>): List<Dependency> {
_dependenciesFor(factoryFunction) : if (isBlank(dependencies)) {
ListWrapper.map(dependencies, (t) => _extractToken(factoryFunction, t)); return _dependenciesFor(factoryFunction);
} else {
var params: List<List<any>> = ListWrapper.map(dependencies, (t) => [t]);
return ListWrapper.map(dependencies, (t) => _extractToken(factoryFunction, t, params));
}
} }
function _dependenciesFor(typeOrFunc): List<any> { function _dependenciesFor(typeOrFunc): List<Dependency> {
var params = reflector.parameters(typeOrFunc); var params = reflector.parameters(typeOrFunc);
if (isBlank(params)) return []; if (isBlank(params)) return [];
if (ListWrapper.any(params, (p) => isBlank(p))) { if (ListWrapper.any(params, (p) => isBlank(p))) {
throw new NoAnnotationError(typeOrFunc); throw new NoAnnotationError(typeOrFunc, params);
} }
return ListWrapper.map(params, (p) => _extractToken(typeOrFunc, p)); return ListWrapper.map(params, (p: List<any>) => _extractToken(typeOrFunc, p, params));
} }
function _extractToken(typeOrFunc, annotations) { function _extractToken(typeOrFunc, annotations /*List<any> | any*/,
params: List<List<any>>): Dependency {
var depProps = []; var depProps = [];
var token = null; var token = null;
var optional = false; var optional = false;
@ -496,7 +501,7 @@ function _extractToken(typeOrFunc, annotations) {
if (isPresent(token)) { if (isPresent(token)) {
return _createDependency(token, asPromise, lazy, optional, depProps); return _createDependency(token, asPromise, lazy, optional, depProps);
} else { } else {
throw new NoAnnotationError(typeOrFunc); throw new NoAnnotationError(typeOrFunc, params);
} }
} }

View File

@ -1,5 +1,5 @@
import {ListWrapper, List} from 'angular2/src/facade/collection'; import {ListWrapper, List} from 'angular2/src/facade/collection';
import {stringify, BaseException} from 'angular2/src/facade/lang'; import {stringify, BaseException, isBlank} from 'angular2/src/facade/lang';
function findFirstClosedCycle(keys: List<any>): List<any> { function findFirstClosedCycle(keys: List<any>): List<any> {
var res = []; var res = [];
@ -179,9 +179,19 @@ export class InvalidBindingError extends BaseException {
export class NoAnnotationError extends BaseException { export class NoAnnotationError extends BaseException {
name: string; name: string;
message: string; message: string;
constructor(typeOrFunc) { constructor(typeOrFunc, params: List<List<any>>) {
super(); super();
this.message = "Cannot resolve all parameters for " + stringify(typeOrFunc) + ". " + var signature = ListWrapper.create();
for (var i = 0, ii = params.length; i < ii; i++) {
var parameter = params[i];
if (isBlank(parameter) || parameter.length == 0) {
ListWrapper.push(signature, '?');
} else {
ListWrapper.push(signature, ListWrapper.map(parameter, stringify).join(' '));
}
}
this.message = "Cannot resolve all parameters for " + stringify(typeOrFunc) + "(" +
signature.join(', ') + "). " +
'Make sure they all have valid type or annotations.'; 'Make sure they all have valid type or annotations.';
} }

View File

@ -1,4 +1,4 @@
import {Type} from 'angular2/src/facade/lang'; import {Type, stringify} from 'angular2/src/facade/lang';
export interface ForwardRefFn { (): any; } export interface ForwardRefFn { (): any; }
@ -30,6 +30,7 @@ export interface ForwardRefFn { (): any; }
*/ */
export function forwardRef(forwardRefFn: ForwardRefFn): Type { export function forwardRef(forwardRefFn: ForwardRefFn): Type {
(<any>forwardRefFn).__forward_ref__ = forwardRef; (<any>forwardRefFn).__forward_ref__ = forwardRef;
(<any>forwardRefFn).toString = function() { return stringify(this()); };
return (<Type><any>forwardRefFn); return (<Type><any>forwardRefFn);
} }

View File

@ -0,0 +1,4 @@
library angular2.di.type_info;
// In dart always return empty, as we can get the co
argsLength(Type type) => 0;

View File

View File

@ -0,0 +1,13 @@
import {Type} from 'angular2/src/facade/lang';
import {GetterFn, SetterFn, MethodFn} from './types';
import {List} from 'angular2/src/facade/collection';
export interface PlatformReflectionCapabilities {
factory(type: Type): Function;
interfaces(type: Type): List<any>;
parameters(type: Type): List<List<any>>;
annotations(type: Type): List<any>;
getter(name: string): GetterFn;
setter(name: string): SetterFn;
method(name: string): MethodFn;
}

View File

@ -3,13 +3,18 @@ library reflection.reflection;
import 'reflector.dart'; import 'reflector.dart';
import 'types.dart'; import 'types.dart';
export 'reflector.dart'; export 'reflector.dart';
import 'platform_reflection_capabilities.dart';
import 'package:angular2/src/facade/lang.dart'; import 'package:angular2/src/facade/lang.dart';
class NoReflectionCapabilities implements IReflectionCapabilities { class NoReflectionCapabilities implements PlatformReflectionCapabilities {
Function factory(Type type) { Function factory(Type type) {
throw "Cannot find reflection information on ${stringify(type)}"; throw "Cannot find reflection information on ${stringify(type)}";
} }
List interfaces(Type type) {
throw "Cannot find reflection information on ${stringify(type)}";
}
List parameters(Type type) { List parameters(Type type) {
throw "Cannot find reflection information on ${stringify(type)}"; throw "Cannot find reflection information on ${stringify(type)}";
} }

View File

@ -3,8 +3,9 @@ library reflection.reflection_capabilities;
import 'package:angular2/src/facade/lang.dart'; import 'package:angular2/src/facade/lang.dart';
import 'types.dart'; import 'types.dart';
import 'dart:mirrors'; import 'dart:mirrors';
import 'platform_reflection_capabilities.dart';
class ReflectionCapabilities implements IReflectionCapabilities { class ReflectionCapabilities implements PlatformReflectionCapabilities {
ReflectionCapabilities([metadataReader]) {} ReflectionCapabilities([metadataReader]) {}
Function factory(Type type) { Function factory(Type type) {

View File

@ -1,8 +1,9 @@
import {Type, isPresent, global, stringify, BaseException} from 'angular2/src/facade/lang'; import {Type, isPresent, global, stringify, BaseException} from 'angular2/src/facade/lang';
import {List, ListWrapper} from 'angular2/src/facade/collection'; import {List, ListWrapper} from 'angular2/src/facade/collection';
import {GetterFn, SetterFn, MethodFn, IReflectionCapabilities} from './types'; import {GetterFn, SetterFn, MethodFn} from './types';
import {PlatformReflectionCapabilities} from 'platform_reflection_capabilities';
export class ReflectionCapabilities implements IReflectionCapabilities { export class ReflectionCapabilities implements PlatformReflectionCapabilities {
private _reflect: any; private _reflect: any;
constructor(reflect?: any) { this._reflect = isPresent(reflect) ? reflect : global.Reflect; } constructor(reflect?: any) { this._reflect = isPresent(reflect) ? reflect : global.Reflect; }

View File

@ -7,17 +7,19 @@ import {
StringMap, StringMap,
StringMapWrapper StringMapWrapper
} from 'angular2/src/facade/collection'; } from 'angular2/src/facade/collection';
import {SetterFn, GetterFn, MethodFn, IReflectionCapabilities} from './types'; import {SetterFn, GetterFn, MethodFn} from './types';
export {SetterFn, GetterFn, MethodFn, IReflectionCapabilities} from './types'; import {PlatformReflectionCapabilities} from './platform_reflection_capabilities';
export {SetterFn, GetterFn, MethodFn} from './types';
export {PlatformReflectionCapabilities} from './platform_reflection_capabilities';
export class Reflector { export class Reflector {
_typeInfo: Map<Type, any>; _typeInfo: Map<Type, any>;
_getters: Map<string, GetterFn>; _getters: Map<string, GetterFn>;
_setters: Map<string, SetterFn>; _setters: Map<string, SetterFn>;
_methods: Map<string, MethodFn>; _methods: Map<string, MethodFn>;
reflectionCapabilities: IReflectionCapabilities; reflectionCapabilities: PlatformReflectionCapabilities;
constructor(reflectionCapabilities: IReflectionCapabilities) { constructor(reflectionCapabilities: PlatformReflectionCapabilities) {
this._typeInfo = MapWrapper.create(); this._typeInfo = MapWrapper.create();
this._getters = MapWrapper.create(); this._getters = MapWrapper.create();
this._setters = MapWrapper.create(); this._setters = MapWrapper.create();

View File

@ -3,13 +3,3 @@ library reflection.types;
typedef SetterFn(Object obj, value); typedef SetterFn(Object obj, value);
typedef GetterFn(Object obj); typedef GetterFn(Object obj);
typedef MethodFn(Object obj, List args); typedef MethodFn(Object obj, List args);
abstract class IReflectionCapabilities {
Function factory(Type type);
List<List<Type>> parameters(Type type);
List<Type> interfaces(Type type);
List annotations(Type type);
GetterFn getter(String name);
SetterFn setter(String name);
MethodFn method(String name);
}

View File

@ -1,21 +1,13 @@
import {Type} from 'angular2/src/facade/lang'; import {Type} from 'angular2/src/facade/lang';
import {List} from 'angular2/src/facade/collection'; import {List} from 'angular2/src/facade/collection';
// HACK: workaround for Traceur behavior. export {Function as GetterFn};
// It expects all transpiled modules to contain this marker. export {Function as SetterFn};
// TODO: remove this when we no longer use traceur export {Function as MethodFn};
export var __esModule = true;
// TODO replace once dgeni is fixed
/**
export type SetterFn = (obj: any, value: any) => void; export type SetterFn = (obj: any, value: any) => void;
export type GetterFn = (obj: any) => any; export type GetterFn = (obj: any) => any;
export type MethodFn = (obj: any, args: List<any>) => any; export type MethodFn = (obj: any, args: List<any>) => any;
**/
export interface IReflectionCapabilities {
factory(type: Type): Function;
parameters(type: Type): List<List<any>>;
interfaces(type: Type): List<any>;
annotations(type: Type): List<any>;
getter(name: string): GetterFn;
setter(name: string): SetterFn;
method(name: string): MethodFn;
}

View File

@ -106,7 +106,7 @@ export function main() {
it('should throw when no type and not @Inject', () => { it('should throw when no type and not @Inject', () => {
expect(() => Injector.resolveAndCreate([NoAnnotations])) expect(() => Injector.resolveAndCreate([NoAnnotations]))
.toThrowError('Cannot resolve all parameters for NoAnnotations. ' + .toThrowError('Cannot resolve all parameters for NoAnnotations(?). ' +
'Make sure they all have valid type or annotations.'); 'Make sure they all have valid type or annotations.');
}); });