diff options
Diffstat (limited to 'clang')
-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 { +}; + + + |