Skip to content
This repository was archived by the owner on May 12, 2025. It is now read-only.

Commit 2e15f7e

Browse files
authored
Prevent wrapping statements into JS.ExpressionStatement (#211)
* Prevent wrapping statements into JS.ExpressionStatement In a visitExpressionStatement parser method, if the expression is a Statement, it shouldn't be wrapped into ExpressionStatement. Also, in a JavaScriptVisitor class, remove unwrapping ExpressionStatement into Statement if it is a Statement.
1 parent 45e89cf commit 2e15f7e

File tree

5 files changed

+170
-14
lines changed

5 files changed

+170
-14
lines changed

openrewrite/src/javascript/parser.ts

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ import {
2323
randomId,
2424
SourceFile
2525
} from "../core";
26-
import {binarySearch, compareTextSpans, getNextSibling, getPreviousSibling, TextSpan, hasFlowAnnotation, checkSyntaxErrors, isValidSurrogateRange} from "./parserUtils";
26+
import {binarySearch, compareTextSpans, getNextSibling, getPreviousSibling, TextSpan, hasFlowAnnotation, checkSyntaxErrors, isValidSurrogateRange, isStatement} from "./parserUtils";
2727
import {JavaScriptTypeMapping} from "./typeMapping";
2828
import path from "node:path";
2929
import {ExpressionStatement, TypeTreeExpression} from ".";
@@ -2626,10 +2626,7 @@ export class JavaScriptParserVisitor {
26262626

26272627
visitExpressionStatement(node: ts.ExpressionStatement): J.Statement {
26282628
const expression = this.visit(node.expression) as J.Expression;
2629-
if (expression instanceof J.MethodInvocation || expression instanceof J.NewClass || expression instanceof J.Unknown ||
2630-
expression instanceof J.AssignmentOperation || expression instanceof J.Ternary || expression instanceof J.Empty ||
2631-
expression instanceof JS.ExpressionStatement || expression instanceof J.Assignment || expression instanceof J.FieldAccess) {
2632-
// FIXME this is a hack we currently require because our `Expression` and `Statement` interfaces don't have any type guards
2629+
if (isStatement(expression)) {
26332630
return expression as J.Statement;
26342631
}
26352632
return new JS.ExpressionStatement(
@@ -2719,7 +2716,7 @@ export class JavaScriptParserVisitor {
27192716
Markers.EMPTY,
27202717
[node.initializer ?
27212718
(ts.isVariableDeclarationList(node.initializer) ? this.rightPadded(this.visit(node.initializer), Space.EMPTY) :
2722-
this.rightPadded(new ExpressionStatement(randomId(), this.visit(node.initializer)), this.suffix(node.initializer))) :
2719+
this.rightPadded(ts.isStatement(node.initializer) ? this.visit(node.initializer) : new ExpressionStatement(randomId(), this.visit(node.initializer)), this.suffix(node.initializer))) :
27232720
this.rightPadded(this.newJEmpty(), this.suffix(this.findChildNode(node, ts.SyntaxKind.OpenParenToken)!))], // to handle for (/*_*/; ; );
27242721
node.condition ? this.rightPadded(this.visit(node.condition), this.suffix(node.condition)) :
27252722
this.rightPadded(this.newJEmpty(), this.suffix(this.findChildNode(node, ts.SyntaxKind.SemicolonToken)!)), // to handle for ( ;/*_*/; );

openrewrite/src/javascript/parserUtils.ts

Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,158 @@
11
import * as ts from "typescript";
2+
import * as J from '../java'
3+
import * as JS from "./tree";
4+
5+
const is_statements = [
6+
J.Assert,
7+
J.Assignment,
8+
J.AssignmentOperation,
9+
J.Block,
10+
J.Break,
11+
J.Case,
12+
J.ClassDeclaration,
13+
J.Continue,
14+
J.DoWhileLoop,
15+
J.Empty,
16+
J.EnumValueSet,
17+
J.Erroneous,
18+
J.FieldAccess,
19+
J.ForEachLoop,
20+
J.ForLoop,
21+
J.If,
22+
J.Import,
23+
J.Label,
24+
J.Lambda,
25+
J.MethodDeclaration,
26+
J.MethodInvocation,
27+
J.NewClass,
28+
J.Package,
29+
J.Return,
30+
J.Switch,
31+
J.Synchronized,
32+
J.Ternary,
33+
J.Throw,
34+
J.Try,
35+
J.Unary,
36+
J.Unknown,
37+
J.VariableDeclarations,
38+
J.WhileLoop,
39+
J.Yield,
40+
JS.ArrowFunction,
41+
JS.BindingElement,
42+
JS.Delete,
43+
JS.Export,
44+
JS.ExportAssignment,
45+
JS.ExportDeclaration,
46+
JS.FunctionDeclaration,
47+
JS.JSForInLoop,
48+
JS.JSForOfLoop,
49+
JS.ImportAttribute,
50+
JS.IndexSignatureDeclaration,
51+
JS.JsAssignmentOperation,
52+
JS.JsImport,
53+
JS.JSMethodDeclaration,
54+
JS.JSTry,
55+
JS.JSVariableDeclarations,
56+
JS.MappedType.KeysRemapping,
57+
JS.MappedType.MappedTypeParameter,
58+
JS.NamespaceDeclaration,
59+
JS.PropertyAssignment,
60+
JS.ScopedVariableDeclarations,
61+
JS.TaggedTemplateExpression,
62+
JS.TemplateExpression,
63+
JS.TrailingTokenStatement,
64+
JS.TypeDeclaration,
65+
JS.Unary,
66+
JS.Void,
67+
JS.WithStatement,
68+
JS.ExpressionStatement,
69+
JS.StatementExpression
70+
]
71+
72+
const is_expressions = [
73+
J.AnnotatedType,
74+
J.Annotation,
75+
J.ArrayAccess,
76+
J.ArrayType,
77+
J.Assignment,
78+
J.AssignmentOperation,
79+
J.Binary,
80+
J.ControlParentheses,
81+
J.Empty,
82+
J.Erroneous,
83+
J.FieldAccess,
84+
J.Identifier,
85+
J.InstanceOf,
86+
J.IntersectionType,
87+
J.Lambda,
88+
J.Literal,
89+
J.MethodInvocation,
90+
J.MemberReference,
91+
J.NewArray,
92+
J.NewClass,
93+
J.NullableType,
94+
J.ParameterizedType,
95+
J.Parentheses,
96+
J.ParenthesizedTypeTree,
97+
J.Primitive,
98+
J.SwitchExpression,
99+
J.Ternary,
100+
J.TypeCast,
101+
J.Unary,
102+
J.Unknown,
103+
J.Wildcard,
104+
JS.Alias,
105+
JS.ArrayBindingPattern,
106+
JS.ArrowFunction,
107+
JS.Await,
108+
JS.BindingElement,
109+
JS.ConditionalType,
110+
JS.DefaultType,
111+
JS.Delete,
112+
JS.ExportSpecifier,
113+
JS.ExpressionWithTypeArguments,
114+
JS.FunctionDeclaration,
115+
JS.FunctionType,
116+
JS.ImportType,
117+
JS.IndexedAccessType,
118+
JS.IndexedAccessType.IndexType,
119+
JS.InferType,
120+
JS.Intersection,
121+
JS.JsAssignmentOperation,
122+
JS.JsBinary,
123+
JS.JsImportSpecifier,
124+
JS.LiteralType,
125+
JS.MappedType,
126+
JS.NamedExports,
127+
JS.NamedImports,
128+
JS.ObjectBindingDeclarations,
129+
JS.SatisfiesExpression,
130+
JS.TaggedTemplateExpression,
131+
JS.TemplateExpression,
132+
JS.TrailingTokenStatement,
133+
JS.Tuple,
134+
JS.TypeInfo,
135+
JS.TypeLiteral,
136+
JS.TypeOf,
137+
JS.TypeOperator,
138+
JS.TypePredicate,
139+
JS.TypeQuery,
140+
JS.TypeTreeExpression,
141+
JS.Unary,
142+
JS.Union,
143+
JS.Void,
144+
JS.Yield,
145+
JS.ExpressionStatement,
146+
JS.StatementExpression
147+
]
148+
149+
export function isStatement(statement: J.J): statement is J.Statement {
150+
return is_statements.some((cls: any) => statement instanceof cls);
151+
}
152+
153+
export function isExpression(expression: J.J): expression is J.Expression {
154+
return is_expressions.some((cls: any) => expression instanceof cls);
155+
}
2156

3157
export function getNextSibling(node: ts.Node): ts.Node | null {
4158
const parent = node.parent;

openrewrite/test/javascript/parser/function.test.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -442,4 +442,15 @@ describe('function mapping', () => {
442442
`)
443443
);
444444
});
445+
446+
test('function invocation', () => {
447+
rewriteRun(
448+
//language=typescript
449+
typeScript(`
450+
!function(e, t) {
451+
console.log("This is an IIFE", e, t);
452+
}("Hello", "World");
453+
`)
454+
);
455+
});
445456
});

openrewrite/test/javascript/parser/void.test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,8 @@ describe('void operator mapping', () => {
1111
//language=typescript
1212
typeScript('void 1', cu => {
1313
const statement = cu.statements[0] as JS.ExpressionStatement;
14-
expect(statement.expression).toBeInstanceOf(JS.Void);
15-
const type = (statement.expression as JS.Void).type as JavaType.Primitive;
14+
expect(statement).toBeInstanceOf(JS.Void);
15+
const type = statement.type as JavaType.Primitive;
1616
expect(type.kind).toBe(JavaType.PrimitiveKind.Void);
1717
})
1818
);

rewrite-javascript/src/main/java/org/openrewrite/javascript/JavaScriptVisitor.java

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -214,9 +214,6 @@ public J visitExpressionStatement(JS.ExpressionStatement statement, P p) {
214214
es = (JS.ExpressionStatement) temp;
215215
}
216216
J expression = visit(es.getExpression(), p);
217-
if (expression instanceof Statement) {
218-
return expression;
219-
}
220217
es = es.withExpression((Expression) Objects.requireNonNull(expression));
221218
return es;
222219
}
@@ -528,9 +525,6 @@ public J visitStatementExpression(JS.StatementExpression expression, P p) {
528525
se = (JS.StatementExpression) temp;
529526
}
530527
J statement = visit(se.getStatement(), p);
531-
if (statement instanceof Expression) {
532-
return statement;
533-
}
534528
se = se.withStatement((Statement) Objects.requireNonNull(statement));
535529
return se;
536530
}

0 commit comments

Comments
 (0)