Skip to content

Commit 089ad0e

Browse files
crisbetothePunderWoman
authored andcommitted
fix(compiler): produce more accurate errors for interpolations (#62258)
Currently when there's a parser error in interpolated text, the compiler reports an error on the entire text node. This can be really noisy in long strings. These changes switch to reporting the errors on the specific expressions that caused them. PR Close #62258
1 parent f1d7ac9 commit 089ad0e

File tree

4 files changed

+48
-9
lines changed

4 files changed

+48
-9
lines changed

packages/compiler-cli/test/ngtsc/ngtsc_spec.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10022,7 +10022,7 @@ runInEachFileSystem((os: string) => {
1002210022
expect(diags.length).toBe(2);
1002310023
expect(diags[0].messageText).toEqual(`Type 'string' is not assignable to type 'number'.`);
1002410024
expect(diags[1].messageText).toContain(
10025-
'Parser Error: Bindings cannot contain assignments at column 5 in [ {{x = 2}}]',
10025+
'Parser Error: Bindings cannot contain assignments at column 5 in [x = 2]',
1002610026
);
1002710027
});
1002810028
});

packages/compiler/src/expression_parser/parser.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -268,12 +268,16 @@ export class Parser {
268268
const expressionNodes: AST[] = [];
269269

270270
for (let i = 0; i < expressions.length; ++i) {
271+
// If we have a token for the specific expression, it's preferrable to use it because it
272+
// allows us to produce more accurate error messages. The expressions are always at the odd
273+
// indexes inside the tokens.
274+
const expressionSpan = interpolatedTokens?.[i * 2 + 1]?.sourceSpan;
271275
const expressionText = expressions[i].text;
272276
const sourceToLex = this._stripComments(expressionText);
273277
const tokens = this._lexer.tokenize(sourceToLex);
274278
const ast = new _ParseAST(
275-
input,
276-
parseSourceSpan,
279+
expressionSpan ? expressionText : input,
280+
expressionSpan || parseSourceSpan,
277281
absoluteOffset,
278282
tokens,
279283
ParseFlags.None,

packages/compiler/test/render3/r3_template_transform_spec.ts

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -784,6 +784,41 @@ describe('R3 template transform', () => {
784784
expect(errors[1].msg).toContain('Invalid character [#]');
785785
expect(errors[2].msg).toContain(`Unexpected token ')'`);
786786
});
787+
788+
it('should report parsing errors on the specific interpolated expressions', () => {
789+
const errors = parse(
790+
`
791+
bunch of text bunch of text bunch of text bunch of text bunch of text bunch of text
792+
bunch of text bunch of text bunch of text bunch of text
793+
794+
{{foo[0}} bunch of text bunch of text bunch of text bunch of text {{.bar}}
795+
796+
bunch of text
797+
bunch of text
798+
bunch of text
799+
bunch of text
800+
bunch of text {{one + #two + baz}}
801+
`,
802+
{
803+
ignoreError: true,
804+
},
805+
).errors;
806+
807+
expect(errors.map((e) => e.span.toString())).toEqual([
808+
'{{foo[0}}',
809+
'{{.bar}}',
810+
'{{one + #two + baz}}',
811+
]);
812+
813+
expect(errors.map((e) => e.msg)).toEqual([
814+
jasmine.stringContaining('Missing expected ] at the end of the expression [foo[0]'),
815+
jasmine.stringContaining('Unexpected token . at column 1 in [.bar]'),
816+
jasmine.stringContaining(
817+
'Private identifiers are not supported. Unexpected private identifier: ' +
818+
'#two at column 7 in [one + #two + baz]',
819+
),
820+
]);
821+
});
787822
});
788823

789824
describe('Ignored elements', () => {

packages/language-service/test/diagnostic_spec.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -193,7 +193,7 @@ describe('getSemanticDiagnostics', () => {
193193
expect(category).toBe(ts.DiagnosticCategory.Error);
194194
expect(file?.fileName).toBe('/test/app.html');
195195
expect(messageText).toContain(
196-
`Parser Error: Bindings cannot contain assignments at column 8 in [{{nope = true}}]`,
196+
`Parser Error: Bindings cannot contain assignments at column 8 in [nope = true]`,
197197
);
198198
});
199199

@@ -231,13 +231,13 @@ describe('getSemanticDiagnostics', () => {
231231
'app.ts': `
232232
import {Component, NgModule} from '@angular/core';
233233
234-
@Component({
234+
@Component({
235235
templateUrl: './app1.html',
236236
standalone: false,
237237
})
238238
export class AppComponent1 { nope = false; }
239239
240-
@Component({
240+
@Component({
241241
templateUrl: './app2.html',
242242
standalone: false,
243243
})
@@ -262,13 +262,13 @@ describe('getSemanticDiagnostics', () => {
262262
const diags1 = project.getDiagnosticsForFile('app1.html');
263263
expect(diags1.length).toBe(1);
264264
expect(diags1[0].messageText).toBe(
265-
'Parser Error: Bindings cannot contain assignments at column 8 in [{{nope = false}}] in /test/app1.html@0:0',
265+
'Parser Error: Bindings cannot contain assignments at column 8 in [nope = false] in /test/app1.html@0:0',
266266
);
267267

268268
const diags2 = project.getDiagnosticsForFile('app2.html');
269269
expect(diags2.length).toBe(1);
270270
expect(diags2[0].messageText).toBe(
271-
'Parser Error: Bindings cannot contain assignments at column 8 in [{{nope = true}}] in /test/app2.html@0:0',
271+
'Parser Error: Bindings cannot contain assignments at column 8 in [nope = true] in /test/app2.html@0:0',
272272
);
273273
});
274274

@@ -386,7 +386,7 @@ describe('getSemanticDiagnostics', () => {
386386
const files = {
387387
'app.ts': `
388388
import {Component} from '@angular/core';
389-
@Component({
389+
@Component({
390390
template: '',
391391
standalone: false,
392392
})

0 commit comments

Comments
 (0)