forked from OSchip/llvm-project
Updating the capability attribute diagnostics to be more capability-neutral. Instead of using terminology such as "lock", "unlock" and "locked", the new terminology is "acquire", "release" and "held". Additionally, the capability attribute's name argument is now reported as part of the diagnostic, instead of hard coding as "mutex."
llvm-svn: 205359
This commit is contained in:
parent
6b444c5c8e
commit
e044904301
|
@ -73,39 +73,46 @@ public:
|
|||
virtual ~ThreadSafetyHandler();
|
||||
|
||||
/// Warn about lock expressions which fail to resolve to lockable objects.
|
||||
/// \param Kind -- the capability's name parameter (role, mutex, etc).
|
||||
/// \param Loc -- the SourceLocation of the unresolved expression.
|
||||
virtual void handleInvalidLockExp(SourceLocation Loc) {}
|
||||
virtual void handleInvalidLockExp(StringRef Kind, SourceLocation Loc) {}
|
||||
|
||||
/// Warn about unlock function calls that do not have a prior matching lock
|
||||
/// expression.
|
||||
/// \param Kind -- the capability's name parameter (role, mutex, etc).
|
||||
/// \param LockName -- A StringRef name for the lock expression, to be printed
|
||||
/// in the error message.
|
||||
/// \param Loc -- The SourceLocation of the Unlock
|
||||
virtual void handleUnmatchedUnlock(Name LockName, SourceLocation Loc) {}
|
||||
virtual void handleUnmatchedUnlock(StringRef Kind, Name LockName,
|
||||
SourceLocation Loc) {}
|
||||
|
||||
/// Warn about an unlock function call that attempts to unlock a lock with
|
||||
/// the incorrect lock kind. For instance, a shared lock being unlocked
|
||||
/// exclusively, or vice versa.
|
||||
/// \param LockName -- A StringRef name for the lock expression, to be printed
|
||||
/// in the error message.
|
||||
/// \param Kind -- the capability's name parameter (role, mutex, etc).
|
||||
/// \param Expected -- the kind of lock expected.
|
||||
/// \param Received -- the kind of lock received.
|
||||
/// \param Loc -- The SourceLocation of the Unlock.
|
||||
virtual void handleIncorrectUnlockKind(Name LockName, LockKind Expected,
|
||||
LockKind Received,
|
||||
virtual void handleIncorrectUnlockKind(StringRef Kind, Name LockName,
|
||||
LockKind Expected, LockKind Received,
|
||||
SourceLocation Loc) {}
|
||||
|
||||
/// Warn about lock function calls for locks which are already held.
|
||||
/// \param Kind -- the capability's name parameter (role, mutex, etc).
|
||||
/// \param LockName -- A StringRef name for the lock expression, to be printed
|
||||
/// in the error message.
|
||||
/// \param Loc -- The location of the second lock expression.
|
||||
virtual void handleDoubleLock(Name LockName, SourceLocation Loc) {}
|
||||
virtual void handleDoubleLock(StringRef Kind, Name LockName,
|
||||
SourceLocation Loc) {}
|
||||
|
||||
/// Warn about situations where a mutex is sometimes held and sometimes not.
|
||||
/// The three situations are:
|
||||
/// 1. a mutex is locked on an "if" branch but not the "else" branch,
|
||||
/// 2, or a mutex is only held at the start of some loop iterations,
|
||||
/// 3. or when a mutex is locked but not unlocked inside a function.
|
||||
/// \param Kind -- the capability's name parameter (role, mutex, etc).
|
||||
/// \param LockName -- A StringRef name for the lock expression, to be printed
|
||||
/// in the error message.
|
||||
/// \param LocLocked -- The location of the lock expression where the mutex is
|
||||
|
@ -113,50 +120,56 @@ public:
|
|||
/// \param LocEndOfScope -- The location of the end of the scope where the
|
||||
/// mutex is no longer held
|
||||
/// \param LEK -- which of the three above cases we should warn for
|
||||
virtual void handleMutexHeldEndOfScope(Name LockName,
|
||||
virtual void handleMutexHeldEndOfScope(StringRef Kind, Name LockName,
|
||||
SourceLocation LocLocked,
|
||||
SourceLocation LocEndOfScope,
|
||||
LockErrorKind LEK){}
|
||||
LockErrorKind LEK) {}
|
||||
|
||||
/// Warn when a mutex is held exclusively and shared at the same point. For
|
||||
/// example, if a mutex is locked exclusively during an if branch and shared
|
||||
/// during the else branch.
|
||||
/// \param Kind -- the capability's name parameter (role, mutex, etc).
|
||||
/// \param LockName -- A StringRef name for the lock expression, to be printed
|
||||
/// in the error message.
|
||||
/// \param Loc1 -- The location of the first lock expression.
|
||||
/// \param Loc2 -- The location of the second lock expression.
|
||||
virtual void handleExclusiveAndShared(Name LockName, SourceLocation Loc1,
|
||||
virtual void handleExclusiveAndShared(StringRef Kind, Name LockName,
|
||||
SourceLocation Loc1,
|
||||
SourceLocation Loc2) {}
|
||||
|
||||
/// Warn when a protected operation occurs while no locks are held.
|
||||
/// \param Kind -- the capability's name parameter (role, mutex, etc).
|
||||
/// \param D -- The decl for the protected variable or function
|
||||
/// \param POK -- The kind of protected operation (e.g. variable access)
|
||||
/// \param AK -- The kind of access (i.e. read or write) that occurred
|
||||
/// \param Loc -- The location of the protected operation.
|
||||
virtual void handleNoMutexHeld(const NamedDecl *D, ProtectedOperationKind POK,
|
||||
AccessKind AK, SourceLocation Loc) {}
|
||||
virtual void handleNoMutexHeld(StringRef Kind, const NamedDecl *D,
|
||||
ProtectedOperationKind POK, AccessKind AK,
|
||||
SourceLocation Loc) {}
|
||||
|
||||
/// Warn when a protected operation occurs while the specific mutex protecting
|
||||
/// the operation is not locked.
|
||||
/// \param Kind -- the capability's name parameter (role, mutex, etc).
|
||||
/// \param D -- The decl for the protected variable or function
|
||||
/// \param POK -- The kind of protected operation (e.g. variable access)
|
||||
/// \param LockName -- A StringRef name for the lock expression, to be printed
|
||||
/// in the error message.
|
||||
/// \param LK -- The kind of access (i.e. read or write) that occurred
|
||||
/// \param Loc -- The location of the protected operation.
|
||||
virtual void handleMutexNotHeld(const NamedDecl *D,
|
||||
virtual void handleMutexNotHeld(StringRef Kind, const NamedDecl *D,
|
||||
ProtectedOperationKind POK, Name LockName,
|
||||
LockKind LK, SourceLocation Loc,
|
||||
Name *PossibleMatch=0) {}
|
||||
Name *PossibleMatch = 0) {}
|
||||
|
||||
/// Warn when a function is called while an excluded mutex is locked. For
|
||||
/// example, the mutex may be locked inside the function.
|
||||
/// \param Kind -- the capability's name parameter (role, mutex, etc).
|
||||
/// \param FunName -- The name of the function
|
||||
/// \param LockName -- A StringRef name for the lock expression, to be printed
|
||||
/// in the error message.
|
||||
/// \param Loc -- The location of the function call.
|
||||
virtual void handleFunExcludesLock(Name FunName, Name LockName,
|
||||
SourceLocation Loc) {}
|
||||
virtual void handleFunExcludesLock(StringRef Kind, Name FunName,
|
||||
Name LockName, SourceLocation Loc) {}
|
||||
|
||||
bool issueBetaWarnings() { return IssueBetaWarnings; }
|
||||
void setIssueBetaWarnings(bool b) { IssueBetaWarnings = b; }
|
||||
|
|
|
@ -2204,45 +2204,43 @@ def err_attribute_argument_out_of_range : Error<
|
|||
":must be between 1 and %2}2">;
|
||||
|
||||
// Thread Safety Analysis
|
||||
def warn_unlock_but_no_lock : Warning<
|
||||
"unlocking '%0' that was not locked">,
|
||||
def warn_unlock_but_no_lock : Warning<"releasing %0 '%1' that was not held">,
|
||||
InGroup<ThreadSafetyAnalysis>, DefaultIgnore;
|
||||
def warn_unlock_kind_mismatch : Warning<
|
||||
"unlocking '%0' using %select{shared|exclusive}1 access, expected "
|
||||
"%select{shared|exclusive}2 access">,
|
||||
"releasing %0 '%1' using %select{shared|exclusive}2 access, expected "
|
||||
"%select{shared|exclusive}3 access">,
|
||||
InGroup<ThreadSafetyAnalysis>, DefaultIgnore;
|
||||
def warn_double_lock : Warning<
|
||||
"locking '%0' that is already locked">,
|
||||
def warn_double_lock : Warning<"acquiring %0 '%1' that is already held">,
|
||||
InGroup<ThreadSafetyAnalysis>, DefaultIgnore;
|
||||
def warn_no_unlock : Warning<
|
||||
"mutex '%0' is still locked at the end of function">,
|
||||
"%0 '%1' is still held at the end of function">,
|
||||
InGroup<ThreadSafetyAnalysis>, DefaultIgnore;
|
||||
def warn_expecting_locked : Warning<
|
||||
"expecting mutex '%0' to be locked at the end of function">,
|
||||
"expecting %0 '%1' to be held at the end of function">,
|
||||
InGroup<ThreadSafetyAnalysis>, DefaultIgnore;
|
||||
// FIXME: improve the error message about locks not in scope
|
||||
def warn_lock_some_predecessors : Warning<
|
||||
"mutex '%0' is not locked on every path through here">,
|
||||
"%0 '%1' is not held on every path through here">,
|
||||
InGroup<ThreadSafetyAnalysis>, DefaultIgnore;
|
||||
def warn_expecting_lock_held_on_loop : Warning<
|
||||
"expecting mutex '%0' to be locked at start of each loop">,
|
||||
"expecting %0 '%1' to be held at start of each loop">,
|
||||
InGroup<ThreadSafetyAnalysis>, DefaultIgnore;
|
||||
def note_locked_here : Note<"mutex acquired here">;
|
||||
def note_locked_here : Note<"%0 acquired here">;
|
||||
def warn_lock_exclusive_and_shared : Warning<
|
||||
"mutex '%0' is locked exclusively and shared in the same scope">,
|
||||
"%0 '%1' is acquired exclusively and shared in the same scope">,
|
||||
InGroup<ThreadSafetyAnalysis>, DefaultIgnore;
|
||||
def note_lock_exclusive_and_shared : Note<
|
||||
"the other lock of mutex '%0' is here">;
|
||||
"the other acquisition of %0 '%1' is here">;
|
||||
def warn_variable_requires_any_lock : Warning<
|
||||
"%select{reading|writing}1 variable '%0' requires locking "
|
||||
"%select{reading|writing}1 variable '%0' requires holding "
|
||||
"%select{any mutex|any mutex exclusively}1">,
|
||||
InGroup<ThreadSafetyAnalysis>, DefaultIgnore;
|
||||
def warn_var_deref_requires_any_lock : Warning<
|
||||
"%select{reading|writing}1 the value pointed to by '%0' requires locking "
|
||||
"%select{reading|writing}1 the value pointed to by '%0' requires holding "
|
||||
"%select{any mutex|any mutex exclusively}1">,
|
||||
InGroup<ThreadSafetyAnalysis>, DefaultIgnore;
|
||||
def warn_fun_excludes_mutex : Warning<
|
||||
"cannot call function '%0' while mutex '%1' is locked">,
|
||||
"cannot call function '%1' while %0 '%2' is held">,
|
||||
InGroup<ThreadSafetyAnalysis>, DefaultIgnore;
|
||||
def warn_cannot_resolve_lock : Warning<
|
||||
"cannot resolve lock expression">,
|
||||
|
@ -2250,28 +2248,26 @@ def warn_cannot_resolve_lock : Warning<
|
|||
|
||||
// Imprecise thread safety warnings
|
||||
def warn_variable_requires_lock : Warning<
|
||||
"%select{reading|writing}2 variable '%0' requires locking "
|
||||
"%select{'%1'|'%1' exclusively}2">,
|
||||
"%select{reading|writing}3 variable '%1' requires holding %0 "
|
||||
"%select{'%2'|'%2' exclusively}3">,
|
||||
InGroup<ThreadSafetyAnalysis>, DefaultIgnore;
|
||||
def warn_var_deref_requires_lock : Warning<
|
||||
"%select{reading|writing}2 the value pointed to by '%0' requires locking "
|
||||
"%select{'%1'|'%1' exclusively}2">,
|
||||
"%select{reading|writing}3 the value pointed to by '%1' requires "
|
||||
"holding %0 %select{'%2'|'%2' exclusively}3">,
|
||||
InGroup<ThreadSafetyAnalysis>, DefaultIgnore;
|
||||
def warn_fun_requires_lock : Warning<
|
||||
"calling function '%0' requires %select{shared|exclusive}2 lock on '%1'">,
|
||||
"calling function '%1' requires holding %0 %select{'%2'|'%2' exclusively}3">,
|
||||
InGroup<ThreadSafetyAnalysis>, DefaultIgnore;
|
||||
|
||||
// Precise thread safety warnings
|
||||
def warn_variable_requires_lock_precise : Warning<
|
||||
"%select{reading|writing}2 variable '%0' requires locking "
|
||||
"%select{'%1'|'%1' exclusively}2">,
|
||||
def warn_variable_requires_lock_precise :
|
||||
Warning<warn_variable_requires_lock.Text>,
|
||||
InGroup<ThreadSafetyPrecise>, DefaultIgnore;
|
||||
def warn_var_deref_requires_lock_precise : Warning<
|
||||
"%select{reading|writing}2 the value pointed to by '%0' requires locking "
|
||||
"%select{'%1'|'%1' exclusively}2">,
|
||||
def warn_var_deref_requires_lock_precise :
|
||||
Warning<warn_var_deref_requires_lock.Text>,
|
||||
InGroup<ThreadSafetyPrecise>, DefaultIgnore;
|
||||
def warn_fun_requires_lock_precise : Warning<
|
||||
"calling function '%0' requires %select{shared|exclusive}2 lock on '%1'">,
|
||||
def warn_fun_requires_lock_precise :
|
||||
Warning<warn_fun_requires_lock.Text>,
|
||||
InGroup<ThreadSafetyPrecise>, DefaultIgnore;
|
||||
def note_found_mutex_near_match : Note<"found near match '%0'">;
|
||||
|
||||
|
|
|
@ -564,15 +564,15 @@ public:
|
|||
|
||||
/// Issue a warning about an invalid lock expression
|
||||
static void warnInvalidLock(ThreadSafetyHandler &Handler,
|
||||
const Expr *MutexExp,
|
||||
const Expr *DeclExp, const NamedDecl* D) {
|
||||
const Expr *MutexExp, const Expr *DeclExp,
|
||||
const NamedDecl *D, StringRef Kind) {
|
||||
SourceLocation Loc;
|
||||
if (DeclExp)
|
||||
Loc = DeclExp->getExprLoc();
|
||||
|
||||
// FIXME: add a note about the attribute location in MutexExp or D
|
||||
if (Loc.isValid())
|
||||
Handler.handleInvalidLockExp(Loc);
|
||||
Handler.handleInvalidLockExp(Kind, Loc);
|
||||
}
|
||||
|
||||
bool operator==(const SExpr &other) const {
|
||||
|
@ -1428,9 +1428,10 @@ class ThreadSafetyAnalyzer {
|
|||
public:
|
||||
ThreadSafetyAnalyzer(ThreadSafetyHandler &H) : Handler(H) {}
|
||||
|
||||
void addLock(FactSet &FSet, const SExpr &Mutex, const LockData &LDat);
|
||||
void removeLock(FactSet &FSet, const SExpr &Mutex,
|
||||
SourceLocation UnlockLoc, bool FullyRemove, LockKind Kind);
|
||||
void addLock(FactSet &FSet, const SExpr &Mutex, const LockData &LDat,
|
||||
StringRef DiagKind);
|
||||
void removeLock(FactSet &FSet, const SExpr &Mutex, SourceLocation UnlockLoc,
|
||||
bool FullyRemove, LockKind Kind, StringRef DiagKind);
|
||||
|
||||
template <typename AttrType>
|
||||
void getMutexIDs(MutexIDList &Mtxs, AttrType *Attr, Expr *Exp,
|
||||
|
@ -1463,12 +1464,89 @@ public:
|
|||
void runAnalysis(AnalysisDeclContext &AC);
|
||||
};
|
||||
|
||||
/// \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))
|
||||
return getValueDecl(CE->getSubExpr());
|
||||
|
||||
if (const auto *DR = dyn_cast<DeclRefExpr>(Exp))
|
||||
return DR->getDecl();
|
||||
|
||||
if (const auto *ME = dyn_cast<MemberExpr>(Exp))
|
||||
return ME->getMemberDecl();
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
template <typename Ty>
|
||||
class has_arg_iterator {
|
||||
typedef char yes[1];
|
||||
typedef char no[2];
|
||||
|
||||
template <typename Inner>
|
||||
static yes& test(Inner *I, decltype(I->args_begin()) * = nullptr);
|
||||
|
||||
template <typename>
|
||||
static no& test(...);
|
||||
|
||||
public:
|
||||
static const bool value = sizeof(test<Ty>(nullptr)) == sizeof(yes);
|
||||
};
|
||||
|
||||
static StringRef ClassifyDiagnostic(const CapabilityAttr *A) {
|
||||
return A->getName();
|
||||
}
|
||||
|
||||
static StringRef ClassifyDiagnostic(QualType VDT) {
|
||||
// We need to look at the declaration of the type of the value to determine
|
||||
// which it is. The type should either be a record or a typedef, or a pointer
|
||||
// or reference thereof.
|
||||
if (const auto *RT = VDT->getAs<RecordType>()) {
|
||||
if (const auto *RD = RT->getDecl())
|
||||
if (const auto *CA = RD->getAttr<CapabilityAttr>())
|
||||
return ClassifyDiagnostic(CA);
|
||||
} else if (const auto *TT = VDT->getAs<TypedefType>()) {
|
||||
if (const auto *TD = TT->getDecl())
|
||||
if (const auto *CA = TD->getAttr<CapabilityAttr>())
|
||||
return ClassifyDiagnostic(CA);
|
||||
} else if (VDT->isPointerType() || VDT->isReferenceType())
|
||||
return ClassifyDiagnostic(VDT->getPointeeType());
|
||||
|
||||
return "mutex";
|
||||
}
|
||||
|
||||
static StringRef ClassifyDiagnostic(const ValueDecl *VD) {
|
||||
assert(VD && "No ValueDecl passed");
|
||||
|
||||
// The ValueDecl is the declaration of a mutex or role (hopefully).
|
||||
return ClassifyDiagnostic(VD->getType());
|
||||
}
|
||||
|
||||
template <typename AttrTy>
|
||||
static typename std::enable_if<!has_arg_iterator<AttrTy>::value,
|
||||
StringRef>::type
|
||||
ClassifyDiagnostic(const AttrTy *A) {
|
||||
if (const ValueDecl *VD = getValueDecl(A->getArg()))
|
||||
return ClassifyDiagnostic(VD);
|
||||
return "mutex";
|
||||
}
|
||||
|
||||
template <typename AttrTy>
|
||||
static typename std::enable_if<has_arg_iterator<AttrTy>::value,
|
||||
StringRef>::type
|
||||
ClassifyDiagnostic(const AttrTy *A) {
|
||||
for (auto I = A->args_begin(), E = A->args_end(); I != E; ++I) {
|
||||
if (const ValueDecl *VD = getValueDecl(*I))
|
||||
return ClassifyDiagnostic(VD);
|
||||
}
|
||||
return "mutex";
|
||||
}
|
||||
|
||||
/// \brief Add a new lock to the lockset, warning if the lock is already there.
|
||||
/// \param Mutex -- the Mutex expression for the lock
|
||||
/// \param LDat -- the LockData for the lock
|
||||
void ThreadSafetyAnalyzer::addLock(FactSet &FSet, const SExpr &Mutex,
|
||||
const LockData &LDat) {
|
||||
const LockData &LDat, StringRef DiagKind) {
|
||||
// FIXME: deal with acquired before/after annotations.
|
||||
// FIXME: Don't always warn when we have support for reentrant locks.
|
||||
if (Mutex.shouldIgnore())
|
||||
|
@ -1476,7 +1554,7 @@ void ThreadSafetyAnalyzer::addLock(FactSet &FSet, const SExpr &Mutex,
|
|||
|
||||
if (FSet.findLock(FactMan, Mutex)) {
|
||||
if (!LDat.Asserted)
|
||||
Handler.handleDoubleLock(Mutex.toString(), LDat.AcquireLoc);
|
||||
Handler.handleDoubleLock(DiagKind, Mutex.toString(), LDat.AcquireLoc);
|
||||
} else {
|
||||
FSet.addLock(FactMan, Mutex, LDat);
|
||||
}
|
||||
|
@ -1488,20 +1566,21 @@ void ThreadSafetyAnalyzer::addLock(FactSet &FSet, const SExpr &Mutex,
|
|||
/// \param UnlockLoc The source location of the unlock (only used in error msg)
|
||||
void ThreadSafetyAnalyzer::removeLock(FactSet &FSet, const SExpr &Mutex,
|
||||
SourceLocation UnlockLoc,
|
||||
bool FullyRemove, LockKind ReceivedKind) {
|
||||
bool FullyRemove, LockKind ReceivedKind,
|
||||
StringRef DiagKind) {
|
||||
if (Mutex.shouldIgnore())
|
||||
return;
|
||||
|
||||
const LockData *LDat = FSet.findLock(FactMan, Mutex);
|
||||
if (!LDat) {
|
||||
Handler.handleUnmatchedUnlock(Mutex.toString(), UnlockLoc);
|
||||
Handler.handleUnmatchedUnlock(DiagKind, Mutex.toString(), UnlockLoc);
|
||||
return;
|
||||
}
|
||||
|
||||
// Generic lock removal doesn't care about lock kind mismatches, but
|
||||
// otherwise diagnose when the lock kinds are mismatched.
|
||||
if (ReceivedKind != LK_Generic && LDat->LKind != ReceivedKind) {
|
||||
Handler.handleIncorrectUnlockKind(Mutex.toString(), LDat->LKind,
|
||||
Handler.handleIncorrectUnlockKind(DiagKind, Mutex.toString(), LDat->LKind,
|
||||
ReceivedKind, UnlockLoc);
|
||||
return;
|
||||
}
|
||||
|
@ -1517,8 +1596,8 @@ void ThreadSafetyAnalyzer::removeLock(FactSet &FSet, const SExpr &Mutex,
|
|||
// We're releasing the underlying mutex, but not destroying the
|
||||
// managing object. Warn on dual release.
|
||||
if (!FSet.findLock(FactMan, LDat->UnderlyingMutex)) {
|
||||
Handler.handleUnmatchedUnlock(LDat->UnderlyingMutex.toString(),
|
||||
UnlockLoc);
|
||||
Handler.handleUnmatchedUnlock(
|
||||
DiagKind, LDat->UnderlyingMutex.toString(), UnlockLoc);
|
||||
}
|
||||
FSet.removeLock(FactMan, LDat->UnderlyingMutex);
|
||||
return;
|
||||
|
@ -1540,7 +1619,7 @@ void ThreadSafetyAnalyzer::getMutexIDs(MutexIDList &Mtxs, AttrType *Attr,
|
|||
// The mutex held is the "this" object.
|
||||
SExpr Mu(0, Exp, D, SelfDecl);
|
||||
if (!Mu.isValid())
|
||||
SExpr::warnInvalidLock(Handler, 0, Exp, D);
|
||||
SExpr::warnInvalidLock(Handler, 0, Exp, D, ClassifyDiagnostic(Attr));
|
||||
else
|
||||
Mtxs.push_back_nodup(Mu);
|
||||
return;
|
||||
|
@ -1549,7 +1628,7 @@ void ThreadSafetyAnalyzer::getMutexIDs(MutexIDList &Mtxs, AttrType *Attr,
|
|||
for (iterator_type I=Attr->args_begin(), E=Attr->args_end(); I != E; ++I) {
|
||||
SExpr Mu(*I, Exp, D, SelfDecl);
|
||||
if (!Mu.isValid())
|
||||
SExpr::warnInvalidLock(Handler, *I, Exp, D);
|
||||
SExpr::warnInvalidLock(Handler, *I, Exp, D, ClassifyDiagnostic(Attr));
|
||||
else
|
||||
Mtxs.push_back_nodup(Mu);
|
||||
}
|
||||
|
@ -1682,6 +1761,7 @@ void ThreadSafetyAnalyzer::getEdgeLockset(FactSet& Result,
|
|||
bool Negate = false;
|
||||
const CFGBlockInfo *PredBlockInfo = &BlockInfo[PredBlock->getBlockID()];
|
||||
const LocalVarContext &LVarCtx = PredBlockInfo->ExitContext;
|
||||
StringRef CapDiagKind = "mutex";
|
||||
|
||||
CallExpr *Exp =
|
||||
const_cast<CallExpr*>(getTrylockCallExpr(Cond, LVarCtx, Negate));
|
||||
|
@ -1705,6 +1785,7 @@ void ThreadSafetyAnalyzer::getEdgeLockset(FactSet& Result,
|
|||
cast<ExclusiveTrylockFunctionAttr>(Attr);
|
||||
getMutexIDs(ExclusiveLocksToAdd, A, Exp, FunDecl,
|
||||
PredBlock, CurrBlock, A->getSuccessValue(), Negate);
|
||||
CapDiagKind = ClassifyDiagnostic(A);
|
||||
break;
|
||||
}
|
||||
case attr::SharedTrylockFunction: {
|
||||
|
@ -1712,6 +1793,7 @@ void ThreadSafetyAnalyzer::getEdgeLockset(FactSet& Result,
|
|||
cast<SharedTrylockFunctionAttr>(Attr);
|
||||
getMutexIDs(SharedLocksToAdd, A, Exp, FunDecl,
|
||||
PredBlock, CurrBlock, A->getSuccessValue(), Negate);
|
||||
CapDiagKind = ClassifyDiagnostic(A);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
|
@ -1721,17 +1803,13 @@ void ThreadSafetyAnalyzer::getEdgeLockset(FactSet& Result,
|
|||
|
||||
// Add and remove locks.
|
||||
SourceLocation Loc = Exp->getExprLoc();
|
||||
for (unsigned i=0,n=ExclusiveLocksToAdd.size(); i<n; ++i) {
|
||||
addLock(Result, ExclusiveLocksToAdd[i],
|
||||
LockData(Loc, LK_Exclusive));
|
||||
}
|
||||
for (unsigned i=0,n=SharedLocksToAdd.size(); i<n; ++i) {
|
||||
addLock(Result, SharedLocksToAdd[i],
|
||||
LockData(Loc, LK_Shared));
|
||||
}
|
||||
for (const auto &ExclusiveLockToAdd : ExclusiveLocksToAdd)
|
||||
addLock(Result, ExclusiveLockToAdd, LockData(Loc, LK_Exclusive),
|
||||
CapDiagKind);
|
||||
for (const auto &SharedLockToAdd : SharedLocksToAdd)
|
||||
addLock(Result, SharedLockToAdd, LockData(Loc, LK_Shared), CapDiagKind);
|
||||
}
|
||||
|
||||
|
||||
/// \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
|
||||
|
@ -1746,11 +1824,12 @@ class BuildLockset : public StmtVisitor<BuildLockset> {
|
|||
unsigned CtxIndex;
|
||||
|
||||
// Helper functions
|
||||
const ValueDecl *getValueDecl(const Expr *Exp);
|
||||
|
||||
void warnIfMutexNotHeld(const NamedDecl *D, const Expr *Exp, AccessKind AK,
|
||||
Expr *MutexExp, ProtectedOperationKind POK);
|
||||
void warnIfMutexHeld(const NamedDecl *D, const Expr *Exp, Expr *MutexExp);
|
||||
Expr *MutexExp, ProtectedOperationKind POK,
|
||||
StringRef DiagKind);
|
||||
void warnIfMutexHeld(const NamedDecl *D, const Expr *Exp, Expr *MutexExp,
|
||||
StringRef DiagKind);
|
||||
|
||||
void checkAccess(const Expr *Exp, AccessKind AK);
|
||||
void checkPtAccess(const Expr *Exp, AccessKind AK);
|
||||
|
@ -1774,31 +1853,17 @@ public:
|
|||
void VisitDeclStmt(DeclStmt *S);
|
||||
};
|
||||
|
||||
|
||||
/// \brief Gets the value decl pointer from DeclRefExprs or MemberExprs
|
||||
const ValueDecl *BuildLockset::getValueDecl(const Expr *Exp) {
|
||||
if (const ImplicitCastExpr *CE = dyn_cast<ImplicitCastExpr>(Exp))
|
||||
return getValueDecl(CE->getSubExpr());
|
||||
|
||||
if (const DeclRefExpr *DR = dyn_cast<DeclRefExpr>(Exp))
|
||||
return DR->getDecl();
|
||||
|
||||
if (const MemberExpr *ME = dyn_cast<MemberExpr>(Exp))
|
||||
return ME->getMemberDecl();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// \brief Warn if the LSet does not contain a lock sufficient to protect access
|
||||
/// of at least the passed in AccessKind.
|
||||
void BuildLockset::warnIfMutexNotHeld(const NamedDecl *D, const Expr *Exp,
|
||||
AccessKind AK, Expr *MutexExp,
|
||||
ProtectedOperationKind POK) {
|
||||
ProtectedOperationKind POK,
|
||||
StringRef DiagKind) {
|
||||
LockKind LK = getLockKindFromAccessKind(AK);
|
||||
|
||||
SExpr Mutex(MutexExp, Exp, D);
|
||||
if (!Mutex.isValid()) {
|
||||
SExpr::warnInvalidLock(Analyzer->Handler, MutexExp, Exp, D);
|
||||
SExpr::warnInvalidLock(Analyzer->Handler, MutexExp, Exp, D, DiagKind);
|
||||
return;
|
||||
} else if (Mutex.shouldIgnore()) {
|
||||
return;
|
||||
|
@ -1814,40 +1879,38 @@ void BuildLockset::warnIfMutexNotHeld(const NamedDecl *D, const Expr *Exp,
|
|||
LDat = &FEntry->LDat;
|
||||
std::string PartMatchStr = FEntry->MutID.toString();
|
||||
StringRef PartMatchName(PartMatchStr);
|
||||
Analyzer->Handler.handleMutexNotHeld(D, POK, Mutex.toString(), LK,
|
||||
Exp->getExprLoc(), &PartMatchName);
|
||||
Analyzer->Handler.handleMutexNotHeld(DiagKind, D, POK, Mutex.toString(),
|
||||
LK, Exp->getExprLoc(),
|
||||
&PartMatchName);
|
||||
} else {
|
||||
// Warn that there's no match at all.
|
||||
Analyzer->Handler.handleMutexNotHeld(D, POK, Mutex.toString(), LK,
|
||||
Exp->getExprLoc());
|
||||
Analyzer->Handler.handleMutexNotHeld(DiagKind, D, POK, Mutex.toString(),
|
||||
LK, Exp->getExprLoc());
|
||||
}
|
||||
NoError = false;
|
||||
}
|
||||
// Make sure the mutex we found is the right kind.
|
||||
if (NoError && LDat && !LDat->isAtLeast(LK))
|
||||
Analyzer->Handler.handleMutexNotHeld(D, POK, Mutex.toString(), LK,
|
||||
Analyzer->Handler.handleMutexNotHeld(DiagKind, D, POK, Mutex.toString(), LK,
|
||||
Exp->getExprLoc());
|
||||
}
|
||||
|
||||
/// \brief Warn if the LSet contains the given lock.
|
||||
void BuildLockset::warnIfMutexHeld(const NamedDecl *D, const Expr* Exp,
|
||||
Expr *MutexExp) {
|
||||
void BuildLockset::warnIfMutexHeld(const NamedDecl *D, const Expr *Exp,
|
||||
Expr *MutexExp,
|
||||
StringRef DiagKind) {
|
||||
SExpr Mutex(MutexExp, Exp, D);
|
||||
if (!Mutex.isValid()) {
|
||||
SExpr::warnInvalidLock(Analyzer->Handler, MutexExp, Exp, D);
|
||||
SExpr::warnInvalidLock(Analyzer->Handler, MutexExp, Exp, D, DiagKind);
|
||||
return;
|
||||
}
|
||||
|
||||
LockData* LDat = FSet.findLock(Analyzer->FactMan, Mutex);
|
||||
if (LDat) {
|
||||
std::string DeclName = D->getNameAsString();
|
||||
StringRef DeclNameSR (DeclName);
|
||||
Analyzer->Handler.handleFunExcludesLock(DeclNameSR, Mutex.toString(),
|
||||
Exp->getExprLoc());
|
||||
}
|
||||
if (LDat)
|
||||
Analyzer->Handler.handleFunExcludesLock(
|
||||
DiagKind, D->getNameAsString(), Mutex.toString(), Exp->getExprLoc());
|
||||
}
|
||||
|
||||
|
||||
/// \brief Checks guarded_by and pt_guarded_by attributes.
|
||||
/// Whenever we identify an access (read or write) to a DeclRefExpr that is
|
||||
/// marked with guarded_by, we must ensure the appropriate mutexes are held.
|
||||
|
@ -1880,11 +1943,12 @@ void BuildLockset::checkAccess(const Expr *Exp, AccessKind AK) {
|
|||
return;
|
||||
|
||||
if (D->hasAttr<GuardedVarAttr>() && FSet.isEmpty())
|
||||
Analyzer->Handler.handleNoMutexHeld(D, POK_VarAccess, AK,
|
||||
Analyzer->Handler.handleNoMutexHeld("mutex", D, POK_VarAccess, AK,
|
||||
Exp->getExprLoc());
|
||||
|
||||
for (const auto *I : D->specific_attrs<GuardedByAttr>())
|
||||
warnIfMutexNotHeld(D, Exp, AK, I->getArg(), POK_VarAccess);
|
||||
warnIfMutexNotHeld(D, Exp, AK, I->getArg(), POK_VarAccess,
|
||||
ClassifyDiagnostic(I));
|
||||
}
|
||||
|
||||
/// \brief Checks pt_guarded_by and pt_guarded_var attributes.
|
||||
|
@ -1912,14 +1976,14 @@ void BuildLockset::checkPtAccess(const Expr *Exp, AccessKind AK) {
|
|||
return;
|
||||
|
||||
if (D->hasAttr<PtGuardedVarAttr>() && FSet.isEmpty())
|
||||
Analyzer->Handler.handleNoMutexHeld(D, POK_VarDereference, AK,
|
||||
Analyzer->Handler.handleNoMutexHeld("mutex", D, POK_VarDereference, AK,
|
||||
Exp->getExprLoc());
|
||||
|
||||
for (auto const *I : D->specific_attrs<PtGuardedByAttr>())
|
||||
warnIfMutexNotHeld(D, Exp, AK, I->getArg(), POK_VarDereference);
|
||||
warnIfMutexNotHeld(D, Exp, AK, I->getArg(), POK_VarDereference,
|
||||
ClassifyDiagnostic(I));
|
||||
}
|
||||
|
||||
|
||||
/// \brief Process a function call, method call, constructor call,
|
||||
/// or destructor call. This involves looking at the attributes on the
|
||||
/// corresponding function/method/constructor/destructor, issuing warnings,
|
||||
|
@ -1935,6 +1999,7 @@ void BuildLockset::handleCall(Expr *Exp, const NamedDecl *D, VarDecl *VD) {
|
|||
const AttrVec &ArgAttrs = D->getAttrs();
|
||||
MutexIDList ExclusiveLocksToAdd, SharedLocksToAdd;
|
||||
MutexIDList ExclusiveLocksToRemove, SharedLocksToRemove, GenericLocksToRemove;
|
||||
StringRef CapDiagKind = "mutex";
|
||||
|
||||
for(unsigned i = 0; i < ArgAttrs.size(); ++i) {
|
||||
Attr *At = const_cast<Attr*>(ArgAttrs[i]);
|
||||
|
@ -1946,6 +2011,8 @@ void BuildLockset::handleCall(Expr *Exp, const NamedDecl *D, VarDecl *VD) {
|
|||
Analyzer->getMutexIDs(A->isShared() ? SharedLocksToAdd
|
||||
: ExclusiveLocksToAdd,
|
||||
A, Exp, D, VD);
|
||||
|
||||
CapDiagKind = ClassifyDiagnostic(A);
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -1957,10 +2024,10 @@ void BuildLockset::handleCall(Expr *Exp, const NamedDecl *D, VarDecl *VD) {
|
|||
|
||||
MutexIDList AssertLocks;
|
||||
Analyzer->getMutexIDs(AssertLocks, A, Exp, D, VD);
|
||||
for (unsigned i=0,n=AssertLocks.size(); i<n; ++i) {
|
||||
Analyzer->addLock(FSet, AssertLocks[i],
|
||||
LockData(Loc, LK_Exclusive, false, true));
|
||||
}
|
||||
for (const auto &AssertLock : AssertLocks)
|
||||
Analyzer->addLock(FSet, AssertLock,
|
||||
LockData(Loc, LK_Exclusive, false, true),
|
||||
ClassifyDiagnostic(A));
|
||||
break;
|
||||
}
|
||||
case attr::AssertSharedLock: {
|
||||
|
@ -1968,10 +2035,10 @@ void BuildLockset::handleCall(Expr *Exp, const NamedDecl *D, VarDecl *VD) {
|
|||
|
||||
MutexIDList AssertLocks;
|
||||
Analyzer->getMutexIDs(AssertLocks, A, Exp, D, VD);
|
||||
for (unsigned i=0,n=AssertLocks.size(); i<n; ++i) {
|
||||
Analyzer->addLock(FSet, AssertLocks[i],
|
||||
LockData(Loc, LK_Shared, false, true));
|
||||
}
|
||||
for (const auto &AssertLock : AssertLocks)
|
||||
Analyzer->addLock(FSet, AssertLock,
|
||||
LockData(Loc, LK_Shared, false, true),
|
||||
ClassifyDiagnostic(A));
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -1985,6 +2052,8 @@ void BuildLockset::handleCall(Expr *Exp, const NamedDecl *D, VarDecl *VD) {
|
|||
Analyzer->getMutexIDs(SharedLocksToRemove, A, Exp, D, VD);
|
||||
else
|
||||
Analyzer->getMutexIDs(ExclusiveLocksToRemove, A, Exp, D, VD);
|
||||
|
||||
CapDiagKind = ClassifyDiagnostic(A);
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -1994,7 +2063,7 @@ void BuildLockset::handleCall(Expr *Exp, const NamedDecl *D, VarDecl *VD) {
|
|||
for (RequiresCapabilityAttr::args_iterator I = A->args_begin(),
|
||||
E = A->args_end(); I != E; ++I)
|
||||
warnIfMutexNotHeld(D, Exp, A->isShared() ? AK_Read : AK_Written, *I,
|
||||
POK_FunctionCall);
|
||||
POK_FunctionCall, ClassifyDiagnostic(A));
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -2003,7 +2072,7 @@ void BuildLockset::handleCall(Expr *Exp, const NamedDecl *D, VarDecl *VD) {
|
|||
|
||||
for (LocksExcludedAttr::args_iterator I = A->args_begin(),
|
||||
E = A->args_end(); I != E; ++I) {
|
||||
warnIfMutexHeld(D, Exp, *I);
|
||||
warnIfMutexHeld(D, Exp, *I, ClassifyDiagnostic(A));
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
@ -2026,9 +2095,11 @@ void BuildLockset::handleCall(Expr *Exp, const NamedDecl *D, VarDecl *VD) {
|
|||
|
||||
// Add locks.
|
||||
for (const auto &M : ExclusiveLocksToAdd)
|
||||
Analyzer->addLock(FSet, M, LockData(Loc, LK_Exclusive, isScopedVar));
|
||||
Analyzer->addLock(FSet, M, LockData(Loc, LK_Exclusive, isScopedVar),
|
||||
CapDiagKind);
|
||||
for (const auto &M : SharedLocksToAdd)
|
||||
Analyzer->addLock(FSet, M, LockData(Loc, LK_Shared, isScopedVar));
|
||||
Analyzer->addLock(FSet, M, LockData(Loc, LK_Shared, isScopedVar),
|
||||
CapDiagKind);
|
||||
|
||||
// Add the managing object as a dummy mutex, mapped to the underlying mutex.
|
||||
// FIXME -- this doesn't work if we acquire multiple locks.
|
||||
|
@ -2038,20 +2109,22 @@ void BuildLockset::handleCall(Expr *Exp, const NamedDecl *D, VarDecl *VD) {
|
|||
SExpr SMutex(&DRE, 0, 0);
|
||||
|
||||
for (const auto &M : ExclusiveLocksToAdd)
|
||||
Analyzer->addLock(FSet, SMutex, LockData(MLoc, LK_Exclusive, M));
|
||||
Analyzer->addLock(FSet, SMutex, LockData(MLoc, LK_Exclusive, M),
|
||||
CapDiagKind);
|
||||
for (const auto &M : SharedLocksToAdd)
|
||||
Analyzer->addLock(FSet, SMutex, LockData(MLoc, LK_Shared, M));
|
||||
Analyzer->addLock(FSet, SMutex, LockData(MLoc, LK_Shared, M),
|
||||
CapDiagKind);
|
||||
}
|
||||
|
||||
// Remove locks.
|
||||
// FIXME -- should only fully remove if the attribute refers to 'this'.
|
||||
bool Dtor = isa<CXXDestructorDecl>(D);
|
||||
for (const auto &M : ExclusiveLocksToRemove)
|
||||
Analyzer->removeLock(FSet, M, Loc, Dtor, LK_Exclusive);
|
||||
Analyzer->removeLock(FSet, M, Loc, Dtor, LK_Exclusive, CapDiagKind);
|
||||
for (const auto &M : SharedLocksToRemove)
|
||||
Analyzer->removeLock(FSet, M, Loc, Dtor, LK_Shared);
|
||||
Analyzer->removeLock(FSet, M, Loc, Dtor, LK_Shared, CapDiagKind);
|
||||
for (const auto &M : GenericLocksToRemove)
|
||||
Analyzer->removeLock(FSet, M, Loc, Dtor, LK_Generic);
|
||||
Analyzer->removeLock(FSet, M, Loc, Dtor, LK_Generic, CapDiagKind);
|
||||
}
|
||||
|
||||
|
||||
|
@ -2212,9 +2285,8 @@ void ThreadSafetyAnalyzer::intersectAndWarn(FactSet &FSet1,
|
|||
if (I1 != FSet1.end()) {
|
||||
const LockData* LDat1 = &FactMan[*I1].LDat;
|
||||
if (LDat1->LKind != LDat2.LKind) {
|
||||
Handler.handleExclusiveAndShared(FSet2Mutex.toString(),
|
||||
LDat2.AcquireLoc,
|
||||
LDat1->AcquireLoc);
|
||||
Handler.handleExclusiveAndShared("mutex", FSet2Mutex.toString(),
|
||||
LDat2.AcquireLoc, LDat1->AcquireLoc);
|
||||
if (Modify && LDat1->LKind != LK_Exclusive) {
|
||||
// Take the exclusive lock, which is the one in FSet2.
|
||||
*I1 = *I;
|
||||
|
@ -2230,15 +2302,14 @@ void ThreadSafetyAnalyzer::intersectAndWarn(FactSet &FSet1,
|
|||
// If this is a scoped lock that manages another mutex, and if the
|
||||
// underlying mutex is still held, then warn about the underlying
|
||||
// mutex.
|
||||
Handler.handleMutexHeldEndOfScope(LDat2.UnderlyingMutex.toString(),
|
||||
LDat2.AcquireLoc,
|
||||
JoinLoc, LEK1);
|
||||
Handler.handleMutexHeldEndOfScope("mutex",
|
||||
LDat2.UnderlyingMutex.toString(),
|
||||
LDat2.AcquireLoc, JoinLoc, LEK1);
|
||||
}
|
||||
}
|
||||
else if (!LDat2.Managed && !FSet2Mutex.isUniversal() && !LDat2.Asserted)
|
||||
Handler.handleMutexHeldEndOfScope(FSet2Mutex.toString(),
|
||||
LDat2.AcquireLoc,
|
||||
JoinLoc, LEK1);
|
||||
Handler.handleMutexHeldEndOfScope("mutex", FSet2Mutex.toString(),
|
||||
LDat2.AcquireLoc, JoinLoc, LEK1);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2254,15 +2325,14 @@ void ThreadSafetyAnalyzer::intersectAndWarn(FactSet &FSet1,
|
|||
// If this is a scoped lock that manages another mutex, and if the
|
||||
// underlying mutex is still held, then warn about the underlying
|
||||
// mutex.
|
||||
Handler.handleMutexHeldEndOfScope(LDat1.UnderlyingMutex.toString(),
|
||||
LDat1.AcquireLoc,
|
||||
JoinLoc, LEK1);
|
||||
Handler.handleMutexHeldEndOfScope("mutex",
|
||||
LDat1.UnderlyingMutex.toString(),
|
||||
LDat1.AcquireLoc, JoinLoc, LEK1);
|
||||
}
|
||||
}
|
||||
else if (!LDat1.Managed && !FSet1Mutex.isUniversal() && !LDat1.Asserted)
|
||||
Handler.handleMutexHeldEndOfScope(FSet1Mutex.toString(),
|
||||
LDat1.AcquireLoc,
|
||||
JoinLoc, LEK2);
|
||||
Handler.handleMutexHeldEndOfScope("mutex", FSet1Mutex.toString(),
|
||||
LDat1.AcquireLoc, JoinLoc, LEK2);
|
||||
if (Modify)
|
||||
FSet1.removeLock(FactMan, FSet1Mutex);
|
||||
}
|
||||
|
@ -2343,6 +2413,7 @@ void ThreadSafetyAnalyzer::runAnalysis(AnalysisDeclContext &AC) {
|
|||
|
||||
MutexIDList ExclusiveLocksToAdd;
|
||||
MutexIDList SharedLocksToAdd;
|
||||
StringRef CapDiagKind = "mutex";
|
||||
|
||||
SourceLocation Loc = D->getLocation();
|
||||
for (unsigned i = 0; i < ArgAttrs.size(); ++i) {
|
||||
|
@ -2351,6 +2422,7 @@ void ThreadSafetyAnalyzer::runAnalysis(AnalysisDeclContext &AC) {
|
|||
if (RequiresCapabilityAttr *A = dyn_cast<RequiresCapabilityAttr>(Attr)) {
|
||||
getMutexIDs(A->isShared() ? SharedLocksToAdd : ExclusiveLocksToAdd, A,
|
||||
0, D);
|
||||
CapDiagKind = ClassifyDiagnostic(A);
|
||||
} else if (auto *A = dyn_cast<ReleaseCapabilityAttr>(Attr)) {
|
||||
// UNLOCK_FUNCTION() is used to hide the underlying lock implementation.
|
||||
// We must ignore such methods.
|
||||
|
@ -2359,12 +2431,14 @@ void ThreadSafetyAnalyzer::runAnalysis(AnalysisDeclContext &AC) {
|
|||
// FIXME -- deal with exclusive vs. shared unlock functions?
|
||||
getMutexIDs(ExclusiveLocksToAdd, A, (Expr*) 0, D);
|
||||
getMutexIDs(LocksReleased, A, (Expr*) 0, D);
|
||||
CapDiagKind = ClassifyDiagnostic(A);
|
||||
} else if (auto *A = dyn_cast<AcquireCapabilityAttr>(Attr)) {
|
||||
if (A->args_size() == 0)
|
||||
return;
|
||||
getMutexIDs(A->isShared() ? SharedLocksAcquired
|
||||
: ExclusiveLocksAcquired,
|
||||
A, nullptr, D);
|
||||
CapDiagKind = ClassifyDiagnostic(A);
|
||||
} else if (isa<ExclusiveTrylockFunctionAttr>(Attr)) {
|
||||
// Don't try to check trylock functions for now
|
||||
return;
|
||||
|
@ -2375,14 +2449,12 @@ void ThreadSafetyAnalyzer::runAnalysis(AnalysisDeclContext &AC) {
|
|||
}
|
||||
|
||||
// FIXME -- Loc can be wrong here.
|
||||
for (unsigned i=0,n=ExclusiveLocksToAdd.size(); i<n; ++i) {
|
||||
addLock(InitialLockset, ExclusiveLocksToAdd[i],
|
||||
LockData(Loc, LK_Exclusive));
|
||||
}
|
||||
for (unsigned i=0,n=SharedLocksToAdd.size(); i<n; ++i) {
|
||||
addLock(InitialLockset, SharedLocksToAdd[i],
|
||||
LockData(Loc, LK_Shared));
|
||||
}
|
||||
for (const auto &ExclusiveLockToAdd : ExclusiveLocksToAdd)
|
||||
addLock(InitialLockset, ExclusiveLockToAdd, LockData(Loc, LK_Exclusive),
|
||||
CapDiagKind);
|
||||
for (const auto &SharedLockToAdd : SharedLocksToAdd)
|
||||
addLock(InitialLockset, SharedLockToAdd, LockData(Loc, LK_Shared),
|
||||
CapDiagKind);
|
||||
}
|
||||
|
||||
for (PostOrderCFGView::iterator I = SortedGraph->begin(),
|
||||
|
|
|
@ -1429,12 +1429,13 @@ class ThreadSafetyReporter : public clang::thread_safety::ThreadSafetyHandler {
|
|||
SourceLocation FunLocation, FunEndLocation;
|
||||
|
||||
// Helper functions
|
||||
void warnLockMismatch(unsigned DiagID, Name LockName, SourceLocation Loc) {
|
||||
void warnLockMismatch(unsigned DiagID, StringRef Kind, Name LockName,
|
||||
SourceLocation Loc) {
|
||||
// Gracefully handle rare cases when the analysis can't get a more
|
||||
// precise source location.
|
||||
if (!Loc.isValid())
|
||||
Loc = FunLocation;
|
||||
PartialDiagnosticAt Warning(Loc, S.PDiag(DiagID) << LockName);
|
||||
PartialDiagnosticAt Warning(Loc, S.PDiag(DiagID) << Kind << LockName);
|
||||
Warnings.push_back(DelayedDiag(Warning, OptionalNotes()));
|
||||
}
|
||||
|
||||
|
@ -1457,28 +1458,31 @@ class ThreadSafetyReporter : public clang::thread_safety::ThreadSafetyHandler {
|
|||
}
|
||||
}
|
||||
|
||||
void handleInvalidLockExp(SourceLocation Loc) override {
|
||||
PartialDiagnosticAt Warning(Loc,
|
||||
S.PDiag(diag::warn_cannot_resolve_lock) << Loc);
|
||||
void handleInvalidLockExp(StringRef Kind, SourceLocation Loc) override {
|
||||
PartialDiagnosticAt Warning(Loc, S.PDiag(diag::warn_cannot_resolve_lock)
|
||||
<< Loc);
|
||||
Warnings.push_back(DelayedDiag(Warning, OptionalNotes()));
|
||||
}
|
||||
void handleUnmatchedUnlock(Name LockName, SourceLocation Loc) override {
|
||||
warnLockMismatch(diag::warn_unlock_but_no_lock, LockName, Loc);
|
||||
void handleUnmatchedUnlock(StringRef Kind, Name LockName,
|
||||
SourceLocation Loc) override {
|
||||
warnLockMismatch(diag::warn_unlock_but_no_lock, Kind, LockName, Loc);
|
||||
}
|
||||
void handleIncorrectUnlockKind(Name LockName, LockKind Expected,
|
||||
LockKind Received,
|
||||
void handleIncorrectUnlockKind(StringRef Kind, Name LockName,
|
||||
LockKind Expected, LockKind Received,
|
||||
SourceLocation Loc) override {
|
||||
if (Loc.isInvalid())
|
||||
Loc = FunLocation;
|
||||
PartialDiagnosticAt Warning(Loc, S.PDiag(diag::warn_unlock_kind_mismatch)
|
||||
<< LockName << Received << Expected);
|
||||
<< Kind << LockName << Received
|
||||
<< Expected);
|
||||
Warnings.push_back(DelayedDiag(Warning, OptionalNotes()));
|
||||
}
|
||||
void handleDoubleLock(Name LockName, SourceLocation Loc) override {
|
||||
warnLockMismatch(diag::warn_double_lock, LockName, Loc);
|
||||
void handleDoubleLock(StringRef Kind, Name LockName, SourceLocation Loc) override {
|
||||
warnLockMismatch(diag::warn_double_lock, Kind, LockName, Loc);
|
||||
}
|
||||
|
||||
void handleMutexHeldEndOfScope(Name LockName, SourceLocation LocLocked,
|
||||
void handleMutexHeldEndOfScope(StringRef Kind, Name LockName,
|
||||
SourceLocation LocLocked,
|
||||
SourceLocation LocEndOfScope,
|
||||
LockErrorKind LEK) override {
|
||||
unsigned DiagID = 0;
|
||||
|
@ -1499,29 +1503,33 @@ class ThreadSafetyReporter : public clang::thread_safety::ThreadSafetyHandler {
|
|||
if (LocEndOfScope.isInvalid())
|
||||
LocEndOfScope = FunEndLocation;
|
||||
|
||||
PartialDiagnosticAt Warning(LocEndOfScope, S.PDiag(DiagID) << LockName);
|
||||
PartialDiagnosticAt Warning(LocEndOfScope, S.PDiag(DiagID) << Kind
|
||||
<< LockName);
|
||||
if (LocLocked.isValid()) {
|
||||
PartialDiagnosticAt Note(LocLocked, S.PDiag(diag::note_locked_here));
|
||||
PartialDiagnosticAt Note(LocLocked, S.PDiag(diag::note_locked_here)
|
||||
<< Kind);
|
||||
Warnings.push_back(DelayedDiag(Warning, OptionalNotes(1, Note)));
|
||||
return;
|
||||
}
|
||||
Warnings.push_back(DelayedDiag(Warning, OptionalNotes()));
|
||||
}
|
||||
|
||||
|
||||
void handleExclusiveAndShared(Name LockName, SourceLocation Loc1,
|
||||
void handleExclusiveAndShared(StringRef Kind, Name LockName,
|
||||
SourceLocation Loc1,
|
||||
SourceLocation Loc2) override {
|
||||
PartialDiagnosticAt Warning(
|
||||
Loc1, S.PDiag(diag::warn_lock_exclusive_and_shared) << LockName);
|
||||
PartialDiagnosticAt Note(
|
||||
Loc2, S.PDiag(diag::note_lock_exclusive_and_shared) << LockName);
|
||||
PartialDiagnosticAt Warning(Loc1,
|
||||
S.PDiag(diag::warn_lock_exclusive_and_shared)
|
||||
<< Kind << LockName);
|
||||
PartialDiagnosticAt Note(Loc2, S.PDiag(diag::note_lock_exclusive_and_shared)
|
||||
<< Kind << LockName);
|
||||
Warnings.push_back(DelayedDiag(Warning, OptionalNotes(1, Note)));
|
||||
}
|
||||
|
||||
void handleNoMutexHeld(const NamedDecl *D, ProtectedOperationKind POK,
|
||||
AccessKind AK, SourceLocation Loc) override {
|
||||
assert((POK == POK_VarAccess || POK == POK_VarDereference)
|
||||
&& "Only works for variables");
|
||||
void handleNoMutexHeld(StringRef Kind, const NamedDecl *D,
|
||||
ProtectedOperationKind POK, AccessKind AK,
|
||||
SourceLocation Loc) override {
|
||||
assert((POK == POK_VarAccess || POK == POK_VarDereference) &&
|
||||
"Only works for variables");
|
||||
unsigned DiagID = POK == POK_VarAccess?
|
||||
diag::warn_variable_requires_any_lock:
|
||||
diag::warn_var_deref_requires_any_lock;
|
||||
|
@ -1530,8 +1538,9 @@ class ThreadSafetyReporter : public clang::thread_safety::ThreadSafetyHandler {
|
|||
Warnings.push_back(DelayedDiag(Warning, OptionalNotes()));
|
||||
}
|
||||
|
||||
void handleMutexNotHeld(const NamedDecl *D, ProtectedOperationKind POK,
|
||||
Name LockName, LockKind LK, SourceLocation Loc,
|
||||
void handleMutexNotHeld(StringRef Kind, const NamedDecl *D,
|
||||
ProtectedOperationKind POK, Name LockName,
|
||||
LockKind LK, SourceLocation Loc,
|
||||
Name *PossibleMatch) override {
|
||||
unsigned DiagID = 0;
|
||||
if (PossibleMatch) {
|
||||
|
@ -1546,10 +1555,11 @@ class ThreadSafetyReporter : public clang::thread_safety::ThreadSafetyHandler {
|
|||
DiagID = diag::warn_fun_requires_lock_precise;
|
||||
break;
|
||||
}
|
||||
PartialDiagnosticAt Warning(Loc, S.PDiag(DiagID)
|
||||
<< D->getNameAsString() << LockName << LK);
|
||||
PartialDiagnosticAt Warning(Loc, S.PDiag(DiagID) << Kind
|
||||
<< D->getNameAsString()
|
||||
<< LockName << LK);
|
||||
PartialDiagnosticAt Note(Loc, S.PDiag(diag::note_found_mutex_near_match)
|
||||
<< *PossibleMatch);
|
||||
<< *PossibleMatch);
|
||||
Warnings.push_back(DelayedDiag(Warning, OptionalNotes(1, Note)));
|
||||
} else {
|
||||
switch (POK) {
|
||||
|
@ -1563,16 +1573,17 @@ class ThreadSafetyReporter : public clang::thread_safety::ThreadSafetyHandler {
|
|||
DiagID = diag::warn_fun_requires_lock;
|
||||
break;
|
||||
}
|
||||
PartialDiagnosticAt Warning(Loc, S.PDiag(DiagID)
|
||||
<< D->getNameAsString() << LockName << LK);
|
||||
PartialDiagnosticAt Warning(Loc, S.PDiag(DiagID) << Kind
|
||||
<< D->getNameAsString()
|
||||
<< LockName << LK);
|
||||
Warnings.push_back(DelayedDiag(Warning, OptionalNotes()));
|
||||
}
|
||||
}
|
||||
|
||||
void handleFunExcludesLock(Name FunName, Name LockName,
|
||||
void handleFunExcludesLock(StringRef Kind, Name FunName, Name LockName,
|
||||
SourceLocation Loc) override {
|
||||
PartialDiagnosticAt Warning(Loc,
|
||||
S.PDiag(diag::warn_fun_excludes_mutex) << FunName << LockName);
|
||||
PartialDiagnosticAt Warning(Loc, S.PDiag(diag::warn_fun_excludes_mutex)
|
||||
<< Kind << FunName << LockName);
|
||||
Warnings.push_back(DelayedDiag(Warning, OptionalNotes()));
|
||||
}
|
||||
};
|
||||
|
|
|
@ -209,33 +209,33 @@ void sls_fun_good_8() {
|
|||
|
||||
void sls_fun_bad_1() {
|
||||
sls_mu.Unlock(); // \
|
||||
// expected-warning{{unlocking 'sls_mu' that was not locked}}
|
||||
// expected-warning{{releasing mutex 'sls_mu' that was not held}}
|
||||
}
|
||||
|
||||
void sls_fun_bad_2() {
|
||||
sls_mu.Lock();
|
||||
sls_mu.Lock(); // \
|
||||
// expected-warning{{locking 'sls_mu' that is already locked}}
|
||||
// expected-warning{{acquiring mutex 'sls_mu' that is already held}}
|
||||
sls_mu.Unlock();
|
||||
}
|
||||
|
||||
void sls_fun_bad_3() {
|
||||
sls_mu.Lock(); // expected-note {{mutex acquired here}}
|
||||
} // expected-warning{{mutex 'sls_mu' is still locked at the end of function}}
|
||||
} // expected-warning{{mutex 'sls_mu' is still held at the end of function}}
|
||||
|
||||
void sls_fun_bad_4() {
|
||||
if (getBool())
|
||||
sls_mu.Lock(); // expected-note{{mutex acquired here}}
|
||||
else
|
||||
sls_mu2.Lock(); // expected-note{{mutex acquired here}}
|
||||
} // expected-warning{{mutex 'sls_mu' is not locked on every path through here}} \
|
||||
// expected-warning{{mutex 'sls_mu2' is not locked on every path through here}}
|
||||
} // expected-warning{{mutex 'sls_mu' is not held on every path through here}} \
|
||||
// expected-warning{{mutex 'sls_mu2' is not held on every path through here}}
|
||||
|
||||
void sls_fun_bad_5() {
|
||||
sls_mu.Lock(); // expected-note {{mutex acquired here}}
|
||||
if (getBool())
|
||||
sls_mu.Unlock();
|
||||
} // expected-warning{{mutex 'sls_mu' is not locked on every path through here}}
|
||||
} // expected-warning{{mutex 'sls_mu' is not held on every path through here}}
|
||||
|
||||
void sls_fun_bad_6() {
|
||||
if (getBool()) {
|
||||
|
@ -248,8 +248,8 @@ void sls_fun_bad_6() {
|
|||
}
|
||||
}
|
||||
sls_mu.Unlock(); // \
|
||||
expected-warning{{mutex 'sls_mu' is not locked on every path through here}}\
|
||||
expected-warning{{unlocking 'sls_mu' that was not locked}}
|
||||
expected-warning{{mutex 'sls_mu' is not held on every path through here}}\
|
||||
expected-warning{{releasing mutex 'sls_mu' that was not held}}
|
||||
}
|
||||
|
||||
void sls_fun_bad_7() {
|
||||
|
@ -259,7 +259,7 @@ void sls_fun_bad_7() {
|
|||
if (getBool()) {
|
||||
if (getBool()) {
|
||||
continue; // \
|
||||
expected-warning{{expecting mutex 'sls_mu' to be locked at start of each loop}}
|
||||
expected-warning{{expecting mutex 'sls_mu' to be held at start of each loop}}
|
||||
}
|
||||
}
|
||||
sls_mu.Lock(); // expected-note {{mutex acquired here}}
|
||||
|
@ -271,14 +271,14 @@ void sls_fun_bad_8() {
|
|||
sls_mu.Lock(); // expected-note{{mutex acquired here}}
|
||||
|
||||
do {
|
||||
sls_mu.Unlock(); // expected-warning{{expecting mutex 'sls_mu' to be locked at start of each loop}}
|
||||
sls_mu.Unlock(); // expected-warning{{expecting mutex 'sls_mu' to be held at start of each loop}}
|
||||
} while (getBool());
|
||||
}
|
||||
|
||||
void sls_fun_bad_9() {
|
||||
do {
|
||||
sls_mu.Lock(); // \
|
||||
// expected-warning{{expecting mutex 'sls_mu' to be locked at start of each loop}} \
|
||||
// expected-warning{{expecting mutex 'sls_mu' to be held at start of each loop}} \
|
||||
// expected-note{{mutex acquired here}}
|
||||
} while (getBool());
|
||||
sls_mu.Unlock();
|
||||
|
@ -286,18 +286,18 @@ void sls_fun_bad_9() {
|
|||
|
||||
void sls_fun_bad_10() {
|
||||
sls_mu.Lock(); // expected-note 2{{mutex acquired here}}
|
||||
while(getBool()) { // expected-warning{{expecting mutex 'sls_mu' to be locked at start of each loop}}
|
||||
while(getBool()) { // expected-warning{{expecting mutex 'sls_mu' to be held at start of each loop}}
|
||||
sls_mu.Unlock();
|
||||
}
|
||||
} // expected-warning{{mutex 'sls_mu' is still locked at the end of function}}
|
||||
} // expected-warning{{mutex 'sls_mu' is still held at the end of function}}
|
||||
|
||||
void sls_fun_bad_11() {
|
||||
while (getBool()) { // \
|
||||
expected-warning{{expecting mutex 'sls_mu' to be locked at start of each loop}}
|
||||
expected-warning{{expecting mutex 'sls_mu' to be held at start of each loop}}
|
||||
sls_mu.Lock(); // expected-note {{mutex acquired here}}
|
||||
}
|
||||
sls_mu.Unlock(); // \
|
||||
// expected-warning{{unlocking 'sls_mu' that was not locked}}
|
||||
// expected-warning{{releasing mutex 'sls_mu' that was not held}}
|
||||
}
|
||||
|
||||
void sls_fun_bad_12() {
|
||||
|
@ -306,7 +306,7 @@ void sls_fun_bad_12() {
|
|||
sls_mu.Unlock();
|
||||
if (getBool()) {
|
||||
if (getBool()) {
|
||||
break; // expected-warning{{mutex 'sls_mu' is not locked on every path through here}}
|
||||
break; // expected-warning{{mutex 'sls_mu' is not held on every path through here}}
|
||||
}
|
||||
}
|
||||
sls_mu.Lock();
|
||||
|
|
|
@ -74,14 +74,14 @@ int get_value(int *p) SHARED_LOCKS_REQUIRED(foo_.mu_){
|
|||
|
||||
int main() {
|
||||
|
||||
Foo_fun1(1); // expected-warning{{calling function 'Foo_fun1' requires shared lock on 'mu2'}} \
|
||||
expected-warning{{calling function 'Foo_fun1' requires exclusive lock on 'mu1'}}
|
||||
Foo_fun1(1); // expected-warning{{calling function 'Foo_fun1' requires holding mutex 'mu2'}} \
|
||||
expected-warning{{calling function 'Foo_fun1' requires holding mutex 'mu1' exclusively}}
|
||||
|
||||
mutex_exclusive_lock(&mu1);
|
||||
mutex_shared_lock(&mu2);
|
||||
Foo_fun1(1);
|
||||
|
||||
mutex_shared_lock(&mu1); // expected-warning{{locking 'mu1' that is already locked}}
|
||||
mutex_shared_lock(&mu1); // expected-warning{{acquiring mutex 'mu1' that is already held}}
|
||||
mutex_unlock(&mu1);
|
||||
mutex_unlock(&mu2);
|
||||
mutex_shared_lock(&mu1);
|
||||
|
@ -95,13 +95,13 @@ int main() {
|
|||
mutex_unlock(&mu1);
|
||||
|
||||
mutex_exclusive_lock(&mu1);
|
||||
Foo_func3(4); // expected-warning{{cannot call function 'Foo_func3' while mutex 'mu1' is locked}}
|
||||
Foo_func3(4); // expected-warning{{cannot call function 'Foo_func3' while mutex 'mu1' is held}}
|
||||
mutex_unlock(&mu1);
|
||||
|
||||
Foo_func3(5);
|
||||
|
||||
set_value(&a_, 0); // expected-warning{{calling function 'set_value' requires exclusive lock on 'foo_.mu_'}}
|
||||
get_value(b_); // expected-warning{{calling function 'get_value' requires shared lock on 'foo_.mu_'}}
|
||||
set_value(&a_, 0); // expected-warning{{calling function 'set_value' requires holding mutex 'foo_.mu_' exclusively}}
|
||||
get_value(b_); // expected-warning{{calling function 'get_value' requires holding mutex 'foo_.mu_'}}
|
||||
mutex_exclusive_lock(foo_.mu_);
|
||||
set_value(&a_, 1);
|
||||
mutex_unlock(foo_.mu_);
|
||||
|
@ -109,19 +109,19 @@ int main() {
|
|||
(void)(get_value(b_) == 1);
|
||||
mutex_unlock(foo_.mu_);
|
||||
|
||||
c_ = 0; // expected-warning{{writing variable 'c_' requires locking any mutex exclusively}}
|
||||
(void)(*d_ == 0); // expected-warning{{reading the value pointed to by 'd_' requires locking any mutex}}
|
||||
c_ = 0; // expected-warning{{writing variable 'c_' requires holding any mutex exclusively}}
|
||||
(void)(*d_ == 0); // expected-warning{{reading the value pointed to by 'd_' requires holding any mutex}}
|
||||
mutex_exclusive_lock(foo_.mu_);
|
||||
c_ = 1;
|
||||
(void)(*d_ == 1);
|
||||
mutex_unlock(foo_.mu_);
|
||||
|
||||
mutex_exclusive_lock(&mu1);
|
||||
mutex_shared_unlock(&mu1); // expected-warning {{unlocking 'mu1' using shared access, expected exclusive access}}
|
||||
mutex_shared_unlock(&mu1); // expected-warning {{releasing mutex 'mu1' using shared access, expected exclusive access}}
|
||||
mutex_exclusive_unlock(&mu1);
|
||||
|
||||
mutex_shared_lock(&mu1);
|
||||
mutex_exclusive_unlock(&mu1); // expected-warning {{unlocking 'mu1' using exclusive access, expected shared access}}
|
||||
mutex_exclusive_unlock(&mu1); // expected-warning {{releasing mutex 'mu1' using exclusive access, expected shared access}}
|
||||
mutex_shared_unlock(&mu1);
|
||||
|
||||
return 0;
|
||||
|
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue