summaryrefslogtreecommitdiffstats
path: root/clang/lib/Analysis/Consumed.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'clang/lib/Analysis/Consumed.cpp')
-rw-r--r--clang/lib/Analysis/Consumed.cpp443
1 files changed, 222 insertions, 221 deletions
diff --git a/clang/lib/Analysis/Consumed.cpp b/clang/lib/Analysis/Consumed.cpp
index 48f074db0aa..ed22bda16aa 100644
--- a/clang/lib/Analysis/Consumed.cpp
+++ b/clang/lib/Analysis/Consumed.cpp
@@ -143,6 +143,7 @@ static bool isCallableInState(const CallableWhenAttr *CWAttr,
return false;
}
+
static bool isConsumableType(const QualType &QT) {
if (QT->isPointerType() || QT->isReferenceType())
return false;
@@ -153,6 +154,23 @@ static bool isConsumableType(const QualType &QT) {
return false;
}
+static bool isAutoCastType(const QualType &QT) {
+ if (QT->isPointerType() || QT->isReferenceType())
+ return false;
+
+ if (const CXXRecordDecl *RD = QT->getAsCXXRecordDecl())
+ return RD->hasAttr<ConsumableAutoCastAttr>();
+
+ return false;
+}
+
+static bool isSetOnReadPtrType(const QualType &QT) {
+ if (const CXXRecordDecl *RD = QT->getPointeeCXXRecordDecl())
+ return RD->hasAttr<ConsumableSetOnReadAttr>();
+ return false;
+}
+
+
static bool isKnownState(ConsumedState State) {
switch (State) {
case CS_Unconsumed:
@@ -166,18 +184,18 @@ static bool isKnownState(ConsumedState State) {
}
static bool isRValueRefish(QualType ParamType) {
- return ParamType->isRValueReferenceType() ||
+ return ParamType->isRValueReferenceType(); /* ||
(ParamType->isLValueReferenceType() &&
!cast<LValueReferenceType>(
- ParamType.getCanonicalType())->isSpelledAsLValue());
+ ParamType.getCanonicalType())->isSpelledAsLValue()); */
}
static bool isTestingFunction(const FunctionDecl *FunDecl) {
return FunDecl->hasAttr<TestTypestateAttr>();
}
-static bool isValueType(QualType ParamType) {
- return !(ParamType->isPointerType() || ParamType->isReferenceType());
+static bool isPointerOrRef(QualType ParamType) {
+ return ParamType->isPointerType() || ParamType->isReferenceType();
}
static ConsumedState mapConsumableAttrState(const QualType QT) {
@@ -455,15 +473,19 @@ class ConsumedStmtVisitor : public ConstStmtVisitor<ConsumedStmtVisitor> {
ConsumedAnalyzer &Analyzer;
ConsumedStateMap *StateMap;
MapType PropagationMap;
+
void forwardInfo(const Stmt *From, const Stmt *To);
- bool isLikeMoveAssignment(const CXXMethodDecl *MethodDecl);
- void propagateReturnType(const Stmt *Call, const FunctionDecl *Fun,
- QualType ReturnType);
+ void copyInfo(const Stmt *From, const Stmt *To, ConsumedState CS);
+ ConsumedState getInfo(const Stmt *From);
+ void setInfo(const Stmt *To, ConsumedState NS);
+ void propagateReturnType(const Stmt *Call, const FunctionDecl *Fun);
public:
void checkCallability(const PropagationInfo &PInfo,
const FunctionDecl *FunDecl,
SourceLocation BlameLoc);
+ bool handleCall(const CallExpr *Call, const Expr *ObjArg,
+ const FunctionDecl *FunD);
void VisitBinaryOperator(const BinaryOperator *BinOp);
void VisitCallExpr(const CallExpr *Call);
@@ -499,68 +521,182 @@ public:
}
};
+
+void ConsumedStmtVisitor::forwardInfo(const Stmt *From, const Stmt *To) {
+ InfoEntry Entry = PropagationMap.find(From);
+ if (Entry != PropagationMap.end())
+ PropagationMap.insert(PairType(To, Entry->second));
+}
+
+
+// Create a new state for To, which is initialized to the state of From.
+// If NS is not CS_None, sets the state of From to NS.
+void ConsumedStmtVisitor::copyInfo(const Stmt *From, const Stmt *To,
+ ConsumedState NS) {
+ InfoEntry Entry = PropagationMap.find(From);
+ if (Entry != PropagationMap.end()) {
+ PropagationInfo& PInfo = Entry->second;
+ ConsumedState CS = PInfo.getAsState(StateMap);
+ if (CS != CS_None)
+ PropagationMap.insert(PairType(To, CS));
+ if (NS != CS_None && PInfo.isPointerToValue())
+ setStateForVarOrTmp(StateMap, PInfo, NS);
+ }
+}
+
+
+// Get the ConsumedState for From
+ConsumedState ConsumedStmtVisitor::getInfo(const Stmt *From) {
+ InfoEntry Entry = PropagationMap.find(From);
+ if (Entry != PropagationMap.end()) {
+ PropagationInfo& PInfo = Entry->second;
+ return PInfo.getAsState(StateMap);
+ }
+ return CS_None;
+}
+
+
+// If we already have info for To then update it, otherwise create a new entry.
+void ConsumedStmtVisitor::setInfo(const Stmt *To, ConsumedState NS) {
+ InfoEntry Entry = PropagationMap.find(To);
+ if (Entry != PropagationMap.end()) {
+ PropagationInfo& PInfo = Entry->second;
+ if (PInfo.isPointerToValue())
+ setStateForVarOrTmp(StateMap, PInfo, NS);
+ } else if (NS != CS_None) {
+ PropagationMap.insert(PairType(To, PropagationInfo(NS)));
+ }
+}
+
+
+
void ConsumedStmtVisitor::checkCallability(const PropagationInfo &PInfo,
const FunctionDecl *FunDecl,
SourceLocation BlameLoc) {
assert(!PInfo.isTest());
-
+
const CallableWhenAttr *CWAttr = FunDecl->getAttr<CallableWhenAttr>();
if (!CWAttr)
return;
-
+
if (PInfo.isVar()) {
ConsumedState VarState = StateMap->getState(PInfo.getVar());
-
+
if (VarState == CS_None || isCallableInState(CWAttr, VarState))
return;
-
+
Analyzer.WarningsHandler.warnUseInInvalidState(
FunDecl->getNameAsString(), PInfo.getVar()->getNameAsString(),
stateToString(VarState), BlameLoc);
-
+
} else {
ConsumedState TmpState = PInfo.getAsState(StateMap);
-
+
if (TmpState == CS_None || isCallableInState(CWAttr, TmpState))
return;
-
+
Analyzer.WarningsHandler.warnUseOfTempInInvalidState(
FunDecl->getNameAsString(), stateToString(TmpState), BlameLoc);
}
}
-void ConsumedStmtVisitor::forwardInfo(const Stmt *From, const Stmt *To) {
- InfoEntry Entry = PropagationMap.find(From);
-
- if (Entry != PropagationMap.end())
- PropagationMap.insert(PairType(To, Entry->second));
-}
-bool ConsumedStmtVisitor::isLikeMoveAssignment(
- const CXXMethodDecl *MethodDecl) {
-
- return MethodDecl->isMoveAssignmentOperator() ||
- (MethodDecl->getOverloadedOperator() == OO_Equal &&
- MethodDecl->getNumParams() == 1 &&
- MethodDecl->getParamDecl(0)->getType()->isRValueReferenceType());
+// Factors out common behavior for function, method, and operator calls.
+// Check parameters and set parameter state if necessary.
+// Returns true if the state of ObjArg is set, or false otherwise.
+bool ConsumedStmtVisitor::handleCall(const CallExpr *Call, const Expr *ObjArg,
+ const FunctionDecl *FunD) {
+ unsigned Offset = 0;
+ if (isa<CXXMethodDecl>(FunD))
+ Offset = 1; // First argument to call is 'this' parameter
+
+ // check explicit parameters
+ for (unsigned Index = Offset; Index < Call->getNumArgs(); ++Index) {
+ // Skip variable argument lists.
+ if (Index - Offset >= FunD->getNumParams())
+ break;
+
+ const ParmVarDecl *Param = FunD->getParamDecl(Index - Offset);
+ QualType ParamType = Param->getType();
+
+ InfoEntry Entry = PropagationMap.find(Call->getArg(Index));
+
+ if (Entry == PropagationMap.end() || Entry->second.isTest())
+ continue;
+ PropagationInfo PInfo = Entry->second;
+
+ // Check that the parameter is in the correct state.
+ if (ParamTypestateAttr *PTA = Param->getAttr<ParamTypestateAttr>()) {
+ ConsumedState ParamState = PInfo.getAsState(StateMap);
+ ConsumedState ExpectedState = mapParamTypestateAttrState(PTA);
+
+ if (ParamState != ExpectedState)
+ Analyzer.WarningsHandler.warnParamTypestateMismatch(
+ Call->getArg(Index)->getExprLoc(),
+ stateToString(ExpectedState), stateToString(ParamState));
+ }
+
+ if (!(Entry->second.isVar() || Entry->second.isTmp()))
+ continue;
+
+ // Adjust state on the caller side.
+ if (isRValueRefish(ParamType))
+ setStateForVarOrTmp(StateMap, PInfo, consumed::CS_Consumed);
+ else if (ReturnTypestateAttr *RT = Param->getAttr<ReturnTypestateAttr>())
+ setStateForVarOrTmp(StateMap, PInfo, mapReturnTypestateAttrState(RT));
+ else if (isPointerOrRef(ParamType)) {
+ if (!ParamType->getPointeeType().isConstQualified() ||
+ isSetOnReadPtrType(ParamType))
+ setStateForVarOrTmp(StateMap, PInfo, consumed::CS_Unknown);
+ }
+ }
+
+ if (!ObjArg)
+ return false;
+
+ // check implicit 'self' parameter, if present
+ InfoEntry Entry = PropagationMap.find(ObjArg);
+ if (Entry != PropagationMap.end()) {
+ PropagationInfo PInfo = Entry->second;
+ checkCallability(PInfo, FunD, Call->getExprLoc());
+
+ if (SetTypestateAttr *STA = FunD->getAttr<SetTypestateAttr>()) {
+ if (PInfo.isVar()) {
+ StateMap->setState(PInfo.getVar(), mapSetTypestateAttrState(STA));
+ return true;
+ }
+ else if (PInfo.isTmp()) {
+ StateMap->setState(PInfo.getTmp(), mapSetTypestateAttrState(STA));
+ return true;
+ }
+ }
+ else if (isTestingFunction(FunD) && PInfo.isVar()) {
+ PropagationMap.insert(PairType(Call,
+ PropagationInfo(PInfo.getVar(), testsFor(FunD))));
+ }
+ }
+ return false;
}
+
void ConsumedStmtVisitor::propagateReturnType(const Stmt *Call,
- const FunctionDecl *Fun,
- QualType ReturnType) {
- if (isConsumableType(ReturnType)) {
-
+ const FunctionDecl *Fun) {
+ QualType RetType = Fun->getCallResultType();
+ if (RetType->isReferenceType())
+ RetType = RetType->getPointeeType();
+
+ if (isConsumableType(RetType)) {
ConsumedState ReturnState;
-
if (ReturnTypestateAttr *RTA = Fun->getAttr<ReturnTypestateAttr>())
ReturnState = mapReturnTypestateAttrState(RTA);
else
- ReturnState = mapConsumableAttrState(ReturnType);
+ ReturnState = mapConsumableAttrState(RetType);
PropagationMap.insert(PairType(Call, PropagationInfo(ReturnState)));
}
}
+
void ConsumedStmtVisitor::VisitBinaryOperator(const BinaryOperator *BinOp) {
switch (BinOp->getOpcode()) {
case BO_LAnd:
@@ -614,63 +750,21 @@ static bool isStdNamespace(const DeclContext *DC) {
}
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 (Call->getNumArgs() == 1 &&
- FunDecl->getNameAsString() == "move" &&
- isStdNamespace(FunDecl->getDeclContext())) {
- forwardInfo(Call->getArg(0), Call);
- return;
- }
-
- unsigned Offset = Call->getNumArgs() - FunDecl->getNumParams();
-
- for (unsigned Index = Offset; Index < Call->getNumArgs(); ++Index) {
- const ParmVarDecl *Param = FunDecl->getParamDecl(Index - Offset);
- QualType ParamType = Param->getType();
-
- InfoEntry Entry = PropagationMap.find(Call->getArg(Index));
-
- if (Entry == PropagationMap.end() || Entry->second.isTest())
- continue;
-
- PropagationInfo PInfo = Entry->second;
-
- // Check that the parameter is in the correct state.
-
- if (ParamTypestateAttr *PTA = Param->getAttr<ParamTypestateAttr>()) {
- ConsumedState ParamState = PInfo.getAsState(StateMap);
- ConsumedState ExpectedState = mapParamTypestateAttrState(PTA);
-
- if (ParamState != ExpectedState)
- Analyzer.WarningsHandler.warnParamTypestateMismatch(
- Call->getArg(Index - Offset)->getExprLoc(),
- stateToString(ExpectedState), stateToString(ParamState));
- }
-
- if (!(Entry->second.isVar() || Entry->second.isTmp()))
- continue;
-
- // Adjust state on the caller side.
-
- if (isRValueRefish(ParamType))
- setStateForVarOrTmp(StateMap, PInfo, consumed::CS_Consumed);
- else if (ReturnTypestateAttr *RT = Param->getAttr<ReturnTypestateAttr>())
- setStateForVarOrTmp(StateMap, PInfo, mapReturnTypestateAttrState(RT));
- else if (!isValueType(ParamType) &&
- !ParamType->getPointeeType().isConstQualified())
- setStateForVarOrTmp(StateMap, PInfo, consumed::CS_Unknown);
- }
-
- QualType RetType = FunDecl->getCallResultType();
- if (RetType->isReferenceType())
- RetType = RetType->getPointeeType();
-
- propagateReturnType(Call, FunDecl, RetType);
+ const FunctionDecl *FunDecl = Call->getDirectCallee();
+ if (!FunDecl)
+ return;
+
+ // Special case for the std::move function.
+ // TODO: Make this more specific. (Deferred)
+ if (Call->getNumArgs() == 1 &&
+ FunDecl->getNameAsString() == "move" &&
+ isStdNamespace(FunDecl->getDeclContext())) {
+ copyInfo(Call->getArg(0), Call, CS_Consumed);
+ return;
}
+
+ handleCall(Call, 0, FunDecl);
+ propagateReturnType(Call, FunDecl);
}
void ConsumedStmtVisitor::VisitCastExpr(const CastExpr *Cast) {
@@ -701,154 +795,57 @@ void ConsumedStmtVisitor::VisitCXXConstructExpr(const CXXConstructExpr *Call) {
if (ReturnTypestateAttr *RTA = Constructor->getAttr<ReturnTypestateAttr>()) {
// TODO: Adjust state of args appropriately.
ConsumedState RetState = mapReturnTypestateAttrState(RTA);
- PropagationMap.insert(PairType(Call, PropagationInfo(RetState)));
- } else if (Constructor->isDefaultConstructor()) {
+ PropagationMap.insert(PairType(Call, PropagationInfo(RetState)));
+ } else if (Constructor->isDefaultConstructor()) {
PropagationMap.insert(PairType(Call,
PropagationInfo(consumed::CS_Consumed)));
- } else if (Constructor->isMoveConstructor()) {
- InfoEntry Entry = PropagationMap.find(Call->getArg(0));
-
- if (Entry != PropagationMap.end()) {
- PropagationInfo PInfo = Entry->second;
-
- if (PInfo.isVar()) {
- const VarDecl* Var = PInfo.getVar();
-
- PropagationMap.insert(PairType(Call,
- PropagationInfo(StateMap->getState(Var))));
-
- StateMap->setState(Var, consumed::CS_Consumed);
-
- } else if (PInfo.isTmp()) {
- const CXXBindTemporaryExpr *Tmp = PInfo.getTmp();
-
- PropagationMap.insert(PairType(Call,
- PropagationInfo(StateMap->getState(Tmp))));
-
- StateMap->setState(Tmp, consumed::CS_Consumed);
-
- } else {
- PropagationMap.insert(PairType(Call, PInfo));
- }
- }
+ } else if (Constructor->isMoveConstructor()) {
+ copyInfo(Call->getArg(0), Call, CS_Consumed);
} else if (Constructor->isCopyConstructor()) {
- forwardInfo(Call->getArg(0), Call);
-
+ // Copy state from arg. If setStateOnRead then set arg to CS_Unknown.
+ ConsumedState NS =
+ isSetOnReadPtrType(Constructor->getThisType(CurrContext)) ?
+ CS_Unknown : CS_None;
+ copyInfo(Call->getArg(0), Call, NS);
} else {
// TODO: Adjust state of args appropriately.
-
ConsumedState RetState = mapConsumableAttrState(ThisType);
PropagationMap.insert(PairType(Call, PropagationInfo(RetState)));
}
}
+
void ConsumedStmtVisitor::VisitCXXMemberCallExpr(
- const CXXMemberCallExpr *Call) {
-
- VisitCallExpr(Call);
-
- InfoEntry Entry = PropagationMap.find(Call->getCallee()->IgnoreParens());
-
- if (Entry != PropagationMap.end()) {
- PropagationInfo PInfo = Entry->second;
- const CXXMethodDecl *MethodDecl = Call->getMethodDecl();
-
- checkCallability(PInfo, MethodDecl, Call->getExprLoc());
-
- SetTypestateAttr *STA = MethodDecl->getAttr<SetTypestateAttr>();
- if (PInfo.isVar()) {
- if (isTestingFunction(MethodDecl))
- PropagationMap.insert(PairType(Call,
- PropagationInfo(PInfo.getVar(), testsFor(MethodDecl))));
- else if (STA)
- StateMap->setState(PInfo.getVar(), mapSetTypestateAttrState(STA));
- } else if (STA && PInfo.isTmp())
- StateMap->setState(PInfo.getTmp(), mapSetTypestateAttrState(STA));
- }
+ const CXXMemberCallExpr *Call) {
+ CXXMethodDecl* MD = Call->getMethodDecl();
+ if (!MD)
+ return;
+
+ handleCall(Call, Call->getImplicitObjectArgument(), MD);
+ propagateReturnType(Call, MD);
}
+
void ConsumedStmtVisitor::VisitCXXOperatorCallExpr(
- const CXXOperatorCallExpr *Call) {
-
+ 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 LPInfo, RPInfo;
-
- if (LEntry != PropagationMap.end() &&
- REntry != PropagationMap.end()) {
-
- LPInfo = LEntry->second;
- RPInfo = REntry->second;
-
- if (LPInfo.isPointerToValue() && RPInfo.isPointerToValue()) {
- setStateForVarOrTmp(StateMap, LPInfo, RPInfo.getAsState(StateMap));
- PropagationMap.insert(PairType(Call, LPInfo));
- setStateForVarOrTmp(StateMap, RPInfo, consumed::CS_Consumed);
-
- } else if (RPInfo.isState()) {
- setStateForVarOrTmp(StateMap, LPInfo, RPInfo.getState());
- PropagationMap.insert(PairType(Call, LPInfo));
-
- } else {
- setStateForVarOrTmp(StateMap, RPInfo, consumed::CS_Consumed);
- }
-
- } else if (LEntry != PropagationMap.end() &&
- REntry == PropagationMap.end()) {
-
- LPInfo = LEntry->second;
-
- assert(!LPInfo.isTest());
-
- if (LPInfo.isPointerToValue()) {
- setStateForVarOrTmp(StateMap, LPInfo, consumed::CS_Unknown);
- PropagationMap.insert(PairType(Call, LPInfo));
-
- } else {
- PropagationMap.insert(PairType(Call,
- PropagationInfo(consumed::CS_Unknown)));
- }
-
- } else if (LEntry == PropagationMap.end() &&
- REntry != PropagationMap.end()) {
-
- RPInfo = REntry->second;
-
- if (RPInfo.isPointerToValue())
- setStateForVarOrTmp(StateMap, RPInfo, consumed::CS_Consumed);
- }
-
- } else {
-
- VisitCallExpr(Call);
-
- InfoEntry Entry = PropagationMap.find(Call->getArg(0));
-
- if (Entry != PropagationMap.end()) {
- PropagationInfo PInfo = Entry->second;
-
- checkCallability(PInfo, FunDecl, Call->getExprLoc());
-
- SetTypestateAttr *STA = FunDecl->getAttr<SetTypestateAttr>();
- if (PInfo.isVar()) {
- if (isTestingFunction(FunDecl))
- PropagationMap.insert(PairType(Call,
- PropagationInfo(PInfo.getVar(), testsFor(FunDecl))));
- else if (STA)
- StateMap->setState(PInfo.getVar(), mapSetTypestateAttrState(STA));
- } else if (STA && PInfo.isTmp())
- StateMap->setState(PInfo.getTmp(), mapSetTypestateAttrState(STA));
- }
+
+ if (Call->getOperator() == OO_Equal) {
+ ConsumedState CS = getInfo(Call->getArg(1));
+ if (!handleCall(Call, Call->getArg(0), FunDecl))
+ setInfo(Call->getArg(0), CS);
+ return;
}
+
+ if (const CXXMemberCallExpr *MCall = dyn_cast<CXXMemberCallExpr>(Call))
+ handleCall(MCall, MCall->getImplicitObjectArgument(), FunDecl);
+ else
+ handleCall(Call, Call->getArg(0), FunDecl);
+
+ propagateReturnType(Call, FunDecl);
}
void ConsumedStmtVisitor::VisitDeclRefExpr(const DeclRefExpr *DeclRef) {
@@ -1281,8 +1278,12 @@ void ConsumedAnalyzer::determineExpectedReturnState(AnalysisDeclContext &AC,
ExpectedReturnState = CS_None;
} else
ExpectedReturnState = mapReturnTypestateAttrState(RTSAttr);
- } else if (isConsumableType(ReturnType))
- ExpectedReturnState = mapConsumableAttrState(ReturnType);
+ } else if (isConsumableType(ReturnType)) {
+ if (isAutoCastType(ReturnType)) // We can auto-cast the state to the
+ ExpectedReturnState = CS_None; // expected state.
+ else
+ ExpectedReturnState = mapConsumableAttrState(ReturnType);
+ }
else
ExpectedReturnState = CS_None;
}
OpenPOWER on IntegriCloud