From ffbfe5cb4fd5fe346714510ecff780d2f6672dfa Mon Sep 17 00:00:00 2001 From: Michael Ernst Date: Sun, 8 Dec 2024 22:25:07 -0800 Subject: [PATCH] Handle method call misinterpreted as `yield` statement --- .../ajava/JointJavacJavaParserVisitor.java | 46 +++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/framework/src/main/java/org/checkerframework/framework/ajava/JointJavacJavaParserVisitor.java b/framework/src/main/java/org/checkerframework/framework/ajava/JointJavacJavaParserVisitor.java index a8d1c1d82e0..e7dd9a9c5bd 100644 --- a/framework/src/main/java/org/checkerframework/framework/ajava/JointJavacJavaParserVisitor.java +++ b/framework/src/main/java/org/checkerframework/framework/ajava/JointJavacJavaParserVisitor.java @@ -3,6 +3,7 @@ import com.github.javaparser.ast.CompilationUnit; import com.github.javaparser.ast.ImportDeclaration; import com.github.javaparser.ast.Node; +import com.github.javaparser.ast.NodeList; import com.github.javaparser.ast.PackageDeclaration; import com.github.javaparser.ast.body.AnnotationDeclaration; import com.github.javaparser.ast.body.AnnotationMemberDeclaration; @@ -750,6 +751,8 @@ public Void visitExpressionStatement(ExpressionStatementTree javacTree, Node jav // surround explicit constructor invocations in an expression statement, we match // javaParserNode to the javac expression rather than the javac expression statement. javacTree.getExpression().accept(this, javaParserNode); + } else if (isYieldAndYield(javacTree, javaParserNode)) { + // There is nothing to do } else { throwUnexpectedNodeType(javacTree, javaParserNode); } @@ -757,6 +760,49 @@ public Void visitExpressionStatement(ExpressionStatementTree javacTree, Node jav return null; } + /** + * Returns true if {@code javacTree} is a {@code yield()} method call and {@code javaParserNode} + * is a {@code yield()} statement. + * + *

There are methods named {@code yield()}, such as one in {@code Thread}. JavaParser parses + * every occurrence of {@code yield} as a yield statement. For example, it considers {@code + * yield();} to be {@code yield ();} which is shorthand for {@code yield ()->{};}. See + * https://github.com/javaparser/javaparser/issues/2332 . + * + * @param javacTree a javac tree + * @param javaParserNode a JavaParser node + * @return true if {@code javacTree} is a {@code yield()} method call and {@code javaParserNode} + * is a {@code yield()} statement + */ + private boolean isYieldAndYield(ExpressionStatementTree javacTree, Node javaParserNode) { + if (javacTree.getExpression().getKind() == Tree.Kind.METHOD_INVOCATION + && javaParserNode instanceof YieldStmt) { + MethodInvocationTree javacInvok = (MethodInvocationTree) javacTree.getExpression(); + ExpressionTree javacInvokMethod = javacInvok.getMethodSelect(); + List javacInvokArgs = javacInvok.getArguments(); + List javacInvokTypeArgs = javacInvok.getTypeArguments(); + if ((javacInvokArgs.isEmpty() + && javacInvokTypeArgs.isEmpty() + && javacInvokMethod.getKind() == Tree.Kind.IDENTIFIER) + && ((IdentifierTree) javacInvokMethod).getName().toString().equals("yield")) { + + YieldStmt javaParserYieldStmt = (YieldStmt) javaParserNode; + Expression javaParserYieldExpression = javaParserYieldStmt.getExpression(); + if (javaParserYieldExpression instanceof LambdaExpr) { + LambdaExpr javaParserLambda = (LambdaExpr) javaParserYieldExpression; + NodeList jpLambdaParams = javaParserLambda.getParameters(); + Statement jpLambdaBody = javaParserLambda.getBody(); + if (jpLambdaParams.isEmpty() + && jpLambdaBody.isBlockStmt() + && jpLambdaBody.asBlockStmt().getStatements().isEmpty()) { + return true; + } + } + } + } + return false; + } + @Override public Void visitForLoop(ForLoopTree javacTree, Node javaParserNode) { ForStmt node = castNode(ForStmt.class, javaParserNode, javacTree);