繁体   English   中英

try-with-resources中的死代码警告,但不是在翻译的try-catch-finally中

[英]Dead code warning in try-with-resources, but not in translated try-catch-finally

下面的代码使用Java 8中引入的try -with-resources结构。 偶然的 Throw ()方法被声明为抛出OccasionalExceptionResourceclose()方法抛出CloseException Eclipse(版本:Neon Release(4.6.0),Build id:20160613-1800)在标有//死代码的行上添加一个警告,表示该分支是死代码。 隐含地,Eclipse确认标有//活动代码的行不是死代码。

Object tryWithResources() throws OccasionalException {
    Object value = null;
    try (Resource resource = new Resource()) {
        occasionallyThrow();
        value = new Object();
    }
    catch (CloseException e) {
        if (value == null) {
            // alive code
        }
        else {
            // dead code
        }
    }
    return value;
}

我很困惑。 如果偶然的Throw()抛出其OccasionalException ,则try -with-resources应将其作为主要异常捕获,然后尝试关闭资源。 如果关闭资源会抛出一个CloseException ,那么它将在OccasionalException下被抑制,因此不会捕获CloseException 因此,唯一需要捕获的CloseException是在try中的块成功完成时,这意味着该值为非null。 所以看起来“死代码”实际上是活着的,而“活着代码”实际上已经死了。 我不确定编译器实际上应该在这里识别出什么,但至少,这里的“死代码”似乎应该被称为死。

更复杂的是,不使用try-with-resources表单的翻译表单根本没有标记任何死代码警告。 (我非常有信心,我的翻译是正确的,基于14.20.3.2。扩展尝试资源 ,但如果这里有bug,我不会完全感到惊讶......)

Object expandedTry() throws OccasionalException {
    Object value = null;
    try {
        Resource resource = new Resource();
        Throwable $primary = null;
        try {
            occasionallyThrow();
            value = new Object();
        }
        catch (Throwable t) {
            $primary = t;
            throw t;
        }
        finally {
            if (resource != null) {
                if ($primary != null) {
                    try {
                        resource.close();
                    }
                    catch (Throwable $suppressed) {
                        $primary.addSuppressed($suppressed);
                    }
                }
                else {
                    resource.close();
                }
            }
        }
    }
    catch (CloseException e) {
        if (value == null) {
            // alive (not dead!)
        }
        else {
            // alive
        }
    }
    return value;
}

我是否遗漏了一些东西会使if-else中的任何一个分支死于其中一个,而不是另一个?

完整代码

这是包含辅助异常类型, Resource类和顶级类定义的完整代码。

public class TestTryWithResources {

    /** Exception thrown by Resource's close() method */
    @SuppressWarnings("serial")
    static class CloseException extends Exception {}

    /** AutoCloseable declared to throw a CloseException */ 
    static class Resource implements AutoCloseable {
        @Override
        public void close() throws CloseException {}
    }

    /** An occasionally thrown exception */
    @SuppressWarnings("serial")
    static class OccasionalException extends Exception {}

    /** Method declared to throw an occasional exception */
    void occasionallyThrow() throws OccasionalException {}

    /*
     * Method using try-with-resources.  Eclipse warns that the 
     * portion marked with "// dead code" is Dead code.
     */
    Object tryWithResources() throws OccasionalException {
        Object value = null;
        try (Resource resource = new Resource()) {
            occasionallyThrow();
            value = new Object();
        }
        catch (CloseException e) {
            if (value == null) {
                // alive code
            }
            else {
                // dead code
            }
        }
        return value;
    }

    /*
     * Method not using try-with-resources.  This is the translation
     * of the try-with-resources in tryWithResources, according to 
     * [14.20.3 try-with-resources][1].  Eclipse does not warn about 
     * any of the code being Dead code.
     * 
     * [1]: https://docs.oracle.com/javase/specs/jls/se7/html/jls-14.html#jls-14.20.3 
     */
    Object expandedTry() throws OccasionalException {
        Object value = null;
        try {
            Resource resource = new Resource();
            Throwable $primary = null;
            try {
                occasionallyThrow();
                value = new Object();
            }
            catch (Throwable t) {
                $primary = t;
                throw t;
            }
            finally {
                if (resource != null) {
                    if ($primary != null) {
                        try {
                            resource.close();
                        }
                        catch (Throwable $suppressed) {
                            $primary.addSuppressed($suppressed);
                        }
                    }
                    else {
                        resource.close();
                    }
                }
            }
        }
        catch (CloseException e) {
            if (value == null) {
                // alive
            }
            else {
                // alive
            }
        }
        return value;
    }
}

回应评论

Amin J的回答建议在设置之后使用资源来改变Eclipse的代码分析。 但这不起作用。 在使用资源之后,例如通过打印它,Luna和Neon中仍然存在死码警告:

Luna死代码警告,尽管在分配值后使用资源

霓虹灯死代码警告,尽管在分配值后使用资源

由于某种原因,静态代码分析器认为资源将在try声明之后立即关闭,而根据教程,资源在语句之后关闭。

try-with-resources语句确保在语句结束时关闭每个资源。

因此,例如,如果您更改代码以在值为(下面的代码)后使用资源,则它不会警告您死代码(但在Eclipse Luna上测试)。

Object tryWithResources() throws OccasionalException {
    Object value = null;
    try (Resource resource = new Resource()) {
        occasionallyThrow();
        value = new Object();
        resource.someMethod(); // using the resource, so eclipse thinks it's not closed yet (correctly)
    }
    catch (CloseException e) {
        if (value == null) {
            // alive code
        }
        else {
            // dead code
        }
    }
    return value;
}

UPDATE

这是我在设置值后使用资源(在本例中为reader)测试的实际代码。

        Object val = null;

        try (BufferedReader reader = new BufferedReader(new FileReader("C:\\file.txt"))) {
            val = new Object();
            System.out.println("got here");
            reader.readLine();
        }
        catch(IOException e){
            System.out.println("io ex");
            if ( val == null){

            }
            else{

            }
        }

这并没有回答为什么 Eclipse会生成警告,或者是否应该生成警告的问题,但这是一种解决方法,至少可以消除暂时的警告。 您可以使用正在测试的值以及异常调用另一个方法,而不是将条件放在catch块中,并从该方法测试对象是否为null,然后执行需要执行的操作:

Object tryWithResources() throws OccasionalException {
    Object value = null;
    try (Resource resource = new Resource()) {
        occasionallyThrow();
        value = new Object();
        System.out.println(resource.toString());
    }
    catch (CloseException e) {
        catchBlock(value, e);  // call auxiliary method
    }
    return value;
}

void catchBlock(Object value, CloseException e) {
    if (value == null) {
        // then
    }
    else {
        // else
    }
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM