From: DeLesley Hutchins Date: Fri, 11 Oct 2013 23:03:26 +0000 (+0000) Subject: Consumed analysis: replace the consumes attribute with a set_typestate X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=f30e194f9d797ebeb2f6c3f96e88160ed1a6519a;p=clang Consumed analysis: replace the consumes attribute with a set_typestate attribute. Patch by chris.wailes@gmail.com; reviewed and edited by delesley. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@192515 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/include/clang/Basic/Attr.td b/include/clang/Basic/Attr.td index 4af914bf0c..c88b9a3ba7 100644 --- a/include/clang/Basic/Attr.td +++ b/include/clang/Basic/Attr.td @@ -967,19 +967,6 @@ def CallableWhen : InheritableAttr { ["Unknown", "Consumed", "Unconsumed"]>]; } -def TestsTypestate : InheritableAttr { - let Spellings = [GNU<"tests_typestate">]; - let Subjects = [CXXMethod]; - let Args = [EnumArgument<"TestState", "ConsumedState", - ["consumed", "unconsumed"], - ["Consumed", "Unconsumed"]>]; -} - -def Consumes : InheritableAttr { - let Spellings = [GNU<"consumes">]; - let Subjects = [CXXMethod]; -} - def ReturnTypestate : InheritableAttr { let Spellings = [GNU<"return_typestate">]; let Subjects = [Function]; @@ -988,6 +975,22 @@ def ReturnTypestate : InheritableAttr { ["Unknown", "Consumed", "Unconsumed"]>]; } +def SetTypestate : InheritableAttr { + let Spellings = [GNU<"set_typestate">]; + let Subjects = [CXXMethod]; + let Args = [EnumArgument<"NewState", "ConsumedState", + ["unknown", "consumed", "unconsumed"], + ["Unknown", "Consumed", "Unconsumed"]>]; +} + +def TestsTypestate : InheritableAttr { + let Spellings = [GNU<"tests_typestate">]; + let Subjects = [CXXMethod]; + let Args = [EnumArgument<"TestState", "ConsumedState", + ["consumed", "unconsumed"], + ["Consumed", "Unconsumed"]>]; +} + // Type safety attributes for `void *' pointers and type tags. def ArgumentWithTypeTag : InheritableAttr { diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td index 194389f547..d393edd941 100644 --- a/include/clang/Basic/DiagnosticSemaKinds.td +++ b/include/clang/Basic/DiagnosticSemaKinds.td @@ -2216,8 +2216,8 @@ def warn_attr_on_unconsumable_class : Warning< def warn_return_typestate_for_unconsumable_type : Warning< "return state set for an unconsumable type '%0'">, InGroup, DefaultIgnore; -def warn_invalid_test_typestate : Warning< - "invalid test typestate '%0'">, InGroup, DefaultIgnore; +def warn_unknown_consumed_state : Warning< + "unknown consumed analysis state '%0'">, InGroup, DefaultIgnore; def warn_return_typestate_mismatch : Warning< "return value not in expected state; expected '%0', observed '%1'">, InGroup, DefaultIgnore; diff --git a/lib/Analysis/Consumed.cpp b/lib/Analysis/Consumed.cpp index d1c03d091b..c51c02335c 100644 --- a/lib/Analysis/Consumed.cpp +++ b/lib/Analysis/Consumed.cpp @@ -157,6 +157,18 @@ static ConsumedState mapConsumableAttrState(const QualType QT) { llvm_unreachable("invalid enum"); } +static ConsumedState mapSetTypestateAttrState(const SetTypestateAttr *STAttr) { + switch (STAttr->getNewState()) { + case SetTypestateAttr::Unknown: + return CS_Unknown; + case SetTypestateAttr::Unconsumed: + return CS_Unconsumed; + case SetTypestateAttr::Consumed: + return CS_Consumed; + } + llvm_unreachable("invalid_enum"); +} + static ConsumedState mapReturnTypestateAttrState(const ReturnTypestateAttr *RTSAttr) { switch (RTSAttr->getState()) { @@ -639,8 +651,9 @@ void ConsumedStmtVisitor::VisitCXXMemberCallExpr( if (isTestingFunction(MethodDecl)) PropagationMap.insert(PairType(Call, PropagationInfo(PInfo.getVar(), testsFor(MethodDecl)))); - else if (MethodDecl->hasAttr()) - StateMap->setState(PInfo.getVar(), consumed::CS_Consumed); + else if (MethodDecl->hasAttr()) + StateMap->setState(PInfo.getVar(), + mapSetTypestateAttrState(MethodDecl->getAttr())); } } } @@ -728,8 +741,9 @@ void ConsumedStmtVisitor::VisitCXXOperatorCallExpr( if (isTestingFunction(FunDecl)) PropagationMap.insert(PairType(Call, PropagationInfo(PInfo.getVar(), testsFor(FunDecl)))); - else if (FunDecl->hasAttr()) - StateMap->setState(PInfo.getVar(), consumed::CS_Consumed); + else if (FunDecl->hasAttr()) + StateMap->setState(PInfo.getVar(), + mapSetTypestateAttrState(FunDecl->getAttr())); } } } diff --git a/lib/Sema/SemaDeclAttr.cpp b/lib/Sema/SemaDeclAttr.cpp index a07d6ca79d..70ae9fd9bc 100644 --- a/lib/Sema/SemaDeclAttr.cpp +++ b/lib/Sema/SemaDeclAttr.cpp @@ -1026,20 +1026,6 @@ static bool checkForConsumableClass(Sema &S, const CXXMethodDecl *MD, return true; } -static void handleConsumesAttr(Sema &S, Decl *D, const AttributeList &Attr) { - if (!isa(D)) { - S.Diag(Attr.getLoc(), diag::warn_attribute_wrong_decl_type) << - Attr.getName() << ExpectedMethod; - return; - } - - if (!checkForConsumableClass(S, cast(D), Attr)) - return; - - D->addAttr(::new (S.Context) - ConsumesAttr(Attr.getRange(), S.Context, - Attr.getAttributeSpellingListIndex())); -} static void handleCallableWhenAttr(Sema &S, Decl *D, const AttributeList &Attr) { @@ -1080,44 +1066,6 @@ static void handleCallableWhenAttr(Sema &S, Decl *D, } -static void handleTestsTypestateAttr(Sema &S, Decl *D, - const AttributeList &Attr) { - if (!checkAttributeNumArgs(S, Attr, 1)) return; - - if (!isa(D)) { - S.Diag(Attr.getLoc(), diag::warn_attribute_wrong_decl_type) << - Attr.getName() << ExpectedMethod; - return; - } - - if (!checkForConsumableClass(S, cast(D), Attr)) - return; - - TestsTypestateAttr::ConsumedState TestState; - - if (Attr.isArgIdent(0)) { - StringRef Param = Attr.getArgAsIdent(0)->Ident->getName(); - - if (Param == "consumed") { - TestState = TestsTypestateAttr::Consumed; - } else if (Param == "unconsumed") { - TestState = TestsTypestateAttr::Unconsumed; - } else { - S.Diag(Attr.getLoc(), diag::warn_invalid_test_typestate) << Param; - return; - } - - } else { - S.Diag(Attr.getLoc(), diag::err_attribute_argument_type) << - Attr.getName() << AANT_ArgumentIdentifier; - return; - } - - D->addAttr(::new (S.Context) - TestsTypestateAttr(Attr.getRange(), S.Context, TestState, - Attr.getAttributeSpellingListIndex())); -} - static void handleReturnTypestateAttr(Sema &S, Decl *D, const AttributeList &Attr) { ReturnTypestateAttr::ConsumedState ReturnState; @@ -1168,6 +1116,84 @@ static void handleReturnTypestateAttr(Sema &S, Decl *D, Attr.getAttributeSpellingListIndex())); } + +static void handleSetTypestateAttr(Sema &S, Decl *D, const AttributeList &Attr) { + if (!checkAttributeNumArgs(S, Attr, 1)) return; + + if (!isa(D)) { + S.Diag(Attr.getLoc(), diag::warn_attribute_wrong_decl_type) << + Attr.getName() << ExpectedMethod; + return; + } + + if (!checkForConsumableClass(S, cast(D), Attr)) + return; + + SetTypestateAttr::ConsumedState NewState; + + if (Attr.isArgIdent(0)) { + StringRef Param = Attr.getArgAsIdent(0)->Ident->getName(); + + if (Param == "unknown") { + NewState = SetTypestateAttr::Unknown; + } else if (Param == "consumed") { + NewState = SetTypestateAttr::Consumed; + } else if (Param == "unconsumed") { + NewState = SetTypestateAttr::Unconsumed; + } else { + S.Diag(Attr.getLoc(), diag::warn_unknown_consumed_state) << Param; + return; + } + + } else { + S.Diag(Attr.getLoc(), diag::err_attribute_argument_type) << + Attr.getName() << AANT_ArgumentIdentifier; + return; + } + + D->addAttr(::new (S.Context) + SetTypestateAttr(Attr.getRange(), S.Context, NewState, + Attr.getAttributeSpellingListIndex())); +} + +static void handleTestsTypestateAttr(Sema &S, Decl *D, + const AttributeList &Attr) { + if (!checkAttributeNumArgs(S, Attr, 1)) return; + + if (!isa(D)) { + S.Diag(Attr.getLoc(), diag::warn_attribute_wrong_decl_type) << + Attr.getName() << ExpectedMethod; + return; + } + + if (!checkForConsumableClass(S, cast(D), Attr)) + return; + + TestsTypestateAttr::ConsumedState TestState; + + if (Attr.isArgIdent(0)) { + StringRef Param = Attr.getArgAsIdent(0)->Ident->getName(); + + if (Param == "consumed") { + TestState = TestsTypestateAttr::Consumed; + } else if (Param == "unconsumed") { + TestState = TestsTypestateAttr::Unconsumed; + } else { + S.Diag(Attr.getLoc(), diag::warn_unknown_consumed_state) << Param; + return; + } + + } else { + S.Diag(Attr.getLoc(), diag::err_attribute_argument_type) << + Attr.getName() << AANT_ArgumentIdentifier; + return; + } + + D->addAttr(::new (S.Context) + TestsTypestateAttr(Attr.getRange(), S.Context, TestState, + Attr.getAttributeSpellingListIndex())); +} + static void handleExtVectorTypeAttr(Sema &S, Scope *scope, Decl *D, const AttributeList &Attr) { TypedefNameDecl *TD = dyn_cast(D); @@ -4793,18 +4819,18 @@ static void ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D, case AttributeList::AT_Consumable: handleConsumableAttr(S, D, Attr); break; - case AttributeList::AT_Consumes: - handleConsumesAttr(S, D, Attr); - break; case AttributeList::AT_CallableWhen: handleCallableWhenAttr(S, D, Attr); break; - case AttributeList::AT_TestsTypestate: - handleTestsTypestateAttr(S, D, Attr); - break; case AttributeList::AT_ReturnTypestate: handleReturnTypestateAttr(S, D, Attr); break; + case AttributeList::AT_SetTypestate: + handleSetTypestateAttr(S, D, Attr); + break; + case AttributeList::AT_TestsTypestate: + handleTestsTypestateAttr(S, D, Attr); + break; // Type safety attributes. case AttributeList::AT_ArgumentWithTypeTag: diff --git a/test/SemaCXX/warn-consumed-analysis.cpp b/test/SemaCXX/warn-consumed-analysis.cpp index b11a74f95e..8a55c8820a 100644 --- a/test/SemaCXX/warn-consumed-analysis.cpp +++ b/test/SemaCXX/warn-consumed-analysis.cpp @@ -4,7 +4,7 @@ #define CALLABLE_WHEN(...) __attribute__ ((callable_when(__VA_ARGS__))) #define CONSUMABLE(state) __attribute__ ((consumable(state))) -#define CONSUMES __attribute__ ((consumes)) +#define SET_TYPESTATE(state) __attribute__ ((set_typestate(state))) #define RETURN_TYPESTATE(state) __attribute__ ((return_typestate(state))) #define TESTS_TYPESTATE(state) __attribute__ ((tests_typestate(state))) @@ -23,7 +23,7 @@ public: ConsumableClass& operator=(ConsumableClass &other); ConsumableClass& operator=(ConsumableClass &&other); - ConsumableClass& operator=(nullptr_t) CONSUMES; + ConsumableClass& operator=(nullptr_t) SET_TYPESTATE(consumed); template ConsumableClass& operator=(ConsumableClass &other); @@ -31,7 +31,7 @@ public: template ConsumableClass& operator=(ConsumableClass &&other); - void operator()(int a) CONSUMES; + void operator()(int a) SET_TYPESTATE(consumed); void operator*() const CALLABLE_WHEN("unconsumed"); void unconsumedCall() const CALLABLE_WHEN("unconsumed"); void callableWhenUnknown() const CALLABLE_WHEN("unconsumed", "unknown"); @@ -44,7 +44,8 @@ public: void constCall() const; void nonconstCall(); - void consume() CONSUMES; + void consume() SET_TYPESTATE(consumed); + void unconsume() SET_TYPESTATE(unconsumed); }; class CONSUMABLE(unconsumed) DestructorTester { @@ -484,7 +485,7 @@ void testConditionalMerge() { *var; // expected-warning {{invalid invocation of method 'operator*' on object 'var' while it is in the 'consumed' state}} } -void testConsumes0() { +void testSetTypestate() { ConsumableClass var(42); *var; @@ -492,15 +493,19 @@ void testConsumes0() { var.consume(); *var; // expected-warning {{invalid invocation of method 'operator*' on object 'var' while it is in the 'consumed' state}} + + var.unconsume(); + + *var; } -void testConsumes1() { +void testConsumes0() { ConsumableClass var(nullptr); *var; // expected-warning {{invalid invocation of method 'operator*' on object 'var' while it is in the 'consumed' state}} } -void testConsumes2() { +void testConsumes1() { ConsumableClass var(42); var.unconsumedCall(); diff --git a/test/SemaCXX/warn-consumed-parsing.cpp b/test/SemaCXX/warn-consumed-parsing.cpp index 0cc8579c94..80021020ca 100644 --- a/test/SemaCXX/warn-consumed-parsing.cpp +++ b/test/SemaCXX/warn-consumed-parsing.cpp @@ -1,10 +1,10 @@ // RUN: %clang_cc1 -fsyntax-only -verify -Wconsumed -std=c++11 %s #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_TYPESTATE(state) __attribute__ ((tests_typestate(state))) +#define CONSUMABLE(state) __attribute__ ((consumable(state))) +#define SET_TYPESTATE(state) __attribute__ ((set_typestate(state))) +#define RETURN_TYPESTATE(state) __attribute__ ((return_typestate(state))) +#define TESTS_TYPESTATE(state) __attribute__ ((tests_typestate(state))) // FIXME: This test is here because the warning is issued by the Consumed // analysis, not SemaDeclAttr. The analysis won't run after an error @@ -17,27 +17,27 @@ int returnTypestateForUnconsumable() { } class AttrTester0 { - void consumes() __attribute__ ((consumes(42))); // expected-error {{attribute takes no arguments}} + void consumes() __attribute__ ((set_typestate())); // expected-error {{attribute takes one argument}} bool testsUnconsumed() __attribute__ ((tests_typestate())); // expected-error {{attribute takes one argument}} 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 var0 SET_TYPESTATE(consumed); // expected-warning {{'set_typestate' attribute only applies to methods}} int var1 TESTS_TYPESTATE(consumed); // expected-warning {{'tests_typestate' attribute only applies to methods}} -int var2 CALLABLE_WHEN(42); // expected-warning {{'callable_when' attribute only applies to methods}} +int var2 CALLABLE_WHEN("consumed"); // 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 function0() SET_TYPESTATE(consumed); // expected-warning {{'set_typestate' attribute only applies to methods}} void function1() TESTS_TYPESTATE(consumed); // expected-warning {{'tests_typestate' attribute only applies to methods}} -void function2() CALLABLE_WHEN(42); // expected-warning {{'callable_when' attribute only applies to methods}} +void function2() CALLABLE_WHEN("consumed"); // 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 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; + void consumes() SET_TYPESTATE(consumed); bool testsUnconsumed() TESTS_TYPESTATE(consumed); }; @@ -46,7 +46,7 @@ AttrTester1 returnTypestateTester1() RETURN_TYPESTATE(42); // expected-error {{' class AttrTester2 { 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}} + void consumes() SET_TYPESTATE(consumed); // expected-warning {{consumed analysis attribute is attached to member of class 'AttrTester2' which isn't marked as consumable}} bool testsUnconsumed() TESTS_TYPESTATE(consumed); // expected-warning {{consumed analysis attribute is attached to member of class 'AttrTester2' which isn't marked as consumable}} };