[analyzer][NFC] Display the correct function name even in crash dumps
authorBalazs Benics <balazs.benics@sigmatechnology.se>
Mon, 12 Jul 2021 07:06:46 +0000 (09:06 +0200)
committerBalazs Benics <balazs.benics@sigmatechnology.se>
Mon, 12 Jul 2021 07:06:46 +0000 (09:06 +0200)
The `-analyzer-display-progress` displayed the function name of the
currently analyzed function. It differs in C and C++. In C++, it
prints the argument types as well in a comma-separated list.
While in C, only the function name is displayed, without the brackets.
E.g.:

  C++: foo(), foo(int, float)
  C:   foo

In crash traces, the analyzer dumps the location contexts, but the
string is not enough for `-analyze-function` in C++ mode.
This patch addresses the issue by dumping the proper function names
even in stack traces.

Reviewed By: NoQ

Differential Revision: https://reviews.llvm.org/D105708

clang/include/clang/Analysis/AnalysisDeclContext.h
clang/lib/Analysis/AnalysisDeclContext.cpp
clang/lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp
clang/test/Analysis/crash-trace.c

index d12582f..102970a 100644 (file)
@@ -200,6 +200,8 @@ public:
   /// \returns Whether the root namespace of \p D is the \c std C++ namespace.
   static bool isInStdNamespace(const Decl *D);
 
+  static std::string getFunctionName(const Decl *D);
+
 private:
   std::unique_ptr<ManagedAnalysis> &getAnalysisImpl(const void *tag);
 
index 783de64..d8466ac 100644 (file)
@@ -337,6 +337,59 @@ bool AnalysisDeclContext::isInStdNamespace(const Decl *D) {
   return ND->isStdNamespace();
 }
 
+std::string AnalysisDeclContext::getFunctionName(const Decl *D) {
+  std::string Str;
+  llvm::raw_string_ostream OS(Str);
+  const ASTContext &Ctx = D->getASTContext();
+
+  if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(D)) {
+    OS << FD->getQualifiedNameAsString();
+
+    // In C++, there are overloads.
+
+    if (Ctx.getLangOpts().CPlusPlus) {
+      OS << '(';
+      for (const auto &P : FD->parameters()) {
+        if (P != *FD->param_begin())
+          OS << ", ";
+        OS << P->getType().getAsString();
+      }
+      OS << ')';
+    }
+
+  } else if (isa<BlockDecl>(D)) {
+    PresumedLoc Loc = Ctx.getSourceManager().getPresumedLoc(D->getLocation());
+
+    if (Loc.isValid()) {
+      OS << "block (line: " << Loc.getLine() << ", col: " << Loc.getColumn()
+         << ')';
+    }
+
+  } else if (const ObjCMethodDecl *OMD = dyn_cast<ObjCMethodDecl>(D)) {
+
+    // FIXME: copy-pasted from CGDebugInfo.cpp.
+    OS << (OMD->isInstanceMethod() ? '-' : '+') << '[';
+    const DeclContext *DC = OMD->getDeclContext();
+    if (const auto *OID = dyn_cast<ObjCImplementationDecl>(DC)) {
+      OS << OID->getName();
+    } else if (const auto *OID = dyn_cast<ObjCInterfaceDecl>(DC)) {
+      OS << OID->getName();
+    } else if (const auto *OC = dyn_cast<ObjCCategoryDecl>(DC)) {
+      if (OC->IsClassExtension()) {
+        OS << OC->getClassInterface()->getName();
+      } else {
+        OS << OC->getIdentifier()->getNameStart() << '('
+           << OC->getIdentifier()->getNameStart() << ')';
+      }
+    } else if (const auto *OCD = dyn_cast<ObjCCategoryImplDecl>(DC)) {
+      OS << OCD->getClassInterface()->getName() << '(' << OCD->getName() << ')';
+    }
+    OS << ' ' << OMD->getSelector().getAsString() << ']';
+  }
+
+  return OS.str();
+}
+
 LocationContextManager &AnalysisDeclContext::getLocationContextManager() {
   assert(
       ADCMgr &&
@@ -456,7 +509,7 @@ void LocationContext::dumpStack(raw_ostream &Out) const {
       Out << "\t#" << Frame << ' ';
       ++Frame;
       if (const auto *D = dyn_cast<NamedDecl>(LCtx->getDecl()))
-        Out << "Calling " << D->getQualifiedNameAsString();
+        Out << "Calling " << AnalysisDeclContext::getFunctionName(D);
       else
         Out << "Calling anonymous code";
       if (const Stmt *S = cast<StackFrameContext>(LCtx)->getCallSite()) {
index 33914e9..03b5c04 100644 (file)
@@ -209,8 +209,8 @@ public:
       } else
         assert(Mode == (AM_Syntax | AM_Path) && "Unexpected mode!");
 
-      llvm::errs() << ": " << Loc.getFilename() << ' ' << getFunctionName(D)
-                   << '\n';
+      llvm::errs() << ": " << Loc.getFilename() << ' '
+                   << AnalysisDeclContext::getFunctionName(D) << '\n';
     }
   }
 
@@ -568,63 +568,10 @@ void AnalysisConsumer::HandleTranslationUnit(ASTContext &C) {
   Mgr.reset();
 }
 
-std::string AnalysisConsumer::getFunctionName(const Decl *D) {
-  std::string Str;
-  llvm::raw_string_ostream OS(Str);
-
-  if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(D)) {
-    OS << FD->getQualifiedNameAsString();
-
-    // In C++, there are overloads.
-    if (Ctx->getLangOpts().CPlusPlus) {
-      OS << '(';
-      for (const auto &P : FD->parameters()) {
-        if (P != *FD->param_begin())
-          OS << ", ";
-        OS << P->getType().getAsString();
-      }
-      OS << ')';
-    }
-
-  } else if (isa<BlockDecl>(D)) {
-    PresumedLoc Loc = Ctx->getSourceManager().getPresumedLoc(D->getLocation());
-
-    if (Loc.isValid()) {
-      OS << "block (line: " << Loc.getLine() << ", col: " << Loc.getColumn()
-         << ')';
-    }
-
-  } else if (const ObjCMethodDecl *OMD = dyn_cast<ObjCMethodDecl>(D)) {
-
-    // FIXME: copy-pasted from CGDebugInfo.cpp.
-    OS << (OMD->isInstanceMethod() ? '-' : '+') << '[';
-    const DeclContext *DC = OMD->getDeclContext();
-    if (const auto *OID = dyn_cast<ObjCImplementationDecl>(DC)) {
-      OS << OID->getName();
-    } else if (const auto *OID = dyn_cast<ObjCInterfaceDecl>(DC)) {
-      OS << OID->getName();
-    } else if (const auto *OC = dyn_cast<ObjCCategoryDecl>(DC)) {
-      if (OC->IsClassExtension()) {
-        OS << OC->getClassInterface()->getName();
-      } else {
-        OS << OC->getIdentifier()->getNameStart() << '('
-           << OC->getIdentifier()->getNameStart() << ')';
-      }
-    } else if (const auto *OCD = dyn_cast<ObjCCategoryImplDecl>(DC)) {
-      OS << OCD->getClassInterface()->getName() << '('
-         << OCD->getName() << ')';
-    }
-    OS << ' ' << OMD->getSelector().getAsString() << ']';
-
-  }
-
-  return OS.str();
-}
-
 AnalysisConsumer::AnalysisMode
 AnalysisConsumer::getModeForDecl(Decl *D, AnalysisMode Mode) {
   if (!Opts->AnalyzeSpecificFunction.empty() &&
-      getFunctionName(D) != Opts->AnalyzeSpecificFunction)
+      AnalysisDeclContext::getFunctionName(D) != Opts->AnalyzeSpecificFunction)
     return AM_None;
 
   // Unless -analyze-all is specified, treat decls differently depending on
index 0c8c8cc..f00db3a 100644 (file)
@@ -1,4 +1,7 @@
-// RUN: not --crash %clang_analyze_cc1 -analyzer-checker=debug.ExprInspection %s 2>&1 | FileCheck %s
+// RUN: not --crash %clang_analyze_cc1 -analyzer-checker=debug.ExprInspection \
+// RUN:   -x c   %s 2>&1 | FileCheck %s --check-prefixes=CHECK,CHECK-C-ONLY
+// RUN: not --crash %clang_analyze_cc1 -analyzer-checker=debug.ExprInspection \
+// RUN:   -x c++ %s 2>&1 | FileCheck %s --check-prefixes=CHECK,CHECK-CXX-ONLY
 // REQUIRES: crash-recovery
 
 // Stack traces require back traces.
@@ -6,17 +9,22 @@
 
 void clang_analyzer_crash(void);
 
-void inlined() {
+void inlined(int x, float y) {
   clang_analyzer_crash();
 }
 
 void test() {
-  inlined();
+  inlined(0, 0);
 }
 
-// CHECK: 0.   Program arguments: {{.*}}clang
-// CHECK-NEXT: 1.      <eof> parser at end of file
-// CHECK-NEXT: 2. While analyzing stack: 
-// CHECK-NEXT:  #0 Calling inlined at line 14
-// CHECK-NEXT:  #1 Calling test
-// CHECK-NEXT: 3.      {{.*}}crash-trace.c:{{[0-9]+}}:3: Error evaluating statement
+// CHECK:                0. Program arguments: {{.*}}clang
+// CHECK-NEXT:           1. <eof> parser at end of file
+// CHECK-NEXT:           2. While analyzing stack:
+//
+// CHECK-C-ONLY-NEXT:       #0 Calling inlined at line 17
+// CHECK-C-ONLY-NEXT:       #1 Calling test
+//
+// CHECK-CXX-ONLY-NEXT:     #0 Calling inlined(int, float) at line 17
+// CHECK-CXX-ONLY-NEXT:     #1 Calling test()
+//
+// CHECK-NEXT:           3. {{.*}}crash-trace.c:{{[0-9]+}}:3: Error evaluating statement