diff options
Diffstat (limited to 'lib/Sema/AnalysisBasedWarnings.cpp')
-rw-r--r-- | lib/Sema/AnalysisBasedWarnings.cpp | 208 |
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:: |