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 | |
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
-rw-r--r-- | clang/include/clang/Basic/Attr.td | 10 | ||||
-rw-r--r-- | clang/lib/Analysis/Consumed.cpp | 443 | ||||
-rw-r--r-- | clang/lib/Sema/SemaDeclAttr.cpp | 7 | ||||
-rw-r--r-- | clang/test/SemaCXX/warn-consumed-analysis.cpp | 87 | ||||
-rw-r--r-- | clang/test/SemaCXX/warn-consumed-parsing.cpp | 10 |
5 files changed, 317 insertions, 240 deletions
diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td index 886b239385a..9a035994b8e 100644 --- a/clang/include/clang/Basic/Attr.td +++ b/clang/include/clang/Basic/Attr.td @@ -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]>; 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; } diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp index 6555a3d27fc..d302e40d7f0 100644 --- a/clang/lib/Sema/SemaDeclAttr.cpp +++ b/clang/lib/Sema/SemaDeclAttr.cpp @@ -877,6 +877,7 @@ static void handleConsumableAttr(Sema &S, Decl *D, const AttributeList &Attr) { 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, Scope *scope, Decl *D, 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; diff --git a/clang/test/SemaCXX/warn-consumed-analysis.cpp b/clang/test/SemaCXX/warn-consumed-analysis.cpp index 2c372c752ba..76f4f8d8111 100644 --- a/clang/test/SemaCXX/warn-consumed-analysis.cpp +++ b/clang/test/SemaCXX/warn-consumed-analysis.cpp @@ -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 diff --git a/clang/test/SemaCXX/warn-consumed-parsing.cpp b/clang/test/SemaCXX/warn-consumed-parsing.cpp index cfd3d3b71dd..5c0a04fffe3 100644 --- a/clang/test/SemaCXX/warn-consumed-parsing.cpp +++ b/clang/test/SemaCXX/warn-consumed-parsing.cpp @@ -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 { +}; + + + |