Search code examples
rascal

Matching declared method name to regex


I am trying to match a Java method name to a regex, but I'm not sure how to do it in Rascal. I want to match a method whose name starts with test (such as JUnit 3 test cases) and transform it to a JUnit 4 test case, with the @Test annotation and removing the test prefix. My code looks like this:

public tuple[int, CompilationUnit] refactorJUnitTestCaseDeclaration(CompilationUnit cu) {
    int total = 0;
    println(cu); 

    CompilationUnit unit =  visit(cu) {
            case(MethodDeclaration)`public void <Identifier name>() <MethodBody body>`: {
            if(/test[A-Z]*/ := name) {
                total += 1;
                newName = name[4..];
                insert (MethodDeclaration)`@Test public void <Identifier newName>() <MethodBody body>`;
            };
        }
    };
   return <total, unit>;
}

This code results in the following error:

Expected str, but got Identifier

Is there any way to access the name method identifier as a String, so I can try to match it? If not, whats the best way to accomplish this task?


Solution

    • The regex pattern operator wants to match against strings only, so you have to map the parse tree of name (which is of type Identifier) to a string like so: "<name>".
    • Similarly, to splice back the new name string into the location of an Identifier you have to map it back to Identifier like so: [Identifier] newName.

    The end result looks like this:

    public tuple[int, CompilationUnit] refactorJUnitTestCaseDeclaration(CompilationUnit cu) {
        int total = 0;
        println(cu); 
    
        CompilationUnit unit =  visit(cu) {
                case(MethodDeclaration)`public void <Identifier name>() <MethodBody body>`: {
                if(/test[A-Z]*/ := "<name>") {
                    total += 1;
                    newName = [Identifier] "<name>"[4..];
                    insert (MethodDeclaration)`@Test public void <Identifier newName>() <MethodBody body>`;
                };
            }
        };
       return <total, unit>;
    }
    

    You can also directly match the tail out with a named group:

    public tuple[int, CompilationUnit] refactorJUnitTestCaseDeclaration(CompilationUnit cu) {
        int total = 0;
        println(cu); 
    
        CompilationUnit unit =  visit(cu) {
                case(MethodDeclaration)`public void <Identifier name>() <MethodBody body>`: {
                if(/test<rest:[A-Z]*>/ := "<name>") {
                    total += 1;
                    newName = [Identifier] rest;
                    insert (MethodDeclaration)`@Test public void <Identifier newName>() <MethodBody body>`;
                };
            }
        };
       return <total, unit>;
    }