diff options
author | Caitlin Sadowski <supertri@google.com> | 2011-08-23 18:46:34 +0000 |
---|---|---|
committer | Caitlin Sadowski <supertri@google.com> | 2011-08-23 18:46:34 +0000 |
commit | 3ac1fbc303d22af2e11a14023ecee7bd7b7d0bfd (patch) | |
tree | 568b56519f7184f527aa21521573b65d31d82f28 /lib/Sema/AnalysisBasedWarnings.cpp | |
parent | f857186fd1995b34185d063a29b11ad4f948519f (diff) |
Thread-safety analysis: adding in a basic lockset tracking system. This
system flags an error when unlocking a lock which was not held, locking
the same lock twice, having a different lockset on each iteration of a
loop, or going out of scope while still holding a lock. In order to
successfully use the lockset, this patch also makes sure that attribute
arguments are attached correctly for later parsing.
This patch was also worked on by DeLesley Hutchins.
Note: This patch has been reviewed by Chandler Carruth and Jeffrey
Yasskin. Feel free to provide post-commit review comments for a
subsequent patch.
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@138350 91177308-0d34-0410-b5e6-96231b3b80d8
Diffstat (limited to 'lib/Sema/AnalysisBasedWarnings.cpp')
-rw-r--r-- | lib/Sema/AnalysisBasedWarnings.cpp | 561 |
1 files changed, 561 insertions, 0 deletions
diff --git a/lib/Sema/AnalysisBasedWarnings.cpp b/lib/Sema/AnalysisBasedWarnings.cpp index 9f5ab50266..99525bae9f 100644 --- a/lib/Sema/AnalysisBasedWarnings.cpp +++ b/lib/Sema/AnalysisBasedWarnings.cpp @@ -25,6 +25,7 @@ #include "clang/AST/StmtObjC.h" #include "clang/AST/StmtCXX.h" #include "clang/AST/EvaluatedExprVisitor.h" +#include "clang/AST/StmtVisitor.h" #include "clang/Analysis/AnalysisContext.h" #include "clang/Analysis/CFG.h" #include "clang/Analysis/Analyses/ReachableCode.h" @@ -32,7 +33,13 @@ #include "clang/Analysis/CFGStmtMap.h" #include "clang/Analysis/Analyses/UninitializedValues.h" #include "llvm/ADT/BitVector.h" +#include "llvm/ADT/FoldingSet.h" +#include "llvm/ADT/ImmutableMap.h" +#include "llvm/ADT/PostOrderIterator.h" +#include "llvm/ADT/SmallVector.h" #include "llvm/Support/Casting.h" +#include <algorithm> +#include <vector> using namespace clang; @@ -585,6 +592,551 @@ public: }; } + +//===----------------------------------------------------------------------===// +// -Wthread-safety +//===----------------------------------------------------------------------===// + +namespace { +/// \brief Implements a set of CFGBlocks using a BitVector. +/// +/// This class contains a minimal interface, primarily dictated by the SetType +/// template parameter of the llvm::po_iterator template, as used with external +/// storage. We also use this set to keep track of which CFGBlocks we visit +/// during the analysis. +class CFGBlockSet { + llvm::BitVector VisitedBlockIDs; + +public: + // po_iterator requires this iterator, but the only interface needed is the + // value_type typedef. + struct iterator { + typedef const CFGBlock *value_type; + }; + + CFGBlockSet() {} + CFGBlockSet(const CFG *G) : VisitedBlockIDs(G->getNumBlockIDs(), false) {} + + /// \brief Set the bit associated with a particular CFGBlock. + /// This is the important method for the SetType template parameter. + bool insert(const CFGBlock *Block) { + if (VisitedBlockIDs.test(Block->getBlockID())) + return false; + VisitedBlockIDs.set(Block->getBlockID()); + return true; + } + + /// \brief Check if the bit for a CFGBlock has been already set. + /// This mehtod is for tracking visited blocks in the main threadsafety loop. + bool alreadySet(const CFGBlock *Block) { + return VisitedBlockIDs.test(Block->getBlockID()); + } +}; + +/// \brief We create a helper class which we use to iterate through CFGBlocks in +/// the topological order. +class TopologicallySortedCFG { + typedef llvm::po_iterator<const CFG*, CFGBlockSet, true> po_iterator; + + std::vector<const CFGBlock*> Blocks; + +public: + typedef std::vector<const CFGBlock*>::reverse_iterator iterator; + + TopologicallySortedCFG(const CFG *CFGraph) { + Blocks.reserve(CFGraph->getNumBlockIDs()); + CFGBlockSet BSet(CFGraph); + + for (po_iterator I = po_iterator::begin(CFGraph, BSet), + E = po_iterator::end(CFGraph, BSet); I != E; ++I) { + Blocks.push_back(*I); + } + } + + iterator begin() { + return Blocks.rbegin(); + } + + iterator end() { + return Blocks.rend(); + } +}; + +/// \brief A Lock object uniquely identifies a particular lock acquired, and is +/// built from an Expr* (i.e. calling a lock function). +/// +/// Thread-safety analysis works by comparing lock expressions. Within the +/// body of a function, an expression such as "x->foo->bar.mu" will resolve to +/// a particular lock object at run-time. Subsequent occurrences of the same +/// expression (where "same" means syntactic equality) will refer to the same +/// run-time object if three conditions hold: +/// (1) Local variables in the expression, such as "x" have not changed. +/// (2) Values on the heap that affect the expression have not changed. +/// (3) The expression involves only pure function calls. +/// The current implementation assumes, but does not verify, that multiple uses +/// of the same lock expression satisfies these criteria. +/// +/// Clang introduces an additional wrinkle, which is that it is difficult to +/// derive canonical expressions, or compare expressions directly for equality. +/// Thus, we identify a lock not by an Expr, but by the set of named +/// declarations that are referenced by the Expr. In other words, +/// x->foo->bar.mu will be a four element vector with the Decls for +/// mu, bar, and foo, and x. The vector will uniquely identify the expression +/// for all practical purposes. +/// +/// Note we will need to perform substitution on "this" and function parameter +/// names when constructing a lock expression. +/// +/// For example: +/// class C { Mutex Mu; void lock() EXCLUSIVE_LOCK_FUNCTION(this->Mu); }; +/// void myFunc(C *X) { ... X->lock() ... } +/// The original expression for the lock acquired by myFunc is "this->Mu", but +/// "X" is substituted for "this" so we get X->Mu(); +/// +/// For another example: +/// foo(MyList *L) EXCLUSIVE_LOCKS_REQUIRED(L->Mu) { ... } +/// MyList *MyL; +/// foo(MyL); // requires lock MyL->Mu to be held +/// +/// FIXME: In C++0x Mutexes are the objects that control access to shared +/// variables, while Locks are the objects that acquire and release Mutexes. We +/// may want to switch to this new terminology soon, in which case we should +/// rename this class "Mutex" and rename "LockId" to "MutexId", as well as +/// making sure that the terms Lock and Mutex throughout this code are +/// consistent with C++0x +/// +/// FIXME: We should also pick one and canonicalize all usage of lock vs acquire +/// and unlock vs release as verbs. +class LockID { + SmallVector<NamedDecl*, 2> DeclSeq; + + /// Build a Decl sequence representing the lock from the given expression. + /// Recursive function that bottoms out when the final DeclRefExpr is reached. + void buildLock(Expr *Exp) { + if (DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(Exp)) { + NamedDecl *ND = cast<NamedDecl>(DRE->getDecl()->getCanonicalDecl()); + DeclSeq.push_back(ND); + } else if (MemberExpr *ME = dyn_cast<MemberExpr>(Exp)) { + NamedDecl *ND = ME->getMemberDecl(); + DeclSeq.push_back(ND); + buildLock(ME->getBase()); + } else { + // FIXME: add diagnostic + llvm::report_fatal_error("Expected lock expression!"); + } + } + +public: + LockID(Expr *LExpr) { + buildLock(LExpr); + assert(!DeclSeq.empty()); + } + + bool operator==(const LockID &other) const { + return DeclSeq == other.DeclSeq; + } + + bool operator!=(const LockID &other) const { + return !(*this == other); + } + + // SmallVector overloads Operator< to do lexicographic ordering. Note that + // we use pointer equality (and <) to compare NamedDecls. This means the order + // of locks in a lockset is nondeterministic. In order to output + // diagnostics in a deterministic ordering, we must order all diagnostics to + // output by SourceLocation when iterating through this lockset. + bool operator<(const LockID &other) const { + return DeclSeq < other.DeclSeq; + } + + /// \brief Returns the name of the first Decl in the list for a given LockId; + /// e.g. the lock expression foo.bar() has name "bar". + /// The caret will point unambiguously to the lock expression, so using this + /// name in diagnostics is a way to get simple, and consistent, lock names. + /// We do not want to output the entire expression text for security reasons. + StringRef getName() const { + return DeclSeq.front()->getName(); + } + + void Profile(llvm::FoldingSetNodeID &ID) const { + for (SmallVectorImpl<NamedDecl*>::const_iterator I = DeclSeq.begin(), + E = DeclSeq.end(); I != E; ++I) { + ID.AddPointer(*I); + } + } +}; + +/// \brief This is a helper class that stores info about the most recent +/// accquire of a Lock. +/// +/// The main body of the analysis maps Locks to LockDatas. +struct LockData { + SourceLocation AcquireLoc; + + LockData(SourceLocation Loc) : AcquireLoc(Loc) {} + + bool operator==(const LockData &other) const { + return AcquireLoc == other.AcquireLoc; + } + + bool operator!=(const LockData &other) const { + return !(*this == other); + } + + void Profile(llvm::FoldingSetNodeID &ID) const { + ID.AddInteger(AcquireLoc.getRawEncoding()); + } +}; + + /// A Lockset maps each lock (defined above) to information about how it has +/// been locked. +typedef llvm::ImmutableMap<LockID, LockData> Lockset; + +/// \brief We use this class to visit different types of expressions in +/// CFGBlocks, and build up the lockset. +/// An expression may cause us to add or remove locks from the lockset, or else +/// output error messages related to missing locks. +/// FIXME: In future, we may be able to not inherit from a visitor. +class BuildLockset : public StmtVisitor<BuildLockset> { + Sema &S; + Lockset LSet; + Lockset::Factory &LocksetFactory; + + // Helper functions + void RemoveLock(SourceLocation UnlockLoc, Expr *LockExp); + void AddLock(SourceLocation LockLoc, Expr *LockExp); + +public: + BuildLockset(Sema &S, Lockset LS, Lockset::Factory &F) + : StmtVisitor<BuildLockset>(), S(S), LSet(LS), + LocksetFactory(F) {} + + Lockset getLockset() { + return LSet; + } + + void VisitDeclRefExpr(DeclRefExpr *Exp); + void VisitCXXMemberCallExpr(CXXMemberCallExpr *Exp); +}; + +/// \brief Add a new lock to the lockset, warning if the lock is already there. +/// \param LockExp The lock expression corresponding to the lock to be added +/// \param LockLoc The source location of the acquire +void BuildLockset::AddLock(SourceLocation LockLoc, Expr *LockExp) { + LockID Lock(LockExp); + LockData NewLockData(LockLoc); + + if (LSet.contains(Lock)) + S.Diag(LockLoc, diag::warn_double_lock) << Lock.getName(); + + LSet = LocksetFactory.add(LSet, Lock, NewLockData); +} + +/// \brief Remove a lock from the lockset, warning if the lock is not there. +/// \param LockExp The lock expression corresponding to the lock to be removed +/// \param UnlockLoc The source location of the unlock (only used in error msg) +void BuildLockset::RemoveLock(SourceLocation UnlockLoc, Expr *LockExp) { + LockID Lock(LockExp); + + Lockset NewLSet = LocksetFactory.remove(LSet, Lock); + if(NewLSet == LSet) + S.Diag(UnlockLoc, diag::warn_unlock_but_no_acquire) << Lock.getName(); + + LSet = NewLSet; +} + +void BuildLockset::VisitDeclRefExpr(DeclRefExpr *Exp) { + // FIXME: checking for guarded_by/var and pt_guarded_by/var +} + +/// \brief When visiting CXXMemberCallExprs we need to examine the attributes on +/// the method that is being called and add, remove or check locks in the +/// lockset accordingly. +void BuildLockset::VisitCXXMemberCallExpr(CXXMemberCallExpr *Exp) { + NamedDecl *D = dyn_cast<NamedDecl>(Exp->getCalleeDecl()); + SourceLocation ExpLocation = Exp->getExprLoc(); + Expr *Parent = Exp->getImplicitObjectArgument(); + + if(!D || !D->hasAttrs()) + return; + + AttrVec &ArgAttrs = D->getAttrs(); + for(unsigned i = 0; i < ArgAttrs.size(); ++i) { + Attr *Attr = ArgAttrs[i]; + switch (Attr->getKind()) { + // When we encounter an exclusive lock function, we need to add the lock + // to our lockset. + case attr::ExclusiveLockFunction: { + ExclusiveLockFunctionAttr *ELFAttr = + cast<ExclusiveLockFunctionAttr>(Attr); + + if (ELFAttr->args_size() == 0) {// The lock held is the "this" object. + AddLock(ExpLocation, Parent); + break; + } + + for (ExclusiveLockFunctionAttr::args_iterator I = ELFAttr->args_begin(), + E = ELFAttr->args_end(); I != E; ++I) + AddLock(ExpLocation, *I); + // FIXME: acquired_after/acquired_before annotations + break; + } + + // When we encounter an unlock function, we need to remove unlocked locks + // from the lockset, and flag a warning if they are not there. + case attr::UnlockFunction: { + UnlockFunctionAttr *UFAttr = cast<UnlockFunctionAttr>(Attr); + + if (UFAttr->args_size() == 0) { // The lock held is the "this" object. + RemoveLock(ExpLocation, Parent); + break; + } + + for (UnlockFunctionAttr::args_iterator I = UFAttr->args_begin(), + E = UFAttr->args_end(); I != E; ++I) + RemoveLock(ExpLocation, *I); + break; + } + + // Ignore other (non thread-safety) attributes + default: + break; + } + } +} + +typedef std::pair<SourceLocation, PartialDiagnostic> DelayedDiag; +typedef llvm::SmallVector<DelayedDiag, 4> DiagList; + +struct SortDiagBySourceLocation { + Sema &S; + + SortDiagBySourceLocation(Sema &S) : S(S) {} + + bool operator()(const DelayedDiag &left, const DelayedDiag &right) { + // Although this call will be slow, this is only called when outputting + // multiple warnings. + return S.getSourceManager().isBeforeInTranslationUnit(left.first, + right.first); + } +}; +} // end anonymous namespace + +/// \brief Emit all buffered diagnostics in order of sourcelocation. +/// We need to output diagnostics produced while iterating through +/// the lockset in deterministic order, so this function orders diagnostics +/// and outputs them. +static void EmitDiagnostics(Sema &S, DiagList &D) { + SortDiagBySourceLocation SortDiagBySL(S); + sort(D.begin(), D.end(), SortDiagBySL); + for (DiagList::iterator I = D.begin(), E = D.end(); I != E; ++I) + S.Diag(I->first, I->second); +} + +/// \brief Compute the intersection of two locksets and issue warnings for any +/// locks in the symmetric difference. +/// +/// This function is used at a merge point in the CFG when comparing the lockset +/// of each branch being merged. For example, given the following sequence: +/// A; if () then B; else C; D; we need to check that the lockset after B and C +/// are the same. In the event of a difference, we use the intersection of these +/// two locksets at the start of D. +static Lockset intersectAndWarn(Sema &S, Lockset LSet1, Lockset LSet2, + Lockset::Factory &Fact) { + Lockset Intersection = LSet1; + DiagList Warnings; + + for (Lockset::iterator I = LSet2.begin(), E = LSet2.end(); I != E; ++I) { + if (!LSet1.contains(I.getKey())) { + const LockID &MissingLock = I.getKey(); + const LockData &MissingLockData = I.getData(); + PartialDiagnostic Warning = + S.PDiag(diag::warn_lock_not_released_in_scope) << MissingLock.getName(); + Warnings.push_back(DelayedDiag(MissingLockData.AcquireLoc, Warning)); + } + } + + for (Lockset::iterator I = LSet1.begin(), E = LSet1.end(); I != E; ++I) { + if (!LSet2.contains(I.getKey())) { + const LockID &MissingLock = I.getKey(); + const LockData &MissingLockData = I.getData(); + PartialDiagnostic Warning = + S.PDiag(diag::warn_lock_not_released_in_scope) << MissingLock.getName(); + Warnings.push_back(DelayedDiag(MissingLockData.AcquireLoc, Warning)); + Intersection = Fact.remove(Intersection, MissingLock); + } + } + + EmitDiagnostics(S, Warnings); + return Intersection; +} + +/// \brief Returns the location of the first Stmt in a Block. +static SourceLocation getFirstStmtLocation(CFGBlock *Block) { + for (CFGBlock::const_iterator BI = Block->begin(), BE = Block->end(); + BI != BE; ++BI) { + if (const CFGStmt *CfgStmt = dyn_cast<CFGStmt>(&(*BI))) + return CfgStmt->getStmt()->getLocStart(); + } + return SourceLocation(); +} + +/// \brief Warn about different locksets along backedges of loops. +/// This function is called when we encounter a back edge. At that point, +/// we need to verify that the lockset before taking the backedge is the +/// same as the lockset before entering the loop. +/// +/// \param LoopEntrySet Locks held before starting the loop +/// \param LoopReentrySet Locks held in the last CFG block of the loop +static void warnBackEdgeUnequalLocksets(Sema &S, const Lockset LoopReentrySet, + const Lockset LoopEntrySet, + SourceLocation FirstLocInLoop) { + assert(FirstLocInLoop.isValid()); + DiagList Warnings; + + // Warn for locks held at the start of the loop, but not the end. + for (Lockset::iterator I = LoopEntrySet.begin(), E = LoopEntrySet.end(); + I != E; ++I) { + if (!LoopReentrySet.contains(I.getKey())) { + const LockID &MissingLock = I.getKey(); + // We report this error at the location of the first statement in a loop + PartialDiagnostic Warning = + S.PDiag(diag::warn_expecting_lock_held_on_loop) + << MissingLock.getName(); + Warnings.push_back(DelayedDiag(FirstLocInLoop, Warning)); + } + } + + // Warn for locks held at the end of the loop, but not at the start. + for (Lockset::iterator I = LoopReentrySet.begin(), E = LoopReentrySet.end(); + I != E; ++I) { + if (!LoopEntrySet.contains(I.getKey())) { + const LockID &MissingLock = I.getKey(); + const LockData &MissingLockData = I.getData(); + PartialDiagnostic Warning = + S.PDiag(diag::warn_lock_not_released_in_scope) << MissingLock.getName(); + Warnings.push_back(DelayedDiag(MissingLockData.AcquireLoc, Warning)); + } + } + + EmitDiagnostics(S, Warnings); +} + +/// \brief Check a function's CFG for thread-safety violations. +/// +/// We traverse the blocks in the CFG, compute the set of locks that are held +/// at the end of each block, and issue warnings for thread safety violations. +/// Each block in the CFG is traversed exactly once. +static void checkThreadSafety(Sema &S, AnalysisContext &AC) { + CFG *CFGraph = AC.getCFG(); + if (!CFGraph) return; + + StringRef FunName; + if (const NamedDecl *ContextDecl = dyn_cast<NamedDecl>(AC.getDecl())) + FunName = ContextDecl->getName(); + + Lockset::Factory LocksetFactory; + + // FIXME: Swith to SmallVector? Otherwise improve performance impact? + std::vector<Lockset> EntryLocksets(CFGraph->getNumBlockIDs(), + LocksetFactory.getEmptyMap()); + std::vector<Lockset> ExitLocksets(CFGraph->getNumBlockIDs(), + LocksetFactory.getEmptyMap()); + + // We need to explore the CFG via a "topological" ordering. + // That way, we will be guaranteed to have information about required + // predecessor locksets when exploring a new block. + TopologicallySortedCFG SortedGraph(CFGraph); + CFGBlockSet VisitedBlocks(CFGraph); + + for (TopologicallySortedCFG::iterator I = SortedGraph.begin(), + E = SortedGraph.end(); I!= E; ++I) { + const CFGBlock *CurrBlock = *I; + int CurrBlockID = CurrBlock->getBlockID(); + + VisitedBlocks.insert(CurrBlock); + + // Use the default initial lockset in case there are no predecessors. + Lockset &Entryset = EntryLocksets[CurrBlockID]; + Lockset &Exitset = ExitLocksets[CurrBlockID]; + + // Iterate through the predecessor blocks and warn if the lockset for all + // predecessors is not the same. We take the entry lockset of the current + // block to be the intersection of all previous locksets. + // FIXME: By keeping the intersection, we may output more errors in future + // for a lock which is not in the intersection, but was in the union. We + // may want to also keep the union in future. As an example, let's say + // the intersection contains Lock L, and the union contains L and M. + // Later we unlock M. At this point, we would output an error because we + // never locked M; although the real error is probably that we forgot to + // lock M on all code paths. Conversely, let's say that later we lock M. + // In this case, we should compare against the intersection instead of the + // union because the real error is probably that we forgot to unlock M on + // all code paths. + bool LocksetInitialized = false; + for (CFGBlock::const_pred_iterator PI = CurrBlock->pred_begin(), + PE = CurrBlock->pred_end(); PI != PE; ++PI) { + + // if *PI -> CurrBlock is a back edge + if (!VisitedBlocks.alreadySet(*PI)) + continue; + + int PrevBlockID = (*PI)->getBlockID(); + if (!LocksetInitialized) { + Entryset = ExitLocksets[PrevBlockID]; + LocksetInitialized = true; + } else { + Entryset = intersectAndWarn(S, Entryset, ExitLocksets[PrevBlockID], + LocksetFactory); + } + } + + BuildLockset LocksetBuilder(S, Entryset, LocksetFactory); + for (CFGBlock::const_iterator BI = CurrBlock->begin(), + BE = CurrBlock->end(); BI != BE; ++BI) { + if (const CFGStmt *CfgStmt = dyn_cast<CFGStmt>(&*BI)) { + LocksetBuilder.Visit(CfgStmt->getStmt()); + } + } + Exitset = LocksetBuilder.getLockset(); + + // For every back edge from CurrBlock (the end of the loop) to another block + // (FirstLoopBlock) we need to check that the Lockset of Block is equal to + // the one held at the beginning of FirstLoopBlock. We can look up the + // Lockset held at the beginning of FirstLoopBlock in the EntryLockSets map. + for (CFGBlock::const_succ_iterator SI = CurrBlock->succ_begin(), + SE = CurrBlock->succ_end(); SI != SE; ++SI) { + + // if CurrBlock -> *SI is *not* a back edge + if (!VisitedBlocks.alreadySet(*SI)) + continue; + + CFGBlock *FirstLoopBlock = *SI; + SourceLocation FirstLoopLocation = getFirstStmtLocation(FirstLoopBlock); + + Lockset PreLoop = EntryLocksets[FirstLoopBlock->getBlockID()]; + Lockset LoopEnd = ExitLocksets[CurrBlockID]; + warnBackEdgeUnequalLocksets(S, LoopEnd, PreLoop, FirstLoopLocation); + } + } + + Lockset FinalLockset = ExitLocksets[CFGraph->getExit().getBlockID()]; + if (!FinalLockset.isEmpty()) { + DiagList Warnings; + for (Lockset::iterator I=FinalLockset.begin(), E=FinalLockset.end(); + I != E; ++I) { + const LockID &MissingLock = I.getKey(); + const LockData &MissingLockData = I.getData(); + PartialDiagnostic Warning = + S.PDiag(diag::warn_locks_not_released) + << MissingLock.getName() << FunName; + Warnings.push_back(DelayedDiag(MissingLockData.AcquireLoc, Warning)); + } + EmitDiagnostics(S, Warnings); + } +} + + //===----------------------------------------------------------------------===// // AnalysisBasedWarnings - Worker object used by Sema to execute analysis-based // warnings on a function, method, or block. @@ -593,6 +1145,7 @@ public: clang::sema::AnalysisBasedWarnings::Policy::Policy() { enableCheckFallThrough = 1; enableCheckUnreachable = 0; + enableThreadSafetyAnalysis = 0; } clang::sema::AnalysisBasedWarnings::AnalysisBasedWarnings(Sema &s) @@ -610,6 +1163,10 @@ clang::sema::AnalysisBasedWarnings::AnalysisBasedWarnings(Sema &s) DefaultPolicy.enableCheckUnreachable = (unsigned) (D.getDiagnosticLevel(diag::warn_unreachable, SourceLocation()) != Diagnostic::Ignored); + DefaultPolicy.enableThreadSafetyAnalysis = (unsigned) + (D.getDiagnosticLevel(diag::warn_double_lock, SourceLocation()) != + Diagnostic::Ignored); + } static void flushDiagnostics(Sema &S, sema::FunctionScopeInfo *fscope) { @@ -735,6 +1292,10 @@ AnalysisBasedWarnings::IssueWarnings(sema::AnalysisBasedWarnings::Policy P, if (P.enableCheckUnreachable) CheckUnreachable(S, AC); + // Check for thread safety violations + if (P.enableThreadSafetyAnalysis) + checkThreadSafety(S, AC); + if (Diags.getDiagnosticLevel(diag::warn_uninit_var, D->getLocStart()) != Diagnostic::Ignored || Diags.getDiagnosticLevel(diag::warn_maybe_uninit_var, D->getLocStart()) |