IT이야기

Eclipse에서 Java의 도달할 수 없는 코드 오류 대 데드 코드 경고?

cyworld 2021. 10. 14. 21:15
반응형

Eclipse에서 Java의 도달할 수 없는 코드 오류 대 데드 코드 경고?


이유를 아는 사람이 있습니까?

public void foo()
{
    System.out.println("Hello");
    return;
    System.out.println("World!");
}

Eclipse에서 "연결할 수 없는 오류"로 보고되지만

public void foo()
{
    System.out.println("Hello");
    if(true) return;
    System.out.println("World!");
}

"데드 코드" 경고만 트리거합니까?

내가 생각할 수 있는 유일한 설명은 Java 컴파일러가 첫 번째에만 플래그를 지정하고 Eclipse의 일부 추가 분석이 두 번째를 파악한다는 것입니다. 그러나 그렇다면 왜 Java 컴파일러는 컴파일 타임에 이 경우를 알아낼 수 없습니까?

Java 컴파일러는 컴파일 시간에 if(true)가 아무런 효과가 없음을 파악하여 본질적으로 동일한 바이트 코드를 생성하지 않습니까? 도달 가능한 코드 분석은 어느 시점에 적용됩니까?

이 질문을 생각하는 더 일반적인 방법은 "연결 가능한 코드 분석이 언제 적용됩니까?"입니다. 두 번째 Java 코드 조각을 최종 바이트 코드로 변환할 때 "if(true)" 런타임에 해당하는 항목이 어느 시점에서 제거되고 두 프로그램의 표현이 동일하게 될 것이라고 확신합니다. 그러면 Java 컴파일러가 도달 가능한 코드 분석을 다시 적용하지 않을까요?


첫 번째는 컴파일 되지 않고 (오류가 있음) 두 번째는 컴파일됩니다(방금 경고가 표시됨). 그게 차이야.

Eclipse가 데드 코드를 감지하는 이유는 JDK와 달리 이러한 종류의 코드를 감지하기 위해 더 미세 조정할 수 있는 내장 컴파일러가 있는 통합 개발 도구의 편리함입니다.

업데이트 : JDK는 실제로 죽은 코드를 제거합니다.

public class Test {
    public void foo() {
        System.out.println("foo");
        if(true)return;
        System.out.println("foo");
    }
    public void bar() {
        System.out.println("bar");
        if(false)return;
        System.out.println("bar");
    }
}

javap -c 말한다:

공개 클래스 테스트 확장 java.lang.Object{
공개 테스트();
  암호:
   0: 로드_0
   1: 특별 호출 #1; //메서드 java/lang/Object."":()V
   4: 반환

공개 무효 foo();
  암호:
   0: getstatic #2; //필드 java/lang/System.out:Ljava/io/PrintStream;
   3: ldc #3; // 문자열 foo
   5: 가상 #4 호출; //메서드 java/io/PrintStream.println:(Ljava/lang/StrV
   8: 반환

공개 무효 바();
  암호:
   0: getstatic #2; //필드 java/lang/System.out:Ljava/io/PrintStream;
   3: ldc #5; //문자열 막대
   5: 가상 #4 호출; //메서드 java/io/PrintStream.println:(Ljava/lang/String;)V
   8: getstatic #2; //필드 java/lang/System.out:Ljava/io/PrintStream;
   11: ldc #5; //문자열 막대
   13: invokevirtual #4; //메서드 java/io/PrintStream.println:(Ljava/lang/String;)V
   16: 반환

}

그것이 (Sun)이 그것에 대해 경고를주지 않는 이유에 관해서는, 나는 전혀 모릅니다 :) 적어도 JDK 컴파일러에는 실제로 DCE (Dead Code Elimination)가 내장되어 있습니다.


연결할 수 없는 코드는 Java 언어 사양 에 따른 오류 입니다.

JLS에서 인용하려면:

이 아이디어는 명령문이 포함된 생성자, 메서드, 인스턴스 이니셜라이저 또는 정적 이니셜라이저의 시작 부분에서 명령문 자체까지 가능한 실행 경로가 있어야 한다는 것입니다. 분석은 진술의 구조를 고려합니다. 조건식이 상수 값이 true인 while, do 및 for 문의 특수 처리를 제외하고 식의 값은 흐름 분석에서 고려되지 않습니다.

이것이 의미하는 바는 if블록이 고려되지 않는다는 것입니다. if명령문 의 경로 중 하나를 통과 하면 최종 인쇄 명령문에 도달할 수 있기 때문입니다. 코드를 다음과 같이 변경한 경우:

public void foo() {
    System.out.println("Hello");
    if (true)
        return;
    else
        return;
    System.out.println("World!");
}

if마지막 줄에 도달할 수 있는 명령문을 통한 경로가 없기 때문에 갑자기 더 이상 컴파일되지 않습니다 .

즉, Java 호환 컴파일러는 첫 번째 코드 조각을 컴파일할 수 없습니다. JLS를 더 인용하려면:

예를 들어 다음 명령문은 컴파일 시간 오류를 발생시킵니다.

while (false) { x=3; }

왜냐하면 진술 x=3; 연결할 수 없습니다. 그러나 표면적으로 유사한 경우:

if (false) { x=3; }

컴파일 타임 오류가 발생하지 않습니다. 최적화 컴파일러는 명령문 x=3; 실행되지 않으며 생성된 클래스 파일에서 해당 명령문에 대한 코드를 생략하도록 선택할 수 있지만 명령문 x=3; 여기에 명시된 기술적 의미에서 "도달할 수 없는" 것으로 간주되지 않습니다.

Eclipse가 죽은 코드에 대해 제공하는 두 번째 경고는 컴파일러에 의해 생성된 경고로 JLS에 따르면 "도달할 수 없음"이 아니지만 실제로는 그렇습니다. 이것은 Eclipse가 제공 하는 추가 린트 스타일 검사입니다. 이것은 전적으로 선택 사항이며 Eclipse 구성을 사용하여 비활성화하거나 경고 대신 컴파일러 오류로 전환할 수 있습니다.

이 두 번째 블록은 "코드 냄새"입니다. if (false)블록은 일반적으로 디버깅 목적으로 코드를 비활성화하기 위해 삽입되며, 남겨두는 것은 일반적으로 우발적이며 따라서 경고입니다.

사실, Eclipse는 두 경로를 모두 사용할 수 있는지 여부를 결정하기 위해 if 문의 가능한 값을 결정하기 위해 훨씬 더 고급 테스트를 수행합니다. 예를 들어 Eclipse는 다음 메서드에서 데드 코드에 대해 불평할 수도 있습니다.

public void foo() {
    System.out.println("Hello");
    boolean bool = Random.nextBoolean();
    if (bool)
        return;
    if (bool || Random.nextBoolean())
      System.out.println("World!");
}

코드의 이 지점 bool에만 있어야 한다고 추론할 수 있기 때문에 두 번째 if 문에 연결할 수 없는 코드를 생성합니다 false. 이러한 짧은 코드 조각에서 두 개의 if 문이 동일한 것을 테스트하는 것이 분명하지만 중간에 10-15개의 코드 줄이 있으면 더 이상 명확하지 않을 수 있습니다.

요약하면 둘의 차이점은 JLS에서 금지하는 것이고 다른 하나는 금지되어 있지만 Eclipse는 프로그래머에 대한 서비스로 감지합니다.


이것은 일종의 조건부 컴파일 을 허용하기 위한 것입니다 .
에 대한 오류는 if아니지만 컴파일러는 while, do-while및 에 대한 오류 플래그를 지정합니다 for.
괜찮습니다.

if (true) return;    // or false
System.out.println("doing something");

이것은 오류입니다

while (true) {
}
System.out.println("unreachable");

while (false) {
    System.out.println("unreachable");
}

do {
} while (true);
System.out.println("unreachable");

for(;;) {
}
System.out.println("unreachable");

JLS 14.21: Unreachable Statements 끝에 설명되어 있습니다 .

이렇게 다르게 처리하는 이유는 프로그래머가 다음과 같은 "플래그 변수"를 정의할 수 있도록 하기 위함입니다.

 static final boolean DEBUG = false;

다음과 같은 코드를 작성하십시오.

   if (DEBUG) { x=3; }

아이디어는 DEBUG 값을 false에서 true로 또는 true에서 false로 변경한 다음 프로그램 텍스트에 대한 다른 변경 없이 코드를 올바르게 컴파일할 수 있어야 한다는 것입니다.


if (true)좀 더 미묘한 "도달"보다; 그 하드 코딩된 코드 return는 항상 다음 코드에 도달할 if수 없도록 만들지 만 의 조건을 변경 하면 다음 명령문에 도달할 수 있기 때문입니다 .

조건이 있다는 것은 조건이 바뀔 가능성이 있다는 것을 의미합니다. true괄호 안에 있는 것보다 더 복잡한 것이 있고 다음 코드가 "죽었다"는 것이 사람 독자에게 분명하지 않은 경우가 있지만 컴파일러가 알아차리고 이에 대해 경고할 수 있습니다.

Eclipse가 여기에 언급되어 있으며 사용자에게 상황이 조금 더 복잡해 보입니다. 그러나 실제로 Eclipse 아래에는 Eclipse가 켜고 끌 수 있는 경고 등에 대한 스위치가 많이 있는 (매우 정교한) Java 컴파일러가 있습니다. 즉, 직접 javac컴파일 에서 다양한 경고/오류를 얻을 수 없으며 모든 경고/오류 를 켜거나 끌 수 있는 편리한 수단이 없습니다. 그러나 더 많은 종소리와 휘파람이있는 동일한 거래입니다.


이를 해결하는 한 가지 방법은 도달할 수 없는 코드가 실수일 가능성이 가장 높고 JLS가 그러한 실수로부터 사용자를 보호하려고 한다는 것입니다.

if (true) return;실제로 의도적으로 이 작업을 수행하려는 경우 허용 하는 것이 JLS 제한을 해결하는 좋은 방법입니다. JLS가 이것을 멈추면 방해가 될 것입니다. 또한 다음을 중지해야 합니다.

 public static boolean DEBUG = true; //In some global class somewhere else


 ...

 if (DEBUG) return; //in a completely unrelated class.

 ...

DEBUG 상수는 완전히 인라인되어 있고 기능적으로는 if 조건에서 true를 입력하는 것과 동일하기 때문입니다. JLS 관점에서 이 두 경우는 매우 유사합니다.


차이점은 런타임과 컴파일 시간 간의 의미 체계에 있습니다. 두 번째 예에서 코드는 바이트코드의 if-else 분기로 컴파일되고 Eclipse는 런타임에 else 부분에 절대 도달하지 않을 것이라고 말할 만큼 간단합니다. Eclipse는 여전히 합법적인 코드이기 때문에 경고만 합니다.

첫 번째 예에서는 코드가 java의 정의에 따라 불법이기 때문에 오류입니다. 컴파일러는 도달할 수 없는 명령문으로 바이트 코드를 생성하는 것을 허용하지 않습니다.


나는 일식에 대해 몇 가지 시도를 했고 JDK의 데드 코드 처리에는 1) 경고 없음, 2) 경고 및 3) 오류의 3가지 종류가 있다고 생각합니다.

일반적인 "IF" 조건부 컴파일 코드의 경우 JDK는 이를 감지하고 데드 코드로 보고하지 않습니다. 상수 부울 플래그로 인해 발생하는 데드 코드의 경우 JDK는 이를 감지하고 경고 수준에서 보고합니다. 프로그램의 제어 흐름으로 인한 데드 코드의 경우 JDK는 이를 오류로 감지합니다.

아래는 내 시도입니다.

    public class Setting {
        public static final boolean FianlDebugFlag = false;
    }


    class B {
    .....

    // no warn, it is typical "IF" conditional compilataion code
    if(Setting.FianlDebugFlag) 
        System.out.println("am i dead?");   
    if(false) 
        System.out.println("am i dead?");   


    // warn, as the dead code is caused by a constant boolean flag
    if(ret!=null && Setting.FianlDebugFlag) 
        System.out.println("am i dead?");   

    if(Setting.FinalDebug)                  
        return null;                                                            
    System.out.println("am i dea?");        

    // error, as the dead code is due to the program's control flow
    return null;
    System.out.println("am i dead");        
    }

"Eclipse에서 Java의 데드 코드 경고" 경고를 무시하려면 eclipse* 내부에서 다음을 수행하십시오.

  1. Window-Preferences-Java-Compiler-Errors/Warnings를 클릭합니다.
  2. "잠재적 프로그래밍 문제"를 클릭하십시오.
  3. "Dead Code eg if(false)" "무시"를 선택하십시오.
  4. 적용 클릭
  5. 확인을 클릭하십시오

eclipse IDE 저장 및 닫기 eclipse를 다시 열면 이러한 특정 경고가 더 이상 나열되지 않습니다.

*이 예제 솔루션의 경우 Java 개발자용 Eclipse IDE - 버전: Mars.2 릴리스(4.5.2)를 사용하고 있습니다.

ReferenceURL : https://stackoverflow.com/questions/2141029/unreachable-code-error-vs-dead-code-warning-in-java-under-eclipse

반응형