5

I have the code

private static class MyVisitor extends VoidVisitorAdapter<Object> {
    @Override
    public void visit(MethodCallExpr exp, Object arg) {
        System.out.println("Scope: "  + exp.getScope());
        System.out.println("Method: " + exp.getName());
        if(exp.getArgs() != null)
            for(Expression e : exp.getArgs()) {
                System.out.println("\tArgument: " + e.toString());

            }
        System.out.println();
    }
}

And

CompilationUnit cu = JavaParser.parse(new File("Test.java"));

for(TypeDeclaration type : cu.getTypes()) {
    for(BodyDeclaration dec : type.getMembers()) {
        if(dec instanceof MethodDeclaration) {
            MethodDeclaration mdec = (MethodDeclaration) dec;
            BlockStmt block = mdec.getBody();
            for(Statement stmt : block.getStmts()) {
                MyVisitor visitor = new MyVisitor();
                s.accept(visitor, null);
            }
        }
    }
}

I have two problems:

  1. How to convert Statement to Expression? I want to check is that Method Call. I tried to check that they are incompatible types by this way : if(stmt instanceof MethodCallExp) but got error. I entered the code I use now. Is that the one way to perform check?
  2. System.out.println("Scope: " + exp.getScope()); will be displayed the object/package/package+class name. But how to get its type? For example for the code System.out.println("Hello world"); Should be outputted

Type: java.io.PrintStream

Dmitry Ginzburg
  • 7,391
  • 2
  • 37
  • 48
ivan
  • 287
  • 3
  • 14

2 Answers2

0
  1. There is no 1:1 relation between Statements and Expressions. E.g. ExpressionStmt contains just one Expression, but WhileStmt contains a condition Expression and a body comprising a further Statement.
  2. There is no type information contained in the FieldAccessExpr returned by the getScope() call. JavaParser does not seem to provide an easy way of getting the type. Using the Node hierarchy and their types you can use the corresponding Reflection classes to get to your goal. For your example that would mean finding the topmost scope (type would be NameExpr) and getting the Class Object (recognizing that System is short for java.lang.System), for the next node (type would be FieldAccessExpr) you would get the Field of the class and can then get the type:
    Class.forName("java.lang.System").getField("out").getType();
    In your code example above you pass your visitor to every statement within the method body. If you create the visitor outside of the loop, it will be able to remember information seen in previous statements. This way, you might add a field to your visitor to store the variable declarations:
    final HashMap varNameToType = new HashMap();
    You can store the variable declarations like below. When you get to a method call, you can use the scope to try and get the type via the map. If the map doesn't contain the Type, you may need to use Reflection, since the variable may be part of the JDK classes. I'm not suggesting that this covers all cases. E.g. when you access a field of your parent class, you wouldn't have any information about that field at your position in the code - you may need to look at different compilation units for that.
@Override
public void visit(
        final VariableDeclarationExpr n,
        final Object arg) {
    final Type varType = n.getType();
    n.getVars().forEach(vd ->
        varNameToType.put(vd.getId().getName(),varType)
    );
}
muued
  • 1,666
  • 13
  • 25
  • 1. Okay 2. But what about objects? For, example, I create String s = "123"; s.trim(); s and trim will be found, but I want to get the type of "s". Is that possible? – ivan Apr 21 '15 at 15:02
  • And is that possible to look into statement with no using visit method? – ivan Apr 21 '15 at 15:32
  • I guess they expect you to remember the type of the variable `s` when you read its declaration. You can't "look into" a `Statement` as long as you only know it to be a `Statement` (abstract superclass of all actual statements). By using the visit methods you can identify the actual type and use the provided getters available then. I won't suggest using an ugly list of if-else clauses performing instanceof checks to identify the type of the statement. – muued Apr 21 '15 at 15:52
  • Yes, I found it out. I just can call method accept for any Statement (block,if, etc) and get all method calls (for example). But how to get the actual type of the object? In my case, when I caught a method call, my purpose is checking the caller package. – ivan Apr 21 '15 at 17:30
  • The scope of the object tells you where to look for declarations. The declarations give you the type of the object. – muued Apr 21 '15 at 18:20
  • Can you please make an example how to access to the type? I tried to do: `MyVisitor2 visitor = new MyVisitor2(); exp.getScope().accept(visitor, null);` and after that catch `visit(VariableDeclarationExpr exp, Object obj)`, but I didn't get any result – ivan Apr 22 '15 at 08:17
  • One more question. How to get the returning type of a method? I think I need to go from the end: get the caller object, find the calling method and check the returning type, if here is more method calls like `string.trim.replace(...).replace(...)` I need to go recursively and check each type and after the returning type of its method. Am I right? Why the developers of JavaParser didn't implement this functional in the library? – ivan Apr 23 '15 at 15:13
  • JavaParser only generates an AST. The scope is a single compilation unit. Thus the parser has no way of determining the return type of a method, since it may have been defined elsewhere. It just checks for validity w.r.t. the language specification. There is no type checking and no guarantee that the method called even exists. If it does, your approach should work, though. – muued Apr 23 '15 at 15:28
0

If you don't need to stick on JavaParser maybe you could solve it in the way the Android lint tools do it. EcjParserTest.java. It uses Lombok under the hood.

At line 380 it compares in the test an output similar to the one you want to have [ExpressionStatement], type: void, resolved method: println java.io.PrintStream.

I had no deeper look, but it looks as a promising starting point

SubOptimal
  • 22,518
  • 3
  • 53
  • 69