RetainCountChecker | Assertion Failure | check-clang-analysis

Dear all,

I am currently trying to suppress diagnostics emitted if the function under consideration has a certain annotate attribute (“check_attribute_annotate” in this case).

Hence, for that, I added the following piece of code to RetainCountChecker.cpp but it is resulting in some assertion failures while performing make -j4 check-clang-analysis.

diff --git a/lib/StaticAnalyzer/Checkers/RetainCountChecker.cpp b/lib/StaticAnalyzer/Checkers/RetainCountChecker.cpp
index 89b1291…9f367be 100644
— a/lib/StaticAnalyzer/Checkers/RetainCountChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/RetainCountChecker.cpp
@@ -1894,6 +1894,20 @@ static bool isSynthesizedAccessor(const StackFrameContext *SFC) {
return SFC->getAnalysisDeclContext()->isBodyAutosynthesized();
}

+bool
+isAnnotatedToSkipDiagnostics(const ExplodedNode *EN) {

  • const FunctionDecl *FD = cast(&EN->getCodeDecl());
  • const IdentifierInfo *II = FD->getIdentifier();

Uhm, why does this thing return `const Decl &` rather than `const Decl *`? Anyway, whatever, never mind.

You're crashing in...

   cast<FunctionDecl>(&EN->getCodeDecl())

...because this Decl returned by `EN->getCodeDecl()` isn't a FunctionDecl.

You can easily see this from the backtrace:

Assertion failed: (isa<X>(Val) && "cast<Ty>() argument of incompatible type!"), function cast, file (...censored...)/llvm/include/llvm/Support/Casting.h, line 254.
0 libLLVMSupport.dylib 0x00000001148fb8fc llvm::sys::PrintStackTrace(llvm::raw_ostream&) + 60
1 libLLVMSupport.dylib 0x00000001148fbec9 PrintStackTraceSignalHandler(void*) + 25
2 libLLVMSupport.dylib 0x00000001148f7b59 llvm::sys::RunSignalHandlers() + 425
3 libLLVMSupport.dylib 0x00000001148fc212 SignalHandler(int) + 354
4 libsystem_platform.dylib 0x00007fffd872cb3a _sigtramp + 26
5 libclangStaticAnalyzerCore.dylib 0x0000000122060c7b llvm::alignAddr(void const*, unsigned long) + 43
6 libsystem_c.dylib 0x00007fffd85b1420 abort + 129
7 libsystem_c.dylib 0x00007fffd8578893 basename_r + 0
8 libclangStaticAnalyzerCheckers.dylib 0x00000001202e6fe7 llvm::cast_retty<clang::FunctionDecl, clang::Decl const*>::ret_type llvm::cast<clang::FunctionDecl, clang::Decl const>(clang::Decl const*) + 103
9 libclangStaticAnalyzerCheckers.dylib 0x00000001208b7aa6 isAnnotatedToSkipDiagnostics(clang::ento::ExplodedNode const*) + 38
(...more stuff...)

You're calling llvm::cast<> (frame 8) from your new function (frame 9), the only cast you have is `cast<FunctionDecl>(&EN->getCodeDecl())`, and the assertion message says that the argument is of incompatible type.

If you want to know what specific decl causes a problem, you can print it in the debugger:

$ lldb -- (...censored...)/debug/./bin/clang -cc1 -internal-isystem (...censored...)/debug/lib/clang/5.0.0/include -nostdsysteminc -analyze -analyzer-constraints=range -analyzer-checker=core,osx.cocoa.RetainCount,osx.cocoa.Dealloc,debug.ExprInspection -analyzer-store=region -verify -Wno-objc-root-class (...censored...)/llvm/tools/clang/test/Analysis/properties.m

(lldb) bt
* thread #1, queue = 'com.apple.main-thread', stop reason = signal SIGABRT
     frame #0: 0x00007fffd864bd42 libsystem_kernel.dylib`__pthread_kill + 10
     frame #1: 0x00007fffd8739787 libsystem_pthread.dylib`pthread_kill + 90
     frame #2: 0x00007fffd85b1420 libsystem_c.dylib`abort + 129
     frame #3: 0x00007fffd8578893 libsystem_c.dylib`__assert_rtn + 320
     frame #4: 0x0000000114983fe7 libclangStaticAnalyzerCheckers.dylib`llvm::cast_retty<clang::FunctionDecl, clang::Decl const*>::ret_type llvm::cast<clang::FunctionDecl, clang::Decl const>(Val=0x00000001170ab5c0) at Casting.h:254
   * frame #5: 0x0000000114f54aa6 libclangStaticAnalyzerCheckers.dylib`isAnnotatedToSkipDiagnostics(EN=0x0000000118815ee0) at RetainCountChecker.cpp:1904
(...more stuff...)

(lldb) f 5
frame #5: 0x0000000114f54aa6 libclangStaticAnalyzerCheckers.dylib`isAnnotatedToSkipDiagnostics(EN=0x0000000118815ee0) at RetainCountChecker.cpp:1904
    1901
    1902 bool
    1903 isAnnotatedToSkipDiagnostics(const ExplodedNode *EN) {
-> 1904 const FunctionDecl *FD = cast<FunctionDecl>(&EN->getCodeDecl());
    1905 const IdentifierInfo *II = FD->getIdentifier();
    1906
    1907 if (II) {

(lldb) p EN->getCodeDecl().dump()
ObjCMethodDecl 0x1170ab5c0 <(...censored...)/llvm/tools/clang/test/Analysis/properties.m:559:1, line:563:1> line:559:1 - testRetainAndRelease 'void'
(...more stuff...)

Note that frames are shifted in the debugger because it stops on the moment when the assertion has failed, so we're on frame 5 instead of frame 9 now.

If you use gdb instead of lldb, just replace "lldb --" with "gdb --args" in the first command, the rest is the same. If you use an IDE, then you should know better how to debug this.

So in this case it's ObjCMethodDecl, and Objective-C methods apparently aren't functions. I'd also worry about BlockDecl which may also appear here.

You should use dyn_cast<> when you're not sure if the cast succeeds - it'd return nullptr instead of crashing, and you can handle it.