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(sniper): fields and local variables with joint declaration cannot be snipered #3386

Open
fermadeiral opened this issue Jun 1, 2020 · 11 comments · Fixed by #3412
Open
Assignees

Comments

@fermadeiral
Copy link
Contributor

Hi all,

I have the following code:

        int a, c = 0;

	void foo(int a) {
		a = a;
	}

I want to remove the statement a = a;, which is fine. However, after the sniper printer prints the code, I get: java.lang.IllegalStateException: Registering symbol: 'c' twice in the same scope. This happens because the printed code is:

        int a, c = 0;

	int c = 0;

	void foo(int a);

See, everything is fine with my transformation, but the variable c is printed twice. I created a failing test case to expose this issue, I hope it helps for fixing the bug:

@Test
	public void testPrintOneLineMultipleVariableDeclaration() {
		testSniper(OneLineMultipleVariableDeclaration.class.getName(), type -> {
			CtMethod<?> m = type.getMethodsByName("foo").get(0);
			m.setBody(null);
		}, (type, printed) -> {
			assertIsPrintedWithExpectedChanges(type, printed, "\\Qvoid foo(int a) {\n\t\ta = a;\n\t}", "void foo(int a);");
		});
	}

This is the content of the OneLineMultipleVariableDeclaration class:

package spoon.test.prettyprinter.testclasses;

public class OneLineMultipleVariableDeclaration {

	int a, c = 0;

	void foo(int a) {
		a = a;
	}
}
@fermadeiral fermadeiral changed the title Sniper printer prints a the declaration of a variable twice when it's declared in the same line as another one. Sniper printer prints the declaration of a variable twice when it's declared in the same line as another variable. Jun 1, 2020
@monperrus
Copy link
Collaborator

For the record, making progress with #3390, #3391, #3392

@fermadeiral
Copy link
Contributor Author

Nice!

@monperrus monperrus changed the title Sniper printer prints the declaration of a variable twice when it's declared in the same line as another variable. bug(sniper): fields with joint declaration cannot be snipered Jun 5, 2020
@nharrand
Copy link
Collaborator

nharrand commented Jun 5, 2020

I've merge the work around!

@fermadeiral
Copy link
Contributor Author

Thanks @monperrus and @nharrand!

@monperrus monperrus changed the title bug(sniper): fields with joint declaration cannot be snipered bug(sniper): fields and local variables with joint declaration cannot be snipered Jun 8, 2020
@fermadeiral
Copy link
Contributor Author

Hi,

The workaround doesn't work for the context I presented here. In my case, I have int a, c = 0;, but in your test you changed it to int a, c;.

The error changed for me now:

spoon.SpoonException: spoon.SpoonException: Unexpected source text: 0;

	at spoon.Launcher.prettyprint(Launcher.java:774)
	at spoon.test.prettyprinter.TestSniperPrinter.testSniper(TestSniperPrinter.java:281)
	at spoon.test.prettyprinter.TestSniperPrinter.testPrintOneLineMultipleVariableDeclaration(TestSniperPrinter.java:113)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.base/java.lang.reflect.Method.invoke(Method.java:566)
	at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:47)
	at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
	at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:44)
	at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
	at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:271)
	at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:70)
	at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:50)
	at org.junit.runners.ParentRunner$3.run(ParentRunner.java:238)
	at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:63)
	at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:236)
	at org.junit.runners.ParentRunner.access$000(ParentRunner.java:53)
	at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:229)
	at org.junit.runners.ParentRunner.run(ParentRunner.java:309)
	at org.junit.runner.JUnitCore.run(JUnitCore.java:160)
	at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68)
	at com.intellij.rt.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:33)
	at com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:230)
	at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:58)
Caused by: spoon.SpoonException: Unexpected source text: 0;
	at spoon.support.sniper.internal.ElementSourceFragment.onCharSequence(ElementSourceFragment.java:770)
	at spoon.support.sniper.internal.ElementSourceFragment.forEachConstantFragment(ElementSourceFragment.java:728)
	at spoon.support.sniper.internal.ElementSourceFragment.getChildrenFragments(ElementSourceFragment.java:519)
	at spoon.support.sniper.internal.ElementSourceFragment.getGroupedChildrenFragments(ElementSourceFragment.java:538)
	at spoon.support.sniper.internal.SourceFragmentContextNormal.<init>(SourceFragmentContextNormal.java:45)
	at spoon.support.sniper.SniperJavaPrettyPrinter$2.printSourceFragment(SniperJavaPrettyPrinter.java:281)
	at spoon.support.sniper.internal.AbstractSourceFragmentPrinter.print(AbstractSourceFragmentPrinter.java:60)
	at spoon.support.sniper.internal.SourceFragmentContextList.print(SourceFragmentContextList.java:19)
	at spoon.support.sniper.SniperJavaPrettyPrinter$2.printSourceFragment(SniperJavaPrettyPrinter.java:270)
	at spoon.support.sniper.internal.AbstractSourceFragmentPrinter.print(AbstractSourceFragmentPrinter.java:60)
	at spoon.support.sniper.internal.SourceFragmentContextNormal.print(SourceFragmentContextNormal.java:38)
	at spoon.support.sniper.SniperJavaPrettyPrinter.executePrintEventInContext(SniperJavaPrettyPrinter.java:311)
	at spoon.support.sniper.SniperJavaPrettyPrinter.scan(SniperJavaPrettyPrinter.java:241)
	at spoon.support.sniper.SniperJavaPrettyPrinter.scan(SniperJavaPrettyPrinter.java:53)
	at spoon.reflect.visitor.DefaultJavaPrettyPrinter.visitCtCompilationUnit(DefaultJavaPrettyPrinter.java:1092)
	at spoon.support.reflect.declaration.CtCompilationUnitImpl.accept(CtCompilationUnitImpl.java:407)
	at spoon.reflect.visitor.DefaultJavaPrettyPrinter.scan(DefaultJavaPrettyPrinter.java:380)
	at spoon.support.sniper.SniperJavaPrettyPrinter.lambda$superScanInContext$2(SniperJavaPrettyPrinter.java:361)
	at spoon.support.sniper.SniperJavaPrettyPrinter.runInContext(SniperJavaPrettyPrinter.java:372)
	at spoon.support.sniper.SniperJavaPrettyPrinter.superScanInContext(SniperJavaPrettyPrinter.java:361)
	at spoon.support.sniper.SniperJavaPrettyPrinter.access$300(SniperJavaPrettyPrinter.java:54)
	at spoon.support.sniper.SniperJavaPrettyPrinter$2.printSourceFragment(SniperJavaPrettyPrinter.java:281)
	at spoon.support.sniper.internal.AbstractSourceFragmentPrinter.print(AbstractSourceFragmentPrinter.java:60)
	at spoon.support.sniper.internal.SourceFragmentContextList.print(SourceFragmentContextList.java:19)
	at spoon.support.sniper.SniperJavaPrettyPrinter.executePrintEventInContext(SniperJavaPrettyPrinter.java:311)
	at spoon.support.sniper.SniperJavaPrettyPrinter.scan(SniperJavaPrettyPrinter.java:241)
	at spoon.support.sniper.SniperJavaPrettyPrinter.scan(SniperJavaPrettyPrinter.java:53)
	at spoon.reflect.visitor.DefaultJavaPrettyPrinter.calculate(DefaultJavaPrettyPrinter.java:2048)
	at spoon.support.sniper.SniperJavaPrettyPrinter.lambda$calculate$0(SniperJavaPrettyPrinter.java:120)
	at spoon.support.sniper.SniperJavaPrettyPrinter.runInContext(SniperJavaPrettyPrinter.java:372)
	at spoon.support.sniper.SniperJavaPrettyPrinter.calculate(SniperJavaPrettyPrinter.java:115)
	at spoon.support.compiler.jdt.JDTBasedSpoonCompiler.getCompilationUnitInputStream(JDTBasedSpoonCompiler.java:678)
	at spoon.support.compiler.jdt.JDTBasedSpoonCompiler.generateProcessedSourceFilesUsingCUs(JDTBasedSpoonCompiler.java:559)
	at spoon.support.compiler.jdt.JDTBasedSpoonCompiler.generateProcessedSourceFiles(JDTBasedSpoonCompiler.java:211)
	at spoon.Launcher.prettyprint(Launcher.java:772)
	... 24 more

@monperrus
Copy link
Collaborator

Thanks @fermadeiral for the follow up, fixed in #3412

@monperrus
Copy link
Collaborator

#3412 was only a partial fix, we still have the problem in some cases, see 736bf3a

@algomaster99 would have a look at this sniper limitation?

@algomaster99
Copy link
Contributor

@monperrus I can have a look at it. Can you assign it to me? It can be in my logs that way.

@monperrus
Copy link
Collaborator

great, thanks. you're assigned.

@algomaster99
Copy link
Contributor

I think the root of this problem is the ElementSourceFragment tree that is being created. For example, the tree of public class Field { String f1,f2,f3 = "42",f4; } is as follows:

firstChild = {ElementSourceFragment@3860} "|41, 47|public|"
nextSibling = {ElementSourceFragment@3866} " f4;|66, 92|String f1,f2,f3 = "42",f4;|"
  firstChild = {ElementSourceFragment@3894} " f3 = "42",|66, 89|String f1,f2,f3 = "42",|"
     firstChild = {ElementSourceFragment@3899} " f2,|66, 79|String f1,f2,|"
        firstChild = {ElementSourceFragment@3897} "java.lang.String f1,|66, 76|String f1,|"
          firstChild = {ElementSourceFragment@3898} "java.lang.String|66, 72|String|"
             firstChild = {ElementSourceFragment@3900} "java.lang.String|66, 72|String|"
               firstChild = {ElementSourceFragment@3901} "java.lang.String|66, 72|String|"
                 firstChild = {ElementSourceFragment@3903} "java.lang.String|66, 72|String|"

The field declarations are children instead of being siblings of one another and their types are not directly associated with them. We should fix this first otherwise, we will keep working around in the printer and miss some of the edge cases.

Note that this result is after commenting the following lines:

// bug #3386: we cannot handle joint declaration yet
//so we ignore them, meaning that fields in joint declarations
// will be reprinted normally
if (f.isPartOfJointDeclaration()) {
return;
}

@algomaster99
Copy link
Contributor

An idea to deal with the above comment is pushed here. It will take a lot of time to reach the goal so I am putting it in my backlog. At least the printer produces a compilable output.

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