2017-11-14 13 views
2

PVS-Studio를 사용하여 테스트 코드를 분석하고 있습니다. 거기는 종종 나는 아직도 마지막 줄에 대한 경고 V522 There might be dereferencing of a potential null pointer 'animal'를 얻을 그러나 형태PVS-Studio의 BOOST_REQUIRE 이후 변수에 NULL이 표시되지 않습니다.

const noAnimal* animal = dynamic_cast<noAnimal*>(...); 
BOOST_REQUIRE(animal); 
BOOST_REQUIRE_EQUAL(animal->GetSpecies(), ...); 

으로 구성한다.

내가 그것을 가능 알고있다 "NULL 반환하지"등의 기능을 표시하지만 유효한 NULL 체크 등의 기능을 표시하거나 animalBOOST_REQUIRE(animal); 후 NULL이 될 수 없음을 어떻게 든 다른 인식 PVS-Studio를하는 것도 가능합니다 ?

assert 향미료를 통해 포인터를 먼저 검사하는 경우에도 마찬가지입니다.

답변

1

흥미로운 사례를 가져 주셔서 감사합니다. 우리는 BOOST_REQUIRE 매크로로 무엇을 할 수 있는지 생각할 것입니다.

어딘가에 당신이 쓸 수

#include <boost/test/included/unit_test.hpp> 

후 :

순간

, 내가 당신에게 다음과 같은 솔루션을 조언 해 줄 수 있습니다

#ifdef PVS_STUDIO 
    #undef BOOST_REQUIRE 
    #define BOOST_REQUIRE(expr) do { if (!(expr)) throw "PVS-Studio"; } while (0) 
#endif 

이 방법을, 당신은에 대한 힌트를 줄 것이다 분석기에서 잘못된 조건이 제어 흐름의 중단을 일으키는 지 확인합니다. 그것은 가장 아름다운 해결책은 아니지만 여러분에게 이야기 할 가치가 있다고 생각합니다. 큰 일에 코멘트에 응답

+0

답장을 보내 주셔서 감사합니다. 이것이 가능할지라도, 모든 testcase 파일에 그 정의를 포함시키는 것은 어려울 것입니다. 또한 이것은 BOOST_REQUIRE에만 국한되지 않고'assert','SDL_Assert' 또는 사용자가 사용할 수있는 다른 커스텀 매크로에도 적용됩니다. 그래서 나는 두 가지 해결책을 보았습니다 : 사용자가 "assert"-macro 이름의 목록을 지정하도록하십시오. 그 다음에는 조건이 true로 가정됩니다 (이것이 assert의 이유 임). – Flamefire

+0

최소한, PVS에 의해 읽혀지고 ('cpp.hint'와 유사한) 사용되는 것으로 가정되는 정의를 가진 글로벌 PVS 전용 파일을 가지고 있어야합니다. 따라서 이러한 정의를 중앙 위치에 배치 할 수 있습니다. 그러나 이것은 포함 된 모든 파일에서 정의가 수행 될 수 있기 때문에 어렵다고 생각합니다. – Flamefire

1

는, 그래서 여기에 나쁜 생각 다음과 같은 주제에 내 상세한 응답이다 :이 비록

는 그 모든 에 정의 포함 통증이있을 것입니다 수 있습니다 테스트 케이스 파일. 또한 BOOST_REQUIRE에만 국한되지 않고 은 assert, SDL_Assert 또는 사용자 이 사용할 수있는 다른 사용자 정의 매크로에도 적용됩니다.

테스트 매크로에는 세 가지 유형이 있으며 각각 별도로 논의해야한다는 것을 이해해야합니다.

첫 번째 유형의 매크로는 디버그 버전에서 문제가 있음을 경고합니다. 일반적인 예는 assert 매크로입니다. 다음 코드는 경고를 생성하는 PVS-스튜디오 분석기가 발생합니다

T* p = dynamic_cast<T *>(x); 
assert(p); 
p->foo(); 

분석기는 여기에 역 참조 가능한 널 포인터를 지적하고 바로 될 것입니다. assert을 사용하는 검사는 릴리스 버전에서 제거되므로 충분하지 않습니다. 즉, 수표가없는 것으로 판명됩니다. 이를 구현하는 더 좋은 방법은 다음과 같이 코드를 다시 작성하는 것입니다.

T* p = dynamic_cast<T *>(x); 
if (p == nullptr) 
{ 
    assert(false); 
    throw Error; 
} 
p->foo(); 

이 코드는 경고를 트리거하지 않습니다.

dynamic_cast이 결코 nullptr을 반환하지 않는다고 100 % 확신 할 수 있습니다. 나는이 주장을 받아들이지 않는다. 캐스트가 항상 정확한지 확신하는 경우 더 빨리 static_cast을 사용해야합니다. 확실하지 않은 경우 포인터를 역 참조하기 전에 포인터를 테스트해야합니다.

글쎄, 알았어, 네가 요점을 알아. 코드는 괜찮은 것으로 확신하지만 dynamic_cast를 사용하여 확인해야합니다.

assert(dynamic_cast<T *>(x) != nullptr); 
T* p = static_cast<T *>(x); 
p->foo(); 

나는 그것을 좋아하지 않아,하지만 느린 dynamic_cast는 운영자가 출시 버전에서 남아있을 것입니다 때문에 분석기는 침묵을 유지합니다 동안 적어도는 빠르다 : OK, 다음 코드를 사용합니다.

다음 유형의 매크로로 이동합니다.

두 번째 유형의 매크로는 디버그 버전에서 문제가 발생했음을 경고하고 테스트에 사용됩니다. 이전 유형과 다른 점은 조건이 거짓이고 오류 메시지가 생성되면 테스트중인 알고리즘을 중지한다는 것입니다.

이러한 매크로의 기본 문제는 함수가 non-returning로 표시되어 있지 않다는 것입니다. 여기에 예제가 있습니다.

예외를 throw하여 오류 메시지를 생성하는 함수가 있다고 가정 해보십시오.

void Error(const char *message); 

그리고 이것이 시험 매크로 선언 방법은 다음과 같습니다 :

T* p = dynamic_cast<T *>(x); 
ENSURE(p); 
p->foo(); 

분석기에 대한 경고를 발행합니다 :

#define ENSURE(x) do { if (!x) Error("zzzz"); } while (0) 

포인터를 사용하여이 선언의 모습입니다 가능한 null 포인터 역 참조이지만 코드는 실제로 안전합니다. 포인터가 null 인 경우 Error 함수는 예외를 throw하여 포인터 역 참조를 방지합니다.

우리는 단순히 예를 들어, 함수 주석 수단 중 하나를 사용하여 그것에 대해 분석기를 알려줄 필요가 :

[[noreturn]] void Error(const char *message); 

나 :

__declspec(noreturn) void Error(const char *message); 

이 거짓 경고를 제거하는 데 도움이됩니다. 따라서 알 수 있듯이 대부분의 경우 매크로를 사용할 때 문제를 쉽게 해결할 수 있습니다.

그러나 제 3 자 라이브러리에서 부주의하게 구현 된 매크로를 처리하는 경우 더 어려울 수 있습니다.

이렇게하면 세 번째 유형의 매크로가 생깁니다. 이들을 변경할 수 없으며, 분석기는 정확하게 작동하는 방법을 알 수 없습니다. 이것은 매크로가 상당히 이국적인 방식으로 구현 될 수 있기 때문에 일반적인 상황입니다.

이 경우에 당신을 위해 왼쪽 세 가지 옵션이 있습니다 :

  1. 는 위양성 억제 중 하나 documentation에 설명 된 수단을 사용하여 경고를 억제가;
  2. 은 이전의 answer에서 설명한 기술을 사용합니다.
  3. 이메일 문의

인기있는 라이브러리에서 다양한 까다로운 매크로를 점차적으로 지원하고 있습니다.사실, 분석기는 여러분이 만날 수있는 대부분의 특정 매크로에 이미 익숙하지만 프로그래머의 상상력은 무한하며 모든 가능한 구현을 미리 예측할 수는 없습니다.

+0

나는 dynamic_cast 이야기에 동의하지 않습니다. 먼저 dynamic_cast (다중 상속 ...)가 필요할 수도 있지만 실패하지 않을 수도 있습니다 (예 : 가상 함수가 형식을 반환 함). 또는 당신이 그것을 주장 2는 유효하지만, 대신 UD의 릴리스 모드에서 널 포인터 - 예외를 얻을 수 있도록 여전히 dynamic_cast는을 유지합니다. 이것은 여분의 수표를 가지고 있고 (해야한다) 불가능한 경우를 던지는 것보다 낫다. (예를 들어, 함수 유형을 반환, 그래서 스트해야하지만 당신은 그 함수의 C & P 오류를 방지하려면 까다로운 매크로 :. 그건 내가 구성 옵션을 제안하는 이유 – Flamefire