diff options
Diffstat (limited to 'contrib/llvm/tools/clang/lib/Sema/AnalysisBasedWarnings.cpp')
-rw-r--r-- | contrib/llvm/tools/clang/lib/Sema/AnalysisBasedWarnings.cpp | 213 |
1 files changed, 177 insertions, 36 deletions
diff --git a/contrib/llvm/tools/clang/lib/Sema/AnalysisBasedWarnings.cpp b/contrib/llvm/tools/clang/lib/Sema/AnalysisBasedWarnings.cpp index 1295339..93e3ecf 100644 --- a/contrib/llvm/tools/clang/lib/Sema/AnalysisBasedWarnings.cpp +++ b/contrib/llvm/tools/clang/lib/Sema/AnalysisBasedWarnings.cpp @@ -25,6 +25,7 @@ #include "clang/AST/StmtObjC.h" #include "clang/AST/StmtVisitor.h" #include "clang/Analysis/Analyses/CFGReachabilityAnalysis.h" +#include "clang/Analysis/Analyses/Consumed.h" #include "clang/Analysis/Analyses/ReachableCode.h" #include "clang/Analysis/Analyses/ThreadSafety.h" #include "clang/Analysis/Analyses/UninitializedValues.h" @@ -438,22 +439,22 @@ static bool SuggestInitializationFixit(Sema &S, const VarDecl *VD) { << FixItHint::CreateInsertion(VD->getLocation(), "__block "); return true; } - + // Don't issue a fixit if there is already an initializer. if (VD->getInit()) return false; - - // Suggest possible initialization (if any). - std::string Init = S.getFixItZeroInitializerForType(VariableTy); - if (Init.empty()) - return false; // Don't suggest a fixit inside macros. if (VD->getLocEnd().isMacroID()) return false; SourceLocation Loc = S.PP.getLocForEndOfToken(VD->getLocEnd()); - + + // Suggest possible initialization (if any). + std::string Init = S.getFixItZeroInitializerForType(VariableTy, Loc); + if (Init.empty()) + return false; + S.Diag(Loc, diag::note_var_fixit_add_initialization) << VD->getDeclName() << FixItHint::CreateInsertion(Loc, Init); return true; @@ -492,6 +493,31 @@ static void DiagUninitUse(Sema &S, const VarDecl *VD, const UninitUse &Use, bool IsCapturedByBlock) { bool Diagnosed = false; + switch (Use.getKind()) { + case UninitUse::Always: + S.Diag(Use.getUser()->getLocStart(), diag::warn_uninit_var) + << VD->getDeclName() << IsCapturedByBlock + << Use.getUser()->getSourceRange(); + return; + + case UninitUse::AfterDecl: + case UninitUse::AfterCall: + S.Diag(VD->getLocation(), diag::warn_sometimes_uninit_var) + << VD->getDeclName() << IsCapturedByBlock + << (Use.getKind() == UninitUse::AfterDecl ? 4 : 5) + << const_cast<DeclContext*>(VD->getLexicalDeclContext()) + << VD->getSourceRange(); + S.Diag(Use.getUser()->getLocStart(), diag::note_uninit_var_use) + << IsCapturedByBlock << Use.getUser()->getSourceRange(); + return; + + case UninitUse::Maybe: + case UninitUse::Sometimes: + // Carry on to report sometimes-uninitialized branches, if possible, + // or a 'may be used uninitialized' diagnostic otherwise. + break; + } + // Diagnose each branch which leads to a sometimes-uninitialized use. for (UninitUse::branch_iterator I = Use.branch_begin(), E = Use.branch_end(); I != E; ++I) { @@ -514,14 +540,10 @@ static void DiagUninitUse(Sema &S, const VarDecl *VD, const UninitUse &Use, : (I->Output ? "1" : "0"); FixItHint Fixit1, Fixit2; - switch (Term->getStmtClass()) { + switch (Term ? Term->getStmtClass() : Stmt::DeclStmtClass) { default: // Don't know how to report this. Just fall back to 'may be used - // uninitialized'. This happens for range-based for, which the user - // can't explicitly fix. - // FIXME: This also happens if the first use of a variable is always - // uninitialized, eg "for (int n; n < 10; ++n)". We should report that - // with the 'is uninitialized' diagnostic. + // uninitialized'. FIXME: Can this happen? continue; // "condition is true / condition is false". @@ -582,6 +604,17 @@ static void DiagUninitUse(Sema &S, const VarDecl *VD, const UninitUse &Use, else Fixit1 = FixItHint::CreateReplacement(Range, FixitStr); break; + case Stmt::CXXForRangeStmtClass: + if (I->Output == 1) { + // The use occurs if a range-based for loop's body never executes. + // That may be impossible, and there's no syntactic fix for this, + // so treat it as a 'may be uninitialized' case. + continue; + } + DiagKind = 1; + Str = "for"; + Range = cast<CXXForRangeStmt>(Term)->getRangeInit()->getSourceRange(); + break; // "condition is true / loop is exited". case Stmt::DoStmtClass: @@ -618,9 +651,7 @@ static void DiagUninitUse(Sema &S, const VarDecl *VD, const UninitUse &Use, } if (!Diagnosed) - S.Diag(Use.getUser()->getLocStart(), - Use.getKind() == UninitUse::Always ? diag::warn_uninit_var - : diag::warn_maybe_uninit_var) + S.Diag(Use.getUser()->getLocStart(), diag::warn_maybe_uninit_var) << VD->getDeclName() << IsCapturedByBlock << Use.getUser()->getSourceRange(); } @@ -1123,7 +1154,7 @@ static void diagnoseRepeatedUseOfWeak(Sema &S, // Show the first time the object was read. S.Diag(FirstRead->getLocStart(), DiagKind) - << ObjectKind << D << FunctionKind + << int(ObjectKind) << D << int(FunctionKind) << FirstRead->getSourceRange(); // Print all the other accesses as notes. @@ -1154,7 +1185,7 @@ struct SLocSort { class UninitValsDiagReporter : public UninitVariablesHandler { Sema &S; typedef SmallVector<UninitUse, 2> UsesVec; - typedef std::pair<UsesVec*, bool> MappedType; + typedef llvm::PointerIntPair<UsesVec *, 1, bool> MappedType; // Prefer using MapVector to DenseMap, so that iteration order will be // the same as insertion order. This is needed to obtain a deterministic // order of diagnostics when calling flushDiagnostics(). @@ -1172,19 +1203,18 @@ public: uses = new UsesMap(); MappedType &V = (*uses)[vd]; - UsesVec *&vec = V.first; - if (!vec) - vec = new UsesVec(); + if (!V.getPointer()) + V.setPointer(new UsesVec()); return V; } void handleUseOfUninitVariable(const VarDecl *vd, const UninitUse &use) { - getUses(vd).first->push_back(use); + getUses(vd).getPointer()->push_back(use); } void handleSelfInit(const VarDecl *vd) { - getUses(vd).second = true; + getUses(vd).setInt(true); } void flushDiagnostics() { @@ -1195,8 +1225,8 @@ public: const VarDecl *vd = i->first; const MappedType &V = i->second; - UsesVec *vec = V.first; - bool hasSelfInit = V.second; + UsesVec *vec = V.getPointer(); + bool hasSelfInit = V.getInt(); // Specially handle the case where we have uses of an uninitialized // variable, but the root cause is an idiomatic self-init. We want @@ -1233,7 +1263,9 @@ public: private: static bool hasAlwaysUninitializedUse(const UsesVec* vec) { for (UsesVec::const_iterator i = vec->begin(), e = vec->end(); i != e; ++i) { - if (i->getKind() == UninitUse::Always) { + if (i->getKind() == UninitUse::Always || + i->getKind() == UninitUse::AfterCall || + i->getKind() == UninitUse::AfterDecl) { return true; } } @@ -1242,12 +1274,8 @@ private: }; } - -//===----------------------------------------------------------------------===// -// -Wthread-safety -//===----------------------------------------------------------------------===// namespace clang { -namespace thread_safety { +namespace { typedef SmallVector<PartialDiagnosticAt, 1> OptionalNotes; typedef std::pair<PartialDiagnosticAt, OptionalNotes> DelayedDiag; typedef std::list<DelayedDiag> DiagList; @@ -1262,7 +1290,13 @@ struct SortDiagBySourceLocation { return SM.isBeforeInTranslationUnit(left.first.first, right.first.first); } }; +}} +//===----------------------------------------------------------------------===// +// -Wthread-safety +//===----------------------------------------------------------------------===// +namespace clang { +namespace thread_safety { namespace { class ThreadSafetyReporter : public clang::thread_safety::ThreadSafetyHandler { Sema &S; @@ -1413,6 +1447,102 @@ class ThreadSafetyReporter : public clang::thread_safety::ThreadSafetyHandler { } //===----------------------------------------------------------------------===// +// -Wconsumed +//===----------------------------------------------------------------------===// + +namespace clang { +namespace consumed { +namespace { +class ConsumedWarningsHandler : public ConsumedWarningsHandlerBase { + + Sema &S; + DiagList Warnings; + +public: + + ConsumedWarningsHandler(Sema &S) : S(S) {} + + void emitDiagnostics() { + Warnings.sort(SortDiagBySourceLocation(S.getSourceManager())); + + for (DiagList::iterator I = Warnings.begin(), E = Warnings.end(); + I != E; ++I) { + + const OptionalNotes &Notes = I->second; + S.Diag(I->first.first, I->first.second); + + for (unsigned NoteI = 0, NoteN = Notes.size(); NoteI != NoteN; ++NoteI) { + S.Diag(Notes[NoteI].first, Notes[NoteI].second); + } + } + } + + void warnLoopStateMismatch(SourceLocation Loc, StringRef VariableName) { + PartialDiagnosticAt Warning(Loc, S.PDiag(diag::warn_loop_state_mismatch) << + VariableName); + + Warnings.push_back(DelayedDiag(Warning, OptionalNotes())); + } + + void warnParamReturnTypestateMismatch(SourceLocation Loc, + StringRef VariableName, + StringRef ExpectedState, + StringRef ObservedState) { + + PartialDiagnosticAt Warning(Loc, S.PDiag( + diag::warn_param_return_typestate_mismatch) << VariableName << + ExpectedState << ObservedState); + + Warnings.push_back(DelayedDiag(Warning, OptionalNotes())); + } + + void warnParamTypestateMismatch(SourceLocation Loc, StringRef ExpectedState, + StringRef ObservedState) { + + PartialDiagnosticAt Warning(Loc, S.PDiag( + diag::warn_param_typestate_mismatch) << ExpectedState << ObservedState); + + Warnings.push_back(DelayedDiag(Warning, OptionalNotes())); + } + + void warnReturnTypestateForUnconsumableType(SourceLocation Loc, + StringRef TypeName) { + PartialDiagnosticAt Warning(Loc, S.PDiag( + diag::warn_return_typestate_for_unconsumable_type) << TypeName); + + Warnings.push_back(DelayedDiag(Warning, OptionalNotes())); + } + + void warnReturnTypestateMismatch(SourceLocation Loc, StringRef ExpectedState, + StringRef ObservedState) { + + PartialDiagnosticAt Warning(Loc, S.PDiag( + diag::warn_return_typestate_mismatch) << ExpectedState << ObservedState); + + Warnings.push_back(DelayedDiag(Warning, OptionalNotes())); + } + + void warnUseOfTempInInvalidState(StringRef MethodName, StringRef State, + SourceLocation Loc) { + + PartialDiagnosticAt Warning(Loc, S.PDiag( + diag::warn_use_of_temp_in_invalid_state) << MethodName << State); + + Warnings.push_back(DelayedDiag(Warning, OptionalNotes())); + } + + void warnUseInInvalidState(StringRef MethodName, StringRef VariableName, + StringRef State, SourceLocation Loc) { + + PartialDiagnosticAt Warning(Loc, S.PDiag(diag::warn_use_in_invalid_state) << + MethodName << VariableName << State); + + Warnings.push_back(DelayedDiag(Warning, OptionalNotes())); + } +}; +}}} + +//===----------------------------------------------------------------------===// // AnalysisBasedWarnings - Worker object used by Sema to execute analysis-based // warnings on a function, method, or block. //===----------------------------------------------------------------------===// @@ -1421,6 +1551,7 @@ clang::sema::AnalysisBasedWarnings::Policy::Policy() { enableCheckFallThrough = 1; enableCheckUnreachable = 0; enableThreadSafetyAnalysis = 0; + enableConsumedAnalysis = 0; } clang::sema::AnalysisBasedWarnings::AnalysisBasedWarnings(Sema &s) @@ -1441,7 +1572,9 @@ clang::sema::AnalysisBasedWarnings::AnalysisBasedWarnings(Sema &s) DefaultPolicy.enableThreadSafetyAnalysis = (unsigned) (D.getDiagnosticLevel(diag::warn_double_lock, SourceLocation()) != DiagnosticsEngine::Ignored); - + DefaultPolicy.enableConsumedAnalysis = (unsigned) + (D.getDiagnosticLevel(diag::warn_use_in_invalid_state, SourceLocation()) != + DiagnosticsEngine::Ignored); } static void flushDiagnostics(Sema &S, sema::FunctionScopeInfo *fscope) { @@ -1486,10 +1619,11 @@ AnalysisBasedWarnings::IssueWarnings(sema::AnalysisBasedWarnings::Policy P, const Stmt *Body = D->getBody(); assert(Body); + // Construct the analysis context with the specified CFG build options. AnalysisDeclContext AC(/* AnalysisDeclContextManager */ 0, D); // 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. + // explosion for destructors that can result and the compile time hit. AC.getCFGBuildOptions().PruneTriviallyFalseEdges = true; AC.getCFGBuildOptions().AddEHEdges = false; AC.getCFGBuildOptions().AddInitializers = true; @@ -1502,7 +1636,8 @@ AnalysisBasedWarnings::IssueWarnings(sema::AnalysisBasedWarnings::Policy P, // prototyping, but we need a way for analyses to say what expressions they // expect to always be CFGElements and then fill in the BuildOptions // appropriately. This is essentially a layering violation. - if (P.enableCheckUnreachable || P.enableThreadSafetyAnalysis) { + if (P.enableCheckUnreachable || P.enableThreadSafetyAnalysis || + P.enableConsumedAnalysis) { // Unreachable code analysis and thread safety require a linearized CFG. AC.getCFGBuildOptions().setAllAlwaysAdd(); } @@ -1518,8 +1653,7 @@ AnalysisBasedWarnings::IssueWarnings(sema::AnalysisBasedWarnings::Policy P, .setAlwaysAdd(Stmt::AttributedStmtClass); } - // Construct the analysis context with the specified CFG build options. - + // Emit delayed diagnostics. if (!fscope->PossiblyUnreachableDiags.empty()) { bool analyzed = false; @@ -1606,6 +1740,13 @@ AnalysisBasedWarnings::IssueWarnings(sema::AnalysisBasedWarnings::Policy P, Reporter.emitDiagnostics(); } + // Check for violations of consumed properties. + if (P.enableConsumedAnalysis) { + consumed::ConsumedWarningsHandler WarningHandler(S); + consumed::ConsumedAnalyzer Analyzer(WarningHandler); + Analyzer.run(AC); + } + if (Diags.getDiagnosticLevel(diag::warn_uninit_var, D->getLocStart()) != DiagnosticsEngine::Ignored || Diags.getDiagnosticLevel(diag::warn_sometimes_uninit_var,D->getLocStart()) |