Skip to content

Commit b039ec3

Browse files
fix(parser): detect and report interpolation in expressions
Fixes angular#3645 Closes angular#3750
1 parent 5ee9630 commit b039ec3

File tree

3 files changed

+56
-11
lines changed

3 files changed

+56
-11
lines changed

modules/angular2/src/change_detection/parser/parser.ts

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -65,18 +65,21 @@ export class Parser {
6565
}
6666

6767
parseAction(input: string, location: any): ASTWithSource {
68+
this._checkNoInterpolation(input, location);
6869
var tokens = this._lexer.tokenize(input);
6970
var ast = new _ParseAST(input, location, tokens, this._reflector, true).parseChain();
7071
return new ASTWithSource(ast, input, location);
7172
}
7273

7374
parseBinding(input: string, location: any): ASTWithSource {
75+
this._checkNoInterpolation(input, location);
7476
var tokens = this._lexer.tokenize(input);
7577
var ast = new _ParseAST(input, location, tokens, this._reflector, false).parseChain();
7678
return new ASTWithSource(ast, input, location);
7779
}
7880

7981
parseSimpleBinding(input: string, location: string): ASTWithSource {
82+
this._checkNoInterpolation(input, location);
8083
var tokens = this._lexer.tokenize(input);
8184
var ast = new _ParseAST(input, location, tokens, this._reflector, false).parseSimpleBinding();
8285
return new ASTWithSource(ast, input, location);
@@ -105,12 +108,9 @@ export class Parser {
105108
var ast = new _ParseAST(input, location, tokens, this._reflector, false).parseChain();
106109
expressions.push(ast);
107110
} else {
108-
var errLocation = '';
109-
for (var j = 0; j < i; j++) {
110-
errLocation += j % 2 === 0 ? parts[j] : `{{${parts[j]}}}`;
111-
}
112111
throw new ParseException('Blank expressions are not allowed in interpolated strings', input,
113-
`at column ${errLocation.length} in`, location);
112+
`at column ${this._findInterpolationErrorColumn(parts, i)} in`,
113+
location);
114114
}
115115
}
116116
return new ASTWithSource(new Interpolation(strings, expressions), input, location);
@@ -119,6 +119,24 @@ export class Parser {
119119
wrapLiteralPrimitive(input: string, location: any): ASTWithSource {
120120
return new ASTWithSource(new LiteralPrimitive(input), input, location);
121121
}
122+
123+
private _checkNoInterpolation(input: string, location: any): void {
124+
var parts = StringWrapper.split(input, INTERPOLATION_REGEXP);
125+
if (parts.length > 1) {
126+
throw new ParseException('Got interpolation ({{}}) where expression was expected', input,
127+
`at column ${this._findInterpolationErrorColumn(parts, 1)} in`,
128+
location);
129+
}
130+
}
131+
132+
private _findInterpolationErrorColumn(parts: string[], partInErrIdx: number): number {
133+
var errLocation = '';
134+
for (var j = 0; j < partInErrIdx; j++) {
135+
errLocation += j % 2 === 0 ? parts[j] : `{{${parts[j]}}}`;
136+
}
137+
138+
return errLocation.length;
139+
}
122140
}
123141

124142
export class _ParseAST {

modules/angular2/test/change_detection/parser/parser_spec.ts

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,11 @@ export function main() {
204204

205205
it('should store the passed-in location',
206206
() => { expect(parseAction('someExpr', 'location').location).toBe('location'); });
207+
208+
it("should throw when encountering interpolation", () => {
209+
expectActionError("{{a()}}")
210+
.toThrowErrorWith('Got interpolation ({{}}) where expression was expected');
211+
});
207212
});
208213

209214
describe("general error handling", () => {
@@ -256,6 +261,11 @@ export function main() {
256261
it('should throw on assignment', () => {
257262
expect(() => parseBinding("a=2")).toThrowError(new RegExp("contain assignments"));
258263
});
264+
265+
it('should throw when encountering interpolation', () => {
266+
expectBindingError("{{a.b}}")
267+
.toThrowErrorWith('Got interpolation ({{}}) where expression was expected');
268+
});
259269
});
260270

261271
describe('parseTemplateBindings', () => {
@@ -398,12 +408,12 @@ export function main() {
398408

399409
it("should throw on empty interpolation expressions", () => {
400410
expect(() => parseInterpolation("{{}}"))
401-
.toThrowError(new RegExp(
402-
"Parser Error: Blank expressions are not allowed in interpolated strings"));
411+
.toThrowErrorWith(
412+
"Parser Error: Blank expressions are not allowed in interpolated strings");
403413

404414
expect(() => parseInterpolation("foo {{ }}"))
405-
.toThrowError(new RegExp(
406-
"Parser Error: Blank expressions are not allowed in interpolated strings"));
415+
.toThrowErrorWith(
416+
"Parser Error: Blank expressions are not allowed in interpolated strings");
407417
});
408418
});
409419

@@ -420,8 +430,13 @@ export function main() {
420430

421431
it("should throw when the given expression is not just a field name", () => {
422432
expect(() => parseSimpleBinding("name + 1"))
423-
.toThrowError(new RegExp(
424-
'Simple binding expression can only contain field access and constants'));
433+
.toThrowErrorWith(
434+
'Simple binding expression can only contain field access and constants');
435+
});
436+
437+
it('should throw when encountering interpolation', () => {
438+
expect(() => parseSimpleBinding('{{exp}}'))
439+
.toThrowErrorWith('Got interpolation ({{}}) where expression was expected');
425440
});
426441
});
427442

modules/angular2/test/render/dom/compiler/property_binding_parser_spec.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,12 @@ export function main() {
4949
expect(process(el('<div [a]v="b"></div>'))[0]).toBe(null);
5050
});
5151

52+
it('should throw when [] binding contains interpolation', () => {
53+
expect(() => process(el('<div [a]="a + {{b()}}"></div>'))[0])
54+
.toThrowErrorWith(
55+
'Got interpolation ({{}}) where expression was expected at column 4 in [a + {{b()}}] in someComponent');
56+
});
57+
5258
it('should detect bind- syntax', () => {
5359
var results = process(el('<div bind-a="b"></div>'));
5460
expect(results[0].propertyBindings.get('a').source).toEqual('b');
@@ -157,6 +163,12 @@ export function main() {
157163
expect(eventBinding.fullName).toEqual('click');
158164
});
159165

166+
it('should throw when () action contains interpolation', () => {
167+
expect(() => process(el('<div (a)="{{b()}}"></div>'))[0])
168+
.toThrowErrorWith(
169+
'Got interpolation ({{}}) where expression was expected at column 0 in [{{b()}}] in someComponent');
170+
});
171+
160172
it('should detect on- syntax', () => {
161173
var results = process(el('<div on-click="b()"></div>'));
162174
var eventBinding = results[0].eventBindings[0];

0 commit comments

Comments
 (0)