Newer
Older
Ted Kremenek
committed
//=- AnalysisBasedWarnings.cpp - Sema warnings based on libAnalysis -*- C++ -*-=//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file defines analysis_warnings::[Policy,Executor].
// Together they are used by Sema to issue warnings based on inexpensive
// static analysis algorithms in libAnalysis.
//
//===----------------------------------------------------------------------===//
#include "clang/Sema/AnalysisBasedWarnings.h"
John McCall
committed
#include "clang/Sema/SemaInternal.h"
Ted Kremenek
committed
#include "clang/Sema/ScopeInfo.h"
#include "clang/Basic/SourceManager.h"
#include "clang/Lex/Preprocessor.h"
#include "clang/AST/DeclObjC.h"
Ted Kremenek
committed
#include "clang/AST/ExprObjC.h"
#include "clang/AST/ExprCXX.h"
#include "clang/AST/StmtObjC.h"
#include "clang/AST/StmtCXX.h"
#include "clang/AST/EvaluatedExprVisitor.h"
#include "clang/AST/StmtVisitor.h"
Ted Kremenek
committed
#include "clang/Analysis/AnalysisContext.h"
#include "clang/Analysis/CFG.h"
#include "clang/Analysis/Analyses/ReachableCode.h"
Ted Kremenek
committed
#include "clang/Analysis/Analyses/CFGReachabilityAnalysis.h"
#include "clang/Analysis/CFGStmtMap.h"
#include "clang/Analysis/Analyses/UninitializedValues.h"
Ted Kremenek
committed
#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"
Ted Kremenek
committed
#include "llvm/Support/Casting.h"
#include <algorithm>
#include <vector>
Ted Kremenek
committed
using namespace clang;
//===----------------------------------------------------------------------===//
// Unreachable code analysis.
//===----------------------------------------------------------------------===//
namespace {
class UnreachableCodeHandler : public reachable_code::Callback {
Sema &S;
public:
UnreachableCodeHandler(Sema &s) : S(s) {}
void HandleUnreachable(SourceLocation L, SourceRange R1, SourceRange R2) {
S.Diag(L, diag::warn_unreachable) << R1 << R2;
}
};
}
/// CheckUnreachable - Check for unreachable code.
static void CheckUnreachable(Sema &S, AnalysisContext &AC) {
UnreachableCodeHandler UC(S);
reachable_code::FindUnreachableCode(AC, UC);
}
//===----------------------------------------------------------------------===//
// Check for missing return value.
//===----------------------------------------------------------------------===//
enum ControlFlowKind {
UnknownFallThrough,
NeverFallThrough,
MaybeFallThrough,
AlwaysFallThrough,
NeverFallThroughOrReturn
};
Ted Kremenek
committed
/// CheckFallThrough - Check that we don't fall off the end of a
/// Statement that should return a value.
///
/// \returns AlwaysFallThrough iff we always fall off the end of the statement,
/// MaybeFallThrough iff we might or might not fall off the end,
/// NeverFallThroughOrReturn iff we never fall off the end of the statement or
/// return. We assume NeverFallThrough iff we never fall off the end of the
/// statement but we may return. We assume that functions not marked noreturn
/// will return.
static ControlFlowKind CheckFallThrough(AnalysisContext &AC) {
CFG *cfg = AC.getCFG();
if (cfg == 0) return UnknownFallThrough;
Ted Kremenek
committed
// The CFG leaves in dead things, and we don't want the dead code paths to
// confuse us, so we mark all live things first.
llvm::BitVector live(cfg->getNumBlockIDs());
Ted Kremenek
committed
unsigned count = reachable_code::ScanReachableFromBlock(&cfg->getEntry(),
Ted Kremenek
committed
live);
bool AddEHEdges = AC.getAddEHEdges();
if (!AddEHEdges && count != cfg->getNumBlockIDs())
// When there are things remaining dead, and we didn't add EH edges
// from CallExprs to the catch clauses, we have to go back and
// mark them as live.
for (CFG::iterator I = cfg->begin(), E = cfg->end(); I != E; ++I) {
CFGBlock &b = **I;
if (!live[b.getBlockID()]) {
if (b.pred_begin() == b.pred_end()) {
if (b.getTerminator() && isa<CXXTryStmt>(b.getTerminator()))
// When not adding EH edges from calls, catch clauses
// can otherwise seem dead. Avoid noting them as dead.
Ted Kremenek
committed
count += reachable_code::ScanReachableFromBlock(&b, live);
Ted Kremenek
committed
continue;
}
}
}
// Now we know what is live, we check the live precessors of the exit block
// and look for fall through paths, being careful to ignore normal returns,
// and exceptional paths.
bool HasLiveReturn = false;
bool HasFakeEdge = false;
bool HasPlainEdge = false;
bool HasAbnormalEdge = false;
Ted Kremenek
committed
// Ignore default cases that aren't likely to be reachable because all
// enums in a switch(X) have explicit case statements.
CFGBlock::FilterOptions FO;
FO.IgnoreDefaultsWithCoveredEnums = 1;
for (CFGBlock::filtered_pred_iterator
I = cfg->getExit().filtered_pred_start_end(FO); I.hasMore(); ++I) {
const CFGBlock& B = **I;
Ted Kremenek
committed
if (!live[B.getBlockID()])
continue;
// Destructors can appear after the 'return' in the CFG. This is
// normal. We need to look pass the destructors for the return
// statement (if it exists).
CFGBlock::const_reverse_iterator ri = B.rbegin(), re = B.rend();
Ted Kremenek
committed
bool hasNoReturnDtor = false;
for ( ; ri != re ; ++ri) {
CFGElement CE = *ri;
Ted Kremenek
committed
// FIXME: The right solution is to just sever the edges in the
// CFG itself.
if (const CFGImplicitDtor *iDtor = ri->getAs<CFGImplicitDtor>())
Ted Kremenek
committed
if (iDtor->isNoReturn(AC.getASTContext())) {
Ted Kremenek
committed
hasNoReturnDtor = true;
HasFakeEdge = true;
break;
}
if (isa<CFGStmt>(CE))
break;
}
Ted Kremenek
committed
if (hasNoReturnDtor)
continue;
// No more CFGElements in the block?
if (ri == re) {
Ted Kremenek
committed
if (B.getTerminator() && isa<CXXTryStmt>(B.getTerminator())) {
HasAbnormalEdge = true;
continue;
}
// A labeled empty statement, or the entry block...
HasPlainEdge = true;
continue;
}
CFGStmt CS = cast<CFGStmt>(*ri);
Ted Kremenek
committed
if (isa<ReturnStmt>(S)) {
HasLiveReturn = true;
continue;
}
if (isa<ObjCAtThrowStmt>(S)) {
HasFakeEdge = true;
continue;
}
if (isa<CXXThrowExpr>(S)) {
HasFakeEdge = true;
continue;
}
if (const AsmStmt *AS = dyn_cast<AsmStmt>(S)) {
if (AS->isMSAsm()) {
HasFakeEdge = true;
HasLiveReturn = true;
continue;
}
}
if (isa<CXXTryStmt>(S)) {
HasAbnormalEdge = true;
continue;
}
bool NoReturnEdge = false;
if (const CallExpr *C = dyn_cast<CallExpr>(S)) {
if (std::find(B.succ_begin(), B.succ_end(), &cfg->getExit())
== B.succ_end()) {
Ted Kremenek
committed
HasAbnormalEdge = true;
continue;
}
const Expr *CEE = C->getCallee()->IgnoreParenCasts();
QualType calleeType = CEE->getType();
if (calleeType == AC.getASTContext().BoundMemberTy) {
calleeType = Expr::findBoundMemberType(CEE);
assert(!calleeType.isNull() && "analyzing unresolved call?");
}
if (getFunctionExtInfo(calleeType).getNoReturn()) {
Ted Kremenek
committed
NoReturnEdge = true;
HasFakeEdge = true;
} else if (const DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(CEE)) {
const ValueDecl *VD = DRE->getDecl();
Ted Kremenek
committed
if (VD->hasAttr<NoReturnAttr>()) {
NoReturnEdge = true;
HasFakeEdge = true;
}
}
}
// FIXME: Add noreturn message sends.
if (NoReturnEdge == false)
HasPlainEdge = true;
}
if (!HasPlainEdge) {
if (HasLiveReturn)
return NeverFallThrough;
return NeverFallThroughOrReturn;
}
if (HasAbnormalEdge || HasFakeEdge || HasLiveReturn)
return MaybeFallThrough;
// This says AlwaysFallThrough for calls to functions that are not marked
// noreturn, that don't return. If people would like this warning to be more
// accurate, such functions should be marked as noreturn.
return AlwaysFallThrough;
}
Ted Kremenek
committed
struct CheckFallThroughDiagnostics {
unsigned diag_MaybeFallThrough_HasNoReturn;
unsigned diag_MaybeFallThrough_ReturnsNonVoid;
unsigned diag_AlwaysFallThrough_HasNoReturn;
unsigned diag_AlwaysFallThrough_ReturnsNonVoid;
unsigned diag_NeverFallThroughOrReturn;
bool funMode;
static CheckFallThroughDiagnostics MakeForFunction(const Decl *Func) {
Ted Kremenek
committed
CheckFallThroughDiagnostics D;
Ted Kremenek
committed
D.diag_MaybeFallThrough_HasNoReturn =
diag::warn_falloff_noreturn_function;
D.diag_MaybeFallThrough_ReturnsNonVoid =
diag::warn_maybe_falloff_nonvoid_function;
D.diag_AlwaysFallThrough_HasNoReturn =
diag::warn_falloff_noreturn_function;
D.diag_AlwaysFallThrough_ReturnsNonVoid =
diag::warn_falloff_nonvoid_function;
// Don't suggest that virtual functions be marked "noreturn", since they
// might be overridden by non-noreturn functions.
bool isVirtualMethod = false;
if (const CXXMethodDecl *Method = dyn_cast<CXXMethodDecl>(Func))
isVirtualMethod = Method->isVirtual();
if (!isVirtualMethod)
D.diag_NeverFallThroughOrReturn =
diag::warn_suggest_noreturn_function;
else
D.diag_NeverFallThroughOrReturn = 0;
Ted Kremenek
committed
D.funMode = true;
return D;
}
Ted Kremenek
committed
static CheckFallThroughDiagnostics MakeForBlock() {
CheckFallThroughDiagnostics D;
D.diag_MaybeFallThrough_HasNoReturn =
diag::err_noreturn_block_has_return_expr;
D.diag_MaybeFallThrough_ReturnsNonVoid =
diag::err_maybe_falloff_nonvoid_block;
D.diag_AlwaysFallThrough_HasNoReturn =
diag::err_noreturn_block_has_return_expr;
D.diag_AlwaysFallThrough_ReturnsNonVoid =
diag::err_falloff_nonvoid_block;
D.diag_NeverFallThroughOrReturn =
diag::warn_suggest_noreturn_block;
D.funMode = false;
return D;
}
Ted Kremenek
committed
bool checkDiagnostics(Diagnostic &D, bool ReturnsVoid,
bool HasNoReturn) const {
if (funMode) {
return (ReturnsVoid ||
D.getDiagnosticLevel(diag::warn_maybe_falloff_nonvoid_function,
FuncLoc) == Diagnostic::Ignored)
&& (!HasNoReturn ||
D.getDiagnosticLevel(diag::warn_noreturn_function_has_return_expr,
FuncLoc) == Diagnostic::Ignored)
&& (!ReturnsVoid ||
D.getDiagnosticLevel(diag::warn_suggest_noreturn_block, FuncLoc)
== Diagnostic::Ignored);
Ted Kremenek
committed
}
Ted Kremenek
committed
// For blocks.
return ReturnsVoid && !HasNoReturn
&& (!ReturnsVoid ||
D.getDiagnosticLevel(diag::warn_suggest_noreturn_block, FuncLoc)
== Diagnostic::Ignored);
Ted Kremenek
committed
}
};
Ted Kremenek
committed
/// CheckFallThroughForFunctionDef - Check that we don't fall off the end of a
/// function that should return a value. Check that we don't fall off the end
/// of a noreturn function. We assume that functions and blocks not marked
/// noreturn will return.
static void CheckFallThroughForBody(Sema &S, const Decl *D, const Stmt *Body,
const BlockExpr *blkExpr,
Ted Kremenek
committed
const CheckFallThroughDiagnostics& CD,
AnalysisContext &AC) {
bool ReturnsVoid = false;
bool HasNoReturn = false;
if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(D)) {
ReturnsVoid = FD->getResultType()->isVoidType();
HasNoReturn = FD->hasAttr<NoReturnAttr>() ||
FD->getType()->getAs<FunctionType>()->getNoReturnAttr();
Ted Kremenek
committed
}
else if (const ObjCMethodDecl *MD = dyn_cast<ObjCMethodDecl>(D)) {
ReturnsVoid = MD->getResultType()->isVoidType();
HasNoReturn = MD->hasAttr<NoReturnAttr>();
}
else if (isa<BlockDecl>(D)) {
QualType BlockTy = blkExpr->getType();
if (const FunctionType *FT =
Ted Kremenek
committed
BlockTy->getPointeeType()->getAs<FunctionType>()) {
if (FT->getResultType()->isVoidType())
ReturnsVoid = true;
if (FT->getNoReturnAttr())
HasNoReturn = true;
}
}
Diagnostic &Diags = S.getDiagnostics();
// Short circuit for compilation speed.
if (CD.checkDiagnostics(Diags, ReturnsVoid, HasNoReturn))
return;
Ted Kremenek
committed
// FIXME: Function try block
if (const CompoundStmt *Compound = dyn_cast<CompoundStmt>(Body)) {
switch (CheckFallThrough(AC)) {
case UnknownFallThrough:
break;
Ted Kremenek
committed
case MaybeFallThrough:
if (HasNoReturn)
S.Diag(Compound->getRBracLoc(),
CD.diag_MaybeFallThrough_HasNoReturn);
else if (!ReturnsVoid)
S.Diag(Compound->getRBracLoc(),
CD.diag_MaybeFallThrough_ReturnsNonVoid);
break;
case AlwaysFallThrough:
if (HasNoReturn)
S.Diag(Compound->getRBracLoc(),
CD.diag_AlwaysFallThrough_HasNoReturn);
else if (!ReturnsVoid)
S.Diag(Compound->getRBracLoc(),
CD.diag_AlwaysFallThrough_ReturnsNonVoid);
break;
case NeverFallThroughOrReturn:
if (ReturnsVoid && !HasNoReturn && CD.diag_NeverFallThroughOrReturn)
Ted Kremenek
committed
S.Diag(Compound->getLBracLoc(),
CD.diag_NeverFallThroughOrReturn);
break;
case NeverFallThrough:
break;
}
}
}
//===----------------------------------------------------------------------===//
// -Wuninitialized
//===----------------------------------------------------------------------===//
namespace {
/// ContainsReference - A visitor class to search for references to
/// a particular declaration (the needle) within any evaluated component of an
/// expression (recursively).
class ContainsReference : public EvaluatedExprVisitor<ContainsReference> {
bool FoundReference;
const DeclRefExpr *Needle;
public:
ContainsReference(ASTContext &Context, const DeclRefExpr *Needle)
: EvaluatedExprVisitor<ContainsReference>(Context),
FoundReference(false), Needle(Needle) {}
void VisitExpr(Expr *E) {
// Stop evaluating if we already have a reference.
if (FoundReference)
return;
EvaluatedExprVisitor<ContainsReference>::VisitExpr(E);
}
void VisitDeclRefExpr(DeclRefExpr *E) {
if (E == Needle)
FoundReference = true;
else
EvaluatedExprVisitor<ContainsReference>::VisitDeclRefExpr(E);
}
bool doesContainReference() const { return FoundReference; }
};
}
/// DiagnoseUninitializedUse -- Helper function for diagnosing uses of an
/// uninitialized variable. This manages the different forms of diagnostic
/// emitted for particular types of uses. Returns true if the use was diagnosed
/// as a warning. If a pariticular use is one we omit warnings for, returns
/// false.
static bool DiagnoseUninitializedUse(Sema &S, const VarDecl *VD,
const Expr *E, bool isAlwaysUninit) {
bool isSelfInit = false;
if (const DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(E)) {
if (isAlwaysUninit) {
// Inspect the initializer of the variable declaration which is
// being referenced prior to its initialization. We emit
// specialized diagnostics for self-initialization, and we
// specifically avoid warning about self references which take the
// form of:
//
// int x = x;
//
// This is used to indicate to GCC that 'x' is intentionally left
// uninitialized. Proven code paths which access 'x' in
// an uninitialized state after this will still warn.
//
// TODO: Should we suppress maybe-uninitialized warnings for
// variables initialized in this way?
if (const Expr *Initializer = VD->getInit()) {
if (DRE == Initializer->IgnoreParenImpCasts())
return false;
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
ContainsReference CR(S.Context, DRE);
CR.Visit(const_cast<Expr*>(Initializer));
isSelfInit = CR.doesContainReference();
}
if (isSelfInit) {
S.Diag(DRE->getLocStart(),
diag::warn_uninit_self_reference_in_init)
<< VD->getDeclName() << VD->getLocation() << DRE->getSourceRange();
} else {
S.Diag(DRE->getLocStart(), diag::warn_uninit_var)
<< VD->getDeclName() << DRE->getSourceRange();
}
} else {
S.Diag(DRE->getLocStart(), diag::warn_maybe_uninit_var)
<< VD->getDeclName() << DRE->getSourceRange();
}
} else {
const BlockExpr *BE = cast<BlockExpr>(E);
S.Diag(BE->getLocStart(),
isAlwaysUninit ? diag::warn_uninit_var_captured_by_block
: diag::warn_maybe_uninit_var_captured_by_block)
<< VD->getDeclName();
}
// Report where the variable was declared when the use wasn't within
// the initializer of that declaration.
if (!isSelfInit)
S.Diag(VD->getLocStart(), diag::note_uninit_var_def)
<< VD->getDeclName();
return true;
}
static void SuggestInitializationFixit(Sema &S, const VarDecl *VD) {
// Don't issue a fixit if there is already an initializer.
if (VD->getInit())
return;
// Suggest possible initialization (if any).
const char *initialization = 0;
QualType VariableTy = VD->getType().getCanonicalType();
if (VariableTy->isObjCObjectPointerType() ||
VariableTy->isBlockPointerType()) {
// Check if 'nil' is defined.
if (S.PP.getMacroInfo(&S.getASTContext().Idents.get("nil")))
initialization = " = nil";
else
initialization = " = 0";
}
else if (VariableTy->isRealFloatingType())
initialization = " = 0.0";
else if (VariableTy->isBooleanType() && S.Context.getLangOptions().CPlusPlus)
initialization = " = false";
else if (VariableTy->isEnumeralType())
return;
else if (VariableTy->isPointerType() || VariableTy->isMemberPointerType()) {
if (S.Context.getLangOptions().CPlusPlus0x)
initialization = " = nullptr";
// Check if 'NULL' is defined.
else if (S.PP.getMacroInfo(&S.getASTContext().Idents.get("NULL")))
initialization = " = NULL";
else
initialization = " = 0";
}
else if (VariableTy->isScalarType())
initialization = " = 0";
if (initialization) {
SourceLocation loc = S.PP.getLocForEndOfToken(VD->getLocEnd());
S.Diag(loc, diag::note_var_fixit_add_initialization)
<< FixItHint::CreateInsertion(loc, initialization);
}
}
Ted Kremenek
committed
typedef std::pair<const Expr*, bool> UninitUse;
Ted Kremenek
committed
bool operator()(const UninitUse &a, const UninitUse &b) {
SourceLocation aLoc = a.first->getLocStart();
SourceLocation bLoc = b.first->getLocStart();
return aLoc.getRawEncoding() < bLoc.getRawEncoding();
}
};
class UninitValsDiagReporter : public UninitVariablesHandler {
Sema &S;
Chris Lattner
committed
typedef SmallVector<UninitUse, 2> UsesVec;
typedef llvm::DenseMap<const VarDecl *, UsesVec*> UsesMap;
UsesMap *uses;
UninitValsDiagReporter(Sema &S) : S(S), uses(0) {}
~UninitValsDiagReporter() {
flushDiagnostics();
}
Ted Kremenek
committed
void handleUseOfUninitVariable(const Expr *ex, const VarDecl *vd,
bool isAlwaysUninit) {
if (!uses)
uses = new UsesMap();
UsesVec *&vec = (*uses)[vd];
if (!vec)
vec = new UsesVec();
Ted Kremenek
committed
vec->push_back(std::make_pair(ex, isAlwaysUninit));
}
void flushDiagnostics() {
if (!uses)
return;
Ted Kremenek
committed
for (UsesMap::iterator i = uses->begin(), e = uses->end(); i != e; ++i) {
const VarDecl *vd = i->first;
UsesVec *vec = i->second;
Ted Kremenek
committed
// Sort the uses by their SourceLocations. While not strictly
// guaranteed to produce them in line/column order, this will provide
// a stable ordering.
std::sort(vec->begin(), vec->end(), SLocSort());
for (UsesVec::iterator vi = vec->begin(), ve = vec->end(); vi != ve;
++vi) {
if (!DiagnoseUninitializedUse(S, vd, vi->first,
/*isAlwaysUninit=*/vi->second))
continue;
SuggestInitializationFixit(S, vd);
// Skip further diagnostics for this variable. We try to warn only on
// the first point at which a variable is used uninitialized.
break;
}
delete vec;
}
delete uses;
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
//===----------------------------------------------------------------------===//
// -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) {
// Note that insert() is called by po_iterator, which doesn't check to make
// sure that Block is non-null. Moreover, the CFGBlock iterator will
// occasionally hand out null pointers for pruned edges, so we catch those
// here.
if (Block == 0)
return false; // if an edge is trivially false.
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 method is for tracking visited blocks in the main threadsafety loop.
/// Block must not be null.
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
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();
}
};
Caitlin Sadowski
committed
/// \brief A LockID object uniquely identifies a particular lock acquired, and
/// is built from an Expr* (i.e. calling a lock function).
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
///
/// 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
Caitlin Sadowski
committed
// of LockIDs 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;
}
Caitlin Sadowski
committed
/// \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.
///
Caitlin Sadowski
committed
/// The main body of the analysis maps LockIDs 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());
}
};
Caitlin Sadowski
committed
/// A Lockset maps each LockID (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
Caitlin Sadowski
committed
void removeLock(SourceLocation UnlockLoc, Expr *LockExp);
void addLock(SourceLocation LockLoc, Expr *LockExp);
Caitlin Sadowski
committed
const ValueDecl *getValueDecl(Expr *Exp);
void checkAccess(Expr *Exp);
void checkDereference(Expr *Exp);
public:
BuildLockset(Sema &S, Lockset LS, Lockset::Factory &F)
: StmtVisitor<BuildLockset>(), S(S), LSet(LS),
LocksetFactory(F) {}
Lockset getLockset() {
return LSet;
}
Caitlin Sadowski
committed
void VisitUnaryOperator(UnaryOperator *UO);
void VisitBinaryOperator(BinaryOperator *BO);
void VisitCastExpr(CastExpr *CE);
void VisitCXXMemberCallExpr(CXXMemberCallExpr *Exp);
};
/// \brief Add a new lock to the lockset, warning if the lock is already there.
/// \param LockLoc The source location of the acquire
Caitlin Sadowski
committed
/// \param LockExp The lock expression corresponding to the lock to be added
Caitlin Sadowski
committed
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)
Caitlin Sadowski
committed
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;
}
Caitlin Sadowski
committed
/// \brief Gets the value decl pointer from DeclRefExprs or MemberExprs
const ValueDecl *BuildLockset::getValueDecl(Expr *Exp) {
if (const DeclRefExpr *DR = dyn_cast<DeclRefExpr>(Exp))
return DR->getDecl();
if (const MemberExpr *ME = dyn_cast<MemberExpr>(Exp))
return ME->getMemberDecl();
return 0;
}
Caitlin Sadowski
committed
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
/// \brief This method identifies variable dereferences and checks pt_guarded_by
/// and pt_guarded_var annotations. Note that we only check these annotations
/// at the time a pointer is dereferenced.
/// FIXME: We need to check for other types of pointer dereferences
/// (e.g. [], ->) and deal with them here.
/// \param Exp An expression that has been read or written.
void BuildLockset::checkDereference(Expr *Exp) {
UnaryOperator *UO = dyn_cast<UnaryOperator>(Exp);
if (!UO || UO->getOpcode() != clang::UO_Deref)
return;
Exp = UO->getSubExpr()->IgnoreParenCasts();
const ValueDecl *D = getValueDecl(Exp);
if(!D || !D->hasAttrs())
return;
if (D->getAttr<PtGuardedVarAttr>() && LSet.isEmpty())
S.Diag(Exp->getExprLoc(), diag::warn_var_deref_requires_any_lock)
<< D->getName();
const AttrVec &ArgAttrs = D->getAttrs();
for(unsigned i = 0, Size = ArgAttrs.size(); i < Size; ++i) {
if (ArgAttrs[i]->getKind() != attr::PtGuardedBy)
continue;
PtGuardedByAttr *PGBAttr = cast<PtGuardedByAttr>(ArgAttrs[i]);
LockID Lock(PGBAttr->getArg());
if (!LSet.contains(Lock))
S.Diag(Exp->getExprLoc(), diag::warn_var_deref_requires_lock)
<< D->getName() << Lock.getName();
}
}
/// \brief Checks guarded_by and guarded_var attributes.
/// Whenever we identify an access (read or write) of a DeclRefExpr or
/// MemberExpr, we need to check whether there are any guarded_by or
/// guarded_var attributes, and make sure we hold the appropriate locks.
void BuildLockset::checkAccess(Expr *Exp) {
const ValueDecl *D = getValueDecl(Exp);
if(!D || !D->hasAttrs())
return;
if (D->getAttr<GuardedVarAttr>() && LSet.isEmpty())
S.Diag(Exp->getExprLoc(), diag::warn_variable_requires_any_lock)
<< D->getName();
const AttrVec &ArgAttrs = D->getAttrs();
for(unsigned i = 0, Size = ArgAttrs.size(); i < Size; ++i) {
if (ArgAttrs[i]->getKind() != attr::GuardedBy)
continue;
GuardedByAttr *GBAttr = cast<GuardedByAttr>(ArgAttrs[i]);
LockID Lock(GBAttr->getArg());
if (!LSet.contains(Lock))
S.Diag(Exp->getExprLoc(), diag::warn_variable_requires_lock)
<< D->getName() << Lock.getName();
}
}
/// \brief For unary operations which read and write a variable, we need to
/// check whether we hold any required locks. 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: {
Expr *SubExp = UO->getSubExpr()->IgnoreParenCasts();
checkAccess(SubExp);
checkDereference(SubExp);
break;
}
default:
break;
}
}
/// For binary operations which assign to a variable (writes), we need to check
/// whether we hold any required locks.
/// FIXME: Deal with non-primitive types.
void BuildLockset::VisitBinaryOperator(BinaryOperator *BO) {
if (!BO->isAssignmentOp())
return;
Expr *LHSExp = BO->getLHS()->IgnoreParenCasts();
checkAccess(LHSExp);
checkDereference(LHSExp);
}
/// Whenever we do an LValue to Rvalue cast, we are reading a variable and
/// need to ensure we hold any required locks.
/// FIXME: Deal with non-primitive types.
void BuildLockset::VisitCastExpr(CastExpr *CE) {
if (CE->getCastKind() != CK_LValueToRValue)
return;
Expr *SubExp = CE->getSubExpr()->IgnoreParenCasts();
checkAccess(SubExp);
checkDereference(SubExp);
}
/// \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.
Caitlin Sadowski
committed
///
/// FIXME: For classes annotated with one of the guarded annotations, we need
/// to treat const method calls as reads and non-const method calls as writes,
/// and check that the appropriate locks are held. Non-const method calls with
/// the same signature as const method calls can be also treated as reads.
void BuildLockset::VisitCXXMemberCallExpr(CXXMemberCallExpr *Exp) {
NamedDecl *D = dyn_cast_or_null<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.
Caitlin Sadowski
committed
addLock(ExpLocation, Parent);