Search code examples
javaparsinglistenerantlr4method-invocation

How to save a method name and names of all methods called inside that method when parsing a java file with ANTLR4?


I am working on a program that parses a Java file using the Java9 lexer and grammar from ANTLRs github. When parsing the file, I want to save each method name and the names of all methods called inside that method, in a HashMap<String, Set<String>>.

I use the listener pattern, where I use the method exitMethodDeclaration to get the method name and exitMethodInvocation to get the method invocations. The problem is that the two methods are invoked at different nodes in the parse tree, hence they take different context as argument. Therefore I am not able to call the method to get the invocations inside exitMethodDeclaration, to my knowledge. I attempted to build my own recursion, such that I could solve the type problem by passing methodDeclarationContext as argument to exitMethodInvocation. This would be very complex though, and I would like to use ANTLRs functionality.

With the code below, the hashSet for each method contains all method invocations for the entire file, and not just for the specific method.

private HashMap<String, HashSet<String>> methodCalls = new HashMap<>();
private HashSet<String> methodCallNames = new HashSet<>();

public void exitMethodDeclaration(Java9Parser.MethodDeclarationContext ctx) {
        String methodName = ctx.methodHeader().methodDeclarator().identifier().getText();
        methodCalls.put(methodName, methodCallNames);
    }

public void exitMethodInvocation(Java9Parser.MethodInvocationContext ctx) {
    try {
        String m = ctx.methodName().identifier().getText();
        methodCallNames.add(m);
    } catch (Exception e) {
    }
}

Any ideas on how to collect nodes of different context type inside the same method, visit a subtree inside a listener/visitor method or other ideas are much welcome!


Solution

  • You can create a Stack of ArrayLists.

    Each time, you enterMethodDeclaration, push a new Empty ArrayList onto your Stack.

    Each time you enterMethodInvocation (or exit... if you prefer), you can add the methodName to the ArrayList you have on stack.peek().

    Whenever you exitMethodDeclartation stack.pop() you ArrayList, and it will have a list of all the method names you encountered in directly in that method call. And, the method name is right there in the Context parameter of exitMethodDeclaration