eclipse-jdt / eclipse.jdt.core

Eclipse Public License 2.0
169 stars 133 forks source link

ASTParser cannot keep elements/nodes after a lambda expression (without a block) #2046

Open liufuyang opened 9 months ago

liufuyang commented 9 months ago

The issue is best to explained with an running code sample here:

        String source = """
            package whatever;
            import java.util.function.*;

            public class C {
              public void execute() {
                int j = 1;
                // Function<String, String> f1 = s1 -> {return "good";}; // works
                Function<String, String> f1 = s1 -> "good"; // not working, combined with issue point blow
                int i1 = 0;
                i;  // issue point
              }
            }
            """;

        ASTParser parser = ASTParser.newParser(AST.getJLSLatest());
        Map<String, String> options = JavaCore.getOptions();

        JavaCore.setComplianceOptions(JavaCore.VERSION_11, options);
        parser.setCompilerOptions(options);
        parser.setKind(ASTParser.K_COMPILATION_UNIT);
        parser.setSource(source.toCharArray());
        parser.setStatementsRecovery(true);
        parser.setBindingsRecovery(true);
        parser.setResolveBindings(true);
        parser.setUnitName("C.java");
        parser.setEnvironment(new String[]{}, new String[]{}, new String[]{}, true);
        CompilationUnit compilationUnit = (CompilationUnit) parser.createAST(null);
        System.out.println(compilationUnit);

        compilationUnit.accept(
            new ASTVisitor() {
                @Override
                public boolean visit(VariableDeclarationFragment node) {
                    IVariableBinding iVariableBinding = node.resolveBinding();
                    if (iVariableBinding != null) {
                        ITypeBinding type = iVariableBinding.getType();
                        System.out.println(iVariableBinding.getName() + ", type: " + type.getQualifiedName());
                    }
                    return super.visit(node);
                }
            }
        );

The output is:

package whatever;
import java.util.function.*;
public class C {
  public void execute(){
    int j=1;
    Function<String,String> f1=s1 -> "good";
  }
}

j, type: int
f1, type: java.util.function.Function<java.lang.String,java.lang.String>
s1, type: java.lang.String

Note that the expected element of i1 in the compilationUnit print or visit print is missing.

Expected output should be like:

package whatever;
import java.util.function.*;
public class C {
  public void execute(){
    int j=1;
    Function<String,String> f1=s1 -> "good";
    int i1=0;
    i;
  }
}

j, type: int
f1, type: java.util.function.Function<java.lang.String,java.lang.String>
s1, type: java.lang.String
i1, type: int

Note that this expected output can be seem by simply switching the line of

Function<String, String> f1 = s1 -> "good";

to

Function<String, String> f1 = s1 -> {return "good";};

I would assume that it should not make any differences for the ASTParser when the lambda expression is written as s1 -> "good" or s1 -> {return "good";}?

Thank you.

subyssurendran666 commented 2 months ago

@mpalat could you please assign this ticket to me ?

srikanth-sankaran commented 2 months ago

@mpalat could you please assign this ticket to me ?

Manoj is on leave - there you have it assigned to you

subyssurendran666 commented 1 month ago

Preliminary analysis

Scenario 1

package test;
import java.util.function.*;
public class C {
  public void execute(){
    int j=1;
    a();
    Function<String,String> f1=s1 -> "good";
    int i1=0;
    sam();
  }
}

The above code, line No 6, I have added an ExpressionStatement(expression as a MethodInvocation) and which is an undeclared method a(). Also added another undeclared method sam() in line No 9. The CU will generate the same what I have added in the test code. Please see the screenshot(same code in Eclipse)

Screenshot 2024-10-23 at 10 41 31 AM

The IDE is showing errors on lines 6 and 9, as both a() and sam() are undeclared, which is expected.

Scenario 2

package test;
import java.util.function.*;
public class C {
  public void execute(){
    int j=1;
    a;
    Function<String,String> f1=s1 -> "good";
    int i1=0;
    sam();
  }
}

Here, line No 6 is an ExpressionStatement with expression as an Assessment). The CU will generate the below code

package test;
import java.util.function.*;
public class C {
  public void execute(){
    int j=1;
    a=$missing$;
    Function<String,String> f1=s1 -> "good";
  }
}

In line No6, the Assessment of the ExpressionStatement is incomplete( that is the main reason a; is replaced with a=$missing$; (Please refer he below screenshot). I assume this is because the SimpleName of the Assessment's RIGHT_HAND_SIDE does not have a valid value.

Additionally, as reported by @liufuyang , the nodes after the lambda have been omitted by the parser.

Screenshot 2024-10-23 at 10 56 03 AM

The error message in the line No 9 has gone. I don't know which is related to the same issue or not. @mpalat could you please share your thoughts ?

mpalat commented 1 month ago

@subyssurendran666 the issue is due to a recovered parse tree from the compilation.

@liufuyang Your input is a code that has compiler errors - which essentially means that your parse tree will be based on error recovery of the compiler. That implies that the structure of the parse-tree may have some constructs missed. You should check whether the code is malformed or not by using code similar to as shown below:

""" MethodDeclaration method = (MethodDeclaration) node; if ((method.getFlags() & ASTNode.MALFORMED) != 0) {.... """