r199169 - Consumed analysis: add two new attributes which fine-tune the behavior of
Aaron Ballman
aaron at aaronballman.com
Tue Jan 14 07:31:18 PST 2014
On Mon, Jan 13, 2014 at 7:36 PM, DeLesley Hutchins <delesley at google.com> wrote:
> Author: delesley
> Date: Mon Jan 13 18:36:53 2014
> New Revision: 199169
>
> URL: http://llvm.org/viewvc/llvm-project?rev=199169&view=rev
> Log:
> 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.
>
> Modified:
> cfe/trunk/include/clang/Basic/Attr.td
> cfe/trunk/lib/Analysis/Consumed.cpp
> cfe/trunk/lib/Sema/SemaDeclAttr.cpp
> cfe/trunk/test/SemaCXX/warn-consumed-analysis.cpp
> cfe/trunk/test/SemaCXX/warn-consumed-parsing.cpp
>
> Modified: cfe/trunk/include/clang/Basic/Attr.td
> URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/Attr.td?rev=199169&r1=199168&r2=199169&view=diff
> ==============================================================================
> --- cfe/trunk/include/clang/Basic/Attr.td (original)
> +++ cfe/trunk/include/clang/Basic/Attr.td Mon Jan 13 18:36:53 2014
> @@ -1187,6 +1187,16 @@ def Consumable : InheritableAttr {
> ["Unknown", "Consumed", "Unconsumed"]>];
> }
>
> +def ConsumableAutoCast : InheritableAttr {
> + let Spellings = [GNU<"consumable_auto_cast_state">];
> + let Subjects = SubjectList<[CXXRecord]>;
> +}
> +
> +def ConsumableSetOnRead : InheritableAttr {
> + let Spellings = [GNU<"consumable_set_state_on_read">];
> + let Subjects = SubjectList<[CXXRecord]>;
> +}
> +
> def CallableWhen : InheritableAttr {
> let Spellings = [GNU<"callable_when">];
> let Subjects = SubjectList<[CXXMethod]>;
>
> Modified: cfe/trunk/lib/Analysis/Consumed.cpp
> URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Analysis/Consumed.cpp?rev=199169&r1=199168&r2=199169&view=diff
> ==============================================================================
> --- cfe/trunk/lib/Analysis/Consumed.cpp (original)
> +++ cfe/trunk/lib/Analysis/Consumed.cpp Mon Jan 13 18:36:53 2014
> @@ -143,6 +143,7 @@ static bool isCallableInState(const Call
> return false;
> }
>
> +
> static bool isConsumableType(const QualType &QT) {
> if (QT->isPointerType() || QT->isReferenceType())
> return false;
> @@ -153,6 +154,23 @@ static bool isConsumableType(const QualT
> 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 S
> }
>
> static bool isRValueRefish(QualType ParamType) {
> - return ParamType->isRValueReferenceType() ||
> + return ParamType->isRValueReferenceType(); /* ||
> (ParamType->isLValueReferenceType() &&
> !cast<LValueReferenceType>(
> - ParamType.getCanonicalType())->isSpelledAsLValue());
> + ParamType.getCanonicalType())->isSpelledAsLValue()); */
Why is this code now commented out?
> }
>
> 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 Const
> 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
I think you have to check whether the function is static or not as well.
> +
> + // 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))
Every time I see this, I think "how do you re-fish something?" ;-)
> + 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))
These can be hoisted into the enclosing if.
> + 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 DeclCon
> }
>
> 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::VisitCXXConstr
> 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::determineExpected
> 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;
> }
>
> Modified: cfe/trunk/lib/Sema/SemaDeclAttr.cpp
> URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaDeclAttr.cpp?rev=199169&r1=199168&r2=199169&view=diff
> ==============================================================================
> --- cfe/trunk/lib/Sema/SemaDeclAttr.cpp (original)
> +++ cfe/trunk/lib/Sema/SemaDeclAttr.cpp Mon Jan 13 18:36:53 2014
> @@ -877,6 +877,7 @@ static void handleConsumableAttr(Sema &S
> Attr.getAttributeSpellingListIndex()));
> }
>
> +
> static bool checkForConsumableClass(Sema &S, const CXXMethodDecl *MD,
> const AttributeList &Attr) {
> ASTContext &CurrContext = S.getASTContext();
> @@ -4264,6 +4265,12 @@ static void ProcessDeclAttribute(Sema &S
> case AttributeList::AT_Consumable:
> handleConsumableAttr(S, D, Attr);
> break;
> + case AttributeList::AT_ConsumableAutoCast:
> + handleSimpleAttribute<ConsumableAutoCastAttr>(S, D, Attr); break;
> + break;
> + case AttributeList::AT_ConsumableSetOnRead:
> + handleSimpleAttribute<ConsumableSetOnReadAttr>(S, D, Attr); break;
> + break;
> case AttributeList::AT_CallableWhen:
> handleCallableWhenAttr(S, D, Attr);
> break;
>
> Modified: cfe/trunk/test/SemaCXX/warn-consumed-analysis.cpp
> URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaCXX/warn-consumed-analysis.cpp?rev=199169&r1=199168&r2=199169&view=diff
> ==============================================================================
> --- cfe/trunk/test/SemaCXX/warn-consumed-analysis.cpp (original)
> +++ cfe/trunk/test/SemaCXX/warn-consumed-analysis.cpp Mon Jan 13 18:36:53 2014
> @@ -645,9 +645,12 @@ void read(bool sf) {
> } // end namespace ContinueICETest
>
>
> -namespace InitializerAssertionFailTest {
> +namespace StatusUseCaseTests {
>
> -class CONSUMABLE(unconsumed) Status {
> +class CONSUMABLE(unconsumed)
> + __attribute__((consumable_auto_cast_state))
> + __attribute__((consumable_set_state_on_read))
> + Status {
> int code;
>
> public:
> @@ -673,7 +676,9 @@ public:
> bool cond();
> Status doSomething();
> void handleStatus(const Status& s RETURN_TYPESTATE(consumed));
> -void handleStatusPtr(const Status* s);
> +void handleStatusRef(Status& s);
> +void handleStatusPtr(Status* s);
> +void handleStatusUnmarked(const Status& s);
>
> void testSimpleTemporaries0() {
> doSomething(); // expected-warning {{invalid invocation of method '~Status' on a temporary object while it is in the 'unconsumed' state}}
> @@ -691,6 +696,15 @@ void testSimpleTemporaries3() {
> Status s = doSomething();
> } // expected-warning {{invalid invocation of method '~Status' on object 's' while it is in the 'unconsumed' state}}
>
> +Status testSimpleTemporariesReturn0() {
> + return doSomething();
> +}
> +
> +Status testSimpleTemporariesReturn1() {
> + Status s = doSomething();
> + return s;
> +}
> +
> void testSimpleTemporaries4() {
> Status s = doSomething();
> s.check();
> @@ -702,8 +716,17 @@ void testSimpleTemporaries5() {
> }
>
> void testSimpleTemporaries6() {
> - Status s = doSomething();
> - handleStatus(s);
> + Status s1 = doSomething();
> + handleStatus(s1);
> +
> + Status s2 = doSomething();
> + handleStatusRef(s2);
> +
> + Status s3 = doSomething();
> + handleStatusPtr(&s3);
> +
> + Status s4 = doSomething();
> + handleStatusUnmarked(s4);
> }
>
> void testSimpleTemporaries7() {
> @@ -745,38 +768,58 @@ void testTemporariesWithConditionals3()
> }
>
> void testTemporariesAndConstructors0() {
> - Status s(doSomething());
> + Status s(doSomething()); // Test the copy constructor.
> s.check();
> }
>
> -void testTemporariesAndConstructors1() {
> - // Test the copy constructor.
> -
> - Status s1 = doSomething();
> +void testTemporariesAndConstructors1F() {
> + Status s1 = doSomething(); // Test the copy constructor.
> + Status s2 = s1;
> +} // expected-warning {{invalid invocation of method '~Status' on object 's2' while it is in the 'unconsumed' state}}
> +
> +void testTemporariesAndConstructors1S() {
> + Status s1 = doSomething(); // Test the copy constructor.
> Status s2(s1);
> s2.check();
> -} // expected-warning {{invalid invocation of method '~Status' on object 's1' while it is in the 'unconsumed' state}}
> +}
>
> -void testTemporariesAndConstructors2() {
> +void testTemporariesAndConstructors2F() {
> // Test the move constructor.
> -
> Status s1 = doSomething();
> - Status s2(static_cast<Status&&>(s1));
> + Status s2 = static_cast<Status&&>(s1);
> +} // expected-warning {{invalid invocation of method '~Status' on object 's2' while it is in the 'unconsumed' state}}
> +
> +void testTemporariesAndConstructors2S() {
> + // Test the move constructor.
> + Status s1 = doSomething();
> + Status s2 = static_cast<Status&&>(s1);
> s2.check();
> }
>
> -void testTemporariesAndOperators0() {
> +void testTemporariesAndOperators0F() {
> + // Test the assignment operator.
> + Status s1 = doSomething();
> + Status s2;
> + s2 = s1;
> +} // expected-warning {{invalid invocation of method '~Status' on object 's2' while it is in the 'unconsumed' state}}
> +
> +void testTemporariesAndOperators0S() {
> // Test the assignment operator.
> -
> Status s1 = doSomething();
> Status s2;
> s2 = s1;
> s2.check();
> -} // expected-warning {{invalid invocation of method '~Status' on object 's1' while it is in the 'unconsumed' state}}
> +}
>
> -void testTemporariesAndOperators1() {
> +void testTemporariesAndOperators1F() {
> + // Test the move assignment operator.
> + Status s1 = doSomething();
> + Status s2;
> + s2 = static_cast<Status&&>(s1);
> +} // expected-warning {{invalid invocation of method '~Status' on object 's2' while it is in the 'unconsumed' state}}
> +
> +void testTemporariesAndOperators1S() {
> // Test the move assignment operator.
> -
> Status s1 = doSomething();
> Status s2;
> s2 = static_cast<Status&&>(s1);
> @@ -791,6 +834,12 @@ void testTemporariesAndOperators2() {
> s2.check();
> }
>
> +Status testReturnAutocast() {
> + Status s = doSomething();
> + s.check(); // consume s
> + return s; // should autocast back to unconsumed
> +}
> +
> } // end namespace InitializerAssertionFailTest
>
>
>
> Modified: cfe/trunk/test/SemaCXX/warn-consumed-parsing.cpp
> URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaCXX/warn-consumed-parsing.cpp?rev=199169&r1=199168&r2=199169&view=diff
> ==============================================================================
> --- cfe/trunk/test/SemaCXX/warn-consumed-parsing.cpp (original)
> +++ cfe/trunk/test/SemaCXX/warn-consumed-parsing.cpp Mon Jan 13 18:36:53 2014
> @@ -53,3 +53,13 @@ class AttrTester2 {
> };
>
> class CONSUMABLE(42) AttrTester3; // expected-error {{'consumable' attribute requires an identifier}}
> +
> +
> +class CONSUMABLE(unconsumed)
> + __attribute__((consumable_auto_cast_state))
> + __attribute__((consumable_set_state_on_read))
> + Status {
> +};
> +
> +
> +
Quite a few style guideline mishaps in here (extra newlines, curly
braces when they should be elided, else clauses on their own line,
etc). It might make sense to run clang-format over the file at some
point.
~Aaron
More information about the cfe-commits
mailing list