Content-Length: 684542 | pFad | https://github.com/angular/angular/commit/7c9b4892e9f6df164e4e289195bff27f2cc9a0ea

84 fix(compiler-cli): preserve required parens in exponentiation express… · angular/angular@7c9b489 · GitHub
Skip to content

Commit 7c9b489

Browse files
committed
fix(compiler-cli): preserve required parens in exponentiation expressions (#60101)
Parentheses are required around a unary operator used in the base of an exponentiation expression. For example: `(-1) ** 3` PR Close #60101
1 parent 4fe489f commit 7c9b489

File tree

7 files changed

+70
-13
lines changed

7 files changed

+70
-13
lines changed

packages/compiler-cli/src/ngtsc/typecheck/test/type_check_block_spec.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ describe('type check blocks', () => {
7979
expect(tcb('{{a * b ** c + d}}')).toContain(
8080
'(((((this).a)) * ((((this).b)) ** (((this).c)))) + (((this).d)))',
8181
);
82-
expect(tcb('{{a ** b ** c}}')).toContain('blah');
82+
expect(tcb('{{a ** b ** c}}')).toContain('((((this).a)) ** ((((this).b)) ** (((this).c))))');
8383
});
8484

8585
it('should handle attribute values for directive inputs', () => {

packages/compiler-cli/test/compliance/test_cases/r3_view_compiler/GOLDEN_PARTIAL.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,7 @@ MyApp.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "0.0.0-
9898
{{ typeof foo?.bar === 'string' }}
9999
{{ typeof foo?.bar | identity }}
100100
{{ void 'test' }}
101+
{{ (-1) ** 3 }}
101102
`, isInline: true, dependencies: [{ kind: "pipe", type: IdentityPipe, name: "identity" }] });
102103
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyApp, decorators: [{
103104
type: Component,
@@ -111,6 +112,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDE
111112
{{ typeof foo?.bar === 'string' }}
112113
{{ typeof foo?.bar | identity }}
113114
{{ void 'test' }}
115+
{{ (-1) ** 3 }}
114116
`,
115117
imports: [IdentityPipe],
116118
}]

packages/compiler-cli/test/compliance/test_cases/r3_view_compiler/operators.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ export class IdentityPipe {
1717
{{ typeof foo?.bar === 'string' }}
1818
{{ typeof foo?.bar | identity }}
1919
{{ void 'test' }}
20+
{{ (-1) ** 3 }}
2021
`,
2122
imports: [IdentityPipe],
2223
})

packages/compiler-cli/test/compliance/test_cases/r3_view_compiler/operators_template.js

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,17 @@ template: function MyApp_Template(rf, $ctx$) {
33
$i0$.ɵɵtext(0);
44
i0.ɵɵpipe(1, "identity");
55
} if (rf & 2) {
6-
i0.ɵɵtextInterpolate8(" ",
6+
i0.ɵɵtextInterpolateV([" ",
77
1 + 2, " ",
88
1 % 2 + 3 / 4 * 5 ** 6, " ",
99
+1, " ",
10-
typeof i0.ɵɵpureFunction0(10, _c0) === "object", " ",
11-
!(typeof i0.ɵɵpureFunction0(11, _c0) === "object"), " ",
10+
typeof i0.ɵɵpureFunction0(11, _c0) === "object", " ",
11+
!(typeof i0.ɵɵpureFunction0(12, _c0) === "object"), " ",
1212
typeof (ctx.foo == null ? null : ctx.foo.bar) === "string", " ",
13-
i0.ɵɵpipeBind1(1, 8, typeof (ctx.foo == null ? null : ctx.foo.bar)), " ",
14-
void "test", " "
15-
);
13+
i0.ɵɵpipeBind1(1, 9, typeof (ctx.foo == null ? null : ctx.foo.bar)), " ",
14+
void "test", " ",
15+
(-1) ** 3, " "
16+
]);
1617
}
1718
}
1819

packages/compiler/src/template/pipeline/src/emit.ts

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -22,14 +22,14 @@ import {
2222
import {deleteAnyCasts} from './phases/any_cast';
2323
import {applyI18nExpressions} from './phases/apply_i18n_expressions';
2424
import {assignI18nSlotDependencies} from './phases/assign_i18n_slot_dependencies';
25+
import {attachSourceLocations} from './phases/attach_source_locations';
2526
import {extractAttributes} from './phases/attribute_extraction';
2627
import {specializeBindings} from './phases/binding_specialization';
2728
import {chain} from './phases/chaining';
2829
import {collapseSingletonInterpolations} from './phases/collapse_singleton_interpolations';
2930
import {generateConditionalExpressions} from './phases/conditionals';
3031
import {collectElementConsts} from './phases/const_collection';
3132
import {convertI18nBindings} from './phases/convert_i18n_bindings';
32-
import {resolveDeferDepsFns} from './phases/resolve_defer_deps_fns';
3333
import {createI18nContexts} from './phases/create_i18n_contexts';
3434
import {deduplicateTextBindings} from './phases/deduplicate_text_bindings';
3535
import {configureDeferInstructions} from './phases/defer_configs';
@@ -38,6 +38,7 @@ import {collapseEmptyInstructions} from './phases/empty_elements';
3838
import {expandSafeReads} from './phases/expand_safe_reads';
3939
import {extractI18nMessages} from './phases/extract_i18n_messages';
4040
import {generateAdvance} from './phases/generate_advance';
41+
import {generateLocalLetReferences} from './phases/generate_local_let_references';
4142
import {generateProjectionDefs} from './phases/generate_projection_def';
4243
import {generateVariables} from './phases/generate_variables';
4344
import {collectConstExpressions} from './phases/has_const_expression_collection';
@@ -62,27 +63,27 @@ import {generatePureLiteralStructures} from './phases/pure_literal_structures';
6263
import {reify} from './phases/reify';
6364
import {removeEmptyBindings} from './phases/remove_empty_bindings';
6465
import {removeI18nContexts} from './phases/remove_i18n_contexts';
66+
import {removeIllegalLetReferences} from './phases/remove_illegal_let_references';
6567
import {removeUnusedI18nAttributesOps} from './phases/remove_unused_i18n_attrs';
68+
import {requiredParentheses} from './phases/required_parentheses';
6669
import {resolveContexts} from './phases/resolve_contexts';
70+
import {resolveDeferDepsFns} from './phases/resolve_defer_deps_fns';
6771
import {resolveDollarEvent} from './phases/resolve_dollar_event';
6872
import {resolveI18nElementPlaceholders} from './phases/resolve_i18n_element_placeholders';
6973
import {resolveI18nExpressionPlaceholders} from './phases/resolve_i18n_expression_placeholders';
7074
import {resolveNames} from './phases/resolve_names';
7175
import {resolveSanitizers} from './phases/resolve_sanitizers';
72-
import {transformTwoWayBindingSet} from './phases/transform_two_way_binding_set';
7376
import {saveAndRestoreView} from './phases/save_restore_view';
7477
import {allocateSlots} from './phases/slot_allocation';
78+
import {optimizeStoreLet} from './phases/store_let_optimization';
7579
import {specializeStyleBindings} from './phases/style_binding_specialization';
7680
import {generateTemporaryVariables} from './phases/temporary_variables';
7781
import {optimizeTrackFns} from './phases/track_fn_optimization';
7882
import {generateTrackVariables} from './phases/track_variables';
83+
import {transformTwoWayBindingSet} from './phases/transform_two_way_binding_set';
7984
import {countVariables} from './phases/var_counting';
8085
import {optimizeVariables} from './phases/variable_optimization';
8186
import {wrapI18nIcus} from './phases/wrap_icus';
82-
import {optimizeStoreLet} from './phases/store_let_optimization';
83-
import {removeIllegalLetReferences} from './phases/remove_illegal_let_references';
84-
import {generateLocalLetReferences} from './phases/generate_local_let_references';
85-
import {attachSourceLocations} from './phases/attach_source_locations';
8687

8788
type Phase =
8889
| {
@@ -139,6 +140,7 @@ const phases: Phase[] = [
139140
{kind: Kind.Both, fn: resolveSanitizers},
140141
{kind: Kind.Tmpl, fn: liftLocalRefs},
141142
{kind: Kind.Both, fn: generateNullishCoalesceExpressions},
143+
{kind: Kind.Both, fn: requiredParentheses},
142144
{kind: Kind.Both, fn: expandSafeReads},
143145
{kind: Kind.Both, fn: generateTemporaryVariables},
144146
{kind: Kind.Both, fn: optimizeVariables},
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
/**
2+
* @license
3+
* Copyright Google LLC All Rights Reserved.
4+
*
5+
* Use of this source code is governed by an MIT-style license that can be
6+
* found in the LICENSE file at https://angular.dev/license
7+
*/
8+
9+
import * as o from '../../../../output/output_ast';
10+
import * as ir from '../../ir';
11+
import type {CompilationJob} from '../compilation';
12+
13+
// TODO: create AST for parentheses when parsing, then we can remove the unnecessary ones instead of
14+
// adding them out of thin air. This should simplify the parsing and give us valid spans for the
15+
// parentheses.
16+
17+
/**
18+
* In some cases we need to add parentheses to expressions for them to be considered valid
19+
* JavaScript. This phase adds parentheses to cover such cases. Currently these cases are:
20+
*
21+
* 1. Unary operators in the base of an exponentiation expression. For example, `-2 ** 3` is not
22+
* valid JavaScript, but `(-2) ** 3` is.
23+
*/
24+
export function requiredParentheses(job: CompilationJob): void {
25+
for (const unit of job.units) {
26+
for (const op of unit.ops()) {
27+
ir.transformExpressionsInOp(
28+
op,
29+
(expr) => {
30+
if (
31+
expr instanceof o.BinaryOperatorExpr &&
32+
expr.operator === o.BinaryOperator.Exponentiation &&
33+
expr.lhs instanceof o.UnaryOperatorExpr
34+
) {
35+
expr.lhs = new o.ParenthesizedExpr(expr.lhs);
36+
}
37+
return expr;
38+
},
39+
ir.VisitorContextFlag.None,
40+
);
41+
}
42+
}
43+
}

packages/language-service/test/quick_info_spec.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -625,6 +625,14 @@ describe('quick info', () => {
625625
const documentation = toText(quickInfo!.documentation);
626626
expect(documentation).toBe('This is the title of the `AppCmp` Component.');
627627
});
628+
629+
it('should work with parenthesized exponentiation expression', () => {
630+
expectQuickInfo({
631+
templateOverride: `{{ (-¦anyValue) ** 2 }}`,
632+
expectedSpanText: 'anyValue',
633+
expectedDisplayString: '(property) AppCmp.anyValue: any',
634+
});
635+
});
628636
});
629637

630638
describe('blocks', () => {

0 commit comments

Comments
 (0)








ApplySandwichStrip

pFad - (p)hone/(F)rame/(a)nonymizer/(d)eclutterfier!      Saves Data!


--- a PPN by Garber Painting Akron. With Image Size Reduction included!

Fetched URL: https://github.com/angular/angular/commit/7c9b4892e9f6df164e4e289195bff27f2cc9a0ea

Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy