diff options
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; } |