8000 fix(compiler): produce more accurate errors for interpolations by crisbeto · Pull Request #62258 · angular/angular · GitHub
[go: up one dir, main page]

Skip to content

fix(compiler): produce more accurate errors for interpolations #62258

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion packages/compiler-cli/test/ngtsc/ngtsc_spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10022,7 +10022,7 @@ runInEachFileSystem((os: string) => {
expect(diags.length).toBe(2);
expect(diags[0].messageText).toEqual(`Type 'string' is not assignable to type 'number'.`);
expect(diags[1].messageText).toContain(
'Parser Error: Bindings cannot contain assignments at column 5 in [ {{x = 2}}]',
'Parser Error: Bindings cannot contain assignments at column 5 in [x = 2]',
);
});
});
Expand Down
8 changes: 6 additions & 2 deletions packages/compiler/src/expression_parser/parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -268,12 +268,16 @@ export class Parser {
const expressionNodes: AST[] = [];

for (let i = 0; i < expressions.length; ++i) {
// If we have a token for the specific expression, it's preferrable to use it because it
// allows us to produce more accurate error messages. The expressions are always at the odd
// indexes inside the tokens.
const expressionSpan = interpolatedTokens?.[i * 2 + 1]?.sourceSpan;
const expressionText = expressions[i].text;
const sourceToLex = this._stripComments(expressionText);
const tokens = this._lexer.tokenize(sourceToLex);
const ast = new _ParseAST(
input,
parseSourceSpan,
expressionSpan ? expressionText : input,
expressionSpan || parseSourceSpan,
absoluteOffset,
tokens,
ParseFlags.None,
Expand Down
35 changes: 35 additions & 0 deletions packages/compiler/test/render3/r3_template_transform_spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -784,6 +784,41 @@ describe('R3 template transform', () => {
expect(errors[1].msg).toContain('Invalid character [#]');
expect(errors[2].msg).toContain(`Unexpected token ')'`);
});

it('should report parsing errors on the specific interpolated expressions', () => {
const errors = parse(
`
bunch of text bunch of text bunch of text bunch of text bunch of text bunch of text
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For reference, the current code will report 3 errors, each of which covers the entire string including the plain text. With the changes the errors are only reported on interpolations.

bunch of text bunch of text bunch of text bunch of text

{{foo[0}} bunch of text bunch of text bunch of text bunch of text {{.bar}}

bunch of text
bunch of text
bunch of text
bunch of text
bunch of text {{one + #two + baz}}
`,
{
ignoreError: true,
},
).errors;

expect(errors.map((e) => e.span.toString())).toEqual([
'{{foo[0}}',
'{{.bar}}',
'{{one + #two + baz}}',
]);

expect(errors.map((e) => e.msg)).toEqual([
jasmine.stringContaining('Missing expected ] at the end of the expression [foo[0]'),
jasmine.stringContaining('Unexpected token . at column 1 in [.bar]'),
jasmine.stringContaining(
'Private identifiers are not supported. Unexpected private identifier: ' +
'#two at column 7 in [one + #two + baz]',
),
]);
});
});

describe('Ignored elements', () => {
Expand Down
12 changes: 6 additions & 6 deletions packages/language-service/test/diagnostic_spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,7 @@ describe('getSemanticDiagnostics', () => {
expect(category).toBe(ts.DiagnosticCategory.Error);
expect(file?.fileName).toBe('/test/app.html');
expect(messageText).toContain(
`Parser Error: Bindings cannot contain assignments at column 8 in [{{nope = true}}]`,
`Parser Error: Bindings cannot contain assignments at column 8 in [nope = true]`,
);
});

Expand Down Expand Up @@ -231,13 +231,13 @@ describe('getSemanticDiagnostics', () => {
'app.ts': `
import {Component, NgModule} from '@angular/core';

@Component({
@Component({
templateUrl: './app1.html',
standalone: false,
})
export class AppComponent1 { nope = false; }

@Component({
@Component({
templateUrl: './app2.html',
standalone: false,
})
Expand All @@ -262,13 +262,13 @@ describe('getSemanticDiagnostics', () => {
const diags1 = project.getDiagnosticsForFile('app1.html');
expect(diags1.length).toBe(1);
expect(diags1[0].messageText).toBe(
'Parser Error: Bindings cannot contain assignments at column 8 in [{{nope = false}}] in /test/app1.html@0:0',
'Parser Error: Bindings cannot contain assignments at column 8 in [nope = false] in /test/app1.html@0:0',
);

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

Expand Down Expand Up @@ -386,7 +386,7 @@ describe('getSemanticDiagnostics', () => {
const files = {
'app.ts': `
import {Component} from '@angular/core';
@Component({
@Component({
template: '',
standalone: false,
})
Expand Down
Loading
0