spoon icon indicating copy to clipboard operation
spoon copied to clipboard

[Bug]: wrong output for try with resources (variable ends up several times in initializer list)

Open xzel23 opened this issue 2 years ago • 1 comments

Describe the bug

Spoon produces wrong output for try-with-resources without resource declaration. In the example, foo1() and bar1() that both contain a resource declaration produce correct results. The output for the other methods is incorrect, the variable should be present only once in the expression in parentheses.

Note that try-with-resources was changed in Java 9 to explicitly allow using final or effectively final variables in the try-with-resources list.

Actually I have another case, where try (in) { ... } gets transformed to the non-compilable try { ... }but I cannot reproduce with a simplified example, so I report these apparent failures first.

Source code you are trying to analyze/transform

import java.io.File;
import java.io.FileNotFoundException;
import java.io.PrintWriter;

class Issue {

    public static void foo1() throws FileNotFoundException {
        try (PrintWriter writer = new PrintWriter(new File("testWrite.txt"))) {
            writer.println("foo");
        }
    }

    public static void bar1() throws FileNotFoundException {
        try (PrintWriter writer = new PrintWriter(new File("testWrite.txt"))) {
            writer.println("bar");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static void foo2() throws FileNotFoundException {
        PrintWriter writer = new PrintWriter(new File("testWrite.txt"));
        try (writer) {
            writer.println("foo");
        }
    }

    public static void bar2() throws FileNotFoundException {
        PrintWriter writer = new PrintWriter(new File("testWrite.txt"));
        try (writer) {
            writer.println("bar");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static void foo3(PrintWriter writer) throws FileNotFoundException {
        try (writer) {
            writer.println("foo");
        }
    }

    public static void bar3(PrintWriter writer) throws FileNotFoundException {
        try (writer) {
            writer.println("bar");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

}

Source code for your Spoon processing

import spoon.Launcher;
import spoon.processing.AbstractProcessor;
import spoon.reflect.declaration.CtElement;
import spoon.reflect.declaration.CtType;

public class Main {
    public static void main(String[] args) {
        // Create and configure a Spoon launcher
        Launcher launcher = new Launcher();
        launcher.addProcessor(new NoOperationProcessor());
        launcher.addInputResource("src/test/resources/Issue.java");
        launcher.run();
        launcher.getModel().getAllTypes().stream()
                .map(CtType::toString)
                .forEach(System.out::println);
    }

    // Create a no-operation processor
    static class NoOperationProcessor extends AbstractProcessor<CtElement> {
        @Override
        public void process(CtElement element) {
            // Do nothing
        }
    }
}

Actual output

class Issue {
    public static void foo1() throws java.io.FileNotFoundException {
        try (java.io.PrintWriter writer = new java.io.PrintWriter(new java.io.File("testWrite.txt"))) {
            writer.println("foo");
        }
    }

    public static void bar1() throws java.io.FileNotFoundException {
        try (java.io.PrintWriter writer = new java.io.PrintWriter(new java.io.File("testWrite.txt"))) {
            writer.println("bar");
        } catch (java.lang.Exception e) {
            e.printStackTrace();
        }
    }

    public static void foo2() throws java.io.FileNotFoundException {
        java.io.PrintWriter writer = new java.io.PrintWriter(new java.io.File("testWrite.txt"));
        try (writer;writer;writer) {
            writer.println("foo");
        }
    }

    public static void bar2() throws java.io.FileNotFoundException {
        java.io.PrintWriter writer = new java.io.PrintWriter(new java.io.File("testWrite.txt"));
        try (writer;writer;writer) {
            writer.println("bar");
        } catch (java.lang.Exception e) {
            e.printStackTrace();
        }
    }

    public static void foo3(java.io.PrintWriter writer) throws java.io.FileNotFoundException {
        try (writer;writer) {
            writer.println("foo");
        }
    }

    public static void bar3(java.io.PrintWriter writer) throws java.io.FileNotFoundException {
        try (writer;writer) {
            writer.println("bar");
        } catch (java.lang.Exception e) {
            e.printStackTrace();
        }
    }
}

Expected output

import java.io.File;
import java.io.FileNotFoundException;
import java.io.PrintWriter;

class Issue {

    public static void foo1() throws FileNotFoundException {
        try (PrintWriter writer = new PrintWriter(new File("testWrite.txt"))) {
            writer.println("foo");
        }
    }

    public static void bar1() throws FileNotFoundException {
        try (PrintWriter writer = new PrintWriter(new File("testWrite.txt"))) {
            writer.println("bar");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static void foo2() throws FileNotFoundException {
        PrintWriter writer = new PrintWriter(new File("testWrite.txt"));
        try (writer) {
            writer.println("foo");
        }
    }

    public static void bar2() throws FileNotFoundException {
        PrintWriter writer = new PrintWriter(new File("testWrite.txt"));
        try (writer) {
            writer.println("bar");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static void foo3(PrintWriter writer) throws FileNotFoundException {
        try (writer) {
            writer.println("foo");
        }
    }

    public static void bar3(PrintWriter writer) throws FileNotFoundException {
        try (writer) {
            writer.println("bar");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

}

Spoon Version

10.4.1

JVM Version

17

What operating system are you using?

macOS Ventura 13.5.2

xzel23 avatar Sep 25 '23 05:09 xzel23

I can confirm this bug, even with compliance level >= 9 and also without the processor. It is caused by https://github.com/INRIA/spoon/blob/02c9e043c1e81ec16da60e1ac541c06394c07272/src/main/java/spoon/support/compiler/jdt/ParentExiter.java#L1067-L1079 which incorrectly searches for declarations in other mehods as well. Generally, there might be a better solution to this.

SirYwell avatar Sep 25 '23 06:09 SirYwell