fix(parser): detect and report interpolation in expressions
Fixes #3645 Closes #3750
This commit is contained in:
parent
5ee9630be1
commit
b039ec3da3
@ -65,18 +65,21 @@ export class Parser {
|
|||||||
}
|
}
|
||||||
|
|
||||||
parseAction(input: string, location: any): ASTWithSource {
|
parseAction(input: string, location: any): ASTWithSource {
|
||||||
|
this._checkNoInterpolation(input, location);
|
||||||
var tokens = this._lexer.tokenize(input);
|
var tokens = this._lexer.tokenize(input);
|
||||||
var ast = new _ParseAST(input, location, tokens, this._reflector, true).parseChain();
|
var ast = new _ParseAST(input, location, tokens, this._reflector, true).parseChain();
|
||||||
return new ASTWithSource(ast, input, location);
|
return new ASTWithSource(ast, input, location);
|
||||||
}
|
}
|
||||||
|
|
||||||
parseBinding(input: string, location: any): ASTWithSource {
|
parseBinding(input: string, location: any): ASTWithSource {
|
||||||
|
this._checkNoInterpolation(input, location);
|
||||||
var tokens = this._lexer.tokenize(input);
|
var tokens = this._lexer.tokenize(input);
|
||||||
var ast = new _ParseAST(input, location, tokens, this._reflector, false).parseChain();
|
var ast = new _ParseAST(input, location, tokens, this._reflector, false).parseChain();
|
||||||
return new ASTWithSource(ast, input, location);
|
return new ASTWithSource(ast, input, location);
|
||||||
}
|
}
|
||||||
|
|
||||||
parseSimpleBinding(input: string, location: string): ASTWithSource {
|
parseSimpleBinding(input: string, location: string): ASTWithSource {
|
||||||
|
this._checkNoInterpolation(input, location);
|
||||||
var tokens = this._lexer.tokenize(input);
|
var tokens = this._lexer.tokenize(input);
|
||||||
var ast = new _ParseAST(input, location, tokens, this._reflector, false).parseSimpleBinding();
|
var ast = new _ParseAST(input, location, tokens, this._reflector, false).parseSimpleBinding();
|
||||||
return new ASTWithSource(ast, input, location);
|
return new ASTWithSource(ast, input, location);
|
||||||
@ -105,12 +108,9 @@ export class Parser {
|
|||||||
var ast = new _ParseAST(input, location, tokens, this._reflector, false).parseChain();
|
var ast = new _ParseAST(input, location, tokens, this._reflector, false).parseChain();
|
||||||
expressions.push(ast);
|
expressions.push(ast);
|
||||||
} else {
|
} else {
|
||||||
var errLocation = '';
|
|
||||||
for (var j = 0; j < i; j++) {
|
|
||||||
errLocation += j % 2 === 0 ? parts[j] : `{{${parts[j]}}}`;
|
|
||||||
}
|
|
||||||
throw new ParseException('Blank expressions are not allowed in interpolated strings', input,
|
throw new ParseException('Blank expressions are not allowed in interpolated strings', input,
|
||||||
`at column ${errLocation.length} in`, location);
|
`at column ${this._findInterpolationErrorColumn(parts, i)} in`,
|
||||||
|
location);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return new ASTWithSource(new Interpolation(strings, expressions), input, location);
|
return new ASTWithSource(new Interpolation(strings, expressions), input, location);
|
||||||
@ -119,6 +119,24 @@ export class Parser {
|
|||||||
wrapLiteralPrimitive(input: string, location: any): ASTWithSource {
|
wrapLiteralPrimitive(input: string, location: any): ASTWithSource {
|
||||||
return new ASTWithSource(new LiteralPrimitive(input), input, location);
|
return new ASTWithSource(new LiteralPrimitive(input), input, location);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private _checkNoInterpolation(input: string, location: any): void {
|
||||||
|
var parts = StringWrapper.split(input, INTERPOLATION_REGEXP);
|
||||||
|
if (parts.length > 1) {
|
||||||
|
throw new ParseException('Got interpolation ({{}}) where expression was expected', input,
|
||||||
|
`at column ${this._findInterpolationErrorColumn(parts, 1)} in`,
|
||||||
|
location);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private _findInterpolationErrorColumn(parts: string[], partInErrIdx: number): number {
|
||||||
|
var errLocation = '';
|
||||||
|
for (var j = 0; j < partInErrIdx; j++) {
|
||||||
|
errLocation += j % 2 === 0 ? parts[j] : `{{${parts[j]}}}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return errLocation.length;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class _ParseAST {
|
export class _ParseAST {
|
||||||
|
@ -204,6 +204,11 @@ export function main() {
|
|||||||
|
|
||||||
it('should store the passed-in location',
|
it('should store the passed-in location',
|
||||||
() => { expect(parseAction('someExpr', 'location').location).toBe('location'); });
|
() => { expect(parseAction('someExpr', 'location').location).toBe('location'); });
|
||||||
|
|
||||||
|
it("should throw when encountering interpolation", () => {
|
||||||
|
expectActionError("{{a()}}")
|
||||||
|
.toThrowErrorWith('Got interpolation ({{}}) where expression was expected');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("general error handling", () => {
|
describe("general error handling", () => {
|
||||||
@ -256,6 +261,11 @@ export function main() {
|
|||||||
it('should throw on assignment', () => {
|
it('should throw on assignment', () => {
|
||||||
expect(() => parseBinding("a=2")).toThrowError(new RegExp("contain assignments"));
|
expect(() => parseBinding("a=2")).toThrowError(new RegExp("contain assignments"));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should throw when encountering interpolation', () => {
|
||||||
|
expectBindingError("{{a.b}}")
|
||||||
|
.toThrowErrorWith('Got interpolation ({{}}) where expression was expected');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('parseTemplateBindings', () => {
|
describe('parseTemplateBindings', () => {
|
||||||
@ -398,12 +408,12 @@ export function main() {
|
|||||||
|
|
||||||
it("should throw on empty interpolation expressions", () => {
|
it("should throw on empty interpolation expressions", () => {
|
||||||
expect(() => parseInterpolation("{{}}"))
|
expect(() => parseInterpolation("{{}}"))
|
||||||
.toThrowError(new RegExp(
|
.toThrowErrorWith(
|
||||||
"Parser Error: Blank expressions are not allowed in interpolated strings"));
|
"Parser Error: Blank expressions are not allowed in interpolated strings");
|
||||||
|
|
||||||
expect(() => parseInterpolation("foo {{ }}"))
|
expect(() => parseInterpolation("foo {{ }}"))
|
||||||
.toThrowError(new RegExp(
|
.toThrowErrorWith(
|
||||||
"Parser Error: Blank expressions are not allowed in interpolated strings"));
|
"Parser Error: Blank expressions are not allowed in interpolated strings");
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -420,8 +430,13 @@ export function main() {
|
|||||||
|
|
||||||
it("should throw when the given expression is not just a field name", () => {
|
it("should throw when the given expression is not just a field name", () => {
|
||||||
expect(() => parseSimpleBinding("name + 1"))
|
expect(() => parseSimpleBinding("name + 1"))
|
||||||
.toThrowError(new RegExp(
|
.toThrowErrorWith(
|
||||||
'Simple binding expression can only contain field access and constants'));
|
'Simple binding expression can only contain field access and constants');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should throw when encountering interpolation', () => {
|
||||||
|
expect(() => parseSimpleBinding('{{exp}}'))
|
||||||
|
.toThrowErrorWith('Got interpolation ({{}}) where expression was expected');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -49,6 +49,12 @@ export function main() {
|
|||||||
expect(process(el('<div [a]v="b"></div>'))[0]).toBe(null);
|
expect(process(el('<div [a]v="b"></div>'))[0]).toBe(null);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should throw when [] binding contains interpolation', () => {
|
||||||
|
expect(() => process(el('<div [a]="a + {{b()}}"></div>'))[0])
|
||||||
|
.toThrowErrorWith(
|
||||||
|
'Got interpolation ({{}}) where expression was expected at column 4 in [a + {{b()}}] in someComponent');
|
||||||
|
});
|
||||||
|
|
||||||
it('should detect bind- syntax', () => {
|
it('should detect bind- syntax', () => {
|
||||||
var results = process(el('<div bind-a="b"></div>'));
|
var results = process(el('<div bind-a="b"></div>'));
|
||||||
expect(results[0].propertyBindings.get('a').source).toEqual('b');
|
expect(results[0].propertyBindings.get('a').source).toEqual('b');
|
||||||
@ -157,6 +163,12 @@ export function main() {
|
|||||||
expect(eventBinding.fullName).toEqual('click');
|
expect(eventBinding.fullName).toEqual('click');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should throw when () action contains interpolation', () => {
|
||||||
|
expect(() => process(el('<div (a)="{{b()}}"></div>'))[0])
|
||||||
|
.toThrowErrorWith(
|
||||||
|
'Got interpolation ({{}}) where expression was expected at column 0 in [{{b()}}] in someComponent');
|
||||||
|
});
|
||||||
|
|
||||||
it('should detect on- syntax', () => {
|
it('should detect on- syntax', () => {
|
||||||
var results = process(el('<div on-click="b()"></div>'));
|
var results = process(el('<div on-click="b()"></div>'));
|
||||||
var eventBinding = results[0].eventBindings[0];
|
var eventBinding = results[0].eventBindings[0];
|
||||||
|
Loading…
x
Reference in New Issue
Block a user