fix(compiler): record correct end of expression (#34690)
This commit fixes a bug with the expression parser wherein the end index of an expression node was recorded as the start index of the next token, not the end index of the current token. Closes #33477 Closes https://github.com/angular/vscode-ng-language-service/issues/433 PR Close #34690
This commit is contained in:
@ -12,17 +12,17 @@ describe('type check blocks diagnostics', () => {
|
||||
describe('parse spans', () => {
|
||||
it('should annotate binary ops', () => {
|
||||
expect(tcbWithSpans('{{ a + b }}'))
|
||||
.toContain('"" + (((ctx).a /*3,5*/) + ((ctx).b /*7,9*/) /*3,9*/);');
|
||||
.toContain('"" + (((ctx).a /*3,4*/) + ((ctx).b /*7,8*/) /*3,8*/);');
|
||||
});
|
||||
|
||||
it('should annotate conditions', () => {
|
||||
expect(tcbWithSpans('{{ a ? b : c }}'))
|
||||
.toContain('((ctx).a /*3,5*/ ? (ctx).b /*7,9*/ : (ctx).c /*11,13*/) /*3,13*/;');
|
||||
.toContain('((ctx).a /*3,4*/ ? (ctx).b /*7,8*/ : (ctx).c /*11,12*/) /*3,12*/;');
|
||||
});
|
||||
|
||||
it('should annotate interpolations', () => {
|
||||
expect(tcbWithSpans('{{ hello }} {{ world }}'))
|
||||
.toContain('"" + (ctx).hello /*3,9*/ + (ctx).world /*15,21*/;');
|
||||
.toContain('"" + (ctx).hello /*3,8*/ + (ctx).world /*15,20*/;');
|
||||
});
|
||||
|
||||
it('should annotate literal map expressions', () => {
|
||||
@ -35,46 +35,46 @@ describe('type check blocks diagnostics', () => {
|
||||
|
||||
it('should annotate literal array expressions', () => {
|
||||
const TEMPLATE = '{{ [a, b] }}';
|
||||
expect(tcbWithSpans(TEMPLATE)).toContain('[(ctx).a /*4,5*/, (ctx).b /*7,8*/] /*3,10*/;');
|
||||
expect(tcbWithSpans(TEMPLATE)).toContain('[(ctx).a /*4,5*/, (ctx).b /*7,8*/] /*3,9*/;');
|
||||
});
|
||||
|
||||
it('should annotate literals', () => {
|
||||
const TEMPLATE = '{{ 123 }}';
|
||||
expect(tcbWithSpans(TEMPLATE)).toContain('123 /*3,7*/;');
|
||||
expect(tcbWithSpans(TEMPLATE)).toContain('123 /*3,6*/;');
|
||||
});
|
||||
|
||||
it('should annotate non-null assertions', () => {
|
||||
const TEMPLATE = `{{ a! }}`;
|
||||
expect(tcbWithSpans(TEMPLATE)).toContain('(((ctx).a /*3,4*/)! /*3,6*/);');
|
||||
expect(tcbWithSpans(TEMPLATE)).toContain('(((ctx).a /*3,4*/)! /*3,5*/);');
|
||||
});
|
||||
|
||||
it('should annotate prefix not', () => {
|
||||
const TEMPLATE = `{{ !a }}`;
|
||||
expect(tcbWithSpans(TEMPLATE)).toContain('!((ctx).a /*4,6*/) /*3,6*/;');
|
||||
expect(tcbWithSpans(TEMPLATE)).toContain('!((ctx).a /*4,5*/) /*3,5*/;');
|
||||
});
|
||||
|
||||
it('should annotate method calls', () => {
|
||||
const TEMPLATE = `{{ method(a, b) }}`;
|
||||
expect(tcbWithSpans(TEMPLATE))
|
||||
.toContain('(ctx).method((ctx).a /*10,11*/, (ctx).b /*13,14*/) /*3,16*/;');
|
||||
.toContain('(ctx).method((ctx).a /*10,11*/, (ctx).b /*13,14*/) /*3,15*/;');
|
||||
});
|
||||
|
||||
it('should annotate method calls of variables', () => {
|
||||
const TEMPLATE = `<ng-template let-method>{{ method(a, b) }}</ng-template>`;
|
||||
expect(tcbWithSpans(TEMPLATE))
|
||||
.toContain('(_t2 /*27,40*/).method((ctx).a /*34,35*/, (ctx).b /*37,38*/) /*27,40*/;');
|
||||
.toContain('(_t2 /*27,39*/).method((ctx).a /*34,35*/, (ctx).b /*37,38*/) /*27,39*/;');
|
||||
});
|
||||
|
||||
it('should annotate function calls', () => {
|
||||
const TEMPLATE = `{{ method(a)(b, c) }}`;
|
||||
expect(tcbWithSpans(TEMPLATE))
|
||||
.toContain(
|
||||
'((ctx).method((ctx).a /*10,11*/) /*3,12*/)((ctx).b /*13,14*/, (ctx).c /*16,17*/) /*3,19*/;');
|
||||
'((ctx).method((ctx).a /*10,11*/) /*3,12*/)((ctx).b /*13,14*/, (ctx).c /*16,17*/) /*3,18*/;');
|
||||
});
|
||||
|
||||
it('should annotate property access', () => {
|
||||
const TEMPLATE = `{{ a.b.c }}`;
|
||||
expect(tcbWithSpans(TEMPLATE)).toContain('(((ctx).a /*3,4*/).b /*3,6*/).c /*3,9*/;');
|
||||
expect(tcbWithSpans(TEMPLATE)).toContain('(((ctx).a /*3,4*/).b /*3,6*/).c /*3,8*/;');
|
||||
});
|
||||
|
||||
it('should annotate property writes', () => {
|
||||
@ -85,7 +85,7 @@ describe('type check blocks diagnostics', () => {
|
||||
|
||||
it('should annotate keyed property access', () => {
|
||||
const TEMPLATE = `{{ a[b] }}`;
|
||||
expect(tcbWithSpans(TEMPLATE)).toContain('((ctx).a /*3,4*/)[(ctx).b /*5,6*/] /*3,8*/;');
|
||||
expect(tcbWithSpans(TEMPLATE)).toContain('((ctx).a /*3,4*/)[(ctx).b /*5,6*/] /*3,7*/;');
|
||||
});
|
||||
|
||||
it('should annotate keyed property writes', () => {
|
||||
@ -97,19 +97,19 @@ describe('type check blocks diagnostics', () => {
|
||||
it('should annotate safe property access', () => {
|
||||
const TEMPLATE = `{{ a?.b }}`;
|
||||
expect(tcbWithSpans(TEMPLATE))
|
||||
.toContain('((null as any) ? ((ctx).a /*3,4*/)!.b : undefined) /*3,8*/');
|
||||
.toContain('((null as any) ? ((ctx).a /*3,4*/)!.b : undefined) /*3,7*/');
|
||||
});
|
||||
|
||||
it('should annotate safe method calls', () => {
|
||||
const TEMPLATE = `{{ a?.method(b) }}`;
|
||||
expect(tcbWithSpans(TEMPLATE))
|
||||
.toContain(
|
||||
'((null as any) ? ((ctx).a /*3,4*/)!.method((ctx).b /*13,14*/) : undefined) /*3,16*/');
|
||||
'((null as any) ? ((ctx).a /*3,4*/)!.method((ctx).b /*13,14*/) : undefined) /*3,15*/');
|
||||
});
|
||||
|
||||
it('should annotate $any casts', () => {
|
||||
const TEMPLATE = `{{ $any(a) }}`;
|
||||
expect(tcbWithSpans(TEMPLATE)).toContain('((ctx).a /*8,9*/ as any) /*3,11*/;');
|
||||
expect(tcbWithSpans(TEMPLATE)).toContain('((ctx).a /*8,9*/ as any) /*3,10*/;');
|
||||
});
|
||||
|
||||
it('should annotate chained expressions', () => {
|
||||
@ -127,17 +127,17 @@ describe('type check blocks diagnostics', () => {
|
||||
}];
|
||||
const block = tcbWithSpans(TEMPLATE, PIPES);
|
||||
expect(block).toContain(
|
||||
'(null as TestPipe).transform((ctx).a /*3,5*/, (ctx).b /*12,14*/) /*3,14*/;');
|
||||
'(null as TestPipe).transform((ctx).a /*3,4*/, (ctx).b /*12,13*/) /*3,13*/;');
|
||||
});
|
||||
|
||||
describe('attaching multiple comments for multiple references', () => {
|
||||
it('should be correct for element refs', () => {
|
||||
const TEMPLATE = `<span #a></span>{{ a || a }}`;
|
||||
expect(tcbWithSpans(TEMPLATE)).toContain('((_t1 /*19,21*/) || (_t1 /*24,26*/) /*19,26*/);');
|
||||
expect(tcbWithSpans(TEMPLATE)).toContain('((_t1 /*19,20*/) || (_t1 /*24,25*/) /*19,25*/);');
|
||||
});
|
||||
it('should be correct for template vars', () => {
|
||||
const TEMPLATE = `<ng-template let-a="b">{{ a || a }}</ng-template>`;
|
||||
expect(tcbWithSpans(TEMPLATE)).toContain('((_t2 /*26,28*/) || (_t2 /*31,33*/) /*26,33*/);');
|
||||
expect(tcbWithSpans(TEMPLATE)).toContain('((_t2 /*26,27*/) || (_t2 /*31,32*/) /*26,32*/);');
|
||||
});
|
||||
it('should be correct for directive refs', () => {
|
||||
const DIRECTIVES: TestDeclaration[] = [{
|
||||
@ -148,7 +148,7 @@ describe('type check blocks diagnostics', () => {
|
||||
}];
|
||||
const TEMPLATE = `<my-cmp #a></my-cmp>{{ a || a }}`;
|
||||
expect(tcbWithSpans(TEMPLATE, DIRECTIVES))
|
||||
.toContain('((_t2 /*23,25*/) || (_t2 /*28,30*/) /*23,30*/);');
|
||||
.toContain('((_t2 /*23,24*/) || (_t2 /*28,29*/) /*23,29*/);');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -176,9 +176,9 @@ runInEachFileSystem((os) => {
|
||||
expect(mappings).toContain(
|
||||
{source: 'items.push(', generated: 'ctx.items.push(', sourceUrl: '../test.ts'});
|
||||
expect(mappings).toContain(
|
||||
{source: `'item' `, generated: `"item"`, sourceUrl: '../test.ts'});
|
||||
{source: `'item'`, generated: `"item"`, sourceUrl: '../test.ts'});
|
||||
expect(mappings).toContain({
|
||||
source: '+ items.length)',
|
||||
source: ' + items.length)',
|
||||
generated: ' + ctx.items.length)',
|
||||
sourceUrl: '../test.ts'
|
||||
});
|
||||
|
Reference in New Issue
Block a user