2013-08-13 05:20:55 +08:00
|
|
|
//===- Consumed.cpp --------------------------------------------*- C++ --*-===//
|
|
|
|
//
|
|
|
|
// The LLVM Compiler Infrastructure
|
|
|
|
//
|
|
|
|
// This file is distributed under the University of Illinois Open Source
|
|
|
|
// License. See LICENSE.TXT for details.
|
|
|
|
//
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
//
|
|
|
|
// A intra-procedural analysis for checking consumed properties. This is based,
|
|
|
|
// in part, on research on linear types.
|
|
|
|
//
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
|
|
|
#include "clang/AST/ASTContext.h"
|
|
|
|
#include "clang/AST/Attr.h"
|
|
|
|
#include "clang/AST/DeclCXX.h"
|
|
|
|
#include "clang/AST/ExprCXX.h"
|
|
|
|
#include "clang/AST/RecursiveASTVisitor.h"
|
|
|
|
#include "clang/AST/StmtVisitor.h"
|
|
|
|
#include "clang/AST/StmtCXX.h"
|
|
|
|
#include "clang/AST/Type.h"
|
|
|
|
#include "clang/Analysis/Analyses/PostOrderCFGView.h"
|
|
|
|
#include "clang/Analysis/AnalysisContext.h"
|
|
|
|
#include "clang/Analysis/CFG.h"
|
|
|
|
#include "clang/Analysis/Analyses/Consumed.h"
|
|
|
|
#include "clang/Basic/OperatorKinds.h"
|
|
|
|
#include "clang/Basic/SourceLocation.h"
|
|
|
|
#include "llvm/ADT/DenseMap.h"
|
|
|
|
#include "llvm/ADT/SmallVector.h"
|
|
|
|
#include "llvm/Support/raw_ostream.h"
|
|
|
|
|
|
|
|
// TODO: Add support for methods with CallableWhenUnconsumed.
|
|
|
|
// TODO: Mark variables as Unknown going into while- or for-loops only if they
|
|
|
|
// are referenced inside that block. (Deferred)
|
|
|
|
// TODO: Add a method(s) to identify which method calls perform what state
|
|
|
|
// transitions. (Deferred)
|
|
|
|
// TODO: Take notes on state transitions to provide better warning messages.
|
|
|
|
// (Deferred)
|
|
|
|
// TODO: Test nested conditionals: A) Checking the same value multiple times,
|
|
|
|
// and 2) Checking different values. (Deferred)
|
|
|
|
// TODO: Test IsFalseVisitor with values in the unknown state. (Deferred)
|
|
|
|
// TODO: Look into combining IsFalseVisitor and TestedVarsVisitor. (Deferred)
|
|
|
|
|
|
|
|
using namespace clang;
|
|
|
|
using namespace consumed;
|
|
|
|
|
|
|
|
// Key method definition
|
|
|
|
ConsumedWarningsHandlerBase::~ConsumedWarningsHandlerBase() {}
|
|
|
|
|
|
|
|
static StringRef stateToString(ConsumedState State) {
|
|
|
|
switch (State) {
|
|
|
|
case consumed::CS_None:
|
|
|
|
return "none";
|
|
|
|
|
|
|
|
case consumed::CS_Unknown:
|
|
|
|
return "unknown";
|
|
|
|
|
|
|
|
case consumed::CS_Unconsumed:
|
|
|
|
return "unconsumed";
|
|
|
|
|
|
|
|
case consumed::CS_Consumed:
|
|
|
|
return "consumed";
|
|
|
|
}
|
2013-08-13 08:11:59 +08:00
|
|
|
llvm_unreachable("invalid enum");
|
2013-08-13 05:20:55 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
namespace {
|
|
|
|
class ConsumedStmtVisitor : public ConstStmtVisitor<ConsumedStmtVisitor> {
|
|
|
|
|
|
|
|
union PropagationUnion {
|
|
|
|
ConsumedState State;
|
|
|
|
const VarDecl *Var;
|
|
|
|
};
|
|
|
|
|
|
|
|
class PropagationInfo {
|
|
|
|
PropagationUnion StateOrVar;
|
|
|
|
|
|
|
|
public:
|
|
|
|
bool IsVar;
|
|
|
|
|
|
|
|
PropagationInfo() : IsVar(false) {
|
|
|
|
StateOrVar.State = consumed::CS_None;
|
|
|
|
}
|
|
|
|
|
|
|
|
PropagationInfo(ConsumedState State) : IsVar(false) {
|
|
|
|
StateOrVar.State = State;
|
|
|
|
}
|
|
|
|
|
|
|
|
PropagationInfo(const VarDecl *Var) : IsVar(true) {
|
|
|
|
StateOrVar.Var = Var;
|
|
|
|
}
|
|
|
|
|
|
|
|
ConsumedState getState() { return StateOrVar.State; };
|
|
|
|
|
|
|
|
const VarDecl * getVar() { return IsVar ? StateOrVar.Var : NULL; };
|
|
|
|
};
|
|
|
|
|
|
|
|
typedef llvm::DenseMap<const Stmt *, PropagationInfo> MapType;
|
|
|
|
typedef std::pair<const Stmt *, PropagationInfo> PairType;
|
|
|
|
typedef MapType::iterator InfoEntry;
|
2013-08-13 07:49:39 +08:00
|
|
|
|
|
|
|
AnalysisDeclContext &AC;
|
2013-08-13 05:20:55 +08:00
|
|
|
ConsumedAnalyzer &Analyzer;
|
|
|
|
ConsumedStateMap *StateMap;
|
|
|
|
MapType PropagationMap;
|
|
|
|
|
|
|
|
void forwardInfo(const Stmt *From, const Stmt *To);
|
|
|
|
bool isLikeMoveAssignment(const CXXMethodDecl *MethodDecl);
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
|
|
|
void Visit(const Stmt *StmtNode);
|
|
|
|
|
|
|
|
void VisitBinaryOperator(const BinaryOperator *BinOp);
|
|
|
|
void VisitCallExpr(const CallExpr *Call);
|
|
|
|
void VisitCastExpr(const CastExpr *Cast);
|
|
|
|
void VisitCXXConstructExpr(const CXXConstructExpr *Call);
|
|
|
|
void VisitCXXMemberCallExpr(const CXXMemberCallExpr *Call);
|
|
|
|
void VisitCXXOperatorCallExpr(const CXXOperatorCallExpr *Call);
|
|
|
|
void VisitDeclRefExpr(const DeclRefExpr *DeclRef);
|
|
|
|
void VisitDeclStmt(const DeclStmt *DelcS);
|
|
|
|
void VisitMaterializeTemporaryExpr(const MaterializeTemporaryExpr *Temp);
|
|
|
|
void VisitMemberExpr(const MemberExpr *MExpr);
|
|
|
|
void VisitUnaryOperator(const UnaryOperator *UOp);
|
|
|
|
void VisitVarDecl(const VarDecl *Var);
|
2013-08-13 07:49:39 +08:00
|
|
|
|
|
|
|
ConsumedStmtVisitor(AnalysisDeclContext &AC, ConsumedAnalyzer &Analyzer,
|
|
|
|
ConsumedStateMap *StateMap)
|
|
|
|
: AC(AC), Analyzer(Analyzer), StateMap(StateMap) {}
|
|
|
|
|
2013-08-13 05:20:55 +08:00
|
|
|
void reset() {
|
|
|
|
PropagationMap.clear();
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
void ConsumedStmtVisitor::forwardInfo(const Stmt *From, const Stmt *To) {
|
|
|
|
InfoEntry Entry = PropagationMap.find(From);
|
|
|
|
|
|
|
|
if (Entry != PropagationMap.end()) {
|
|
|
|
PropagationMap.insert(PairType(To, PropagationInfo(Entry->second)));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool ConsumedStmtVisitor::isLikeMoveAssignment(
|
|
|
|
const CXXMethodDecl *MethodDecl) {
|
|
|
|
|
|
|
|
return MethodDecl->isMoveAssignmentOperator() ||
|
|
|
|
(MethodDecl->getOverloadedOperator() == OO_Equal &&
|
|
|
|
MethodDecl->getNumParams() == 1 &&
|
|
|
|
MethodDecl->getParamDecl(0)->getType()->isRValueReferenceType());
|
|
|
|
}
|
|
|
|
|
|
|
|
void ConsumedStmtVisitor::VisitBinaryOperator(const BinaryOperator *BinOp) {
|
|
|
|
switch (BinOp->getOpcode()) {
|
|
|
|
case BO_PtrMemD:
|
|
|
|
case BO_PtrMemI:
|
|
|
|
forwardInfo(BinOp->getLHS(), BinOp);
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void ConsumedStmtVisitor::Visit(const Stmt *StmtNode) {
|
2013-08-13 06:02:09 +08:00
|
|
|
ConstStmtVisitor<ConsumedStmtVisitor>::Visit(StmtNode);
|
2013-08-13 05:20:55 +08:00
|
|
|
|
|
|
|
for (Stmt::const_child_iterator CI = StmtNode->child_begin(),
|
|
|
|
CE = StmtNode->child_end(); CI != CE; ++CI) {
|
|
|
|
|
|
|
|
PropagationMap.erase(*CI);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void ConsumedStmtVisitor::VisitCallExpr(const CallExpr *Call) {
|
|
|
|
if (const FunctionDecl *FunDecl =
|
|
|
|
dyn_cast_or_null<FunctionDecl>(Call->getDirectCallee())) {
|
|
|
|
|
|
|
|
// Special case for the std::move function.
|
|
|
|
// TODO: Make this more specific. (Deferred)
|
|
|
|
if (FunDecl->getNameAsString() == "move") {
|
|
|
|
InfoEntry Entry = PropagationMap.find(Call->getArg(0));
|
|
|
|
|
|
|
|
if (Entry != PropagationMap.end()) {
|
|
|
|
PropagationMap.insert(PairType(Call, Entry->second));
|
|
|
|
}
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
unsigned Offset = Call->getNumArgs() - FunDecl->getNumParams();
|
|
|
|
|
|
|
|
for (unsigned Index = Offset; Index < Call->getNumArgs(); ++Index) {
|
|
|
|
QualType ParamType = FunDecl->getParamDecl(Index - Offset)->getType();
|
|
|
|
|
|
|
|
InfoEntry Entry = PropagationMap.find(Call->getArg(Index));
|
|
|
|
|
|
|
|
if (Entry == PropagationMap.end() || !Entry->second.IsVar) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
PropagationInfo PState = Entry->second;
|
|
|
|
|
|
|
|
if (ParamType->isRValueReferenceType() ||
|
|
|
|
(ParamType->isLValueReferenceType() &&
|
|
|
|
!cast<LValueReferenceType>(*ParamType).isSpelledAsLValue())) {
|
|
|
|
|
|
|
|
StateMap->setState(PState.getVar(), consumed::CS_Consumed);
|
|
|
|
|
|
|
|
} else if (!(ParamType.isConstQualified() ||
|
|
|
|
((ParamType->isReferenceType() ||
|
|
|
|
ParamType->isPointerType()) &&
|
|
|
|
ParamType->getPointeeType().isConstQualified()))) {
|
|
|
|
|
|
|
|
StateMap->setState(PState.getVar(), consumed::CS_Unknown);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void ConsumedStmtVisitor::VisitCastExpr(const CastExpr *Cast) {
|
|
|
|
InfoEntry Entry = PropagationMap.find(Cast->getSubExpr());
|
|
|
|
|
|
|
|
if (Entry != PropagationMap.end())
|
|
|
|
PropagationMap.insert(PairType(Cast, Entry->second));
|
|
|
|
}
|
|
|
|
|
|
|
|
void ConsumedStmtVisitor::VisitCXXConstructExpr(const CXXConstructExpr *Call) {
|
|
|
|
CXXConstructorDecl *Constructor = Call->getConstructor();
|
2013-08-13 07:49:39 +08:00
|
|
|
|
|
|
|
ASTContext &CurrContext = AC.getASTContext();
|
2013-08-13 05:20:55 +08:00
|
|
|
QualType ThisType = Constructor->getThisType(CurrContext)->getPointeeType();
|
|
|
|
|
|
|
|
if (Analyzer.isConsumableType(ThisType)) {
|
|
|
|
if (Constructor->hasAttr<ConsumesAttr>() ||
|
|
|
|
Constructor->isDefaultConstructor()) {
|
|
|
|
|
|
|
|
PropagationMap.insert(PairType(Call,
|
|
|
|
PropagationInfo(consumed::CS_Consumed)));
|
|
|
|
|
|
|
|
} else if (Constructor->isMoveConstructor()) {
|
|
|
|
|
|
|
|
PropagationInfo PState =
|
|
|
|
PropagationMap.find(Call->getArg(0))->second;
|
|
|
|
|
|
|
|
if (PState.IsVar) {
|
|
|
|
const VarDecl* Var = PState.getVar();
|
|
|
|
|
|
|
|
PropagationMap.insert(PairType(Call,
|
|
|
|
PropagationInfo(StateMap->getState(Var))));
|
|
|
|
|
|
|
|
StateMap->setState(Var, consumed::CS_Consumed);
|
|
|
|
|
|
|
|
} else {
|
|
|
|
PropagationMap.insert(PairType(Call, PState));
|
|
|
|
}
|
|
|
|
|
|
|
|
} else if (Constructor->isCopyConstructor()) {
|
|
|
|
MapType::iterator Entry = PropagationMap.find(Call->getArg(0));
|
|
|
|
|
|
|
|
if (Entry != PropagationMap.end())
|
|
|
|
PropagationMap.insert(PairType(Call, Entry->second));
|
|
|
|
|
|
|
|
} else {
|
|
|
|
PropagationMap.insert(PairType(Call,
|
|
|
|
PropagationInfo(consumed::CS_Unconsumed)));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void ConsumedStmtVisitor::VisitCXXMemberCallExpr(
|
|
|
|
const CXXMemberCallExpr *Call) {
|
|
|
|
|
|
|
|
VisitCallExpr(Call);
|
|
|
|
|
|
|
|
InfoEntry Entry = PropagationMap.find(Call->getCallee()->IgnoreParens());
|
|
|
|
|
|
|
|
if (Entry != PropagationMap.end()) {
|
|
|
|
PropagationInfo PState = Entry->second;
|
|
|
|
if (!PState.IsVar) return;
|
|
|
|
|
|
|
|
const CXXMethodDecl *Method = Call->getMethodDecl();
|
|
|
|
|
|
|
|
if (Method->hasAttr<ConsumesAttr>())
|
|
|
|
StateMap->setState(PState.getVar(), consumed::CS_Consumed);
|
|
|
|
else if (!Method->isConst())
|
|
|
|
StateMap->setState(PState.getVar(), consumed::CS_Unknown);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void ConsumedStmtVisitor::VisitCXXOperatorCallExpr(
|
|
|
|
const CXXOperatorCallExpr *Call) {
|
|
|
|
|
|
|
|
const FunctionDecl *FunDecl =
|
|
|
|
dyn_cast_or_null<FunctionDecl>(Call->getDirectCallee());
|
|
|
|
|
|
|
|
if (!FunDecl) return;
|
|
|
|
|
|
|
|
if (isa<CXXMethodDecl>(FunDecl) &&
|
|
|
|
isLikeMoveAssignment(cast<CXXMethodDecl>(FunDecl))) {
|
|
|
|
|
|
|
|
InfoEntry LEntry = PropagationMap.find(Call->getArg(0));
|
|
|
|
InfoEntry REntry = PropagationMap.find(Call->getArg(1));
|
|
|
|
|
|
|
|
PropagationInfo LPState, RPState;
|
|
|
|
|
|
|
|
if (LEntry != PropagationMap.end() &&
|
|
|
|
REntry != PropagationMap.end()) {
|
|
|
|
|
|
|
|
LPState = LEntry->second;
|
|
|
|
RPState = REntry->second;
|
|
|
|
|
|
|
|
if (LPState.IsVar && RPState.IsVar) {
|
|
|
|
StateMap->setState(LPState.getVar(),
|
|
|
|
StateMap->getState(RPState.getVar()));
|
|
|
|
|
|
|
|
StateMap->setState(RPState.getVar(), consumed::CS_Consumed);
|
|
|
|
|
|
|
|
PropagationMap.insert(PairType(Call, LPState));
|
|
|
|
|
|
|
|
} else if (LPState.IsVar && !RPState.IsVar) {
|
|
|
|
StateMap->setState(LPState.getVar(), RPState.getState());
|
|
|
|
|
|
|
|
PropagationMap.insert(PairType(Call, LPState));
|
|
|
|
|
|
|
|
} else if (!LPState.IsVar && RPState.IsVar) {
|
|
|
|
PropagationMap.insert(PairType(Call,
|
|
|
|
PropagationInfo(StateMap->getState(RPState.getVar()))));
|
|
|
|
|
|
|
|
StateMap->setState(RPState.getVar(), consumed::CS_Consumed);
|
|
|
|
|
|
|
|
} else {
|
|
|
|
PropagationMap.insert(PairType(Call, RPState));
|
|
|
|
}
|
|
|
|
|
|
|
|
} else if (LEntry != PropagationMap.end() &&
|
|
|
|
REntry == PropagationMap.end()) {
|
|
|
|
|
|
|
|
LPState = LEntry->second;
|
|
|
|
|
|
|
|
if (LPState.IsVar) {
|
|
|
|
StateMap->setState(LPState.getVar(), consumed::CS_Unknown);
|
|
|
|
|
|
|
|
PropagationMap.insert(PairType(Call, LPState));
|
|
|
|
|
|
|
|
} else {
|
|
|
|
PropagationMap.insert(PairType(Call,
|
|
|
|
PropagationInfo(consumed::CS_Unknown)));
|
|
|
|
}
|
|
|
|
|
|
|
|
} else if (LEntry == PropagationMap.end() &&
|
|
|
|
REntry != PropagationMap.end()) {
|
|
|
|
|
|
|
|
RPState = REntry->second;
|
|
|
|
|
|
|
|
if (RPState.IsVar) {
|
|
|
|
const VarDecl *Var = RPState.getVar();
|
|
|
|
|
|
|
|
PropagationMap.insert(PairType(Call,
|
|
|
|
PropagationInfo(StateMap->getState(Var))));
|
|
|
|
|
|
|
|
StateMap->setState(Var, consumed::CS_Consumed);
|
|
|
|
|
|
|
|
} else {
|
|
|
|
PropagationMap.insert(PairType(Call, RPState));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
VisitCallExpr(Call);
|
|
|
|
|
|
|
|
InfoEntry Entry = PropagationMap.find(Call->getArg(0));
|
|
|
|
|
|
|
|
if (Entry != PropagationMap.end()) {
|
|
|
|
|
|
|
|
PropagationInfo PState = Entry->second;
|
|
|
|
|
|
|
|
// TODO: When we support CallableWhenConsumed this will have to check for
|
|
|
|
// the different attributes and change the behavior bellow.
|
|
|
|
// (Deferred)
|
|
|
|
if (FunDecl->hasAttr<CallableWhenUnconsumedAttr>()) {
|
|
|
|
if (PState.IsVar) {
|
|
|
|
const VarDecl *Var = PState.getVar();
|
|
|
|
|
|
|
|
switch (StateMap->getState(Var)) {
|
|
|
|
case CS_Consumed:
|
|
|
|
Analyzer.WarningsHandler.warnUseWhileConsumed(
|
|
|
|
FunDecl->getNameAsString(), Var->getNameAsString(),
|
|
|
|
Call->getExprLoc());
|
|
|
|
break;
|
|
|
|
|
|
|
|
case CS_Unknown:
|
|
|
|
Analyzer.WarningsHandler.warnUseInUnknownState(
|
|
|
|
FunDecl->getNameAsString(), Var->getNameAsString(),
|
|
|
|
Call->getExprLoc());
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
} else {
|
|
|
|
switch (PState.getState()) {
|
|
|
|
case CS_Consumed:
|
|
|
|
Analyzer.WarningsHandler.warnUseOfTempWhileConsumed(
|
|
|
|
FunDecl->getNameAsString(), Call->getExprLoc());
|
|
|
|
break;
|
|
|
|
|
|
|
|
case CS_Unknown:
|
|
|
|
Analyzer.WarningsHandler.warnUseOfTempInUnknownState(
|
|
|
|
FunDecl->getNameAsString(), Call->getExprLoc());
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Handle non-constant member operators.
|
|
|
|
if (const CXXMethodDecl *MethodDecl =
|
|
|
|
dyn_cast_or_null<CXXMethodDecl>(FunDecl)) {
|
|
|
|
|
|
|
|
if (!MethodDecl->isConst() && PState.IsVar)
|
|
|
|
StateMap->setState(PState.getVar(), consumed::CS_Unknown);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void ConsumedStmtVisitor::VisitDeclRefExpr(const DeclRefExpr *DeclRef) {
|
|
|
|
if (const VarDecl *Var = dyn_cast_or_null<VarDecl>(DeclRef->getDecl()))
|
|
|
|
if (StateMap->getState(Var) != consumed::CS_None)
|
|
|
|
PropagationMap.insert(PairType(DeclRef, PropagationInfo(Var)));
|
|
|
|
}
|
|
|
|
|
|
|
|
void ConsumedStmtVisitor::VisitDeclStmt(const DeclStmt *DeclS) {
|
|
|
|
for (DeclStmt::const_decl_iterator DI = DeclS->decl_begin(),
|
|
|
|
DE = DeclS->decl_end(); DI != DE; ++DI) {
|
|
|
|
|
|
|
|
if (isa<VarDecl>(*DI)) VisitVarDecl(cast<VarDecl>(*DI));
|
|
|
|
}
|
|
|
|
|
|
|
|
if (DeclS->isSingleDecl())
|
|
|
|
if (const VarDecl *Var = dyn_cast_or_null<VarDecl>(DeclS->getSingleDecl()))
|
|
|
|
PropagationMap.insert(PairType(DeclS, PropagationInfo(Var)));
|
|
|
|
}
|
|
|
|
|
|
|
|
void ConsumedStmtVisitor::VisitMaterializeTemporaryExpr(
|
|
|
|
const MaterializeTemporaryExpr *Temp) {
|
|
|
|
|
|
|
|
InfoEntry Entry = PropagationMap.find(Temp->GetTemporaryExpr());
|
|
|
|
|
|
|
|
if (Entry != PropagationMap.end())
|
|
|
|
PropagationMap.insert(PairType(Temp, Entry->second));
|
|
|
|
}
|
|
|
|
|
|
|
|
void ConsumedStmtVisitor::VisitMemberExpr(const MemberExpr *MExpr) {
|
|
|
|
forwardInfo(MExpr->getBase(), MExpr);
|
|
|
|
}
|
|
|
|
|
|
|
|
void ConsumedStmtVisitor::VisitUnaryOperator(const UnaryOperator *UOp) {
|
|
|
|
if (UOp->getOpcode() == UO_AddrOf) {
|
|
|
|
InfoEntry Entry = PropagationMap.find(UOp->getSubExpr());
|
|
|
|
|
|
|
|
if (Entry != PropagationMap.end())
|
|
|
|
PropagationMap.insert(PairType(UOp, Entry->second));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void ConsumedStmtVisitor::VisitVarDecl(const VarDecl *Var) {
|
|
|
|
if (Analyzer.isConsumableType(Var->getType())) {
|
|
|
|
PropagationInfo PState =
|
|
|
|
PropagationMap.find(Var->getInit())->second;
|
|
|
|
|
|
|
|
StateMap->setState(Var, PState.IsVar ?
|
|
|
|
StateMap->getState(PState.getVar()) : PState.getState());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} // end anonymous::ConsumedStmtVisitor
|
|
|
|
|
|
|
|
namespace {
|
|
|
|
|
|
|
|
// TODO: Handle variable definitions, e.g. bool valid = x.isValid();
|
|
|
|
// if (valid) ...; (Deferred)
|
|
|
|
class TestedVarsVisitor : public RecursiveASTVisitor<TestedVarsVisitor> {
|
|
|
|
|
|
|
|
bool Invert;
|
|
|
|
SourceLocation CurrTestLoc;
|
|
|
|
|
|
|
|
ConsumedStateMap *StateMap;
|
|
|
|
|
|
|
|
public:
|
|
|
|
bool IsUsefulConditional;
|
|
|
|
VarTestResult Test;
|
|
|
|
|
|
|
|
TestedVarsVisitor(ConsumedStateMap *StateMap) : Invert(false),
|
|
|
|
StateMap(StateMap), IsUsefulConditional(false) {}
|
|
|
|
|
|
|
|
bool VisitCallExpr(CallExpr *Call);
|
|
|
|
bool VisitDeclRefExpr(DeclRefExpr *DeclRef);
|
|
|
|
bool VisitUnaryOperator(UnaryOperator *UnaryOp);
|
|
|
|
};
|
|
|
|
|
|
|
|
bool TestedVarsVisitor::VisitCallExpr(CallExpr *Call) {
|
|
|
|
if (const CXXMethodDecl *Method =
|
|
|
|
dyn_cast_or_null<CXXMethodDecl>(Call->getDirectCallee())) {
|
|
|
|
|
|
|
|
if (isTestingFunction(Method)) {
|
|
|
|
CurrTestLoc = Call->getExprLoc();
|
|
|
|
IsUsefulConditional = true;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
IsUsefulConditional = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool TestedVarsVisitor::VisitDeclRefExpr(DeclRefExpr *DeclRef) {
|
|
|
|
if (const VarDecl *Var = dyn_cast_or_null<VarDecl>(DeclRef->getDecl())) {
|
|
|
|
if (StateMap->getState(Var) != consumed::CS_None) {
|
|
|
|
Test = VarTestResult(Var, CurrTestLoc, !Invert);
|
|
|
|
}
|
|
|
|
|
|
|
|
} else {
|
|
|
|
IsUsefulConditional = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return IsUsefulConditional;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool TestedVarsVisitor::VisitUnaryOperator(UnaryOperator *UnaryOp) {
|
|
|
|
if (UnaryOp->getOpcode() == UO_LNot) {
|
|
|
|
Invert = true;
|
|
|
|
TraverseStmt(UnaryOp->getSubExpr());
|
|
|
|
|
|
|
|
} else {
|
|
|
|
IsUsefulConditional = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
} // end anonymouse::TestedVarsVisitor
|
|
|
|
|
|
|
|
namespace clang {
|
|
|
|
namespace consumed {
|
|
|
|
|
|
|
|
void ConsumedBlockInfo::addInfo(const CFGBlock *Block,
|
|
|
|
ConsumedStateMap *StateMap,
|
|
|
|
bool &AlreadyOwned) {
|
|
|
|
|
|
|
|
if (VisitedBlocks.alreadySet(Block)) return;
|
|
|
|
|
|
|
|
ConsumedStateMap *Entry = StateMapsArray[Block->getBlockID()];
|
|
|
|
|
|
|
|
if (Entry) {
|
|
|
|
Entry->intersect(StateMap);
|
|
|
|
|
|
|
|
} else if (AlreadyOwned) {
|
|
|
|
StateMapsArray[Block->getBlockID()] = new ConsumedStateMap(*StateMap);
|
|
|
|
|
|
|
|
} else {
|
|
|
|
StateMapsArray[Block->getBlockID()] = StateMap;
|
|
|
|
AlreadyOwned = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void ConsumedBlockInfo::addInfo(const CFGBlock *Block,
|
|
|
|
ConsumedStateMap *StateMap) {
|
|
|
|
|
|
|
|
if (VisitedBlocks.alreadySet(Block)) {
|
|
|
|
delete StateMap;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
ConsumedStateMap *Entry = StateMapsArray[Block->getBlockID()];
|
|
|
|
|
|
|
|
if (Entry) {
|
|
|
|
Entry->intersect(StateMap);
|
|
|
|
delete StateMap;
|
|
|
|
|
|
|
|
} else {
|
|
|
|
StateMapsArray[Block->getBlockID()] = StateMap;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
ConsumedStateMap* ConsumedBlockInfo::getInfo(const CFGBlock *Block) {
|
|
|
|
return StateMapsArray[Block->getBlockID()];
|
|
|
|
}
|
|
|
|
|
|
|
|
void ConsumedBlockInfo::markVisited(const CFGBlock *Block) {
|
|
|
|
VisitedBlocks.insert(Block);
|
|
|
|
}
|
|
|
|
|
|
|
|
ConsumedState ConsumedStateMap::getState(const VarDecl *Var) {
|
|
|
|
MapType::const_iterator Entry = Map.find(Var);
|
|
|
|
|
|
|
|
if (Entry != Map.end()) {
|
|
|
|
return Entry->second;
|
|
|
|
|
|
|
|
} else {
|
|
|
|
return CS_None;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void ConsumedStateMap::intersect(const ConsumedStateMap *Other) {
|
|
|
|
ConsumedState LocalState;
|
|
|
|
|
|
|
|
for (MapType::const_iterator DMI = Other->Map.begin(),
|
|
|
|
DME = Other->Map.end(); DMI != DME; ++DMI) {
|
|
|
|
|
|
|
|
LocalState = this->getState(DMI->first);
|
|
|
|
|
|
|
|
if (LocalState != CS_None && LocalState != DMI->second)
|
|
|
|
setState(DMI->first, CS_Unknown);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void ConsumedStateMap::makeUnknown() {
|
|
|
|
PairType Pair;
|
|
|
|
|
|
|
|
for (MapType::const_iterator DMI = Map.begin(), DME = Map.end(); DMI != DME;
|
|
|
|
++DMI) {
|
|
|
|
|
|
|
|
Pair = *DMI;
|
|
|
|
|
|
|
|
Map.erase(Pair.first);
|
|
|
|
Map.insert(PairType(Pair.first, CS_Unknown));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void ConsumedStateMap::setState(const VarDecl *Var, ConsumedState State) {
|
|
|
|
Map[Var] = State;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool ConsumedAnalyzer::isConsumableType(QualType Type) {
|
|
|
|
const CXXRecordDecl *RD =
|
|
|
|
dyn_cast_or_null<CXXRecordDecl>(Type->getAsCXXRecordDecl());
|
|
|
|
|
|
|
|
if (!RD) return false;
|
|
|
|
|
|
|
|
std::pair<CacheMapType::iterator, bool> Entry =
|
|
|
|
ConsumableTypeCache.insert(std::make_pair(RD, false));
|
|
|
|
|
|
|
|
if (Entry.second)
|
|
|
|
Entry.first->second = hasConsumableAttributes(RD);
|
|
|
|
|
|
|
|
return Entry.first->second;
|
|
|
|
}
|
|
|
|
|
|
|
|
// TODO: Walk the base classes to see if any of them are unique types.
|
|
|
|
// (Deferred)
|
|
|
|
bool ConsumedAnalyzer::hasConsumableAttributes(const CXXRecordDecl *RD) {
|
|
|
|
for (CXXRecordDecl::method_iterator MI = RD->method_begin(),
|
|
|
|
ME = RD->method_end(); MI != ME; ++MI) {
|
|
|
|
|
|
|
|
for (Decl::attr_iterator AI = (*MI)->attr_begin(), AE = (*MI)->attr_end();
|
|
|
|
AI != AE; ++AI) {
|
|
|
|
|
|
|
|
switch ((*AI)->getKind()) {
|
|
|
|
case attr::CallableWhenUnconsumed:
|
|
|
|
case attr::TestsUnconsumed:
|
|
|
|
return true;
|
|
|
|
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// TODO: Handle other forms of branching with precision, including while- and
|
|
|
|
// for-loops. (Deferred)
|
|
|
|
void ConsumedAnalyzer::splitState(const CFGBlock *CurrBlock,
|
|
|
|
const IfStmt *Terminator) {
|
|
|
|
|
|
|
|
TestedVarsVisitor Visitor(CurrStates);
|
|
|
|
Visitor.TraverseStmt(const_cast<Expr*>(Terminator->getCond()));
|
|
|
|
|
|
|
|
bool HasElse = Terminator->getElse() != NULL;
|
|
|
|
|
|
|
|
ConsumedStateMap *ElseOrMergeStates = new ConsumedStateMap(*CurrStates);
|
|
|
|
|
|
|
|
if (Visitor.IsUsefulConditional) {
|
|
|
|
ConsumedState VarState = CurrStates->getState(Visitor.Test.Var);
|
|
|
|
|
|
|
|
if (VarState != CS_Unknown) {
|
|
|
|
// FIXME: Make this not warn if the test is from a macro expansion.
|
|
|
|
// (Deferred)
|
|
|
|
WarningsHandler.warnUnnecessaryTest(Visitor.Test.Var->getNameAsString(),
|
|
|
|
stateToString(VarState), Visitor.Test.Loc);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (Visitor.Test.UnconsumedInTrueBranch) {
|
|
|
|
CurrStates->setState(Visitor.Test.Var, CS_Unconsumed);
|
|
|
|
if (HasElse) ElseOrMergeStates->setState(Visitor.Test.Var, CS_Consumed);
|
|
|
|
|
|
|
|
} else {
|
|
|
|
CurrStates->setState(Visitor.Test.Var, CS_Consumed);
|
|
|
|
if (HasElse) ElseOrMergeStates->setState(Visitor.Test.Var, CS_Unconsumed);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
CFGBlock::const_succ_iterator SI = CurrBlock->succ_begin();
|
|
|
|
|
|
|
|
if (*SI) BlockInfo.addInfo(*SI, CurrStates);
|
|
|
|
if (*++SI) BlockInfo.addInfo(*SI, ElseOrMergeStates);
|
|
|
|
}
|
|
|
|
|
|
|
|
void ConsumedAnalyzer::run(AnalysisDeclContext &AC) {
|
|
|
|
const FunctionDecl *D = dyn_cast_or_null<FunctionDecl>(AC.getDecl());
|
|
|
|
|
|
|
|
if (!D) return;
|
|
|
|
|
|
|
|
BlockInfo = ConsumedBlockInfo(AC.getCFG());
|
|
|
|
|
|
|
|
PostOrderCFGView *SortedGraph = AC.getAnalysis<PostOrderCFGView>();
|
|
|
|
|
|
|
|
CurrStates = new ConsumedStateMap();
|
|
|
|
|
|
|
|
// Visit all of the function's basic blocks.
|
|
|
|
for (PostOrderCFGView::iterator I = SortedGraph->begin(),
|
|
|
|
E = SortedGraph->end(); I != E; ++I) {
|
|
|
|
|
|
|
|
const CFGBlock *CurrBlock = *I;
|
|
|
|
BlockInfo.markVisited(CurrBlock);
|
|
|
|
|
|
|
|
if (CurrStates == NULL)
|
|
|
|
CurrStates = BlockInfo.getInfo(CurrBlock);
|
2013-08-13 07:49:39 +08:00
|
|
|
|
|
|
|
ConsumedStmtVisitor Visitor(AC, *this, CurrStates);
|
|
|
|
|
2013-08-13 05:20:55 +08:00
|
|
|
// Visit all of the basic block's statements.
|
|
|
|
for (CFGBlock::const_iterator BI = CurrBlock->begin(),
|
|
|
|
BE = CurrBlock->end(); BI != BE; ++BI) {
|
|
|
|
|
|
|
|
if (BI->getKind() == CFGElement::Statement)
|
|
|
|
Visitor.Visit(BI->castAs<CFGStmt>().getStmt());
|
|
|
|
}
|
|
|
|
|
|
|
|
// TODO: Remove any variables that have reached the end of their
|
|
|
|
// lifetimes from the state map. (Deferred)
|
|
|
|
|
|
|
|
if (const IfStmt *Terminator =
|
|
|
|
dyn_cast_or_null<IfStmt>(CurrBlock->getTerminator().getStmt())) {
|
|
|
|
|
|
|
|
splitState(CurrBlock, Terminator);
|
|
|
|
CurrStates = NULL;
|
|
|
|
|
|
|
|
} else if (CurrBlock->succ_size() > 1) {
|
|
|
|
CurrStates->makeUnknown();
|
|
|
|
|
|
|
|
bool OwnershipTaken = false;
|
|
|
|
|
|
|
|
for (CFGBlock::const_succ_iterator SI = CurrBlock->succ_begin(),
|
|
|
|
SE = CurrBlock->succ_end(); SI != SE; ++SI) {
|
|
|
|
|
|
|
|
if (*SI) BlockInfo.addInfo(*SI, CurrStates, OwnershipTaken);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!OwnershipTaken)
|
|
|
|
delete CurrStates;
|
|
|
|
|
|
|
|
CurrStates = NULL;
|
|
|
|
|
|
|
|
} else if (CurrBlock->succ_size() == 1 &&
|
|
|
|
(*CurrBlock->succ_begin())->pred_size() > 1) {
|
|
|
|
|
|
|
|
BlockInfo.addInfo(*CurrBlock->succ_begin(), CurrStates);
|
|
|
|
CurrStates = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
Visitor.reset();
|
|
|
|
} // End of block iterator.
|
|
|
|
|
|
|
|
// Delete the last existing state map.
|
|
|
|
delete CurrStates;
|
|
|
|
|
|
|
|
WarningsHandler.emitDiagnostics();
|
|
|
|
}
|
|
|
|
|
|
|
|
bool isTestingFunction(const CXXMethodDecl *Method) {
|
|
|
|
return Method->hasAttr<TestsUnconsumedAttr>();
|
|
|
|
}
|
|
|
|
|
|
|
|
}} // end namespace clang::consumed
|