summaryrefslogtreecommitdiffstats
path: root/lib/Sema/AnalysisBasedWarnings.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'lib/Sema/AnalysisBasedWarnings.cpp')
-rw-r--r--lib/Sema/AnalysisBasedWarnings.cpp208
1 files changed, 177 insertions, 31 deletions
diff --git a/lib/Sema/AnalysisBasedWarnings.cpp b/lib/Sema/AnalysisBasedWarnings.cpp
index cfebed6..63f561d 100644
--- a/lib/Sema/AnalysisBasedWarnings.cpp
+++ b/lib/Sema/AnalysisBasedWarnings.cpp
@@ -16,6 +16,7 @@
#include "clang/Sema/AnalysisBasedWarnings.h"
#include "clang/Sema/SemaInternal.h"
#include "clang/Basic/SourceManager.h"
+#include "clang/Lex/Preprocessor.h"
#include "clang/AST/DeclObjC.h"
#include "clang/AST/DeclCXX.h"
#include "clang/AST/ExprObjC.h"
@@ -25,6 +26,7 @@
#include "clang/Analysis/AnalysisContext.h"
#include "clang/Analysis/CFG.h"
#include "clang/Analysis/Analyses/ReachableCode.h"
+#include "clang/Analysis/Analyses/UninitializedValuesV2.h"
#include "llvm/ADT/BitVector.h"
#include "llvm/Support/Casting.h"
@@ -108,24 +110,41 @@ static ControlFlowKind CheckFallThrough(AnalysisContext &AC) {
bool HasFakeEdge = false;
bool HasPlainEdge = false;
bool HasAbnormalEdge = false;
- for (CFGBlock::pred_iterator I=cfg->getExit().pred_begin(),
- E = cfg->getExit().pred_end();
- I != E;
- ++I) {
- CFGBlock& B = **I;
+
+ // Ignore default cases that aren't likely to be reachable because all
+ // enums in a switch(X) have explicit case statements.
+ CFGBlock::FilterOptions FO;
+ FO.IgnoreDefaultsWithCoveredEnums = 1;
+
+ for (CFGBlock::filtered_pred_iterator
+ I = cfg->getExit().filtered_pred_start_end(FO); I.hasMore(); ++I) {
+ const CFGBlock& B = **I;
if (!live[B.getBlockID()])
continue;
- if (B.size() == 0) {
+
+ // Destructors can appear after the 'return' in the CFG. This is
+ // normal. We need to look pass the destructors for the return
+ // statement (if it exists).
+ CFGBlock::const_reverse_iterator ri = B.rbegin(), re = B.rend();
+ for ( ; ri != re ; ++ri) {
+ CFGElement CE = *ri;
+ if (isa<CFGStmt>(CE))
+ break;
+ }
+
+ // No more CFGElements in the block?
+ if (ri == re) {
if (B.getTerminator() && isa<CXXTryStmt>(B.getTerminator())) {
HasAbnormalEdge = true;
continue;
}
-
// A labeled empty statement, or the entry block...
HasPlainEdge = true;
continue;
}
- Stmt *S = B[B.size()-1];
+
+ CFGStmt CS = cast<CFGStmt>(*ri);
+ Stmt *S = CS.getStmt();
if (isa<ReturnStmt>(S)) {
HasLiveReturn = true;
continue;
@@ -169,19 +188,6 @@ static ControlFlowKind CheckFallThrough(AnalysisContext &AC) {
}
}
}
- // FIXME: Remove this hack once temporaries and their destructors are
- // modeled correctly by the CFG.
- if (CXXExprWithTemporaries *E = dyn_cast<CXXExprWithTemporaries>(S)) {
- for (unsigned I = 0, N = E->getNumTemporaries(); I != N; ++I) {
- const FunctionDecl *FD = E->getTemporary(I)->getDestructor();
- if (FD->hasAttr<NoReturnAttr>() ||
- FD->getType()->getAs<FunctionType>()->getNoReturnAttr()) {
- NoReturnEdge = true;
- HasFakeEdge = true;
- break;
- }
- }
- }
// FIXME: Add noreturn message sends.
if (NoReturnEdge == false)
HasPlainEdge = true;
@@ -208,9 +214,11 @@ struct CheckFallThroughDiagnostics {
unsigned diag_AlwaysFallThrough_ReturnsNonVoid;
unsigned diag_NeverFallThroughOrReturn;
bool funMode;
+ SourceLocation FuncLoc;
static CheckFallThroughDiagnostics MakeForFunction(const Decl *Func) {
CheckFallThroughDiagnostics D;
+ D.FuncLoc = Func->getLocation();
D.diag_MaybeFallThrough_HasNoReturn =
diag::warn_falloff_noreturn_function;
D.diag_MaybeFallThrough_ReturnsNonVoid =
@@ -255,18 +263,22 @@ struct CheckFallThroughDiagnostics {
bool checkDiagnostics(Diagnostic &D, bool ReturnsVoid,
bool HasNoReturn) const {
if (funMode) {
- return (D.getDiagnosticLevel(diag::warn_maybe_falloff_nonvoid_function)
- == Diagnostic::Ignored || ReturnsVoid)
- && (D.getDiagnosticLevel(diag::warn_noreturn_function_has_return_expr)
- == Diagnostic::Ignored || !HasNoReturn)
- && (D.getDiagnosticLevel(diag::warn_suggest_noreturn_block)
- == Diagnostic::Ignored || !ReturnsVoid);
+ return (ReturnsVoid ||
+ D.getDiagnosticLevel(diag::warn_maybe_falloff_nonvoid_function,
+ FuncLoc) == Diagnostic::Ignored)
+ && (!HasNoReturn ||
+ D.getDiagnosticLevel(diag::warn_noreturn_function_has_return_expr,
+ FuncLoc) == Diagnostic::Ignored)
+ && (!ReturnsVoid ||
+ D.getDiagnosticLevel(diag::warn_suggest_noreturn_block, FuncLoc)
+ == Diagnostic::Ignored);
}
// For blocks.
return ReturnsVoid && !HasNoReturn
- && (D.getDiagnosticLevel(diag::warn_suggest_noreturn_block)
- == Diagnostic::Ignored || !ReturnsVoid);
+ && (!ReturnsVoid ||
+ D.getDiagnosticLevel(diag::warn_suggest_noreturn_block, FuncLoc)
+ == Diagnostic::Ignored);
}
};
@@ -343,6 +355,112 @@ static void CheckFallThroughForBody(Sema &S, const Decl *D, const Stmt *Body,
}
//===----------------------------------------------------------------------===//
+// -Wuninitialized
+//===----------------------------------------------------------------------===//
+
+namespace {
+struct SLocSort {
+ bool operator()(const Expr *a, const Expr *b) {
+ SourceLocation aLoc = a->getLocStart();
+ SourceLocation bLoc = b->getLocStart();
+ return aLoc.getRawEncoding() < bLoc.getRawEncoding();
+ }
+};
+
+class UninitValsDiagReporter : public UninitVariablesHandler {
+ Sema &S;
+ typedef llvm::SmallVector<const Expr *, 2> UsesVec;
+ typedef llvm::DenseMap<const VarDecl *, UsesVec*> UsesMap;
+ UsesMap *uses;
+
+public:
+ UninitValsDiagReporter(Sema &S) : S(S), uses(0) {}
+ ~UninitValsDiagReporter() {
+ flushDiagnostics();
+ }
+
+ void handleUseOfUninitVariable(const Expr *ex, const VarDecl *vd) {
+ if (!uses)
+ uses = new UsesMap();
+
+ UsesVec *&vec = (*uses)[vd];
+ if (!vec)
+ vec = new UsesVec();
+
+ vec->push_back(ex);
+ }
+
+ void flushDiagnostics() {
+ if (!uses)
+ return;
+
+ for (UsesMap::iterator i = uses->begin(), e = uses->end(); i != e; ++i) {
+ const VarDecl *vd = i->first;
+ UsesVec *vec = i->second;
+
+ bool fixitIssued = false;
+
+ // Sort the uses by their SourceLocations. While not strictly
+ // guaranteed to produce them in line/column order, this will provide
+ // a stable ordering.
+ std::sort(vec->begin(), vec->end(), SLocSort());
+
+ for (UsesVec::iterator vi = vec->begin(), ve = vec->end(); vi != ve; ++vi)
+ {
+ if (const DeclRefExpr *dr = dyn_cast<DeclRefExpr>(*vi)) {
+ S.Diag(dr->getLocStart(), diag::warn_uninit_var)
+ << vd->getDeclName() << dr->getSourceRange();
+ }
+ else {
+ const BlockExpr *be = cast<BlockExpr>(*vi);
+ S.Diag(be->getLocStart(), diag::warn_uninit_var_captured_by_block)
+ << vd->getDeclName();
+ }
+
+ // Report where the variable was declared.
+ S.Diag(vd->getLocStart(), diag::note_uninit_var_def)
+ << vd->getDeclName();
+
+ // Only report the fixit once.
+ if (fixitIssued)
+ continue;
+
+ fixitIssued = true;
+
+ // Suggest possible initialization (if any).
+ const char *initialization = 0;
+ QualType vdTy = vd->getType().getCanonicalType();
+
+ if (vdTy->getAs<ObjCObjectPointerType>()) {
+ // Check if 'nil' is defined.
+ if (S.PP.getMacroInfo(&S.getASTContext().Idents.get("nil")))
+ initialization = " = nil";
+ else
+ initialization = " = 0";
+ }
+ else if (vdTy->isRealFloatingType())
+ initialization = " = 0.0";
+ else if (vdTy->isBooleanType() && S.Context.getLangOptions().CPlusPlus)
+ initialization = " = false";
+ else if (vdTy->isEnumeralType())
+ continue;
+ else if (vdTy->isScalarType())
+ initialization = " = 0";
+
+ if (initialization) {
+ SourceLocation loc = S.PP.getLocForEndOfToken(vd->getLocEnd());
+ S.Diag(loc, diag::note_var_fixit_add_initialization)
+ << FixItHint::CreateInsertion(loc, initialization);
+ }
+ }
+ delete vec;
+ }
+ delete uses;
+ }
+};
+}
+
+//===----------------------------------------------------------------------===//
// AnalysisBasedWarnings - Worker object used by Sema to execute analysis-based
// warnings on a function, method, or block.
//===----------------------------------------------------------------------===//
@@ -355,7 +473,8 @@ clang::sema::AnalysisBasedWarnings::Policy::Policy() {
clang::sema::AnalysisBasedWarnings::AnalysisBasedWarnings(Sema &s) : S(s) {
Diagnostic &D = S.getDiagnostics();
DefaultPolicy.enableCheckUnreachable = (unsigned)
- (D.getDiagnosticLevel(diag::warn_unreachable) != Diagnostic::Ignored);
+ (D.getDiagnosticLevel(diag::warn_unreachable, SourceLocation()) !=
+ Diagnostic::Ignored);
}
void clang::sema::
@@ -390,7 +509,8 @@ AnalysisBasedWarnings::IssueWarnings(sema::AnalysisBasedWarnings::Policy P,
// Don't generate EH edges for CallExprs as we'd like to avoid the n^2
// explosion for destrutors that can result and the compile time hit.
- AnalysisContext AC(D, 0, false);
+ AnalysisContext AC(D, 0, /*useUnoptimizedCFG=*/false, /*addehedges=*/false,
+ /*addImplicitDtors=*/true, /*addInitializers=*/true);
// Warning: check missing 'return'
if (P.enableCheckFallThrough) {
@@ -403,6 +523,32 @@ AnalysisBasedWarnings::IssueWarnings(sema::AnalysisBasedWarnings::Policy P,
// Warning: check for unreachable code
if (P.enableCheckUnreachable)
CheckUnreachable(S, AC);
+
+ if (Diags.getDiagnosticLevel(diag::warn_uninit_var, D->getLocStart())
+ != Diagnostic::Ignored) {
+ ASTContext &ctx = D->getASTContext();
+ llvm::OwningPtr<CFG> tmpCFG;
+ bool useAlternateCFG = false;
+ if (ctx.getLangOptions().CPlusPlus) {
+ // Temporary workaround: implicit dtors in the CFG can confuse
+ // the path-sensitivity in the uninitialized values analysis.
+ // For now create (if necessary) a separate CFG without implicit dtors.
+ // FIXME: We should not need to do this, as it results in multiple
+ // CFGs getting constructed.
+ CFG::BuildOptions B;
+ B.AddEHEdges = false;
+ B.AddImplicitDtors = false;
+ B.AddInitializers = true;
+ tmpCFG.reset(CFG::buildCFG(D, AC.getBody(), &ctx, B));
+ useAlternateCFG = true;
+ }
+ CFG *cfg = useAlternateCFG ? tmpCFG.get() : AC.getCFG();
+ if (cfg) {
+ UninitValsDiagReporter reporter(S);
+ runUninitializedVariablesAnalysis(*cast<DeclContext>(D), *cfg, AC,
+ reporter);
+ }
+ }
}
void clang::sema::
OpenPOWER on IntegriCloud