diff options
author | DeLesley Hutchins <delesley@google.com> | 2014-01-14 00:36:53 +0000 |
---|---|---|
committer | DeLesley Hutchins <delesley@google.com> | 2014-01-14 00:36:53 +0000 |
commit | f28bbec90e447b0960aab0a00993ea93ebe294ab (patch) | |
tree | 8ab95cff57668ba9f404805dd2d3a380690d18f0 /clang/lib/Analysis/Consumed.cpp | |
parent | 6eba90710273be713b1f2736ce6d9c97bcf2f8be (diff) | |
download | bcm5719-llvm-f28bbec90e447b0960aab0a00993ea93ebe294ab.tar.gz bcm5719-llvm-f28bbec90e447b0960aab0a00993ea93ebe294ab.zip |
Consumed analysis: add two new attributes which fine-tune the behavior of
consumable objects. These are useful for implementing error codes that
must be checked. Patch also includes some significant refactoring, which was
necesary to implement the new behavior.
llvm-svn: 199169
Diffstat (limited to 'clang/lib/Analysis/Consumed.cpp')
-rw-r--r-- | clang/lib/Analysis/Consumed.cpp | 443 |
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; } |