diff --git a/clang/docs/analyzer/checkers.rst b/clang/docs/analyzer/checkers.rst --- a/clang/docs/analyzer/checkers.rst +++ b/clang/docs/analyzer/checkers.rst @@ -1890,40 +1890,6 @@ } -alpha.deadcode -^^^^^^^^^^^^^^ -.. _alpha-deadcode-UnreachableCode: - -alpha.deadcode.UnreachableCode (C, C++) -""""""""""""""""""""""""""""""""""""""" -Check unreachable code. - -.. code-block:: cpp - - // C - int test() { - int x = 1; - while(x); - return x; // warn - } - - // C++ - void test() { - int a = 2; - - while (a > 1) - a--; - - if (a > 1) - a++; // warn - } - - // Objective-C - void test(id x) { - return; - [x retain]; // warn - } - alpha.fuchsia ^^^^^^^^^^^^^ diff --git a/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td b/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td --- a/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td +++ b/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td @@ -844,14 +844,6 @@ } // end DeadCode -let ParentPackage = DeadCodeAlpha in { - -def UnreachableCodeChecker : Checker<"UnreachableCode">, - HelpText<"Check unreachable code">, - Documentation; - -} // end "alpha.deadcode" - //===----------------------------------------------------------------------===// // Performance checkers. //===----------------------------------------------------------------------===// diff --git a/clang/lib/StaticAnalyzer/Checkers/CMakeLists.txt b/clang/lib/StaticAnalyzer/Checkers/CMakeLists.txt --- a/clang/lib/StaticAnalyzer/Checkers/CMakeLists.txt +++ b/clang/lib/StaticAnalyzer/Checkers/CMakeLists.txt @@ -122,7 +122,6 @@ UninitializedObject/UninitializedObjectChecker.cpp UninitializedObject/UninitializedPointee.cpp UnixAPIChecker.cpp - UnreachableCodeChecker.cpp VforkChecker.cpp VLASizeChecker.cpp ValistChecker.cpp diff --git a/clang/lib/StaticAnalyzer/Checkers/UnreachableCodeChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/UnreachableCodeChecker.cpp deleted file mode 100644 --- a/clang/lib/StaticAnalyzer/Checkers/UnreachableCodeChecker.cpp +++ /dev/null @@ -1,262 +0,0 @@ -//==- UnreachableCodeChecker.cpp - Generalized dead code checker -*- C++ -*-==// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -// This file implements a generalized unreachable code checker using a -// path-sensitive analysis. We mark any path visited, and then walk the CFG as a -// post-analysis to determine what was never visited. -// -// A similar flow-sensitive only check exists in Analysis/ReachableCode.cpp -//===----------------------------------------------------------------------===// - -#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" -#include "clang/AST/ParentMap.h" -#include "clang/Basic/Builtins.h" -#include "clang/Basic/SourceManager.h" -#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" -#include "clang/StaticAnalyzer/Core/Checker.h" -#include "clang/StaticAnalyzer/Core/CheckerManager.h" -#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" -#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerHelpers.h" -#include "clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h" -#include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h" -#include "llvm/ADT/SmallSet.h" - -using namespace clang; -using namespace ento; - -namespace { -class UnreachableCodeChecker : public Checker { -public: - void checkEndAnalysis(ExplodedGraph &G, BugReporter &B, - ExprEngine &Eng) const; -private: - typedef llvm::SmallSet CFGBlocksSet; - - static inline const Stmt *getUnreachableStmt(const CFGBlock *CB); - static void FindUnreachableEntryPoints(const CFGBlock *CB, - CFGBlocksSet &reachable, - CFGBlocksSet &visited); - static bool isInvalidPath(const CFGBlock *CB, const ParentMap &PM); - static inline bool isEmptyCFGBlock(const CFGBlock *CB); -}; -} - -void UnreachableCodeChecker::checkEndAnalysis(ExplodedGraph &G, - BugReporter &B, - ExprEngine &Eng) const { - CFGBlocksSet reachable, visited; - - if (Eng.hasWorkRemaining()) - return; - - const Decl *D = nullptr; - CFG *C = nullptr; - const ParentMap *PM = nullptr; - const LocationContext *LC = nullptr; - // Iterate over ExplodedGraph - for (ExplodedGraph::node_iterator I = G.nodes_begin(), E = G.nodes_end(); - I != E; ++I) { - const ProgramPoint &P = I->getLocation(); - LC = P.getLocationContext(); - if (!LC->inTopFrame()) - continue; - - if (!D) - D = LC->getAnalysisDeclContext()->getDecl(); - - // Save the CFG if we don't have it already - if (!C) - C = LC->getAnalysisDeclContext()->getUnoptimizedCFG(); - if (!PM) - PM = &LC->getParentMap(); - - if (Optional BE = P.getAs()) { - const CFGBlock *CB = BE->getBlock(); - reachable.insert(CB->getBlockID()); - } - } - - // Bail out if we didn't get the CFG or the ParentMap. - if (!D || !C || !PM) - return; - - // Don't do anything for template instantiations. Proving that code - // in a template instantiation is unreachable means proving that it is - // unreachable in all instantiations. - if (const FunctionDecl *FD = dyn_cast(D)) - if (FD->isTemplateInstantiation()) - return; - - // Find CFGBlocks that were not covered by any node - for (CFG::const_iterator I = C->begin(), E = C->end(); I != E; ++I) { - const CFGBlock *CB = *I; - // Check if the block is unreachable - if (reachable.count(CB->getBlockID())) - continue; - - // Check if the block is empty (an artificial block) - if (isEmptyCFGBlock(CB)) - continue; - - // Find the entry points for this block - if (!visited.count(CB->getBlockID())) - FindUnreachableEntryPoints(CB, reachable, visited); - - // This block may have been pruned; check if we still want to report it - if (reachable.count(CB->getBlockID())) - continue; - - // Check for false positives - if (isInvalidPath(CB, *PM)) - continue; - - // It is good practice to always have a "default" label in a "switch", even - // if we should never get there. It can be used to detect errors, for - // instance. Unreachable code directly under a "default" label is therefore - // likely to be a false positive. - if (const Stmt *label = CB->getLabel()) - if (label->getStmtClass() == Stmt::DefaultStmtClass) - continue; - - // Special case for __builtin_unreachable. - // FIXME: This should be extended to include other unreachable markers, - // such as llvm_unreachable. - if (!CB->empty()) { - bool foundUnreachable = false; - for (CFGBlock::const_iterator ci = CB->begin(), ce = CB->end(); - ci != ce; ++ci) { - if (Optional S = (*ci).getAs()) - if (const CallExpr *CE = dyn_cast(S->getStmt())) { - if (CE->getBuiltinCallee() == Builtin::BI__builtin_unreachable || - CE->isBuiltinAssumeFalse(Eng.getContext())) { - foundUnreachable = true; - break; - } - } - } - if (foundUnreachable) - continue; - } - - // We found a block that wasn't covered - find the statement to report - SourceRange SR; - PathDiagnosticLocation DL; - SourceLocation SL; - if (const Stmt *S = getUnreachableStmt(CB)) { - // In macros, 'do {...} while (0)' is often used. Don't warn about the - // condition 0 when it is unreachable. - if (S->getBeginLoc().isMacroID()) - if (const auto *I = dyn_cast(S)) - if (I->getValue() == 0ULL) - if (const Stmt *Parent = PM->getParent(S)) - if (isa(Parent)) - continue; - SR = S->getSourceRange(); - DL = PathDiagnosticLocation::createBegin(S, B.getSourceManager(), LC); - SL = DL.asLocation(); - if (SR.isInvalid() || !SL.isValid()) - continue; - } - else - continue; - - // Check if the SourceLocation is in a system header - const SourceManager &SM = B.getSourceManager(); - if (SM.isInSystemHeader(SL) || SM.isInExternCSystemHeader(SL)) - continue; - - B.EmitBasicReport(D, this, "Unreachable code", categories::UnusedCode, - "This statement is never executed", DL, SR); - } -} - -// Recursively finds the entry point(s) for this dead CFGBlock. -void UnreachableCodeChecker::FindUnreachableEntryPoints(const CFGBlock *CB, - CFGBlocksSet &reachable, - CFGBlocksSet &visited) { - visited.insert(CB->getBlockID()); - - for (CFGBlock::const_pred_iterator I = CB->pred_begin(), E = CB->pred_end(); - I != E; ++I) { - if (!*I) - continue; - - if (!reachable.count((*I)->getBlockID())) { - // If we find an unreachable predecessor, mark this block as reachable so - // we don't report this block - reachable.insert(CB->getBlockID()); - if (!visited.count((*I)->getBlockID())) - // If we haven't previously visited the unreachable predecessor, recurse - FindUnreachableEntryPoints(*I, reachable, visited); - } - } -} - -// Find the Stmt* in a CFGBlock for reporting a warning -const Stmt *UnreachableCodeChecker::getUnreachableStmt(const CFGBlock *CB) { - for (CFGBlock::const_iterator I = CB->begin(), E = CB->end(); I != E; ++I) { - if (Optional S = I->getAs()) { - if (!isa(S->getStmt())) - return S->getStmt(); - } - } - if (const Stmt *S = CB->getTerminatorStmt()) - return S; - else - return nullptr; -} - -// Determines if the path to this CFGBlock contained an element that infers this -// block is a false positive. We assume that FindUnreachableEntryPoints has -// already marked only the entry points to any dead code, so we need only to -// find the condition that led to this block (the predecessor of this block.) -// There will never be more than one predecessor. -bool UnreachableCodeChecker::isInvalidPath(const CFGBlock *CB, - const ParentMap &PM) { - // We only expect a predecessor size of 0 or 1. If it is >1, then an external - // condition has broken our assumption (for example, a sink being placed by - // another check). In these cases, we choose not to report. - if (CB->pred_size() > 1) - return true; - - // If there are no predecessors, then this block is trivially unreachable - if (CB->pred_size() == 0) - return false; - - const CFGBlock *pred = *CB->pred_begin(); - if (!pred) - return false; - - // Get the predecessor block's terminator condition - const Stmt *cond = pred->getTerminatorCondition(); - - //assert(cond && "CFGBlock's predecessor has a terminator condition"); - // The previous assertion is invalid in some cases (eg do/while). Leaving - // reporting of these situations on at the moment to help triage these cases. - if (!cond) - return false; - - // Run each of the checks on the conditions - return containsMacro(cond) || containsEnum(cond) || - containsStaticLocal(cond) || containsBuiltinOffsetOf(cond) || - containsStmt(cond); -} - -// Returns true if the given CFGBlock is empty -bool UnreachableCodeChecker::isEmptyCFGBlock(const CFGBlock *CB) { - return CB->getLabel() == nullptr // No labels - && CB->size() == 0 // No statements - && !CB->getTerminatorStmt(); // No terminator -} - -void ento::registerUnreachableCodeChecker(CheckerManager &mgr) { - mgr.registerChecker(); -} - -bool ento::shouldRegisterUnreachableCodeChecker(const CheckerManager &mgr) { - return true; -} diff --git a/clang/test/Analysis/malloc-annotations.c b/clang/test/Analysis/malloc-annotations.c --- a/clang/test/Analysis/malloc-annotations.c +++ b/clang/test/Analysis/malloc-annotations.c @@ -1,10 +1,12 @@ // RUN: %clang_analyze_cc1 -analyzer-store=region -verify \ // RUN: -analyzer-checker=core \ -// RUN: -analyzer-checker=alpha.deadcode.UnreachableCode \ +// RUN: -analyzer-checker=debug.ExprInspection \ // RUN: -analyzer-checker=alpha.core.CastSize \ // RUN: -analyzer-checker=unix.Malloc \ // RUN: -analyzer-config unix.DynamicMemoryModeling:Optimistic=true %s +void clang_analyzer_warnIfReached(void); + typedef __typeof(sizeof(int)) size_t; void *malloc(size_t); void free(void *); @@ -262,7 +264,8 @@ char *buf = calloc(2,2); char result = buf[3]; // no-warning if (buf[1] != 0) { - free(buf); // expected-warning{{never executed}} + clang_analyzer_warnIfReached(); // no-warning + free(buf); } return result; // expected-warning{{Potential leak of memory pointed to by}} } diff --git a/clang/test/Analysis/malloc-annotations.cpp b/clang/test/Analysis/malloc-annotations.cpp --- a/clang/test/Analysis/malloc-annotations.cpp +++ b/clang/test/Analysis/malloc-annotations.cpp @@ -1,6 +1,5 @@ // RUN: %clang_analyze_cc1 -analyzer-store=region -verify \ // RUN: -analyzer-checker=core \ -// RUN: -analyzer-checker=alpha.deadcode.UnreachableCode \ // RUN: -analyzer-checker=alpha.core.CastSize \ // RUN: -analyzer-checker=unix.Malloc \ // RUN: -analyzer-config unix.DynamicMemoryModeling:Optimistic=true %s diff --git a/clang/test/Analysis/malloc.c b/clang/test/Analysis/malloc.c --- a/clang/test/Analysis/malloc.c +++ b/clang/test/Analysis/malloc.c @@ -1,6 +1,6 @@ // RUN: %clang_analyze_cc1 -Wno-strict-prototypes -Wno-error=implicit-int -analyzer-store=region -verify %s \ // RUN: -analyzer-checker=core \ -// RUN: -analyzer-checker=alpha.deadcode.UnreachableCode \ +// RUN: -analyzer-checker=debug.ExprInspection \ // RUN: -analyzer-checker=alpha.core.CastSize \ // RUN: -analyzer-checker=unix \ // RUN: -analyzer-checker=debug.ExprInspection @@ -10,6 +10,7 @@ void clang_analyzer_eval(int); void clang_analyzer_dump(int); void clang_analyzer_dumpExtent(void *); +void clang_analyzer_warnIfReached(void); // Without -fms-compatibility, wchar_t isn't a builtin type. MSVC defines // _WCHAR_T_DEFINED if wchar_t is available. Microsoft recommends that you use @@ -711,7 +712,8 @@ char *buf = calloc(2,2); char result = buf[3]; // no-warning if (buf[1] != 0) { - free(buf); // expected-warning{{never executed}} + clang_analyzer_warnIfReached(); // no-warning + free(buf); } return result; // expected-warning{{Potential leak of memory pointed to by 'buf'}} } @@ -1381,11 +1383,13 @@ int x = 0; int *x1 = malloc(8); int *x4 = x1; + clang_analyzer_warnIfReached(); // expected-warning{{REACHABLE}} if (x1 == x4) { free(x1); return 5/x; // expected-warning{{Division by zero}} } - return x;// expected-warning{{This statement is never executed}} + clang_analyzer_warnIfReached(); // no-warning + return x; } int HeapAssignment(void) { diff --git a/clang/test/Analysis/malloc.cpp b/clang/test/Analysis/malloc.cpp --- a/clang/test/Analysis/malloc.cpp +++ b/clang/test/Analysis/malloc.cpp @@ -1,6 +1,5 @@ // RUN: %clang_analyze_cc1 -w -verify %s \ // RUN: -analyzer-checker=core \ -// RUN: -analyzer-checker=alpha.deadcode.UnreachableCode \ // RUN: -analyzer-checker=alpha.core.CastSize \ // RUN: -analyzer-checker=unix.Malloc \ // RUN: -analyzer-checker=cplusplus.NewDelete @@ -8,14 +7,12 @@ // RUN: %clang_analyze_cc1 -w -verify %s \ // RUN: -triple i386-unknown-linux-gnu \ // RUN: -analyzer-checker=core \ -// RUN: -analyzer-checker=alpha.deadcode.UnreachableCode \ // RUN: -analyzer-checker=alpha.core.CastSize \ // RUN: -analyzer-checker=unix.Malloc \ // RUN: -analyzer-checker=cplusplus.NewDelete // RUN: %clang_analyze_cc1 -w -verify %s -DTEST_INLINABLE_ALLOCATORS \ // RUN: -analyzer-checker=core \ -// RUN: -analyzer-checker=alpha.deadcode.UnreachableCode \ // RUN: -analyzer-checker=alpha.core.CastSize \ // RUN: -analyzer-checker=unix.Malloc \ // RUN: -analyzer-checker=cplusplus.NewDelete @@ -23,7 +20,6 @@ // RUN: %clang_analyze_cc1 -w -verify %s -DTEST_INLINABLE_ALLOCATORS \ // RUN: -triple i386-unknown-linux-gnu \ // RUN: -analyzer-checker=core \ -// RUN: -analyzer-checker=alpha.deadcode.UnreachableCode \ // RUN: -analyzer-checker=alpha.core.CastSize \ // RUN: -analyzer-checker=unix.Malloc \ // RUN: -analyzer-checker=cplusplus.NewDelete diff --git a/clang/test/Analysis/qt_malloc.cpp b/clang/test/Analysis/qt_malloc.cpp --- a/clang/test/Analysis/qt_malloc.cpp +++ b/clang/test/Analysis/qt_malloc.cpp @@ -1,4 +1,4 @@ -// RUN: %clang_analyze_cc1 -std=c++11 -analyzer-checker=core,alpha.deadcode.UnreachableCode,alpha.core.CastSize,unix.Malloc,cplusplus -analyzer-store=region -verify %s +// RUN: %clang_analyze_cc1 -std=c++11 -analyzer-checker=core,alpha.core.CastSize,unix.Malloc,cplusplus -analyzer-store=region -verify %s // expected-no-diagnostics #include "Inputs/qt-simulator.h" diff --git a/clang/test/Analysis/unreachable-code-path.c b/clang/test/Analysis/unreachable-code-path.c deleted file mode 100644 --- a/clang/test/Analysis/unreachable-code-path.c +++ /dev/null @@ -1,226 +0,0 @@ -// RUN: %clang_analyze_cc1 -analyzer-checker=core,deadcode.DeadStores,alpha.deadcode.UnreachableCode -verify -analyzer-opt-analyze-nested-blocks -Wno-unused-value %s - -extern void foo(int a); - -// The first few tests are non-path specific - we should be able to find them - -void test(unsigned a) { - switch (a) { - a += 5; // expected-warning{{never executed}} - case 2: - a *= 10; - case 3: - a %= 2; - } - foo(a); -} - -void test2(unsigned a) { - help: - if (a > 0) - return; - if (a == 0) - return; - foo(a); // expected-warning{{never executed}} - goto help; -} - -void test3(unsigned a) { - while(1); - if (a > 5) { // expected-warning{{never executed}} - return; - } -} - -// These next tests are path-sensitive - -void test4(void) { - int a = 5; - - while (a > 1) - a -= 2; - - if (a > 1) { - a = a + 56; // expected-warning{{never executed}} - } - - foo(a); -} - -extern void bar(char c); - -void test5(const char *c) { - foo(c[0]); - - if (!c) { - bar(1); // expected-warning{{never executed}} - } -} - -// These next tests are false positives and should not generate warnings - -void test6(const char *c) { - if (c) return; - if (!c) return; - __builtin_unreachable(); // no-warning - __builtin_assume(0); // no-warning -} - -// Compile-time constant false positives -#define CONSTANT 0 -enum test_enum { Off, On }; -void test7(void) { - if (CONSTANT) - return; // no-warning - - if (sizeof(int)) - return; // no-warning - - if (Off) - return; // no-warning -} - -void test8(void) { - static unsigned a = 0; - - if (a) - a = 123; // no-warning - - a = 5; -} - -// Check for bugs where multiple statements are reported -void test9(unsigned a) { - switch (a) { - if (a) // expected-warning{{never executed}} - foo(a + 5); // no-warning - else // no-warning - foo(a); // no-warning - case 1: - case 2: - break; - default: - break; - } -} - -// Tests from flow-sensitive version -void test10(void) { - goto c; - d: - goto e; // expected-warning {{never executed}} - c: ; - int i; - return; - goto b; // expected-warning {{never executed}} - goto a; // expected-warning {{never executed}} - b: - i = 1; // no-warning - a: - i = 2; // no-warning - goto f; - e: - goto d; - f: ; -} - -// test11: we can actually end up in the default case, even if it is not -// obvious: there might be something wrong with the given argument. -enum foobar { FOO, BAR }; -extern void error(void); -void test11(enum foobar fb) { - switch (fb) { - case FOO: - break; - case BAR: - break; - default: - error(); // no-warning - return; - error(); // expected-warning {{never executed}} - } -} - -void inlined(int condition) { - if (condition) { - foo(5); // no-warning - } else { - foo(6); - } -} - -void testInlined(void) { - extern int coin(void); - int cond = coin(); - if (!cond) { - inlined(0); - if (cond) { - foo(5); // expected-warning {{never executed}} - } - } -} - -// Don't warn about unreachable VarDecl. -void dostuff(int*A); -void varDecl1(int X) { - switch (X) { - int A; // No warning here. - case 1: - dostuff(&A); - break; - case 2: - dostuff(&A); - break; - } -} -void varDecl2(int X) { - switch (X) { - int A=1; // expected-warning {{never executed}} - case 1: - dostuff(&A); - break; - case 2: - dostuff(&A); - break; - } -} - -// Ensure that ExplodedGraph and unoptimized CFG match. -void test12(int x) { - switch (x) { - case 1: - break; // not unreachable - case 2: - do { } while (0); - break; - } -} - -// Don't merge return nodes in ExplodedGraph unless they are same. -extern int table[]; -static int inlineFunction(const int i) { - if (table[i] != 0) - return 1; - return 0; -} -void test13(int i) { - int x = inlineFunction(i); - x && x < 10; // no-warning -} - -// Don't warn in a macro -#define RETURN(X) do { return; } while (0) -void macro(void) { - RETURN(1); // no-warning -} - -// Avoid FP when macro argument is known -void writeSomething(int *x); -#define MACRO(C) \ - if (!C) { \ - static int x; \ - writeSomething(&x); \ - } -void macro2(void) { - MACRO(1); -} diff --git a/clang/www/analyzer/alpha_checks.html b/clang/www/analyzer/alpha_checks.html --- a/clang/www/analyzer/alpha_checks.html +++ b/clang/www/analyzer/alpha_checks.html @@ -447,48 +447,6 @@ - - -

Dead Code Alpha Checkers

- - - - - - - -
Name, DescriptionExample
-

LLVM Checkers