Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Bug] Issues with CtClass::compileAndReplaceSnippets compiling method call after comment #3687

Closed
lapplislazuli opened this issue Nov 11, 2020 · 2 comments · Fixed by #3888
Closed
Assignees
Labels

Comments

@lapplislazuli
Copy link

Hi there,

I came accross an weird issue.
I wanted to add an empty method to a class, as well as a simple method call in another method.

Expected Output

From:

class A {
    public int mult(int a, int b){
         return a * b;
    }
}

To:

class A {
    public int mult(int a, int b){
         // New Comment
         otherMethod();
         return a * b;
    }
   
   private void otherMethod(){

   }
}

Actual Output

after first compileAndReplaceSnippets:

class A {
    public int mult(int a, int b){
         // New Comment
         return a * b;
         return a * b;
    }
   
   private void otherMethod(){

   }
}

after second compileAndReplaceSnippets, it throws an error for unreachable code (which seems reasonable).

Code to reproduce

    @Tag("Regression")
    @Test
    void testSnippetReplacement_ReplaceMethodCallSnippetAfterComment_shouldNotThrowError(){
        String initialClass = "package some; class A { public int mult(int a, int b){return a * b;}";

        CtClass<?> containingClass = Launcher.parseClass(initialClass);
        String methodName = "vanishingMethodCall";

        var methodToAlter= containingClass.getMethodsByName("mult").get(0);

        // Build an empty method, return void
        CtMethod emptyMethod = containingClass.getFactory().createMethod();
        emptyMethod.setSimpleName(methodName);
        emptyMethod.setParent(containingClass);
        emptyMethod.setType(emptyMethod.getFactory().Type().VOID_PRIMITIVE);
        emptyMethod.addModifier(ModifierKind.PRIVATE);
        emptyMethod.setBody(emptyMethod.getFactory().createBlock());

        containingClass.addMethod(emptyMethod);

        containingClass.compileAndReplaceSnippets();

        methodToAlter.getBody().addStatement(0,
                containingClass.getFactory().createCodeSnippetStatement(methodName+"()"));

        methodToAlter.getBody().addStatement(0,containingClass.getFactory().createInlineComment("I seem to break the test"));

        // This creates the double return
        containingClass.compileAndReplaceSnippets();
        // This fails the test, as the second return does not compile
        containingClass.compileAndReplaceSnippets();

        return;
    }

Further Info / Considerations

This only occurs when there is a comment before the method. If you remove the line where the InlineComment is added, the test succeeds.

It also duplicates longer statements, such as if-blocks. it seems to duplicate any statement after the comment.

Other

I am using Spoon-Core 8.3.0, in a maven project. Spoon is only used as a library.

As a workaround, at the moment I'll try to not call the method with a snippet.

@lapplislazuli lapplislazuli changed the title [Bug] Issues with CtClass::compileAndReplaceSnippets [Bug] Issues with CtClass::compileAndReplaceSnippets compiling method call after comment Nov 11, 2020
@lapplislazuli
Copy link
Author

Short addendum:

This behaviour occurrs apparently on all comment types not only on inline comments.

Also I'd be happy if you could give me a small pointer how to properly build the method invocation :)

@MartinWitt MartinWitt added the bug label Nov 12, 2020
@MartinWitt MartinWitt self-assigned this Nov 15, 2020
@MartinWitt
Copy link
Collaborator

MartinWitt commented Nov 15, 2020

´The error is inside SnippetCompilationHelper#compileAndReplaceSnippetsIn CtPath#evaluateOn returns an empty List and we use iterator.next(); on it [link]. Ignoring empty lists does not fix the problem.

elements2after.put(p, p.evaluateOn(f.getModel().getRootPackage()).iterator().next());

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
2 participants