-//===- ThreadSafety.cpp ----------------------------------------*- C++ --*-===//
+//===- ThreadSafety.cpp ---------------------------------------------------===//
//
// The LLVM Compiler Infrastructure
//
#include "clang/Analysis/Analyses/ThreadSafety.h"
#include "clang/AST/Attr.h"
+#include "clang/AST/Decl.h"
#include "clang/AST/DeclCXX.h"
+#include "clang/AST/DeclGroup.h"
+#include "clang/AST/Expr.h"
#include "clang/AST/ExprCXX.h"
-#include "clang/AST/StmtCXX.h"
+#include "clang/AST/OperationKinds.h"
+#include "clang/AST/Stmt.h"
#include "clang/AST/StmtVisitor.h"
+#include "clang/AST/Type.h"
#include "clang/Analysis/Analyses/PostOrderCFGView.h"
#include "clang/Analysis/Analyses/ThreadSafetyCommon.h"
-#include "clang/Analysis/Analyses/ThreadSafetyLogical.h"
#include "clang/Analysis/Analyses/ThreadSafetyTIL.h"
#include "clang/Analysis/Analyses/ThreadSafetyTraverse.h"
+#include "clang/Analysis/Analyses/ThreadSafetyUtil.h"
#include "clang/Analysis/AnalysisDeclContext.h"
#include "clang/Analysis/CFG.h"
-#include "clang/Analysis/CFGStmtMap.h"
+#include "clang/Basic/LLVM.h"
#include "clang/Basic/OperatorKinds.h"
#include "clang/Basic/SourceLocation.h"
-#include "clang/Basic/SourceManager.h"
+#include "clang/Basic/Specifiers.h"
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/ImmutableMap.h"
-#include "llvm/ADT/PostOrderIterator.h"
+#include "llvm/ADT/Optional.h"
+#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/Allocator.h"
+#include "llvm/Support/Casting.h"
+#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/raw_ostream.h"
#include <algorithm>
-#include <ostream>
-#include <sstream>
+#include <cassert>
+#include <functional>
+#include <iterator>
+#include <memory>
+#include <string>
+#include <type_traits>
#include <utility>
#include <vector>
+
using namespace clang;
using namespace threadSafety;
// Key method definition
-ThreadSafetyHandler::~ThreadSafetyHandler() {}
+ThreadSafetyHandler::~ThreadSafetyHandler() = default;
namespace {
+
class TILPrinter :
- public til::PrettyPrinter<TILPrinter, llvm::raw_ostream> {};
+ public til::PrettyPrinter<TILPrinter, llvm::raw_ostream> {};
+} // namespace
/// Issue a warning about an invalid lock expression
static void warnInvalidLock(ThreadSafetyHandler &Handler,
Handler.handleInvalidLockExp(Kind, Loc);
}
+namespace {
+
/// \brief A set of CapabilityInfo objects, which are compiled from the
/// requires attributes on a function.
class CapExprSet : public SmallVector<CapabilityExpr, 4> {
/// shared.
class FactEntry : public CapabilityExpr {
private:
- LockKind LKind; ///< exclusive or shared
- SourceLocation AcquireLoc; ///< where it was acquired.
- bool Asserted; ///< true if the lock was asserted
- bool Declared; ///< true if the lock was declared
+ /// Exclusive or shared.
+ LockKind LKind;
+
+ /// Where it was acquired.
+ SourceLocation AcquireLoc;
+
+ /// True if the lock was asserted.
+ bool Asserted;
+
+ /// True if the lock was declared.
+ bool Declared;
public:
FactEntry(const CapabilityExpr &CE, LockKind LK, SourceLocation Loc,
bool Asrt, bool Declrd = false)
: CapabilityExpr(CE), LKind(LK), AcquireLoc(Loc), Asserted(Asrt),
Declared(Declrd) {}
+ virtual ~FactEntry() = default;
- virtual ~FactEntry() {}
-
- LockKind kind() const { return LKind; }
- SourceLocation loc() const { return AcquireLoc; }
- bool asserted() const { return Asserted; }
- bool declared() const { return Declared; }
+ LockKind kind() const { return LKind; }
+ SourceLocation loc() const { return AcquireLoc; }
+ bool asserted() const { return Asserted; }
+ bool declared() const { return Declared; }
void setDeclared(bool D) { Declared = D; }
}
};
-
-typedef unsigned short FactID;
+using FactID = unsigned short;
/// \brief FactManager manages the memory for all facts that are created during
/// the analysis of a single routine.
FactEntry &operator[](FactID F) { return *Facts[F]; }
};
-
/// \brief A FactSet is the set of facts that are known to be true at a
/// particular program point. FactSets must be small, because they are
/// frequently copied, and are thus implemented as a set of indices into a
/// may involve partial pattern matches, rather than exact matches.
class FactSet {
private:
- typedef SmallVector<FactID, 4> FactVec;
+ using FactVec = SmallVector<FactID, 4>;
FactVec FactIDs;
public:
- typedef FactVec::iterator iterator;
- typedef FactVec::const_iterator const_iterator;
+ using iterator = FactVec::iterator;
+ using const_iterator = FactVec::const_iterator;
- iterator begin() { return FactIDs.begin(); }
+ iterator begin() { return FactIDs.begin(); }
const_iterator begin() const { return FactIDs.begin(); }
- iterator end() { return FactIDs.end(); }
+ iterator end() { return FactIDs.end(); }
const_iterator end() const { return FactIDs.end(); }
bool isEmpty() const { return FactIDs.size() == 0; }
// Return true if the set contains only negative facts
bool isEmpty(FactManager &FactMan) const {
- for (FactID FID : *this) {
+ for (const auto FID : *this) {
if (!FactMan[FID].negative())
return false;
}
};
class ThreadSafetyAnalyzer;
+
} // namespace
namespace clang {
namespace threadSafety {
+
class BeforeSet {
private:
- typedef SmallVector<const ValueDecl*, 4> BeforeVect;
+ using BeforeVect = SmallVector<const ValueDecl *, 4>;
struct BeforeInfo {
- BeforeInfo() : Visited(0) {}
- BeforeInfo(BeforeInfo &&) = default;
-
BeforeVect Vect;
- int Visited;
+ int Visited = 0;
+
+ BeforeInfo() = default;
+ BeforeInfo(BeforeInfo &&) = default;
};
- typedef llvm::DenseMap<const ValueDecl *, std::unique_ptr<BeforeInfo>>
- BeforeMap;
- typedef llvm::DenseMap<const ValueDecl*, bool> CycleMap;
+ using BeforeMap =
+ llvm::DenseMap<const ValueDecl *, std::unique_ptr<BeforeInfo>>;
+ using CycleMap = llvm::DenseMap<const ValueDecl *, bool>;
public:
- BeforeSet() { }
+ BeforeSet() = default;
BeforeInfo* insertAttrExprs(const ValueDecl* Vd,
ThreadSafetyAnalyzer& Analyzer);
private:
BeforeMap BMap;
- CycleMap CycMap;
+ CycleMap CycMap;
};
-} // end namespace threadSafety
-} // end namespace clang
+
+} // namespace threadSafety
+} // namespace clang
namespace {
-typedef llvm::ImmutableMap<const NamedDecl*, unsigned> LocalVarContext;
+
class LocalVariableMap;
+using LocalVarContext = llvm::ImmutableMap<const NamedDecl *, unsigned>;
+
/// A side (entry or exit) of a CFG node.
enum CFGBlockSide { CBS_Entry, CBS_Exit };
/// maintained for each block in the CFG. See LocalVariableMap for more
/// information about the contexts.
struct CFGBlockInfo {
- FactSet EntrySet; // Lockset held at entry to block
- FactSet ExitSet; // Lockset held at exit from block
- LocalVarContext EntryContext; // Context held at entry to block
- LocalVarContext ExitContext; // Context held at exit from block
- SourceLocation EntryLoc; // Location of first statement in block
- SourceLocation ExitLoc; // Location of last statement in block.
- unsigned EntryIndex; // Used to replay contexts later
- bool Reachable; // Is this block reachable?
+ // Lockset held at entry to block
+ FactSet EntrySet;
+
+ // Lockset held at exit from block
+ FactSet ExitSet;
+
+ // Context held at entry to block
+ LocalVarContext EntryContext;
+
+ // Context held at exit from block
+ LocalVarContext ExitContext;
+
+ // Location of first statement in block
+ SourceLocation EntryLoc;
+
+ // Location of last statement in block.
+ SourceLocation ExitLoc;
+
+ // Used to replay contexts later
+ unsigned EntryIndex;
+
+ // Is this block reachable?
+ bool Reachable = false;
const FactSet &getSet(CFGBlockSide Side) const {
return Side == CBS_Entry ? EntrySet : ExitSet;
}
+
SourceLocation getLocation(CFGBlockSide Side) const {
return Side == CBS_Entry ? EntryLoc : ExitLoc;
}
private:
CFGBlockInfo(LocalVarContext EmptyCtx)
- : EntryContext(EmptyCtx), ExitContext(EmptyCtx), Reachable(false)
- { }
+ : EntryContext(EmptyCtx), ExitContext(EmptyCtx) {}
public:
static CFGBlockInfo getEmptyBlockInfo(LocalVariableMap &M);
};
-
-
// A LocalVariableMap maintains a map from local variables to their currently
// valid definitions. It provides SSA-like functionality when traversing the
// CFG. Like SSA, each definition or assignment to a variable is assigned a
// that Context to look up the definitions of variables.
class LocalVariableMap {
public:
- typedef LocalVarContext Context;
+ using Context = LocalVarContext;
/// A VarDefinition consists of an expression, representing the value of the
/// variable, along with the context in which that expression should be
public:
friend class LocalVariableMap;
- const NamedDecl *Dec; // The original declaration for this variable.
- const Expr *Exp; // The expression for this variable, OR
- unsigned Ref; // Reference to another VarDefinition
- Context Ctx; // The map with which Exp should be interpreted.
+ // The original declaration for this variable.
+ const NamedDecl *Dec;
+
+ // The expression for this variable, OR
+ const Expr *Exp = nullptr;
+
+ // Reference to another VarDefinition
+ unsigned Ref = 0;
+
+ // The map with which Exp should be interpreted.
+ Context Ctx;
bool isReference() { return !Exp; }
private:
// Create ordinary variable definition
VarDefinition(const NamedDecl *D, const Expr *E, Context C)
- : Dec(D), Exp(E), Ref(0), Ctx(C)
- { }
+ : Dec(D), Exp(E), Ctx(C) {}
// Create reference to previous definition
VarDefinition(const NamedDecl *D, unsigned R, Context C)
- : Dec(D), Exp(nullptr), Ref(R), Ctx(C)
- { }
+ : Dec(D), Ref(R), Ctx(C) {}
};
private:
Context::Factory ContextFactory;
std::vector<VarDefinition> VarDefinitions;
std::vector<unsigned> CtxIndices;
- std::vector<std::pair<Stmt*, Context> > SavedContexts;
+ std::vector<std::pair<Stmt *, Context>> SavedContexts;
public:
LocalVariableMap() {
std::vector<CFGBlockInfo> &BlockInfo);
protected:
+ friend class VarMapBuilder;
+
// Get the current context index
unsigned getContextIndex() { return SavedContexts.size()-1; }
// Save the current context for later replay
void saveContext(Stmt *S, Context C) {
- SavedContexts.push_back(std::make_pair(S,C));
+ SavedContexts.push_back(std::make_pair(S, C));
}
// Adds a new definition to the given context, and returns a new context.
Context intersectContexts(Context C1, Context C2);
Context createReferenceContext(Context C);
void intersectBackEdge(Context C1, Context C2);
-
- friend class VarMapBuilder;
};
+} // namespace
// This has to be defined after LocalVariableMap.
CFGBlockInfo CFGBlockInfo::getEmptyBlockInfo(LocalVariableMap &M) {
return CFGBlockInfo(M.getEmptyContext());
}
+namespace {
/// Visitor which builds a LocalVariableMap
class VarMapBuilder : public StmtVisitor<VarMapBuilder> {
LocalVariableMap::Context Ctx;
VarMapBuilder(LocalVariableMap *VM, LocalVariableMap::Context C)
- : VMap(VM), Ctx(C) {}
+ : VMap(VM), Ctx(C) {}
void VisitDeclStmt(DeclStmt *S);
void VisitBinaryOperator(BinaryOperator *BO);
};
+} // namespace
// Add new local variables to the variable map
void VarMapBuilder::VisitDeclStmt(DeclStmt *S) {
Expr *LHSExp = BO->getLHS()->IgnoreParenCasts();
// Update the variable map and current context.
- if (DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(LHSExp)) {
- ValueDecl *VDec = DRE->getDecl();
+ if (const auto *DRE = dyn_cast<DeclRefExpr>(LHSExp)) {
+ const ValueDecl *VDec = DRE->getDecl();
if (Ctx.lookup(VDec)) {
if (BO->getOpcode() == BO_Assign)
Ctx = VMap->updateDefinition(VDec, BO->getRHS(), Ctx);
}
}
-
// Computes the intersection of two contexts. The intersection is the
// set of variables which have the same definition in both contexts;
// variables with different definitions are discarded.
}
}
-
// Traverse the CFG in topological order, so all predecessors of a block
// (excluding back-edges) are visited before the block itself. At
// each point in the code, we calculate a Context, which holds the set of
// while (b) { x -> x2, y -> y1 | [1st:] x2=x1; [2nd:] x2=NULL; }
// x = x+1; { x -> x3, y -> y1 | x3 = x2 + 1, ... }
// ... { y -> y1 | x3 = 2, x2 = 1, ... }
-//
void LocalVariableMap::traverseCFG(CFG *CFGraph,
const PostOrderCFGView *SortedGraph,
std::vector<CFGBlockInfo> &BlockInfo) {
// Visit all the statements in the basic block.
VarMapBuilder VMapBuilder(this, CurrBlockInfo->EntryContext);
- for (CFGBlock::const_iterator BI = CurrBlock->begin(),
- BE = CurrBlock->end(); BI != BE; ++BI) {
- switch (BI->getKind()) {
+ for (const auto &BI : *CurrBlock) {
+ switch (BI.getKind()) {
case CFGElement::Statement: {
- CFGStmt CS = BI->castAs<CFGStmt>();
- VMapBuilder.Visit(const_cast<Stmt*>(CS.getStmt()));
+ CFGStmt CS = BI.castAs<CFGStmt>();
+ VMapBuilder.Visit(const_cast<Stmt *>(CS.getStmt()));
break;
}
default:
if (CurrBlockInfo->ExitLoc.isValid()) {
// This block contains at least one statement. Find the source location
// of the first statement in the block.
- for (CFGBlock::const_iterator BI = CurrBlock->begin(),
- BE = CurrBlock->end(); BI != BE; ++BI) {
+ for (const auto &BI : *CurrBlock) {
// FIXME: Handle other CFGElement kinds.
- if (Optional<CFGStmt> CS = BI->getAs<CFGStmt>()) {
+ if (Optional<CFGStmt> CS = BI.getAs<CFGStmt>()) {
CurrBlockInfo->EntryLoc = CS->getStmt()->getLocStart();
break;
}
}
}
+namespace {
+
class LockableFactEntry : public FactEntry {
private:
- bool Managed; ///< managed by ScopedLockable object
+ /// managed by ScopedLockable object
+ bool Managed;
public:
LockableFactEntry(const CapabilityExpr &CE, LockKind LK, SourceLocation Loc,
handleRemovalFromIntersection(const FactSet &FSet, FactManager &FactMan,
SourceLocation JoinLoc, LockErrorKind LEK,
ThreadSafetyHandler &Handler) const override {
- for (const til::SExpr *UnderlyingMutex : UnderlyingMutexes) {
+ for (const auto *UnderlyingMutex : UnderlyingMutexes) {
if (FSet.findLock(FactMan, CapabilityExpr(UnderlyingMutex, false))) {
// If this scoped lock manages another mutex, and if the underlying
// mutex is still held, then warn about the underlying mutex.
bool FullyRemove, ThreadSafetyHandler &Handler,
StringRef DiagKind) const override {
assert(!Cp.negative() && "Managing object cannot be negative.");
- for (const til::SExpr *UnderlyingMutex : UnderlyingMutexes) {
+ for (const auto *UnderlyingMutex : UnderlyingMutexes) {
CapabilityExpr UnderCp(UnderlyingMutex, false);
auto UnderEntry = llvm::make_unique<LockableFactEntry>(
!UnderCp, LK_Exclusive, UnlockLoc);
threadSafety::til::MemRegionRef Arena;
threadSafety::SExprBuilder SxBuilder;
- ThreadSafetyHandler &Handler;
- const CXXMethodDecl *CurrentMethod;
- LocalVariableMap LocalVarMap;
- FactManager FactMan;
+ ThreadSafetyHandler &Handler;
+ const CXXMethodDecl *CurrentMethod;
+ LocalVariableMap LocalVarMap;
+ FactManager FactMan;
std::vector<CFGBlockInfo> BlockInfo;
- BeforeSet* GlobalBeforeSet;
+ BeforeSet *GlobalBeforeSet;
public:
ThreadSafetyAnalyzer(ThreadSafetyHandler &H, BeforeSet* Bset)
- : Arena(&Bpa), SxBuilder(Arena), Handler(H), GlobalBeforeSet(Bset) {}
+ : Arena(&Bpa), SxBuilder(Arena), Handler(H), GlobalBeforeSet(Bset) {}
bool inCurrentScope(const CapabilityExpr &CapE);
void runAnalysis(AnalysisDeclContext &AC);
};
+
} // namespace
/// Process acquired_before and acquired_after attributes on Vd.
Info = InfoPtr.get();
}
- for (Attr* At : Vd->attrs()) {
+ for (const auto *At : Vd->attrs()) {
switch (At->getKind()) {
case attr::AcquiredBefore: {
- auto *A = cast<AcquiredBeforeAttr>(At);
+ const auto *A = cast<AcquiredBeforeAttr>(At);
// Read exprs from the attribute, and add them to BeforeVect.
for (const auto *Arg : A->args()) {
Analyzer.SxBuilder.translateAttrExpr(Arg, nullptr);
if (const ValueDecl *Cpvd = Cp.valueDecl()) {
Info->Vect.push_back(Cpvd);
- auto It = BMap.find(Cpvd);
+ const auto It = BMap.find(Cpvd);
if (It == BMap.end())
insertAttrExprs(Cpvd, Analyzer);
}
break;
}
case attr::AcquiredAfter: {
- auto *A = cast<AcquiredAfterAttr>(At);
+ const auto *A = cast<AcquiredAfterAttr>(At);
// Read exprs from the attribute, and add them to BeforeVect.
for (const auto *Arg : A->args()) {
InfoVect.push_back(Info);
Info->Visited = 1;
- for (auto *Vdb : Info->Vect) {
+ for (const auto *Vdb : Info->Vect) {
// Exclude mutexes in our immediate before set.
if (FSet.containsMutexDecl(Analyzer.FactMan, Vdb)) {
StringRef L1 = StartVd->getName();
traverse(StartVd);
- for (auto* Info : InfoVect)
+ for (auto *Info : InfoVect)
Info->Visited = 0;
}
-
-
/// \brief Gets the value decl pointer from DeclRefExprs or MemberExprs.
static const ValueDecl *getValueDecl(const Expr *Exp) {
if (const auto *CE = dyn_cast<ImplicitCastExpr>(Exp))
}
namespace {
+
template <typename Ty>
class has_arg_iterator_range {
- typedef char yes[1];
- typedef char no[2];
+ using yes = char[1];
+ using no = char[2];
template <typename Inner>
static yes& test(Inner *I, decltype(I->args()) * = nullptr);
public:
static const bool value = sizeof(test<Ty>(nullptr)) == sizeof(yes);
};
+
} // namespace
static StringRef ClassifyDiagnostic(const CapabilityAttr *A) {
return "mutex";
}
-
-inline bool ThreadSafetyAnalyzer::inCurrentScope(const CapabilityExpr &CapE) {
+bool ThreadSafetyAnalyzer::inCurrentScope(const CapabilityExpr &CapE) {
if (!CurrentMethod)
return false;
- if (auto *P = dyn_cast_or_null<til::Project>(CapE.sexpr())) {
- auto *VD = P->clangDecl();
+ if (const auto *P = dyn_cast_or_null<til::Project>(CapE.sexpr())) {
+ const auto *VD = P->clangDecl();
if (VD)
return VD->getDeclContext() == CurrentMethod->getDeclContext();
}
return false;
}
-
/// \brief Add a new lock to the lockset, warning if the lock is already there.
/// \param ReqAttr -- true if this is part of an initial Requires attribute.
void ThreadSafetyAnalyzer::addLock(FactSet &FSet,
}
}
-
/// \brief Remove a lock from the lockset, warning if the lock is not there.
/// \param UnlockLoc The source location of the unlock (only used in error msg)
void ThreadSafetyAnalyzer::removeLock(FactSet &FSet, const CapabilityExpr &Cp,
DiagKind);
}
-
/// \brief Extract the list of mutexIDs from the attribute on an expression,
/// and push them onto Mtxs, discarding any duplicates.
template <typename AttrType>
}
}
-
/// \brief Extract the list of mutexIDs from a trylock attribute. If the
/// trylock applies to the given edge, then push them onto Mtxs, discarding
/// any duplicates.
Expr *BrE, bool Neg) {
// Find out which branch has the lock
bool branch = false;
- if (CXXBoolLiteralExpr *BLE = dyn_cast_or_null<CXXBoolLiteralExpr>(BrE))
+ if (const auto *BLE = dyn_cast_or_null<CXXBoolLiteralExpr>(BrE))
branch = BLE->getValue();
- else if (IntegerLiteral *ILE = dyn_cast_or_null<IntegerLiteral>(BrE))
+ else if (const auto *ILE = dyn_cast_or_null<IntegerLiteral>(BrE))
branch = ILE->getValue().getBoolValue();
int branchnum = branch ? 0 : 1;
if (isa<CXXNullPtrLiteralExpr>(E) || isa<GNUNullExpr>(E)) {
TCond = false;
return true;
- } else if (CXXBoolLiteralExpr *BLE = dyn_cast<CXXBoolLiteralExpr>(E)) {
+ } else if (const auto *BLE = dyn_cast<CXXBoolLiteralExpr>(E)) {
TCond = BLE->getValue();
return true;
- } else if (IntegerLiteral *ILE = dyn_cast<IntegerLiteral>(E)) {
+ } else if (const auto *ILE = dyn_cast<IntegerLiteral>(E)) {
TCond = ILE->getValue().getBoolValue();
return true;
- } else if (ImplicitCastExpr *CE = dyn_cast<ImplicitCastExpr>(E)) {
+ } else if (auto *CE = dyn_cast<ImplicitCastExpr>(E))
return getStaticBooleanValue(CE->getSubExpr(), TCond);
- }
return false;
}
-
// If Cond can be traced back to a function call, return the call expression.
// The negate variable should be called with false, and will be set to true
// if the function call is negated, e.g. if (!mu.tryLock(...))
if (!Cond)
return nullptr;
- if (const CallExpr *CallExp = dyn_cast<CallExpr>(Cond)) {
+ if (const auto *CallExp = dyn_cast<CallExpr>(Cond))
return CallExp;
- }
- else if (const ParenExpr *PE = dyn_cast<ParenExpr>(Cond)) {
+ else if (const auto *PE = dyn_cast<ParenExpr>(Cond))
return getTrylockCallExpr(PE->getSubExpr(), C, Negate);
- }
- else if (const ImplicitCastExpr *CE = dyn_cast<ImplicitCastExpr>(Cond)) {
+ else if (const auto *CE = dyn_cast<ImplicitCastExpr>(Cond))
return getTrylockCallExpr(CE->getSubExpr(), C, Negate);
- }
- else if (const ExprWithCleanups* EWC = dyn_cast<ExprWithCleanups>(Cond)) {
+ else if (const auto *EWC = dyn_cast<ExprWithCleanups>(Cond))
return getTrylockCallExpr(EWC->getSubExpr(), C, Negate);
- }
- else if (const DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(Cond)) {
+ else if (const auto *DRE = dyn_cast<DeclRefExpr>(Cond)) {
const Expr *E = LocalVarMap.lookupExpr(DRE->getDecl(), C);
return getTrylockCallExpr(E, C, Negate);
}
- else if (const UnaryOperator *UOP = dyn_cast<UnaryOperator>(Cond)) {
+ else if (const auto *UOP = dyn_cast<UnaryOperator>(Cond)) {
if (UOP->getOpcode() == UO_LNot) {
Negate = !Negate;
return getTrylockCallExpr(UOP->getSubExpr(), C, Negate);
}
return nullptr;
}
- else if (const BinaryOperator *BOP = dyn_cast<BinaryOperator>(Cond)) {
+ else if (const auto *BOP = dyn_cast<BinaryOperator>(Cond)) {
if (BOP->getOpcode() == BO_EQ || BOP->getOpcode() == BO_NE) {
if (BOP->getOpcode() == BO_NE)
Negate = !Negate;
// LHS must have been evaluated in a different block.
return getTrylockCallExpr(BOP->getRHS(), C, Negate);
}
- if (BOP->getOpcode() == BO_LOr) {
+ if (BOP->getOpcode() == BO_LOr)
return getTrylockCallExpr(BOP->getRHS(), C, Negate);
- }
return nullptr;
}
return nullptr;
}
-
/// \brief Find the lockset that holds on the edge between PredBlock
/// and CurrBlock. The edge set is the exit set of PredBlock (passed
/// as the ExitSet parameter) plus any trylocks, which are conditionally held.
const LocalVarContext &LVarCtx = PredBlockInfo->ExitContext;
StringRef CapDiagKind = "mutex";
- CallExpr *Exp =
- const_cast<CallExpr*>(getTrylockCallExpr(Cond, LVarCtx, Negate));
+ auto *Exp = const_cast<CallExpr *>(getTrylockCallExpr(Cond, LVarCtx, Negate));
if (!Exp)
return;
- NamedDecl *FunDecl = dyn_cast_or_null<NamedDecl>(Exp->getCalleeDecl());
+ auto *FunDecl = dyn_cast_or_null<NamedDecl>(Exp->getCalleeDecl());
if(!FunDecl || !FunDecl->hasAttrs())
return;
CapExprSet SharedLocksToAdd;
// If the condition is a call to a Trylock function, then grab the attributes
- for (auto *Attr : FunDecl->attrs()) {
+ for (const auto *Attr : FunDecl->attrs()) {
switch (Attr->getKind()) {
case attr::ExclusiveTrylockFunction: {
- ExclusiveTrylockFunctionAttr *A =
- cast<ExclusiveTrylockFunctionAttr>(Attr);
+ const auto *A = cast<ExclusiveTrylockFunctionAttr>(Attr);
getMutexIDs(ExclusiveLocksToAdd, A, Exp, FunDecl,
PredBlock, CurrBlock, A->getSuccessValue(), Negate);
CapDiagKind = ClassifyDiagnostic(A);
break;
}
case attr::SharedTrylockFunction: {
- SharedTrylockFunctionAttr *A =
- cast<SharedTrylockFunctionAttr>(Attr);
+ const auto *A = cast<SharedTrylockFunctionAttr>(Attr);
getMutexIDs(SharedLocksToAdd, A, Exp, FunDecl,
PredBlock, CurrBlock, A->getSuccessValue(), Negate);
CapDiagKind = ClassifyDiagnostic(A);
}
namespace {
+
/// \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
public:
BuildLockset(ThreadSafetyAnalyzer *Anlzr, CFGBlockInfo &Info)
- : StmtVisitor<BuildLockset>(),
- Analyzer(Anlzr),
- FSet(Info.EntrySet),
- LVarCtx(Info.EntryContext),
- CtxIndex(Info.EntryIndex)
- {}
+ : StmtVisitor<BuildLockset>(), Analyzer(Anlzr), FSet(Info.EntrySet),
+ LVarCtx(Info.EntryContext), CtxIndex(Info.EntryIndex) {}
void VisitUnaryOperator(UnaryOperator *UO);
void VisitBinaryOperator(BinaryOperator *BO);
void VisitCXXConstructExpr(CXXConstructExpr *Exp);
void VisitDeclStmt(DeclStmt *S);
};
+
} // namespace
/// \brief Warn if the LSet does not contain a lock sufficient to protect access
break;
}
- if (const UnaryOperator *UO = dyn_cast<UnaryOperator>(Exp)) {
+ if (const auto *UO = dyn_cast<UnaryOperator>(Exp)) {
// For dereferences
- if (UO->getOpcode() == clang::UO_Deref)
+ if (UO->getOpcode() == UO_Deref)
checkPtAccess(UO->getSubExpr(), AK, POK);
return;
}
- if (const ArraySubscriptExpr *AE = dyn_cast<ArraySubscriptExpr>(Exp)) {
+ if (const auto *AE = dyn_cast<ArraySubscriptExpr>(Exp)) {
checkPtAccess(AE->getLHS(), AK, POK);
return;
}
- if (const MemberExpr *ME = dyn_cast<MemberExpr>(Exp)) {
+ if (const auto *ME = dyn_cast<MemberExpr>(Exp)) {
if (ME->isArrow())
checkPtAccess(ME->getBase(), AK, POK);
else
ClassifyDiagnostic(I), Loc);
}
-
/// \brief Checks pt_guarded_by and pt_guarded_var attributes.
/// POK is the same operationKind that was passed to checkAccess.
void BuildLockset::checkPtAccess(const Expr *Exp, AccessKind AK,
ProtectedOperationKind POK) {
while (true) {
- if (const ParenExpr *PE = dyn_cast<ParenExpr>(Exp)) {
+ if (const auto *PE = dyn_cast<ParenExpr>(Exp)) {
Exp = PE->getSubExpr();
continue;
}
- if (const CastExpr *CE = dyn_cast<CastExpr>(Exp)) {
+ if (const auto *CE = dyn_cast<CastExpr>(Exp)) {
if (CE->getCastKind() == CK_ArrayToPointerDecay) {
// If it's an actual array, and not a pointer, then it's elements
// are protected by GUARDED_BY, not PT_GUARDED_BY;
// Figure out if we're constructing an object of scoped lockable class
bool isScopedVar = false;
if (VD) {
- if (const CXXConstructorDecl *CD = dyn_cast<const CXXConstructorDecl>(D)) {
+ if (const auto *CD = dyn_cast<const CXXConstructorDecl>(D)) {
const CXXRecordDecl* PD = CD->getParent();
if (PD && PD->hasAttr<ScopedLockableAttr>())
isScopedVar = true;
}
for(Attr *Atconst : D->attrs()) {
- Attr* At = const_cast<Attr*>(Atconst);
+ auto *At = const_cast<Attr *>(Atconst);
switch (At->getKind()) {
// When we encounter a lock function, we need to add the lock to our
// lockset.
case attr::AcquireCapability: {
- auto *A = cast<AcquireCapabilityAttr>(At);
+ const auto *A = cast<AcquireCapabilityAttr>(At);
Analyzer->getMutexIDs(A->isShared() ? SharedLocksToAdd
: ExclusiveLocksToAdd,
A, Exp, D, VD);
// a warning if it is already there, and will not generate a warning
// if it is not removed.
case attr::AssertExclusiveLock: {
- AssertExclusiveLockAttr *A = cast<AssertExclusiveLockAttr>(At);
+ const auto *A = cast<AssertExclusiveLockAttr>(At);
CapExprSet AssertLocks;
Analyzer->getMutexIDs(AssertLocks, A, Exp, D, VD);
break;
}
case attr::AssertSharedLock: {
- AssertSharedLockAttr *A = cast<AssertSharedLockAttr>(At);
+ const auto *A = cast<AssertSharedLockAttr>(At);
CapExprSet AssertLocks;
Analyzer->getMutexIDs(AssertLocks, A, Exp, D, VD);
}
case attr::AssertCapability: {
- AssertCapabilityAttr *A = cast<AssertCapabilityAttr>(At);
+ const auto *A = cast<AssertCapabilityAttr>(At);
CapExprSet AssertLocks;
Analyzer->getMutexIDs(AssertLocks, A, Exp, D, VD);
for (const auto &AssertLock : AssertLocks)
// When we encounter an unlock function, we need to remove unlocked
// mutexes from the lockset, and flag a warning if they are not there.
case attr::ReleaseCapability: {
- auto *A = cast<ReleaseCapabilityAttr>(At);
+ const auto *A = cast<ReleaseCapabilityAttr>(At);
if (A->isGeneric())
Analyzer->getMutexIDs(GenericLocksToRemove, A, Exp, D, VD);
else if (A->isShared())
}
case attr::RequiresCapability: {
- RequiresCapabilityAttr *A = cast<RequiresCapabilityAttr>(At);
+ const auto *A = cast<RequiresCapabilityAttr>(At);
for (auto *Arg : A->args()) {
warnIfMutexNotHeld(D, Exp, A->isShared() ? AK_Read : AK_Written, Arg,
POK_FunctionCall, ClassifyDiagnostic(A),
}
case attr::LocksExcluded: {
- LocksExcludedAttr *A = cast<LocksExcludedAttr>(At);
+ const auto *A = cast<LocksExcludedAttr>(At);
for (auto *Arg : A->args())
warnIfMutexHeld(D, Exp, Arg, ClassifyDiagnostic(A));
break;
Analyzer->removeLock(FSet, M, Loc, Dtor, LK_Generic, CapDiagKind);
}
-
/// \brief For unary operations which read and write a variable, we need to
/// check whether we hold any required mutexes. Reads are checked in
/// VisitCastExpr.
void BuildLockset::VisitUnaryOperator(UnaryOperator *UO) {
switch (UO->getOpcode()) {
- case clang::UO_PostDec:
- case clang::UO_PostInc:
- case clang::UO_PreDec:
- case clang::UO_PreInc: {
+ case UO_PostDec:
+ case UO_PostInc:
+ case UO_PreDec:
+ case UO_PreInc:
checkAccess(UO->getSubExpr(), AK_Written);
break;
- }
default:
break;
}
checkAccess(BO->getLHS(), AK_Written);
}
-
/// Whenever we do an LValue to Rvalue cast, we are reading a variable and
/// need to ensure we hold any required mutexes.
/// FIXME: Deal with non-primitive types.
checkAccess(CE->getSubExpr(), AK_Read);
}
-
void BuildLockset::VisitCallExpr(CallExpr *Exp) {
bool ExamineArgs = true;
bool OperatorFun = false;
- if (CXXMemberCallExpr *CE = dyn_cast<CXXMemberCallExpr>(Exp)) {
- MemberExpr *ME = dyn_cast<MemberExpr>(CE->getCallee());
+ if (const auto *CE = dyn_cast<CXXMemberCallExpr>(Exp)) {
+ const auto *ME = dyn_cast<MemberExpr>(CE->getCallee());
// ME can be null when calling a method pointer
- CXXMethodDecl *MD = CE->getMethodDecl();
+ const CXXMethodDecl *MD = CE->getMethodDecl();
if (ME && MD) {
if (ME->isArrow()) {
- if (MD->isConst()) {
+ if (MD->isConst())
checkPtAccess(CE->getImplicitObjectArgument(), AK_Read);
- } else { // FIXME -- should be AK_Written
+ else // FIXME -- should be AK_Written
checkPtAccess(CE->getImplicitObjectArgument(), AK_Read);
- }
} else {
if (MD->isConst())
checkAccess(CE->getImplicitObjectArgument(), AK_Read);
checkAccess(CE->getImplicitObjectArgument(), AK_Read);
}
}
- } else if (CXXOperatorCallExpr *OE = dyn_cast<CXXOperatorCallExpr>(Exp)) {
+ } else if (const auto *OE = dyn_cast<CXXOperatorCallExpr>(Exp)) {
OperatorFun = true;
auto OEop = OE->getOperator();
if (ExamineArgs) {
if (FunctionDecl *FD = Exp->getDirectCallee()) {
-
// NO_THREAD_SAFETY_ANALYSIS does double duty here. Normally it
// only turns off checking within the body of a function, but we also
// use it to turn off checking in arguments to the function. This
// could result in some false negatives, but the alternative is to
// create yet another attribute.
- //
if (!FD->hasAttr<NoThreadSafetyAnalysisAttr>()) {
unsigned Fn = FD->getNumParams();
unsigned Cn = Exp->getNumArgs();
}
}
- NamedDecl *D = dyn_cast_or_null<NamedDecl>(Exp->getCalleeDecl());
+ auto *D = dyn_cast_or_null<NamedDecl>(Exp->getCalleeDecl());
if(!D || !D->hasAttrs())
return;
handleCall(Exp, D);
// pick the first declared such constructor rather than trying to guess which
// one is more appropriate.
CXXConstructorDecl *CopyCtor = nullptr;
- for (CXXConstructorDecl *Ctor : RD->ctors()) {
+ for (auto *Ctor : RD->ctors()) {
if (Ctor->isDeleted())
continue;
if (Ctor->isMoveConstructor())
LVarCtx = Analyzer->LocalVarMap.getNextContext(CtxIndex, S, LVarCtx);
for (auto *D : S->getDeclGroup()) {
- if (VarDecl *VD = dyn_cast_or_null<VarDecl>(D)) {
+ if (auto *VD = dyn_cast_or_null<VarDecl>(D)) {
Expr *E = VD->getInit();
if (!E)
continue;
if (auto *BTE = dyn_cast<CXXBindTemporaryExpr>(E))
E = BTE->getSubExpr();
- if (CXXConstructExpr *CE = dyn_cast<CXXConstructExpr>(E)) {
- NamedDecl *CtorD = dyn_cast_or_null<NamedDecl>(CE->getConstructor());
+ if (const auto *CE = dyn_cast<CXXConstructExpr>(E)) {
+ const auto *CtorD = dyn_cast_or_null<NamedDecl>(CE->getConstructor());
if (!CtorD || !CtorD->hasAttrs())
continue;
handleCall(E, CtorD, VD);
}
}
-
-
/// \brief Compute the intersection of two locksets and issue warnings for any
/// locks in the symmetric difference.
///
}
}
-
// Return true if block B never continues to its successors.
static bool neverReturns(const CFGBlock *B) {
if (B->hasNoReturnElement())
return false;
}
-
/// \brief Check a function's CFG for thread-safety violations.
///
/// We traverse the blocks in the CFG, compute the set of mutexes that are held
CFG *CFGraph = walker.getGraph();
const NamedDecl *D = walker.getDecl();
- const FunctionDecl *CurrentFunction = dyn_cast<FunctionDecl>(D);
+ const auto *CurrentFunction = dyn_cast<FunctionDecl>(D);
CurrentMethod = dyn_cast<CXXMethodDecl>(D);
if (D->hasAttr<NoThreadSafetyAnalysisAttr>())
SmallVector<CFGBlock *, 8> SpecialBlocks;
for (CFGBlock::const_pred_iterator PI = CurrBlock->pred_begin(),
PE = CurrBlock->pred_end(); PI != PE; ++PI) {
-
// if *PI -> CurrBlock is a back edge
if (*PI == nullptr || !VisitedBlocks.alreadySet(*PI))
continue;
BuildLockset LocksetBuilder(this, *CurrBlockInfo);
// Visit all the statements in the basic block.
- for (CFGBlock::const_iterator BI = CurrBlock->begin(),
- BE = CurrBlock->end(); BI != BE; ++BI) {
- switch (BI->getKind()) {
+ for (const auto &BI : *CurrBlock) {
+ switch (BI.getKind()) {
case CFGElement::Statement: {
- CFGStmt CS = BI->castAs<CFGStmt>();
- LocksetBuilder.Visit(const_cast<Stmt*>(CS.getStmt()));
+ CFGStmt CS = BI.castAs<CFGStmt>();
+ LocksetBuilder.Visit(const_cast<Stmt *>(CS.getStmt()));
break;
}
// Ignore BaseDtor, MemberDtor, and TemporaryDtor for now.
case CFGElement::AutomaticObjectDtor: {
- CFGAutomaticObjDtor AD = BI->castAs<CFGAutomaticObjDtor>();
- CXXDestructorDecl *DD = const_cast<CXXDestructorDecl *>(
+ CFGAutomaticObjDtor AD = BI.castAs<CFGAutomaticObjDtor>();
+ auto *DD = const_cast<CXXDestructorDecl *>(
AD.getDestructorDecl(AC.getASTContext()));
if (!DD->hasAttrs())
break;
// Create a dummy expression,
- VarDecl *VD = const_cast<VarDecl*>(AD.getVarDecl());
+ auto *VD = const_cast<VarDecl *>(AD.getVarDecl());
DeclRefExpr DRE(VD, false, VD->getType().getNonReferenceType(),
VK_LValue, AD.getTriggerStmt()->getLocEnd());
LocksetBuilder.handleCall(&DRE, DD);
// 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 (*SI == nullptr || !VisitedBlocks.alreadySet(*SI))
continue;
Handler.leaveFunction(CurrentFunction);
}
-
/// \brief Check a function's CFG for thread-safety violations.
///
/// We traverse the blocks in the CFG, compute the set of mutexes that are held