r192515 - Consumed analysis: replace the consumes attribute with a set_typestate

Delesley Hutchins delesley at google.com
Mon Oct 14 07:57:59 PDT 2013


Thanks for the review, Aaron!  Excellent points -- I'll fix those
issues in a future patch.  FYI, my goal right now is to get all of
Chris's stuff working and upstream first; I'll go back and refine the
code afterword.  You had some good comments earlier regarding use of
raw pointers that I want to address too.  I appreciate the code
review,

  -DeLesley


On Sat, Oct 12, 2013 at 8:01 AM, Aaron Ballman <aaron at aaronballman.com> wrote:
> A couple of points below:
>
> On Fri, Oct 11, 2013 at 7:03 PM, DeLesley Hutchins <delesley at google.com> wrote:
>> Author: delesley
>> Date: Fri Oct 11 18:03:26 2013
>> New Revision: 192515
>>
>> URL: http://llvm.org/viewvc/llvm-project?rev=192515&view=rev
>> Log:
>> Consumed analysis: replace the consumes attribute with a set_typestate
>> attribute.  Patch by chris.wailes at gmail.com; reviewed and edited by delesley.
>>
>> Modified:
>>     cfe/trunk/include/clang/Basic/Attr.td
>>     cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td
>>     cfe/trunk/lib/Analysis/Consumed.cpp
>>     cfe/trunk/lib/Sema/SemaDeclAttr.cpp
>>     cfe/trunk/test/SemaCXX/warn-consumed-analysis.cpp
>>     cfe/trunk/test/SemaCXX/warn-consumed-parsing.cpp
>>
>> Modified: cfe/trunk/include/clang/Basic/Attr.td
>> URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/Attr.td?rev=192515&r1=192514&r2=192515&view=diff
>> ==============================================================================
>> --- cfe/trunk/include/clang/Basic/Attr.td (original)
>> +++ cfe/trunk/include/clang/Basic/Attr.td Fri Oct 11 18:03:26 2013
>> @@ -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 {
>>
>> Modified: cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td
>> URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td?rev=192515&r1=192514&r2=192515&view=diff
>> ==============================================================================
>> --- cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td (original)
>> +++ cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td Fri Oct 11 18:03:26 2013
>> @@ -2216,8 +2216,8 @@ def warn_attr_on_unconsumable_class : Wa
>>  def warn_return_typestate_for_unconsumable_type : Warning<
>>    "return state set for an unconsumable type '%0'">, InGroup<Consumed>,
>>    DefaultIgnore;
>> -def warn_invalid_test_typestate : Warning<
>> -  "invalid test typestate '%0'">, InGroup<Consumed>, DefaultIgnore;
>> +def warn_unknown_consumed_state : Warning<
>> +   "unknown consumed analysis state '%0'">, InGroup<Consumed>, DefaultIgnore;
>
> This is not needed (more info below).
>
>>  def warn_return_typestate_mismatch : Warning<
>>    "return value not in expected state; expected '%0', observed '%1'">,
>>    InGroup<Consumed>, DefaultIgnore;
>>
>> Modified: cfe/trunk/lib/Analysis/Consumed.cpp
>> URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Analysis/Consumed.cpp?rev=192515&r1=192514&r2=192515&view=diff
>> ==============================================================================
>> --- cfe/trunk/lib/Analysis/Consumed.cpp (original)
>> +++ cfe/trunk/lib/Analysis/Consumed.cpp Fri Oct 11 18:03:26 2013
>> @@ -157,6 +157,18 @@ static ConsumedState mapConsumableAttrSt
>>    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::VisitCXXMember
>>        if (isTestingFunction(MethodDecl))
>>          PropagationMap.insert(PairType(Call,
>>            PropagationInfo(PInfo.getVar(), testsFor(MethodDecl))));
>> -      else if (MethodDecl->hasAttr<ConsumesAttr>())
>> -        StateMap->setState(PInfo.getVar(), consumed::CS_Consumed);
>> +      else if (MethodDecl->hasAttr<SetTypestateAttr>())
>> +        StateMap->setState(PInfo.getVar(),
>> +          mapSetTypestateAttrState(MethodDecl->getAttr<SetTypestateAttr>()));
>>      }
>>    }
>>  }
>> @@ -728,8 +741,9 @@ void ConsumedStmtVisitor::VisitCXXOperat
>>          if (isTestingFunction(FunDecl))
>>            PropagationMap.insert(PairType(Call,
>>              PropagationInfo(PInfo.getVar(), testsFor(FunDecl))));
>> -        else if (FunDecl->hasAttr<ConsumesAttr>())
>> -          StateMap->setState(PInfo.getVar(), consumed::CS_Consumed);
>> +        else if (FunDecl->hasAttr<SetTypestateAttr>())
>> +          StateMap->setState(PInfo.getVar(),
>> +            mapSetTypestateAttrState(FunDecl->getAttr<SetTypestateAttr>()));
>>        }
>>      }
>>    }
>>
>> Modified: cfe/trunk/lib/Sema/SemaDeclAttr.cpp
>> URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaDeclAttr.cpp?rev=192515&r1=192514&r2=192515&view=diff
>> ==============================================================================
>> --- cfe/trunk/lib/Sema/SemaDeclAttr.cpp (original)
>> +++ cfe/trunk/lib/Sema/SemaDeclAttr.cpp Fri Oct 11 18:03:26 2013
>> @@ -1026,20 +1026,6 @@ static bool checkForConsumableClass(Sema
>>    return true;
>>  }
>>
>> -static void handleConsumesAttr(Sema &S, Decl *D, const AttributeList &Attr) {
>> -  if (!isa<CXXMethodDecl>(D)) {
>> -    S.Diag(Attr.getLoc(), diag::warn_attribute_wrong_decl_type) <<
>> -      Attr.getName() << ExpectedMethod;
>> -    return;
>> -  }
>> -
>> -  if (!checkForConsumableClass(S, cast<CXXMethodDecl>(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
>>  }
>>
>>
>> -static void handleTestsTypestateAttr(Sema &S, Decl *D,
>> -                                     const AttributeList &Attr) {
>> -  if (!checkAttributeNumArgs(S, Attr, 1)) return;
>> -
>> -  if (!isa<CXXMethodDecl>(D)) {
>> -    S.Diag(Attr.getLoc(), diag::warn_attribute_wrong_decl_type) <<
>> -      Attr.getName() << ExpectedMethod;
>> -    return;
>> -  }
>> -
>> -  if (!checkForConsumableClass(S, cast<CXXMethodDecl>(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(Se
>>                                   Attr.getAttributeSpellingListIndex()));
>>  }
>>
>> +
>> +static void handleSetTypestateAttr(Sema &S, Decl *D, const AttributeList &Attr) {
>> +  if (!checkAttributeNumArgs(S, Attr, 1)) return;
>> +
>> +  if (!isa<CXXMethodDecl>(D)) {
>> +    S.Diag(Attr.getLoc(), diag::warn_attribute_wrong_decl_type) <<
>> +      Attr.getName() << ExpectedMethod;
>> +    return;
>> +  }
>> +
>> +  if (!checkForConsumableClass(S, cast<CXXMethodDecl>(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;
>> +  }
>
> This whole block (from isArgIdent down) can be replaced with
> ConvertStrToTypeSetState (or something akin to that) -- it's td
> generated code for converting enumerations to their proper value,
> including many diagnostics.  As an example, here's visibility:
>
>   VisibilityAttr::VisibilityType type;
>   if (!VisibilityAttr::ConvertStrToVisibilityType(TypeStr, type)) {
>     S.Diag(LiteralLoc, diag::warn_attribute_type_not_supported)
>       << Attr.getName() << TypeStr;
>     return;
>   }
>
> You should be using the warn_attribute_type_not_support diagnostic for
> compatibility.
>
>> +
>> +  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<CXXMethodDecl>(D)) {
>> +    S.Diag(Attr.getLoc(), diag::warn_attribute_wrong_decl_type) <<
>> +      Attr.getName() << ExpectedMethod;
>> +    return;
>> +  }
>> +
>> +  if (!checkForConsumableClass(S, cast<CXXMethodDecl>(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;
>> +  }
>
> Same comments apply here as above.
>
>> +
>> +  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<TypedefNameDecl>(D);
>> @@ -4793,18 +4819,18 @@ static void ProcessDeclAttribute(Sema &S
>>    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:
>>
>> Modified: cfe/trunk/test/SemaCXX/warn-consumed-analysis.cpp
>> URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaCXX/warn-consumed-analysis.cpp?rev=192515&r1=192514&r2=192515&view=diff
>> ==============================================================================
>> --- cfe/trunk/test/SemaCXX/warn-consumed-analysis.cpp (original)
>> +++ cfe/trunk/test/SemaCXX/warn-consumed-analysis.cpp Fri Oct 11 18:03:26 2013
>> @@ -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<T>& operator=(ConsumableClass<T>  &other);
>>    ConsumableClass<T>& operator=(ConsumableClass<T> &&other);
>> -  ConsumableClass<T>& operator=(nullptr_t) CONSUMES;
>> +  ConsumableClass<T>& operator=(nullptr_t) SET_TYPESTATE(consumed);
>>
>>    template <typename U>
>>    ConsumableClass<T>& operator=(ConsumableClass<U>  &other);
>> @@ -31,7 +31,7 @@ public:
>>    template <typename U>
>>    ConsumableClass<T>& operator=(ConsumableClass<U> &&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<int> 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<int> 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<int> var(42);
>>
>>    var.unconsumedCall();
>>
>> Modified: cfe/trunk/test/SemaCXX/warn-consumed-parsing.cpp
>> URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaCXX/warn-consumed-parsing.cpp?rev=192515&r1=192514&r2=192515&view=diff
>> ==============================================================================
>> --- cfe/trunk/test/SemaCXX/warn-consumed-parsing.cpp (original)
>> +++ cfe/trunk/test/SemaCXX/warn-consumed-parsing.cpp Fri Oct 11 18:03:26 2013
>> @@ -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() RET
>>
>>  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}}
>>  };
>>
>>
>>
>> _______________________________________________
>> cfe-commits mailing list
>> cfe-commits at cs.uiuc.edu
>> http://lists.cs.uiuc.edu/mailman/listinfo/cfe-commits
>
> ~Aaron



-- 
DeLesley Hutchins | Software Engineer | delesley at google.com | 505-206-0315



More information about the cfe-commits mailing list