From 66540857c08de7f1be9bea48381548d3942cf9d1 Mon Sep 17 00:00:00 2001 From: DeLesley Hutchins Date: Fri, 4 Oct 2013 21:28:06 +0000 Subject: [PATCH] Consumed Analysis: Change callable_when so that it can take a list of states that a function can be called in. This reduced the total number of annotations needed and makes writing more complicated behaviour less burdensome. Patch by chriswails@gmail.com. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@191983 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/Analysis/Analyses/Consumed.h | 44 +-- include/clang/Basic/Attr.td | 16 +- include/clang/Basic/DiagnosticSemaKinds.td | 16 +- lib/Analysis/Consumed.cpp | 169 ++++++----- lib/Sema/AnalysisBasedWarnings.cpp | 32 +-- lib/Sema/SemaDeclAttr.cpp | 40 ++- .../SemaCXX/warn-consumed-analysis-strict.cpp | 171 +---------- test/SemaCXX/warn-consumed-analysis.cpp | 265 ++++++++++++++---- test/SemaCXX/warn-consumed-parsing.cpp | 22 +- utils/TableGen/ClangAttrEmitter.cpp | 95 ++++++- 10 files changed, 488 insertions(+), 382 deletions(-) diff --git a/include/clang/Analysis/Analyses/Consumed.h b/include/clang/Analysis/Analyses/Consumed.h index e473d1e427..cc17a12b05 100644 --- a/include/clang/Analysis/Analyses/Consumed.h +++ b/include/clang/Analysis/Analyses/Consumed.h @@ -25,6 +25,15 @@ namespace clang { namespace consumed { + enum ConsumedState { + // No state information for the given variable. + CS_None, + + CS_Unknown, + CS_Unconsumed, + CS_Consumed + }; + class ConsumedStmtVisitor; typedef SmallVector OptionalNotes; @@ -71,52 +80,29 @@ namespace consumed { /// \param MethodName -- The name of the method that was incorrectly /// invoked. /// - /// \param Loc -- The SourceLocation of the method invocation. - virtual void warnUseOfTempWhileConsumed(StringRef MethodName, - SourceLocation Loc) {} - - /// \brief Warn about use-in-unknown-state errors. - /// \param MethodName -- The name of the method that was incorrectly - /// invoked. + /// \param State -- The state the object was used in. /// /// \param Loc -- The SourceLocation of the method invocation. - virtual void warnUseOfTempInUnknownState(StringRef MethodName, + virtual void warnUseOfTempInInvalidState(StringRef MethodName, + StringRef State, SourceLocation Loc) {} /// \brief Warn about use-while-consumed errors. /// \param MethodName -- The name of the method that was incorrectly /// invoked. /// - /// \param VariableName -- The name of the variable that holds the unique - /// value. - /// - /// \param Loc -- The SourceLocation of the method invocation. - virtual void warnUseWhileConsumed(StringRef MethodName, - StringRef VariableName, - SourceLocation Loc) {} - - /// \brief Warn about use-in-unknown-state errors. - /// \param MethodName -- The name of the method that was incorrectly - /// invoked. + /// \param State -- The state the object was used in. /// /// \param VariableName -- The name of the variable that holds the unique /// value. /// /// \param Loc -- The SourceLocation of the method invocation. - virtual void warnUseInUnknownState(StringRef MethodName, + virtual void warnUseInInvalidState(StringRef MethodName, StringRef VariableName, + StringRef State, SourceLocation Loc) {} }; - enum ConsumedState { - // No state information for the given variable. - CS_None, - - CS_Unknown, - CS_Unconsumed, - CS_Consumed - }; - class ConsumedStateMap { typedef llvm::DenseMap MapType; diff --git a/include/clang/Basic/Attr.td b/include/clang/Basic/Attr.td index 51ed654e99..dd87e3e8c0 100644 --- a/include/clang/Basic/Attr.td +++ b/include/clang/Basic/Attr.td @@ -81,6 +81,15 @@ class EnumArgument values, list Enums = enums; } +// FIXME: There should be a VariadicArgument type that takes any other type +// of argument and generates the appropriate type. +class VariadicEnumArgument values, + list enums> : Argument { + string Type = type; + list Values = values; + list Enums = enums; +} + // This handles one spelling of an attribute. class Spelling { string Name = name; @@ -950,9 +959,12 @@ def Consumable : InheritableAttr { ["Unknown", "Consumed", "Unconsumed"]>]; } -def CallableWhenUnconsumed : InheritableAttr { - let Spellings = [GNU<"callable_when_unconsumed">]; +def CallableWhen : InheritableAttr { + let Spellings = [GNU<"callable_when">]; let Subjects = [CXXMethod]; + let Args = [VariadicEnumArgument<"CallableState", "ConsumedState", + ["unknown", "consumed", "unconsumed"], + ["Unknown", "Consumed", "Unconsumed"]>]; } def TestsUnconsumed : InheritableAttr { diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td index 87ce732cf6..f9a5e1ab36 100644 --- a/include/clang/Basic/DiagnosticSemaKinds.td +++ b/include/clang/Basic/DiagnosticSemaKinds.td @@ -2195,12 +2195,12 @@ def warn_thread_safety_beta : Warning< "Thread safety beta warning.">, InGroup, DefaultIgnore; // Consumed warnings -def warn_use_while_consumed : Warning< - "invocation of method '%0' on object '%1' while it is in the 'consumed' " +def warn_use_in_invalid_state : Warning< + "invalid invocation of method '%0' on object '%1' while it is in the '%2' " "state">, InGroup, DefaultIgnore; -def warn_use_of_temp_while_consumed : Warning< - "invocation of method '%0' on a temporary object while it is in the " - "'consumed' state">, InGroup, DefaultIgnore; +def warn_use_of_temp_in_invalid_state : Warning< + "invalid invocation of method '%0' on a temporary object while it is in the " + "'%1' state">, InGroup, DefaultIgnore; def warn_attr_on_unconsumable_class : Warning< "consumed analysis attribute is attached to member of class '%0' which isn't " "marked as consumable">, InGroup, DefaultIgnore; @@ -2212,12 +2212,6 @@ def warn_return_typestate_mismatch : Warning< InGroup, DefaultIgnore; // ConsumedStrict warnings -def warn_use_in_unknown_state : Warning< - "invocation of method '%0' on object '%1' while it is in an unknown state">, - InGroup, DefaultIgnore; -def warn_use_of_temp_in_unknown_state : Warning< - "invocation of method '%0' on a temporary object while it is in an unknown " - "state">, InGroup, DefaultIgnore; def warn_unnecessary_test : Warning< "unnecessary test. Variable '%0' is known to be in the '%1' state">, InGroup, DefaultIgnore; diff --git a/lib/Analysis/Consumed.cpp b/lib/Analysis/Consumed.cpp index 7cd029020a..ee8dd77ff6 100644 --- a/lib/Analysis/Consumed.cpp +++ b/lib/Analysis/Consumed.cpp @@ -33,6 +33,8 @@ // TODO: Add notes about the actual and expected state for // TODO: Correctly identify unreachable blocks when chaining boolean operators. +// TODO: Adjust the parser and AttributesList class to support lists of +// identifiers. // TODO: Warn about unreachable code. // TODO: Switch to using a bitmap to track unreachable blocks. // TODO: Mark variables as Unknown going into while- or for-loops only if they @@ -66,6 +68,37 @@ static ConsumedState invertConsumedUnconsumed(ConsumedState State) { llvm_unreachable("invalid enum"); } +static bool isCallableInState(const CallableWhenAttr *CWAttr, + ConsumedState State) { + + CallableWhenAttr::callableState_iterator I = CWAttr->callableState_begin(), + E = CWAttr->callableState_end(); + + for (; I != E; ++I) { + + ConsumedState MappedAttrState = CS_None; + + switch (*I) { + case CallableWhenAttr::Unknown: + MappedAttrState = CS_Unknown; + break; + + case CallableWhenAttr::Unconsumed: + MappedAttrState = CS_Unconsumed; + break; + + case CallableWhenAttr::Consumed: + MappedAttrState = CS_Consumed; + break; + } + + if (MappedAttrState == State) + return true; + } + + return false; +} + static bool isConsumableType(const QualType &QT) { if (const CXXRecordDecl *RD = QT->getAsCXXRecordDecl()) return RD->hasAttr(); @@ -174,6 +207,8 @@ class PropagationInfo { BinTestTy BinTest; }; + QualType TempType; + public: PropagationInfo() : InfoType(IT_None) {} @@ -208,7 +243,9 @@ public: BinTest.RTest.TestsFor = RTestsFor; } - PropagationInfo(ConsumedState State) : InfoType(IT_State), State(State) {} + PropagationInfo(ConsumedState State, QualType TempType) + : InfoType(IT_State), State(State), TempType(TempType) {} + PropagationInfo(const VarDecl *Var) : InfoType(IT_Var), Var(Var) {} const ConsumedState & getState() const { @@ -216,6 +253,11 @@ public: return State; } + const QualType & getTempType() const { + assert(InfoType == IT_State); + return TempType; + } + const VarTestResult & getTest() const { assert(InfoType == IT_Test); return Test; @@ -327,51 +369,38 @@ public: } }; -// TODO: When we support CallableWhenConsumed this will have to check for -// the different attributes and change the behavior bellow. (Deferred) void ConsumedStmtVisitor::checkCallability(const PropagationInfo &PInfo, const FunctionDecl *FunDecl, const CallExpr *Call) { - if (!FunDecl->hasAttr()) return; + if (!FunDecl->hasAttr()) + return; + + const CallableWhenAttr *CWAttr = FunDecl->getAttr(); if (PInfo.isVar()) { const VarDecl *Var = PInfo.getVar(); + ConsumedState VarState = StateMap->getState(Var); - switch (StateMap->getState(Var)) { - case CS_Consumed: - Analyzer.WarningsHandler.warnUseWhileConsumed( - FunDecl->getNameAsString(), Var->getNameAsString(), - Call->getExprLoc()); - break; + assert(VarState != CS_None && "Invalid state"); - case CS_Unknown: - Analyzer.WarningsHandler.warnUseInUnknownState( - FunDecl->getNameAsString(), Var->getNameAsString(), - Call->getExprLoc()); - break; - - case CS_None: - case CS_Unconsumed: - break; - } + if (isCallableInState(CWAttr, VarState)) + return; - } else { - switch (PInfo.getState()) { - case CS_Consumed: - Analyzer.WarningsHandler.warnUseOfTempWhileConsumed( - FunDecl->getNameAsString(), Call->getExprLoc()); - break; + Analyzer.WarningsHandler.warnUseInInvalidState( + FunDecl->getNameAsString(), Var->getNameAsString(), + stateToString(VarState), Call->getExprLoc()); - case CS_Unknown: - Analyzer.WarningsHandler.warnUseOfTempInUnknownState( - FunDecl->getNameAsString(), Call->getExprLoc()); - break; - - case CS_None: - case CS_Unconsumed: - break; - } + } else if (PInfo.isState()) { + + assert(PInfo.getState() != CS_None && "Invalid state"); + + if (isCallableInState(CWAttr, PInfo.getState())) + return; + + Analyzer.WarningsHandler.warnUseOfTempInInvalidState( + FunDecl->getNameAsString(), stateToString(PInfo.getState()), + Call->getExprLoc()); } } @@ -421,7 +450,7 @@ void ConsumedStmtVisitor::propagateReturnType(const Stmt *Call, ReturnState = mapConsumableAttrState(ReturnType); PropagationMap.insert(PairType(Call, - PropagationInfo(ReturnState))); + PropagationInfo(ReturnState, ReturnType))); } } @@ -522,7 +551,11 @@ void ConsumedStmtVisitor::VisitCallExpr(const CallExpr *Call) { } } - propagateReturnType(Call, FunDecl, FunDecl->getCallResultType()); + QualType RetType = FunDecl->getCallResultType(); + if (RetType->isReferenceType()) + RetType = RetType->getPointeeType(); + + propagateReturnType(Call, FunDecl, RetType); } } @@ -540,7 +573,7 @@ void ConsumedStmtVisitor::VisitCXXConstructExpr(const CXXConstructExpr *Call) { if (Constructor->isDefaultConstructor()) { PropagationMap.insert(PairType(Call, - PropagationInfo(consumed::CS_Consumed))); + PropagationInfo(consumed::CS_Consumed, ThisType))); } else if (Constructor->isMoveConstructor()) { @@ -551,7 +584,7 @@ void ConsumedStmtVisitor::VisitCXXConstructExpr(const CXXConstructExpr *Call) { const VarDecl* Var = PInfo.getVar(); PropagationMap.insert(PairType(Call, - PropagationInfo(StateMap->getState(Var)))); + PropagationInfo(StateMap->getState(Var), ThisType))); StateMap->setState(Var, consumed::CS_Consumed); @@ -630,7 +663,8 @@ void ConsumedStmtVisitor::VisitCXXOperatorCallExpr( } else if (!LPInfo.isVar() && RPInfo.isVar()) { PropagationMap.insert(PairType(Call, - PropagationInfo(StateMap->getState(RPInfo.getVar())))); + PropagationInfo(StateMap->getState(RPInfo.getVar()), + LPInfo.getTempType()))); StateMap->setState(RPInfo.getVar(), consumed::CS_Consumed); @@ -648,27 +682,16 @@ void ConsumedStmtVisitor::VisitCXXOperatorCallExpr( PropagationMap.insert(PairType(Call, LPInfo)); - } else { + } else if (LPInfo.isState()) { PropagationMap.insert(PairType(Call, - PropagationInfo(consumed::CS_Unknown))); + PropagationInfo(consumed::CS_Unknown, LPInfo.getTempType()))); } } else if (LEntry == PropagationMap.end() && REntry != PropagationMap.end()) { - RPInfo = REntry->second; - - if (RPInfo.isVar()) { - const VarDecl *Var = RPInfo.getVar(); - - PropagationMap.insert(PairType(Call, - PropagationInfo(StateMap->getState(Var)))); - - StateMap->setState(Var, consumed::CS_Consumed); - - } else { - PropagationMap.insert(PairType(Call, RPInfo)); - } + if (REntry->second.isVar()) + StateMap->setState(REntry->second.getVar(), consumed::CS_Consumed); } } else { @@ -776,6 +799,7 @@ void ConsumedStmtVisitor::VisitUnaryOperator(const UnaryOperator *UOp) { } } +// TODO: See if I need to check for reference types here. void ConsumedStmtVisitor::VisitVarDecl(const VarDecl *Var) { if (isConsumableType(Var->getType())) { if (Var->hasInit()) { @@ -803,13 +827,12 @@ void splitVarStateForIf(const IfStmt * IfNode, const VarTestResult &Test, if (VarState == CS_Unknown) { ThenStates->setState(Test.Var, Test.TestsFor); - if (ElseStates) - ElseStates->setState(Test.Var, invertConsumedUnconsumed(Test.TestsFor)); + ElseStates->setState(Test.Var, invertConsumedUnconsumed(Test.TestsFor)); } else if (VarState == invertConsumedUnconsumed(Test.TestsFor)) { ThenStates->markUnreachable(); - } else if (VarState == Test.TestsFor && ElseStates) { + } else if (VarState == Test.TestsFor) { ElseStates->markUnreachable(); } } @@ -832,31 +855,27 @@ void splitVarStateForIfBinOp(const PropagationInfo &PInfo, ThenStates->markUnreachable(); } else if (LState == LTest.TestsFor && isKnownState(RState)) { - if (RState == RTest.TestsFor) { - if (ElseStates) - ElseStates->markUnreachable(); - } else { + if (RState == RTest.TestsFor) + ElseStates->markUnreachable(); + else ThenStates->markUnreachable(); - } } } else { - if (LState == CS_Unknown && ElseStates) { + if (LState == CS_Unknown) { ElseStates->setState(LTest.Var, invertConsumedUnconsumed(LTest.TestsFor)); - } else if (LState == LTest.TestsFor && ElseStates) { + } else if (LState == LTest.TestsFor) { ElseStates->markUnreachable(); } else if (LState == invertConsumedUnconsumed(LTest.TestsFor) && isKnownState(RState)) { - if (RState == RTest.TestsFor) { - if (ElseStates) - ElseStates->markUnreachable(); - } else { + if (RState == RTest.TestsFor) + ElseStates->markUnreachable(); + else ThenStates->markUnreachable(); - } } } } @@ -868,7 +887,7 @@ void splitVarStateForIfBinOp(const PropagationInfo &PInfo, else if (RState == invertConsumedUnconsumed(RTest.TestsFor)) ThenStates->markUnreachable(); - } else if (ElseStates) { + } else { if (RState == CS_Unknown) ElseStates->setState(RTest.Var, invertConsumedUnconsumed(RTest.TestsFor)); @@ -1016,7 +1035,6 @@ bool ConsumedAnalyzer::splitState(const CFGBlock *CurrBlock, if (const IfStmt *IfNode = dyn_cast_or_null(CurrBlock->getTerminator().getStmt())) { - bool HasElse = IfNode->getElse() != NULL; const Stmt *Cond = IfNode->getCond(); PInfo = Visitor.getInfo(Cond); @@ -1026,15 +1044,12 @@ bool ConsumedAnalyzer::splitState(const CFGBlock *CurrBlock, if (PInfo.isTest()) { CurrStates->setSource(Cond); FalseStates->setSource(Cond); - - splitVarStateForIf(IfNode, PInfo.getTest(), CurrStates, - HasElse ? FalseStates : NULL); + splitVarStateForIf(IfNode, PInfo.getTest(), CurrStates, FalseStates); } else if (PInfo.isBinTest()) { CurrStates->setSource(PInfo.testSourceNode()); FalseStates->setSource(PInfo.testSourceNode()); - - splitVarStateForIfBinOp(PInfo, CurrStates, HasElse ? FalseStates : NULL); + splitVarStateForIfBinOp(PInfo, CurrStates, FalseStates); } else { delete FalseStates; diff --git a/lib/Sema/AnalysisBasedWarnings.cpp b/lib/Sema/AnalysisBasedWarnings.cpp index cc1cb0b342..a206acd7f2 100644 --- a/lib/Sema/AnalysisBasedWarnings.cpp +++ b/lib/Sema/AnalysisBasedWarnings.cpp @@ -1503,36 +1503,20 @@ public: Warnings.push_back(DelayedDiag(Warning, OptionalNotes())); } - void warnUseOfTempWhileConsumed(StringRef MethodName, SourceLocation Loc) { + void warnUseOfTempInInvalidState(StringRef MethodName, StringRef State, + SourceLocation Loc) { PartialDiagnosticAt Warning(Loc, S.PDiag( - diag::warn_use_of_temp_while_consumed) << MethodName); + diag::warn_use_of_temp_in_invalid_state) << MethodName << State); Warnings.push_back(DelayedDiag(Warning, OptionalNotes())); } - void warnUseOfTempInUnknownState(StringRef MethodName, SourceLocation Loc) { + void warnUseInInvalidState(StringRef MethodName, StringRef VariableName, + StringRef State, SourceLocation Loc) { - PartialDiagnosticAt Warning(Loc, S.PDiag( - diag::warn_use_of_temp_in_unknown_state) << MethodName); - - Warnings.push_back(DelayedDiag(Warning, OptionalNotes())); - } - - void warnUseWhileConsumed(StringRef MethodName, StringRef VariableName, - SourceLocation Loc) { - - PartialDiagnosticAt Warning(Loc, S.PDiag(diag::warn_use_while_consumed) << - MethodName << VariableName); - - Warnings.push_back(DelayedDiag(Warning, OptionalNotes())); - } - - void warnUseInUnknownState(StringRef MethodName, StringRef VariableName, - SourceLocation Loc) { - - PartialDiagnosticAt Warning(Loc, S.PDiag(diag::warn_use_in_unknown_state) << - MethodName << VariableName); + PartialDiagnosticAt Warning(Loc, S.PDiag(diag::warn_use_in_invalid_state) << + MethodName << VariableName << State); Warnings.push_back(DelayedDiag(Warning, OptionalNotes())); } @@ -1570,7 +1554,7 @@ clang::sema::AnalysisBasedWarnings::AnalysisBasedWarnings(Sema &s) (D.getDiagnosticLevel(diag::warn_double_lock, SourceLocation()) != DiagnosticsEngine::Ignored); DefaultPolicy.enableConsumedAnalysis = (unsigned) - (D.getDiagnosticLevel(diag::warn_use_while_consumed, SourceLocation()) != + (D.getDiagnosticLevel(diag::warn_use_in_invalid_state, SourceLocation()) != DiagnosticsEngine::Ignored); } diff --git a/lib/Sema/SemaDeclAttr.cpp b/lib/Sema/SemaDeclAttr.cpp index c6337a5015..41cc3fd285 100644 --- a/lib/Sema/SemaDeclAttr.cpp +++ b/lib/Sema/SemaDeclAttr.cpp @@ -1041,8 +1041,11 @@ static void handleConsumesAttr(Sema &S, Decl *D, const AttributeList &Attr) { Attr.getAttributeSpellingListIndex())); } -static void handleCallableWhenUnconsumedAttr(Sema &S, Decl *D, - const AttributeList &Attr) { +static void handleCallableWhenAttr(Sema &S, Decl *D, + const AttributeList &Attr) { + + if (!checkAttributeAtLeastNumArgs(S, Attr, 1)) return; + if (!isa(D)) { S.Diag(Attr.getLoc(), diag::warn_attribute_wrong_decl_type) << Attr.getName() << ExpectedMethod; @@ -1052,9 +1055,34 @@ static void handleCallableWhenUnconsumedAttr(Sema &S, Decl *D, if (!checkForConsumableClass(S, cast(D), Attr)) return; + SmallVector States; + for (unsigned ArgIndex = 0; ArgIndex < Attr.getNumArgs(); ++ArgIndex) { + CallableWhenAttr::ConsumedState CallableState; + + if (Attr.isArgExpr(ArgIndex) && + isa(Attr.getArgAsExpr(ArgIndex))) { + + Expr *Arg = Attr.getArgAsExpr(ArgIndex); + StringRef StateString = cast(Arg)->getString(); + + if (!CallableWhenAttr::ConvertStrToConsumedState(StateString, + CallableState)) { + S.Diag(Arg->getExprLoc(), diag::warn_attribute_type_not_supported) + << Attr.getName() << StateString; + return; + } + + States.push_back(CallableState); + } else { + S.Diag(Attr.getLoc(), diag::err_attribute_argument_type) << Attr.getName() + << AANT_ArgumentString; + return; + } + } + D->addAttr(::new (S.Context) - CallableWhenUnconsumedAttr(Attr.getRange(), S.Context, - Attr.getAttributeSpellingListIndex())); + CallableWhenAttr(Attr.getRange(), S.Context, States.data(), + States.size(), Attr.getAttributeSpellingListIndex())); } static void handleTestsConsumedAttr(Sema &S, Decl *D, @@ -4767,8 +4795,8 @@ static void ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D, case AttributeList::AT_Consumes: handleConsumesAttr(S, D, Attr); break; - case AttributeList::AT_CallableWhenUnconsumed: - handleCallableWhenUnconsumedAttr(S, D, Attr); + case AttributeList::AT_CallableWhen: + handleCallableWhenAttr(S, D, Attr); break; case AttributeList::AT_TestsConsumed: handleTestsConsumedAttr(S, D, Attr); diff --git a/test/SemaCXX/warn-consumed-analysis-strict.cpp b/test/SemaCXX/warn-consumed-analysis-strict.cpp index 98d6894261..8ef3e8833b 100644 --- a/test/SemaCXX/warn-consumed-analysis-strict.cpp +++ b/test/SemaCXX/warn-consumed-analysis-strict.cpp @@ -1,10 +1,10 @@ // RUN: %clang_cc1 -fsyntax-only -verify -Wconsumed-strict -std=c++11 %s -#define CALLABLE_WHEN_UNCONSUMED __attribute__ ((callable_when_unconsumed)) -#define CONSUMABLE(state) __attribute__ ((consumable(state))) -#define CONSUMES __attribute__ ((consumes)) -#define RETURN_TYPESTATE(state) __attribute__ ((return_typestate(state))) -#define TESTS_UNCONSUMED __attribute__ ((tests_unconsumed)) +#define CALLABLE_WHEN(...) __attribute__ ((callable_when(__VA_ARGS__))) +#define CONSUMABLE(state) __attribute__ ((consumable(state))) +#define CONSUMES __attribute__ ((consumes)) +#define RETURN_TYPESTATE(state) __attribute__ ((return_typestate(state))) +#define TESTS_UNCONSUMED __attribute__ ((tests_unconsumed)) #define TEST_VAR(Var) Var.isValid() @@ -32,8 +32,8 @@ class CONSUMABLE(unconsumed) ConsumableClass { ConsumableClass& operator=(ConsumableClass &&other); void operator()(int a) CONSUMES; - void operator*() const CALLABLE_WHEN_UNCONSUMED; - void unconsumedCall() const CALLABLE_WHEN_UNCONSUMED; + void operator*() const CALLABLE_WHEN("unconsumed"); + void unconsumedCall() const CALLABLE_WHEN("unconsumed"); bool isValid() const TESTS_UNCONSUMED; operator bool() const TESTS_UNCONSUMED; @@ -45,9 +45,6 @@ class CONSUMABLE(unconsumed) ConsumableClass { void consume() CONSUMES; }; -void baf0(ConsumableClass &var); -void baf1(ConsumableClass *var); - void testIfStmt() { ConsumableClass var; @@ -60,137 +57,6 @@ void testIfStmt() { } } -void testComplexConditionals() { - ConsumableClass var0, var1, var2; - - // Coerce all variables into the unknown state. - baf0(var0); - baf0(var1); - baf0(var2); - - if (var0 && var1) { - *var0; - *var1; - - } else { - *var0; // expected-warning {{invocation of method 'operator*' on object 'var0' while it is in an unknown state}} - *var1; // expected-warning {{invocation of method 'operator*' on object 'var1' while it is in an unknown state}} - } - - if (var0 || var1) { - *var0; // expected-warning {{invocation of method 'operator*' on object 'var0' while it is in an unknown state}} - *var1; // expected-warning {{invocation of method 'operator*' on object 'var1' while it is in an unknown state}} - - } else { - *var0; // expected-warning {{invocation of method 'operator*' on object 'var0' while it is in the 'consumed' state}} - *var1; // expected-warning {{invocation of method 'operator*' on object 'var1' while it is in the 'consumed' state}} - } - - if (var0 && !var1) { - *var0; - *var1; // expected-warning {{invocation of method 'operator*' on object 'var1' while it is in the 'consumed' state}} - - } else { - *var0; // expected-warning {{invocation of method 'operator*' on object 'var0' while it is in an unknown state}} - *var1; // expected-warning {{invocation of method 'operator*' on object 'var1' while it is in an unknown state}} - } - - if (var0 || !var1) { - *var0; // expected-warning {{invocation of method 'operator*' on object 'var0' while it is in an unknown state}} - *var1; // expected-warning {{invocation of method 'operator*' on object 'var1' while it is in an unknown state}} - - } else { - *var0; // expected-warning {{invocation of method 'operator*' on object 'var0' while it is in the 'consumed' state}} - *var1; - } - - if (!var0 && !var1) { - *var0; // expected-warning {{invocation of method 'operator*' on object 'var0' while it is in the 'consumed' state}} - *var1; // expected-warning {{invocation of method 'operator*' on object 'var1' while it is in the 'consumed' state}} - - } else { - *var0; // expected-warning {{invocation of method 'operator*' on object 'var0' while it is in an unknown state}} - *var1; // expected-warning {{invocation of method 'operator*' on object 'var1' while it is in an unknown state}} - } - - if (!(var0 || var1)) { - *var0; // expected-warning {{invocation of method 'operator*' on object 'var0' while it is in the 'consumed' state}} - *var1; // expected-warning {{invocation of method 'operator*' on object 'var1' while it is in the 'consumed' state}} - - } else { - *var0; // expected-warning {{invocation of method 'operator*' on object 'var0' while it is in an unknown state}} - *var1; // expected-warning {{invocation of method 'operator*' on object 'var1' while it is in an unknown state}} - } - - if (!var0 || !var1) { - *var0; // expected-warning {{invocation of method 'operator*' on object 'var0' while it is in an unknown state}} - *var1; // expected-warning {{invocation of method 'operator*' on object 'var1' while it is in an unknown state}} - - } else { - *var0; - *var1; - } - - if (!(var0 && var1)) { - *var0; // expected-warning {{invocation of method 'operator*' on object 'var0' while it is in an unknown state}} - *var1; // expected-warning {{invocation of method 'operator*' on object 'var1' while it is in an unknown state}} - - } else { - *var0; - *var1; - } - - if (var0 && var1 && var2) { - *var0; - *var1; - *var2; - - } else { - *var0; // expected-warning {{invocation of method 'operator*' on object 'var0' while it is in an unknown state}} - *var1; // expected-warning {{invocation of method 'operator*' on object 'var1' while it is in an unknown state}} - *var2; // expected-warning {{invocation of method 'operator*' on object 'var2' while it is in an unknown state}} - } - -#if 0 - // FIXME: Get this test to pass. - if (var0 || var1 || var2) { - *var0; // expected-warning {{invocation of method 'operator*' on object 'var0' while it is in an unknown state}} - *var1; // expected-warning {{invocation of method 'operator*' on object 'var1' while it is in an unknown state}} - *var2; // expected-warning {{invocation of method 'operator*' on object 'var2' while it is in an unknown state}} - - } else { - *var0; // expected-warning {{invocation of method 'operator*' on object 'var0' while it is in the 'consumed' state}} - *var1; // expected-warning {{invocation of method 'operator*' on object 'var1' while it is in the 'consumed' state}} - *var2; // expected-warning {{invocation of method 'operator*' on object 'var2' while it is in the 'consumed' state}} - } -#endif -} - -void testCallingConventions() { - ConsumableClass var(42); - - baf0(var); - *var; // expected-warning {{invocation of method 'operator*' on object 'var' while it is in an unknown state}} - - var = ConsumableClass(42); - baf1(&var); - *var; // expected-warning {{invocation of method 'operator*' on object 'var' while it is in an unknown state}} -} - -void testConstAndNonConstMemberFunctions() { - ConsumableClass var(42); - - var.constCall(); - *var; - - var.nonconstCall(); - *var; -} - -void testFunctionParam0(ConsumableClass ¶m) { - *param; // expected-warning {{invocation of method 'operator*' on object 'param' while it is in an unknown state}} -} - void testNoWarnTestFromMacroExpansion() { ConsumableClass var(42); @@ -198,26 +64,3 @@ void testNoWarnTestFromMacroExpansion() { *var; } } - -void testSimpleForLoop() { - ConsumableClass var; - - for (int i = 0; i < 10; ++i) { - *var; // expected-warning {{invocation of method 'operator*' on object 'var' while it is in an unknown state}} - } - - *var; // expected-warning {{invocation of method 'operator*' on object 'var' while it is in an unknown state}} -} - -void testSimpleWhileLoop() { - int i = 0; - - ConsumableClass var; - - while (i < 10) { - *var; // expected-warning {{invocation of method 'operator*' on object 'var' while it is in an unknown state}} - ++i; - } - - *var; // expected-warning {{invocation of method 'operator*' on object 'var' while it is in an unknown state}} -} diff --git a/test/SemaCXX/warn-consumed-analysis.cpp b/test/SemaCXX/warn-consumed-analysis.cpp index 8d4bff705c..c33e3a197a 100644 --- a/test/SemaCXX/warn-consumed-analysis.cpp +++ b/test/SemaCXX/warn-consumed-analysis.cpp @@ -1,10 +1,12 @@ // RUN: %clang_cc1 -fsyntax-only -verify -Wconsumed -std=c++11 %s -#define CALLABLE_WHEN_UNCONSUMED __attribute__ ((callable_when_unconsumed)) -#define CONSUMABLE(state) __attribute__ ((consumable(state))) -#define CONSUMES __attribute__ ((consumes)) -#define RETURN_TYPESTATE(state) __attribute__ ((return_typestate(state))) -#define TESTS_UNCONSUMED __attribute__ ((tests_unconsumed)) +// TODO: Switch to using macros for the expected warnings. + +#define CALLABLE_WHEN(...) __attribute__ ((callable_when(__VA_ARGS__))) +#define CONSUMABLE(state) __attribute__ ((consumable(state))) +#define CONSUMES __attribute__ ((consumes)) +#define RETURN_TYPESTATE(state) __attribute__ ((return_typestate(state))) +#define TESTS_UNCONSUMED __attribute__ ((tests_unconsumed)) typedef decltype(nullptr) nullptr_t; @@ -30,8 +32,9 @@ class CONSUMABLE(unconsumed) ConsumableClass { ConsumableClass& operator=(ConsumableClass &&other); void operator()(int a) CONSUMES; - void operator*() const CALLABLE_WHEN_UNCONSUMED; - void unconsumedCall() const CALLABLE_WHEN_UNCONSUMED; + void operator*() const CALLABLE_WHEN("unconsumed"); + void unconsumedCall() const CALLABLE_WHEN("unconsumed"); + void callableWhenUnknown() const CALLABLE_WHEN("unconsumed", "unknown"); bool isValid() const TESTS_UNCONSUMED; operator bool() const TESTS_UNCONSUMED; @@ -47,7 +50,9 @@ void baf0(const ConsumableClass var); void baf1(const ConsumableClass &var); void baf2(const ConsumableClass *var); -void baf3(ConsumableClass &&var); +void baf3(ConsumableClass &var); +void baf4(ConsumableClass *var); +void baf5(ConsumableClass &&var); ConsumableClass returnsUnconsumed() { return ConsumableClass(); // expected-warning {{return value not in expected state; expected 'unconsumed', observed 'consumed'}} @@ -58,39 +63,41 @@ ConsumableClass returnsConsumed() { return ConsumableClass(); } +ConsumableClass returnsUnknown() RETURN_TYPESTATE(unknown); + void testInitialization() { ConsumableClass var0; ConsumableClass var1 = ConsumableClass(); var0 = ConsumableClass(); - *var0; // expected-warning {{invocation of method 'operator*' on object 'var0' while it is in the 'consumed' state}} - *var1; // expected-warning {{invocation of method 'operator*' on object 'var1' while it is in the 'consumed' state}} + *var0; // expected-warning {{invalid invocation of method 'operator*' on object 'var0' while it is in the 'consumed' state}} + *var1; // expected-warning {{invalid invocation of method 'operator*' on object 'var1' while it is in the 'consumed' state}} if (var0.isValid()) { *var0; *var1; } else { - *var0; // expected-warning {{invocation of method 'operator*' on object 'var0' while it is in the 'consumed' state}} + *var0; // expected-warning {{invalid invocation of method 'operator*' on object 'var0' while it is in the 'consumed' state}} } } void testTempValue() { - *ConsumableClass(); // expected-warning {{invocation of method 'operator*' on a temporary object while it is in the 'consumed' state}} + *ConsumableClass(); // expected-warning {{invalid invocation of method 'operator*' on a temporary object while it is in the 'consumed' state}} } void testSimpleRValueRefs() { ConsumableClass var0; ConsumableClass var1(42); - *var0; // expected-warning {{invocation of method 'operator*' on object 'var0' while it is in the 'consumed' state}} + *var0; // expected-warning {{invalid invocation of method 'operator*' on object 'var0' while it is in the 'consumed' state}} *var1; var0 = static_cast&&>(var1); *var0; - *var1; // expected-warning {{invocation of method 'operator*' on object 'var1' while it is in the 'consumed' state}} + *var1; // expected-warning {{invalid invocation of method 'operator*' on object 'var1' while it is in the 'consumed' state}} } void testIfStmt() { @@ -99,11 +106,11 @@ void testIfStmt() { if (var.isValid()) { *var; } else { - *var; // expected-warning {{invocation of method 'operator*' on object 'var' while it is in the 'consumed' state}} + *var; // expected-warning {{invalid invocation of method 'operator*' on object 'var' while it is in the 'consumed' state}} } if (!var.isValid()) { - *var; // expected-warning {{invocation of method 'operator*' on object 'var' while it is in the 'consumed' state}} + *var; // expected-warning {{invalid invocation of method 'operator*' on object 'var' while it is in the 'consumed' state}} } else { *var; } @@ -111,17 +118,17 @@ void testIfStmt() { if (var) { // Empty } else { - *var; // expected-warning {{invocation of method 'operator*' on object 'var' while it is in the 'consumed' state}} + *var; // expected-warning {{invalid invocation of method 'operator*' on object 'var' while it is in the 'consumed' state}} } if (var != nullptr) { // Empty } else { - *var; // expected-warning {{invocation of method 'operator*' on object 'var' while it is in the 'consumed' state}} + *var; // expected-warning {{invalid invocation of method 'operator*' on object 'var' while it is in the 'consumed' state}} } } -void testComplexConditionals() { +void testComplexConditionals0() { ConsumableClass var0, var1, var2; if (var0 && var1) { @@ -129,8 +136,8 @@ void testComplexConditionals() { *var1; } else { - *var0; // expected-warning {{invocation of method 'operator*' on object 'var0' while it is in the 'consumed' state}} - *var1; // expected-warning {{invocation of method 'operator*' on object 'var1' while it is in the 'consumed' state}} + *var0; // expected-warning {{invalid invocation of method 'operator*' on object 'var0' while it is in the 'consumed' state}} + *var1; // expected-warning {{invalid invocation of method 'operator*' on object 'var1' while it is in the 'consumed' state}} } if (var0 || var1) { @@ -138,8 +145,8 @@ void testComplexConditionals() { *var1; } else { - *var0; // expected-warning {{invocation of method 'operator*' on object 'var0' while it is in the 'consumed' state}} - *var1; // expected-warning {{invocation of method 'operator*' on object 'var1' while it is in the 'consumed' state}} + *var0; // expected-warning {{invalid invocation of method 'operator*' on object 'var0' while it is in the 'consumed' state}} + *var1; // expected-warning {{invalid invocation of method 'operator*' on object 'var1' while it is in the 'consumed' state}} } if (var0 && !var1) { @@ -147,13 +154,13 @@ void testComplexConditionals() { *var1; } else { - *var0; // expected-warning {{invocation of method 'operator*' on object 'var0' while it is in the 'consumed' state}} - *var1; // expected-warning {{invocation of method 'operator*' on object 'var1' while it is in the 'consumed' state}} + *var0; // expected-warning {{invalid invocation of method 'operator*' on object 'var0' while it is in the 'consumed' state}} + *var1; // expected-warning {{invalid invocation of method 'operator*' on object 'var1' while it is in the 'consumed' state}} } if (var0 || !var1) { - *var0; // expected-warning {{invocation of method 'operator*' on object 'var0' while it is in the 'consumed' state}} - *var1; // expected-warning {{invocation of method 'operator*' on object 'var1' while it is in the 'consumed' state}} + *var0; // expected-warning {{invalid invocation of method 'operator*' on object 'var0' while it is in the 'consumed' state}} + *var1; // expected-warning {{invalid invocation of method 'operator*' on object 'var1' while it is in the 'consumed' state}} } else { *var0; @@ -161,8 +168,8 @@ void testComplexConditionals() { } if (!var0 && !var1) { - *var0; // expected-warning {{invocation of method 'operator*' on object 'var0' while it is in the 'consumed' state}} - *var1; // expected-warning {{invocation of method 'operator*' on object 'var1' while it is in the 'consumed' state}} + *var0; // expected-warning {{invalid invocation of method 'operator*' on object 'var0' while it is in the 'consumed' state}} + *var1; // expected-warning {{invalid invocation of method 'operator*' on object 'var1' while it is in the 'consumed' state}} } else { *var0; @@ -170,8 +177,8 @@ void testComplexConditionals() { } if (!var0 || !var1) { - *var0; // expected-warning {{invocation of method 'operator*' on object 'var0' while it is in the 'consumed' state}} - *var1; // expected-warning {{invocation of method 'operator*' on object 'var1' while it is in the 'consumed' state}} + *var0; // expected-warning {{invalid invocation of method 'operator*' on object 'var0' while it is in the 'consumed' state}} + *var1; // expected-warning {{invalid invocation of method 'operator*' on object 'var1' while it is in the 'consumed' state}} } else { *var0; @@ -179,8 +186,8 @@ void testComplexConditionals() { } if (!(var0 && var1)) { - *var0; // expected-warning {{invocation of method 'operator*' on object 'var0' while it is in the 'consumed' state}} - *var1; // expected-warning {{invocation of method 'operator*' on object 'var1' while it is in the 'consumed' state}} + *var0; // expected-warning {{invalid invocation of method 'operator*' on object 'var0' while it is in the 'consumed' state}} + *var1; // expected-warning {{invalid invocation of method 'operator*' on object 'var1' while it is in the 'consumed' state}} } else { *var0; @@ -188,8 +195,8 @@ void testComplexConditionals() { } if (!(var0 || var1)) { - *var0; // expected-warning {{invocation of method 'operator*' on object 'var0' while it is in the 'consumed' state}} - *var1; // expected-warning {{invocation of method 'operator*' on object 'var1' while it is in the 'consumed' state}} + *var0; // expected-warning {{invalid invocation of method 'operator*' on object 'var0' while it is in the 'consumed' state}} + *var1; // expected-warning {{invalid invocation of method 'operator*' on object 'var1' while it is in the 'consumed' state}} } else { *var0; @@ -202,9 +209,9 @@ void testComplexConditionals() { *var2; } else { - *var0; // expected-warning {{invocation of method 'operator*' on object 'var0' while it is in the 'consumed' state}} - *var1; // expected-warning {{invocation of method 'operator*' on object 'var1' while it is in the 'consumed' state}} - *var2; // expected-warning {{invocation of method 'operator*' on object 'var2' while it is in the 'consumed' state}} + *var0; // expected-warning {{invalid invocation of method 'operator*' on object 'var0' while it is in the 'consumed' state}} + *var1; // expected-warning {{invalid invocation of method 'operator*' on object 'var1' while it is in the 'consumed' state}} + *var2; // expected-warning {{invalid invocation of method 'operator*' on object 'var2' while it is in the 'consumed' state}} } #if 0 @@ -215,9 +222,115 @@ void testComplexConditionals() { *var2; } else { - *var0; // expected-warning {{invocation of method 'operator*' on object 'var0' while it is in the 'consumed' state}} - *var1; // expected-warning {{invocation of method 'operator*' on object 'var1' while it is in the 'consumed' state}} - *var2; // expected-warning {{invocation of method 'operator*' on object 'var2' while it is in the 'consumed' state}} + *var0; // expected-warning {{invalid invocation of method 'operator*' on object 'var0' while it is in the 'consumed' state}} + *var1; // expected-warning {{invalid invocation of method 'operator*' on object 'var1' while it is in the 'consumed' state}} + *var2; // expected-warning {{invalid invocation of method 'operator*' on object 'var2' while it is in the 'consumed' state}} + } +#endif +} + +void testComplexConditionals1() { + ConsumableClass var0, var1, var2; + + // Coerce all variables into the unknown state. + baf3(var0); + baf3(var1); + baf3(var2); + + if (var0 && var1) { + *var0; + *var1; + + } else { + *var0; // expected-warning {{invalid invocation of method 'operator*' on object 'var0' while it is in the 'unknown' state}} + *var1; // expected-warning {{invalid invocation of method 'operator*' on object 'var1' while it is in the 'unknown' state}} + } + + if (var0 || var1) { + *var0; // expected-warning {{invalid invocation of method 'operator*' on object 'var0' while it is in the 'unknown' state}} + *var1; // expected-warning {{invalid invocation of method 'operator*' on object 'var1' while it is in the 'unknown' state}} + + } else { + *var0; // expected-warning {{invalid invocation of method 'operator*' on object 'var0' while it is in the 'consumed' state}} + *var1; // expected-warning {{invalid invocation of method 'operator*' on object 'var1' while it is in the 'consumed' state}} + } + + if (var0 && !var1) { + *var0; + *var1; // expected-warning {{invalid invocation of method 'operator*' on object 'var1' while it is in the 'consumed' state}} + + } else { + *var0; // expected-warning {{invalid invocation of method 'operator*' on object 'var0' while it is in the 'unknown' state}} + *var1; // expected-warning {{invalid invocation of method 'operator*' on object 'var1' while it is in the 'unknown' state}} + } + + if (var0 || !var1) { + *var0; // expected-warning {{invalid invocation of method 'operator*' on object 'var0' while it is in the 'unknown' state}} + *var1; // expected-warning {{invalid invocation of method 'operator*' on object 'var1' while it is in the 'unknown' state}} + + } else { + *var0; // expected-warning {{invalid invocation of method 'operator*' on object 'var0' while it is in the 'consumed' state}} + *var1; + } + + if (!var0 && !var1) { + *var0; // expected-warning {{invalid invocation of method 'operator*' on object 'var0' while it is in the 'consumed' state}} + *var1; // expected-warning {{invalid invocation of method 'operator*' on object 'var1' while it is in the 'consumed' state}} + + } else { + *var0; // expected-warning {{invalid invocation of method 'operator*' on object 'var0' while it is in the 'unknown' state}} + *var1; // expected-warning {{invalid invocation of method 'operator*' on object 'var1' while it is in the 'unknown' state}} + } + + if (!(var0 || var1)) { + *var0; // expected-warning {{invalid invocation of method 'operator*' on object 'var0' while it is in the 'consumed' state}} + *var1; // expected-warning {{invalid invocation of method 'operator*' on object 'var1' while it is in the 'consumed' state}} + + } else { + *var0; // expected-warning {{invalid invocation of method 'operator*' on object 'var0' while it is in the 'unknown' state}} + *var1; // expected-warning {{invalid invocation of method 'operator*' on object 'var1' while it is in the 'unknown' state}} + } + + if (!var0 || !var1) { + *var0; // expected-warning {{invalid invocation of method 'operator*' on object 'var0' while it is in the 'unknown' state}} + *var1; // expected-warning {{invalid invocation of method 'operator*' on object 'var1' while it is in the 'unknown' state}} + + } else { + *var0; + *var1; + } + + if (!(var0 && var1)) { + *var0; // expected-warning {{invalid invocation of method 'operator*' on object 'var0' while it is in the 'unknown' state}} + *var1; // expected-warning {{invalid invocation of method 'operator*' on object 'var1' while it is in the 'unknown' state}} + + } else { + *var0; + *var1; + } + + if (var0 && var1 && var2) { + *var0; + *var1; + *var2; + + } else { + *var0; // expected-warning {{invalid invocation of method 'operator*' on object 'var0' while it is in the 'unknown' state}} + *var1; // expected-warning {{invalid invocation of method 'operator*' on object 'var1' while it is in the 'unknown' state}} + *var2; // expected-warning {{invalid invocation of method 'operator*' on object 'var2' while it is in the 'unknown' state}} + } + +#if 0 + // FIXME: Get this test to pass. + if (var0 || var1 || var2) { + *var0; // expected-warning {{invalid invocation of method 'operator*' on object 'var0' while it is in the 'unknown' state}} + *var1; // expected-warning {{invalid invocation of method 'operator*' on object 'var1' while it is in the 'unknown' state}} + *var2; // expected-warning {{invalid invocation of method 'operator*' on object 'var2' while it is in the 'unknown' state}} + + } else { + *var0; // expected-warning {{invalid invocation of method 'operator*' on object 'var0' while it is in the 'consumed' state}} + *var1; // expected-warning {{invalid invocation of method 'operator*' on object 'var1' while it is in the 'consumed' state}} + *var2; // expected-warning {{invalid invocation of method 'operator*' on object 'var2' while it is in the 'consumed' state}} } #endif } @@ -226,7 +339,7 @@ void testStateChangeInBranch() { ConsumableClass var; // Make var enter the 'unknown' state. - baf1(var); + baf3(var); if (!var) { var = ConsumableClass(42); @@ -259,8 +372,34 @@ void testCallingConventions() { baf2(&var); *var; - baf3(static_cast&&>(var)); - *var; // expected-warning {{invocation of method 'operator*' on object 'var' while it is in the 'consumed' state}} + baf3(var); + *var; // expected-warning {{invalid invocation of method 'operator*' on object 'var' while it is in the 'unknown' state}} + + var = ConsumableClass(42); + baf4(&var); + *var; // expected-warning {{invalid invocation of method 'operator*' on object 'var' while it is in the 'unknown' state}} + + var = ConsumableClass(42); + baf5(static_cast&&>(var)); + *var; // expected-warning {{invalid invocation of method 'operator*' on object 'var' while it is in the 'consumed' state}} +} + +void testConstAndNonConstMemberFunctions() { + ConsumableClass var(42); + + var.constCall(); + *var; + + var.nonconstCall(); + *var; +} + +void testFunctionParam0(ConsumableClass param) { + *param; +} + +void testFunctionParam1(ConsumableClass ¶m) { + *param; // expected-warning {{invalid invocation of method 'operator*' on object 'param' while it is in the 'unknown' state}} } void testReturnStates() { @@ -270,24 +409,34 @@ void testReturnStates() { *var; var = returnsConsumed(); - *var; // expected-warning {{invocation of method 'operator*' on object 'var' while it is in the 'consumed' state}} + *var; // expected-warning {{invalid invocation of method 'operator*' on object 'var' while it is in the 'consumed' state}} +} + +void testCallableWhen() { + ConsumableClass var(42); + + *var; + + baf3(var); + + var.callableWhenUnknown(); } void testMoveAsignmentish() { ConsumableClass var0; ConsumableClass var1(42); - *var0; // expected-warning {{invocation of method 'operator*' on object 'var0' while it is in the 'consumed' state}} + *var0; // expected-warning {{invalid invocation of method 'operator*' on object 'var0' while it is in the 'consumed' state}} *var1; var0 = static_cast&&>(var1); *var0; - *var1; // expected-warning {{invocation of method 'operator*' on object 'var1' while it is in the 'consumed' state}} + *var1; // expected-warning {{invalid invocation of method 'operator*' on object 'var1' while it is in the 'consumed' state}} var1 = ConsumableClass(42); var1 = nullptr; - *var1; // expected-warning {{invocation of method 'operator*' on object 'var1' while it is in the 'consumed' state}} + *var1; // expected-warning {{invalid invocation of method 'operator*' on object 'var1' while it is in the 'consumed' state}} } void testConditionalMerge() { @@ -297,7 +446,7 @@ void testConditionalMerge() { // Empty } - *var; // expected-warning {{invocation of method 'operator*' on object 'var' while it is in the 'consumed' state}} + *var; // expected-warning {{invalid invocation of method 'operator*' on object 'var' while it is in the 'consumed' state}} if (var.isValid()) { // Empty @@ -305,7 +454,7 @@ void testConditionalMerge() { // Empty } - *var; // expected-warning {{invocation of method 'operator*' on object 'var' while it is in the 'consumed' state}} + *var; // expected-warning {{invalid invocation of method 'operator*' on object 'var' while it is in the 'consumed' state}} } void testConsumes0() { @@ -315,13 +464,13 @@ void testConsumes0() { var.consume(); - *var; // expected-warning {{invocation of method 'operator*' on object 'var' while it is in the 'consumed' state}} + *var; // expected-warning {{invalid invocation of method 'operator*' on object 'var' while it is in the 'consumed' state}} } void testConsumes1() { ConsumableClass var(nullptr); - *var; // expected-warning {{invocation of method 'operator*' on object 'var' while it is in the 'consumed' state}} + *var; // expected-warning {{invalid invocation of method 'operator*' on object 'var' while it is in the 'consumed' state}} } void testConsumes2() { @@ -330,10 +479,10 @@ void testConsumes2() { var.unconsumedCall(); var(6); - var.unconsumedCall(); // expected-warning {{invocation of method 'unconsumedCall' on object 'var' while it is in the 'consumed' state}} + var.unconsumedCall(); // expected-warning {{invalid invocation of method 'unconsumedCall' on object 'var' while it is in the 'consumed' state}} } -void testNonsenseState() { +void testUnreachableBlock() { ConsumableClass var(42); if (var) { @@ -349,10 +498,10 @@ void testSimpleForLoop() { ConsumableClass var; for (int i = 0; i < 10; ++i) { - *var; + *var; // expected-warning {{invalid invocation of method 'operator*' on object 'var' while it is in the 'unknown' state}} } - *var; + *var; // expected-warning {{invalid invocation of method 'operator*' on object 'var' while it is in the 'unknown' state}} } void testSimpleWhileLoop() { @@ -361,9 +510,9 @@ void testSimpleWhileLoop() { ConsumableClass var; while (i < 10) { - *var; + *var; // expected-warning {{invalid invocation of method 'operator*' on object 'var' while it is in the 'unknown' state}} ++i; } - *var; + *var; // expected-warning {{invalid invocation of method 'operator*' on object 'var' while it is in the 'unknown' state}} } diff --git a/test/SemaCXX/warn-consumed-parsing.cpp b/test/SemaCXX/warn-consumed-parsing.cpp index 9275861985..153c3b79e4 100644 --- a/test/SemaCXX/warn-consumed-parsing.cpp +++ b/test/SemaCXX/warn-consumed-parsing.cpp @@ -1,6 +1,6 @@ // RUN: %clang_cc1 -fsyntax-only -verify -Wconsumed -std=c++11 %s -#define CALLABLE_WHEN_UNCONSUMED __attribute__ ((callable_when_unconsumed)) +#define CALLABLE_WHEN(...) __attribute__ ((callable_when(__VA_ARGS__))) #define CONSUMABLE(state) __attribute__ ((consumable(state))) #define CONSUMES __attribute__ ((consumes)) #define RETURN_TYPESTATE(state) __attribute__ ((return_typestate(state))) @@ -19,33 +19,35 @@ int returnTypestateForUnconsumable() { class AttrTester0 { void consumes() __attribute__ ((consumes(42))); // expected-error {{attribute takes no arguments}} bool testsUnconsumed() __attribute__ ((tests_unconsumed(42))); // expected-error {{attribute takes no arguments}} - void callableWhenUnconsumed() __attribute__ ((callable_when_unconsumed(42))); // expected-error {{attribute takes no arguments}} + void callableWhen() __attribute__ ((callable_when())); // expected-error {{attribute takes at least 1 argument}} }; int var0 CONSUMES; // expected-warning {{'consumes' attribute only applies to methods}} int var1 TESTS_UNCONSUMED; // expected-warning {{'tests_unconsumed' attribute only applies to methods}} -int var2 CALLABLE_WHEN_UNCONSUMED; // expected-warning {{'callable_when_unconsumed' attribute only applies to methods}} +int var2 CALLABLE_WHEN(42); // expected-warning {{'callable_when' attribute only applies to methods}} int var3 CONSUMABLE(consumed); // expected-warning {{'consumable' attribute only applies to classes}} int var4 RETURN_TYPESTATE(consumed); // expected-warning {{'return_typestate' attribute only applies to functions}} void function0() CONSUMES; // expected-warning {{'consumes' attribute only applies to methods}} void function1() TESTS_UNCONSUMED; // expected-warning {{'tests_unconsumed' attribute only applies to methods}} -void function2() CALLABLE_WHEN_UNCONSUMED; // expected-warning {{'callable_when_unconsumed' attribute only applies to methods}} +void function2() CALLABLE_WHEN(42); // expected-warning {{'callable_when' attribute only applies to methods}} void function3() CONSUMABLE(consumed); // expected-warning {{'consumable' attribute only applies to classes}} class CONSUMABLE(unknown) AttrTester1 { - void callableWhenUnconsumed() CALLABLE_WHEN_UNCONSUMED; - void consumes() CONSUMES; - bool testsUnconsumed() TESTS_UNCONSUMED; + void callableWhen0() CALLABLE_WHEN("unconsumed"); + void callableWhen1() CALLABLE_WHEN(42); // expected-error {{'callable_when' attribute requires a string}} + void callableWhen2() CALLABLE_WHEN("foo"); // expected-warning {{'callable_when' attribute argument not supported: foo}} + void consumes() CONSUMES; + bool testsUnconsumed() TESTS_UNCONSUMED; }; AttrTester1 returnTypestateTester0() RETURN_TYPESTATE(not_a_state); // expected-warning {{'return_typestate' attribute argument not supported: 'not_a_state'}} AttrTester1 returnTypestateTester1() RETURN_TYPESTATE(42); // expected-error {{'return_typestate' attribute requires an identifier}} class AttrTester2 { - void callableWhenUnconsumed() CALLABLE_WHEN_UNCONSUMED; // expected-warning {{consumed analysis attribute is attached to member of class 'AttrTester2' which isn't marked as consumable}} - void consumes() CONSUMES; // expected-warning {{consumed analysis attribute is attached to member of class 'AttrTester2' which isn't marked as consumable}} - bool testsUnconsumed() TESTS_UNCONSUMED; // expected-warning {{consumed analysis attribute is attached to member of class 'AttrTester2' which isn't marked as consumable}} + void callableWhen() CALLABLE_WHEN("unconsumed"); // expected-warning {{consumed analysis attribute is attached to member of class 'AttrTester2' which isn't marked as consumable}} + void consumes() CONSUMES; // expected-warning {{consumed analysis attribute is attached to member of class 'AttrTester2' which isn't marked as consumable}} + bool testsUnconsumed() TESTS_UNCONSUMED; // expected-warning {{consumed analysis attribute is attached to member of class 'AttrTester2' which isn't marked as consumable}} }; class CONSUMABLE(42) AttrTester3; // expected-error {{'consumable' attribute requires an identifier}} diff --git a/utils/TableGen/ClangAttrEmitter.cpp b/utils/TableGen/ClangAttrEmitter.cpp index 7f42e455bd..6e44ac4146 100644 --- a/utils/TableGen/ClangAttrEmitter.cpp +++ b/utils/TableGen/ClangAttrEmitter.cpp @@ -136,6 +136,7 @@ namespace { virtual void writeHasChildren(raw_ostream &OS) const { OS << "false"; } virtual bool isEnumArg() const { return false; } + virtual bool isVariadicEnumArg() const { return false; } }; class SimpleArgument : public Argument { @@ -474,7 +475,7 @@ namespace { << ";\n"; OS << " " << getLowerName() << ".reserve(" << getLowerName() << "Size);\n"; - OS << " for (unsigned i = " << getLowerName() << "Size; i; --i)\n"; + OS << " for (unsigned i = " << getLowerName() << "Size; i; --i)\n"; std::string read = ReadPCHRecord(type); OS << " " << getLowerName() << ".push_back(" << read << ");\n"; @@ -604,6 +605,93 @@ namespace { OS << " }\n"; } }; + + class VariadicEnumArgument: public VariadicArgument { + std::string type, QualifiedTypeName; + std::vector values, enums, uniques; + public: + VariadicEnumArgument(Record &Arg, StringRef Attr) + : VariadicArgument(Arg, Attr, Arg.getValueAsString("Type")), + type(Arg.getValueAsString("Type")), + values(getValueAsListOfStrings(Arg, "Values")), + enums(getValueAsListOfStrings(Arg, "Enums")), + uniques(enums) + { + // Calculate the various enum values + std::sort(uniques.begin(), uniques.end()); + uniques.erase(std::unique(uniques.begin(), uniques.end()), uniques.end()); + + QualifiedTypeName = getAttrName().str() + "Attr::" + type; + + // FIXME: Emit a proper error + assert(!uniques.empty()); + } + + bool isVariadicEnumArg() const { return true; } + + void writeDeclarations(raw_ostream &OS) const { + std::vector::const_iterator i = uniques.begin(), + e = uniques.end(); + // The last one needs to not have a comma. + --e; + + OS << "public:\n"; + OS << " enum " << type << " {\n"; + for (; i != e; ++i) + OS << " " << *i << ",\n"; + OS << " " << *e << "\n"; + OS << " };\n"; + OS << "private:\n"; + + VariadicArgument::writeDeclarations(OS); + } + void writeDump(raw_ostream &OS) const { + OS << " for (" << getAttrName() << "Attr::" << getLowerName() + << "_iterator I = SA->" << getLowerName() << "_begin(), E = SA->" + << getLowerName() << "_end(); I != E; ++I) {\n"; + OS << " switch(*I) {\n"; + for (std::vector::const_iterator UI = uniques.begin(), + UE = uniques.end(); UI != UE; ++UI) { + OS << " case " << getAttrName() << "Attr::" << *UI << ":\n"; + OS << " OS << \" " << *UI << "\";\n"; + OS << " break;\n"; + } + OS << " }\n"; + OS << " }\n"; + } + void writePCHReadDecls(raw_ostream &OS) const { + OS << " unsigned " << getLowerName() << "Size = Record[Idx++];\n"; + OS << " SmallVector<" << QualifiedTypeName << ", 4> " << getLowerName() + << ";\n"; + OS << " " << getLowerName() << ".reserve(" << getLowerName() + << "Size);\n"; + OS << " for (unsigned i = " << getLowerName() << "Size; i; --i)\n"; + OS << " " << getLowerName() << ".push_back(" << "static_cast<" + << QualifiedTypeName << ">(Record[Idx++]));\n"; + } + void writePCHWrite(raw_ostream &OS) const{ + OS << " Record.push_back(SA->" << getLowerName() << "_size());\n"; + OS << " for (" << getAttrName() << "Attr::" << getLowerName() + << "_iterator i = SA->" << getLowerName() << "_begin(), e = SA->" + << getLowerName() << "_end(); i != e; ++i)\n"; + OS << " " << WritePCHRecord(QualifiedTypeName, "(*i)"); + } + void writeConversion(raw_ostream &OS) const { + OS << " static bool ConvertStrTo" << type << "(StringRef Val, "; + OS << type << " &Out) {\n"; + OS << " Optional<" << type << "> R = llvm::StringSwitch >(Val)\n"; + for (size_t I = 0; I < enums.size(); ++I) { + OS << " .Case(\"" << values[I] << "\", "; + OS << getAttrName() << "Attr::" << enums[I] << ")\n"; + } + OS << " .Default(Optional<" << type << ">());\n"; + OS << " if (R) {\n"; + OS << " Out = *R;\n return true;\n }\n"; + OS << " return false;\n"; + OS << " }\n"; + } + }; class VersionArgument : public Argument { public: @@ -768,6 +856,8 @@ static Argument *createArgument(Record &Arg, StringRef Attr, Ptr = new SimpleArgument(Arg, Attr, "SourceLocation"); else if (ArgName == "VariadicUnsignedArgument") Ptr = new VariadicArgument(Arg, Attr, "unsigned"); + else if (ArgName == "VariadicEnumArgument") + Ptr = new VariadicEnumArgument(Arg, Attr); else if (ArgName == "VariadicExprArgument") Ptr = new VariadicExprArgument(Arg, Attr); else if (ArgName == "VersionArgument") @@ -1051,6 +1141,9 @@ void EmitClangAttrClass(RecordKeeper &Records, raw_ostream &OS) { if ((*ai)->isEnumArg()) { EnumArgument *EA = (EnumArgument *)*ai; EA->writeConversion(OS); + } else if ((*ai)->isVariadicEnumArg()) { + VariadicEnumArgument *VEA = (VariadicEnumArgument *)*ai; + VEA->writeConversion(OS); } } -- 2.40.0