[PATCH] Add 'return_typestate' function attribute

Aaron Ballman aaron.ballman at gmail.com
Tue Sep 3 06:59:38 PDT 2013


One trivial nitpick below, otherwise LGTM.

> Index: include/clang/Analysis/Analyses/Consumed.h
> ===================================================================
> --- include/clang/Analysis/Analyses/Consumed.h
> +++ include/clang/Analysis/Analyses/Consumed.h
> @@ -39,11 +39,29 @@
>
>      /// \brief Emit the warnings and notes left by the analysis.
>      virtual void emitDiagnostics() {}
> -
> +
> +    // FIXME: This can be removed when the attr propagation fix for templated
> +    //        classes lands.
> +    /// \brief Warn about return typestates set for unconsumable types.
> +    ///
> +    /// \param Loc -- The location of the attributes.
> +    ///
> +    /// \param TypeName -- The name of the unconsumable type.
> +    virtual void warnReturnTypestateForUnconsumableType(SourceLocation Loc,
> +                                                        StringRef TypeName) {}
> +
> +    /// \brief Warn about return typestate mismatches.
> +    /// \param Loc -- The SourceLocation of the return statement.
> +    virtual void warnReturnTypestateMismatch(SourceLocation Loc,
> +                                             StringRef ExpectedState,
> +                                             StringRef ObservedState) {}
> +
>      /// \brief Warn about unnecessary-test errors.
>      /// \param VariableName -- The name of the variable that holds the unique
>      /// value.
>      ///
> +    /// \param VariableState -- The known state of the value.
> +    ///
>      /// \param Loc -- The SourceLocation of the unnecessary test.
>      virtual void warnUnnecessaryTest(StringRef VariableName,
>                                       StringRef VariableState,
> @@ -170,6 +188,8 @@
>      ConsumedBlockInfo BlockInfo;
>      ConsumedStateMap *CurrStates;
>
> +    ConsumedState ExpectedReturnState;
> +
>      bool hasConsumableAttributes(const CXXRecordDecl *RD);
>      bool splitState(const CFGBlock *CurrBlock,
>                      const ConsumedStmtVisitor &Visitor);
> @@ -181,6 +201,8 @@
>      ConsumedAnalyzer(ConsumedWarningsHandlerBase &WarningsHandler)
>          : WarningsHandler(WarningsHandler) {}
>
> +    ConsumedState getExpectedReturnState() const { return ExpectedReturnState; }
> +
>      /// \brief Check a function's CFG for consumed violations.
>      ///
>      /// We traverse the blocks in the CFG, keeping track of the state of each
> Index: include/clang/Basic/Attr.td
> ===================================================================
> --- include/clang/Basic/Attr.td
> +++ include/clang/Basic/Attr.td
> @@ -953,6 +953,14 @@
>    let Subjects = [CXXMethod];
>  }
>
> +def ReturnTypestate : InheritableAttr {
> +  let Spellings = [GNU<"return_typestate">];
> +  let Subjects = [Function];
> +  let Args = [EnumArgument<"State", "ConsumedState",
> +                           ["unknown", "consumed", "unconsumed"],
> +                           ["Unknown", "Consumed", "Unconsumed"]>];
> +}
> +
>  // Type safety attributes for `void *' pointers and type tags.
>
>  def ArgumentWithTypeTag : InheritableAttr {
> Index: include/clang/Basic/DiagnosticSemaKinds.td
> ===================================================================
> --- include/clang/Basic/DiagnosticSemaKinds.td
> +++ include/clang/Basic/DiagnosticSemaKinds.td
> @@ -2192,8 +2192,16 @@
>    "invocation of method '%0' on a temporary object while it is in the "
>    "'consumed' state">, InGroup<Consumed>, DefaultIgnore;
>  def warn_attr_on_unconsumable_class : Warning<
> -  "consumed analysis attribute is attached to class '%0' which isn't marked "
> -  "as consumable">, InGroup<Consumed>, DefaultIgnore;
> +  "consumed analysis attribute is attached to member of class '%0' which isn't "
> +  "marked as consumable">, InGroup<Consumed>, DefaultIgnore;
> +def warn_return_typestate_for_unconsumable_type : Warning<
> +  "return state set for an unconsumable type '%0'">, InGroup<Consumed>,
> +  DefaultIgnore;
> +def warn_unknown_consumed_state : Warning<
> +  "unknown consumed analysis state '%0'">, InGroup<Consumed>, DefaultIgnore;
> +def warn_return_typestate_mismatch : Warning<
> +  "return value not in expected state; expected '%0', observed '%1'">,
> +  InGroup<Consumed>, DefaultIgnore;
>
>  // ConsumedStrict warnings
>  def warn_use_in_unknown_state : Warning<
> Index: lib/Analysis/Consumed.cpp
> ===================================================================
> --- lib/Analysis/Consumed.cpp
> +++ lib/Analysis/Consumed.cpp
> @@ -31,6 +31,7 @@
>  #include "llvm/Support/Compiler.h"
>  #include "llvm/Support/raw_ostream.h"
>
> +// TODO: Add notes about the actual and expected state for
>  // TODO: Correctly identify unreachable blocks when chaining boolean operators.
>  // TODO: Warn about unreachable code.
>  // TODO: Switch to using a bitmap to track unreachable blocks.
> @@ -88,6 +89,19 @@
>    return FunDecl->hasAttr<TestsUnconsumedAttr>();
>  }
>
> +static ConsumedState mapReturnTypestateAttrState(
> +  const ReturnTypestateAttr *RTSAttr) {
> +
> +  switch (RTSAttr->getState()) {
> +  case ReturnTypestateAttr::Unknown:
> +    return CS_Unknown;
> +  case ReturnTypestateAttr::Unconsumed:
> +    return CS_Unconsumed;
> +  case ReturnTypestateAttr::Consumed:
> +    return CS_Consumed;
> +  }
> +}
> +
>  static StringRef stateToString(ConsumedState State) {
>    switch (State) {
>    case consumed::CS_None:
> @@ -256,6 +270,8 @@
>    void forwardInfo(const Stmt *From, const Stmt *To);
>    void handleTestingFunctionCall(const CallExpr *Call, const VarDecl *Var);
>    bool isLikeMoveAssignment(const CXXMethodDecl *MethodDecl);
> +  void propagateReturnType(const Stmt *Call, const FunctionDecl *Fun,
> +                           QualType ReturnType);
>
>  public:
>
> @@ -272,6 +288,7 @@
>    void VisitMaterializeTemporaryExpr(const MaterializeTemporaryExpr *Temp);
>    void VisitMemberExpr(const MemberExpr *MExpr);
>    void VisitParmVarDecl(const ParmVarDecl *Param);
> +  void VisitReturnStmt(const ReturnStmt *Ret);
>    void VisitUnaryOperator(const UnaryOperator *UOp);
>    void VisitVarDecl(const VarDecl *Var);
>
> @@ -373,6 +390,24 @@
>            MethodDecl->getParamDecl(0)->getType()->isRValueReferenceType());
>  }
>
> +void ConsumedStmtVisitor::propagateReturnType(const Stmt *Call,
> +                                              const FunctionDecl *Fun,
> +                                              QualType ReturnType) {
> +  if (isConsumableType(ReturnType)) {
> +
> +    ConsumedState ReturnState;
> +
> +    if (Fun->hasAttr<ReturnTypestateAttr>())
> +      ReturnState = mapReturnTypestateAttrState(
> +        Fun->getAttr<ReturnTypestateAttr>());
> +    else
> +      ReturnState = CS_Unknown;
> +
> +    PropagationMap.insert(PairType(Call,
> +      PropagationInfo(ReturnState)));
> +  }
> +}
> +
>  void ConsumedStmtVisitor::Visit(const Stmt *StmtNode) {
>
>    ConstStmtVisitor<ConsumedStmtVisitor>::Visit(StmtNode);
> @@ -469,6 +504,8 @@
>          StateMap->setState(PInfo.getVar(), consumed::CS_Unknown);
>        }
>      }
> +
> +    propagateReturnType(Call, FunDecl, FunDecl->getCallResultType());
>    }
>  }
>
> @@ -483,8 +520,7 @@
>    QualType ThisType = Constructor->getThisType(CurrContext)->getPointeeType();
>
>    if (isConsumableType(ThisType)) {
> -    if (Constructor->hasAttr<ConsumesAttr>() ||
> -        Constructor->isDefaultConstructor()) {
> +    if (Constructor->isDefaultConstructor()) {
>
>        PropagationMap.insert(PairType(Call,
>          PropagationInfo(consumed::CS_Consumed)));
> @@ -513,8 +549,7 @@
>          PropagationMap.insert(PairType(Call, Entry->second));
>
>      } else {
> -      PropagationMap.insert(PairType(Call,
> -        PropagationInfo(consumed::CS_Unconsumed)));
> +      propagateReturnType(Call, Constructor, ThisType);
>      }
>    }
>  }
> @@ -677,6 +712,24 @@
>      StateMap->setState(Param, consumed::CS_Unknown);
>  }
>
> +void ConsumedStmtVisitor::VisitReturnStmt(const ReturnStmt *Ret) {
> +  if (ConsumedState ExpectedState = Analyzer.getExpectedReturnState()) {
> +    InfoEntry Entry = PropagationMap.find(Ret->getRetValue());
> +
> +    if (Entry != PropagationMap.end()) {
> +      assert(Entry->second.isState() || Entry->second.isVar());
> +
> +      ConsumedState RetState = Entry->second.isState() ?
> +        Entry->second.getState() : StateMap->getState(Entry->second.getVar());
> +
> +      if (RetState != Analyzer.getExpectedReturnState())

You can use ExpectedState here since you assigned it above.

> +        Analyzer.WarningsHandler.warnReturnTypestateMismatch(
> +          Ret->getReturnLoc(), stateToString(ExpectedState),
> +          stateToString(RetState));
> +    }
> +  }
> +}
> +
>  void ConsumedStmtVisitor::VisitUnaryOperator(const UnaryOperator *UOp) {
>    InfoEntry Entry = PropagationMap.find(UOp->getSubExpr()->IgnoreParens());
>    if (Entry == PropagationMap.end()) return;
> @@ -997,6 +1050,53 @@
>
>    if (!D) return;
>
> +  // FIXME: This should be removed when template instantiation propagates
> +  //        attributes at template specialization definition, not declaration.
> +  //        When it is removed the test needs to be enabled in SemaDeclAttr.cpp.
> +  QualType ReturnType;
> +  if (const CXXConstructorDecl *Constructor = dyn_cast<CXXConstructorDecl>(D)) {
> +    ASTContext &CurrContext = AC.getASTContext();
> +    ReturnType = Constructor->getThisType(CurrContext)->getPointeeType();
> +
> +  } else {
> +    ReturnType = D->getCallResultType();
> +  }
> +
> +  // Determine the expected return value.
> +  if (D->hasAttr<ReturnTypestateAttr>()) {
> +
> +    ReturnTypestateAttr *RTSAttr = D->getAttr<ReturnTypestateAttr>();
> +
> +    const CXXRecordDecl *RD = ReturnType->getAsCXXRecordDecl();
> +    if (!RD || !RD->hasAttr<ConsumableAttr>()) {
> +        // FIXME: This branch can be removed with the code above.
> +        WarningsHandler.warnReturnTypestateForUnconsumableType(
> +          RTSAttr->getLocation(), ReturnType.getAsString());
> +        ExpectedReturnState = CS_None;
> +
> +    } else {
> +      switch (RTSAttr->getState()) {
> +      case ReturnTypestateAttr::Unknown:
> +        ExpectedReturnState = CS_Unknown;
> +        break;
> +
> +      case ReturnTypestateAttr::Unconsumed:
> +        ExpectedReturnState = CS_Unconsumed;
> +        break;
> +
> +      case ReturnTypestateAttr::Consumed:
> +        ExpectedReturnState = CS_Consumed;
> +        break;
> +      }
> +    }
> +
> +  } else if (isConsumableType(ReturnType)) {
> +    ExpectedReturnState = CS_Unknown;
> +
> +  } else {
> +    ExpectedReturnState = CS_None;
> +  }
> +
>    BlockInfo = ConsumedBlockInfo(AC.getCFG());
>
>    PostOrderCFGView *SortedGraph = AC.getAnalysis<PostOrderCFGView>();
> Index: lib/Sema/AnalysisBasedWarnings.cpp
> ===================================================================
> --- lib/Sema/AnalysisBasedWarnings.cpp
> +++ lib/Sema/AnalysisBasedWarnings.cpp
> @@ -1445,11 +1445,23 @@
>      }
>    }
>
> -  /// Warn about unnecessary-test errors.
> -  /// \param VariableName -- The name of the variable that holds the unique
> -  /// value.
> -  ///
> -  /// \param Loc -- The SourceLocation of the unnecessary test.
> +  void warnReturnTypestateForUnconsumableType(SourceLocation Loc,
> +                                              StringRef TypeName) {
> +    PartialDiagnosticAt Warning(Loc, S.PDiag(
> +      diag::warn_return_typestate_for_unconsumable_type) << TypeName);
> +
> +    Warnings.push_back(DelayedDiag(Warning, OptionalNotes()));
> +  }
> +
> +  void warnReturnTypestateMismatch(SourceLocation Loc, StringRef ExpectedState,
> +                                   StringRef ObservedState) {
> +
> +    PartialDiagnosticAt Warning(Loc, S.PDiag(
> +      diag::warn_return_typestate_mismatch) << ExpectedState << ObservedState);
> +
> +    Warnings.push_back(DelayedDiag(Warning, OptionalNotes()));
> +  }
> +
>    void warnUnnecessaryTest(StringRef VariableName, StringRef VariableState,
>                             SourceLocation Loc) {
>
> @@ -1459,11 +1471,6 @@
>      Warnings.push_back(DelayedDiag(Warning, OptionalNotes()));
>    }
>
> -  /// Warn about use-while-consumed errors.
> -  /// \param MethodName -- The name of the method that was incorrectly
> -  /// invoked.
> -  ///
> -  /// \param Loc -- The SourceLocation of the method invocation.
>    void warnUseOfTempWhileConsumed(StringRef MethodName, SourceLocation Loc) {
>
>      PartialDiagnosticAt Warning(Loc, S.PDiag(
> @@ -1472,11 +1479,6 @@
>      Warnings.push_back(DelayedDiag(Warning, OptionalNotes()));
>    }
>
> -  /// Warn about use-in-unknown-state errors.
> -  /// \param MethodName -- The name of the method that was incorrectly
> -  /// invoked.
> -  ///
> -  /// \param Loc -- The SourceLocation of the method invocation.
>    void warnUseOfTempInUnknownState(StringRef MethodName, SourceLocation Loc) {
>
>      PartialDiagnosticAt Warning(Loc, S.PDiag(
> @@ -1485,14 +1487,6 @@
>      Warnings.push_back(DelayedDiag(Warning, OptionalNotes()));
>    }
>
> -  /// 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.
>    void warnUseWhileConsumed(StringRef MethodName, StringRef VariableName,
>                              SourceLocation Loc) {
>
> @@ -1502,14 +1496,6 @@
>      Warnings.push_back(DelayedDiag(Warning, OptionalNotes()));
>    }
>
> -  /// Warn about use-in-unknown-state 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.
>    void warnUseInUnknownState(StringRef MethodName, StringRef VariableName,
>                               SourceLocation Loc) {
>
> Index: lib/Sema/SemaDeclAttr.cpp
> ===================================================================
> --- lib/Sema/SemaDeclAttr.cpp
> +++ lib/Sema/SemaDeclAttr.cpp
> @@ -1069,6 +1069,64 @@
>                                   Attr.getAttributeSpellingListIndex()));
>  }
>
> +static void handleReturnTypestateAttr(Sema &S, Decl *D,
> +                                      const AttributeList &Attr) {
> +  if (!checkAttributeNumArgs(S, Attr, 1)) return;
> +
> +  ReturnTypestateAttr::ConsumedState ReturnState;
> +
> +  if (Attr.isArgIdent(0)) {
> +    StringRef Param = Attr.getArgAsIdent(0)->Ident->getName();
> +
> +    if (Param == "unknown") {
> +      ReturnState = ReturnTypestateAttr::Unknown;
> +    } else if (Param == "consumed") {
> +      ReturnState = ReturnTypestateAttr::Consumed;
> +    } else if (Param == "unconsumed") {
> +      ReturnState = ReturnTypestateAttr::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;
> +  }
> +
> +  if (!isa<FunctionDecl>(D)) {
> +    S.Diag(Attr.getLoc(), diag::warn_attribute_wrong_decl_type) <<
> +      Attr.getName() << ExpectedFunction;
> +    return;
> +  }
> +
> +  // FIXME: This check is currently being done in the analysis.  It can be
> +  //        enabled here only after the parser propagates attributes at
> +  //        template specialization definition, not declaration.
> +  //QualType ReturnType;
> +  //
> +  //if (const CXXConstructorDecl *Constructor = dyn_cast<CXXConstructorDecl>(D)) {
> +  //  ReturnType = Constructor->getThisType(S.getASTContext())->getPointeeType();
> +  //
> +  //} else {
> +  //
> +  //  ReturnType = cast<FunctionDecl>(D)->getCallResultType();
> +  //}
> +  //
> +  //const CXXRecordDecl *RD = ReturnType->getAsCXXRecordDecl();
> +  //
> +  //if (!RD || !RD->hasAttr<ConsumableAttr>()) {
> +  //    S.Diag(Attr.getLoc(), diag::warn_return_state_for_unconsumable_type) <<
> +  //      ReturnType.getAsString();
> +  //    return;
> +  //}
> +
> +  D->addAttr(::new (S.Context)
> +             ReturnTypestateAttr(Attr.getRange(), S.Context, ReturnState,
> +                                 Attr.getAttributeSpellingListIndex()));
> +}
> +
>  static void handleExtVectorTypeAttr(Sema &S, Scope *scope, Decl *D,
>                                      const AttributeList &Attr) {
>    TypedefNameDecl *TD = dyn_cast<TypedefNameDecl>(D);
> @@ -5024,6 +5082,9 @@
>    case AttributeList::AT_TestsUnconsumed:
>      handleTestsUnconsumedAttr(S, D, Attr);
>      break;
> +  case AttributeList::AT_ReturnTypestate:
> +    handleReturnTypestateAttr(S, D, Attr);
> +    break;
>
>    // Type safety attributes.
>    case AttributeList::AT_ArgumentWithTypeTag:
> Index: test/SemaCXX/warn-consumed-analysis-strict.cpp
> ===================================================================
> --- test/SemaCXX/warn-consumed-analysis-strict.cpp
> +++ test/SemaCXX/warn-consumed-analysis-strict.cpp
> @@ -3,6 +3,7 @@
>  #define CALLABLE_WHEN_UNCONSUMED __attribute__ ((callable_when_unconsumed))
>  #define CONSUMABLE               __attribute__ ((consumable))
>  #define CONSUMES                 __attribute__ ((consumes))
> +#define RETURN_TYPESTATE(State)  __attribute__ ((return_typestate(State)))
>  #define TESTS_UNCONSUMED         __attribute__ ((tests_unconsumed))
>
>  #define TEST_VAR(Var) Var.isValid()
> @@ -15,9 +16,10 @@
>
>    public:
>    ConsumableClass();
> -  ConsumableClass(T val);
> -  ConsumableClass(ConsumableClass<T> &other);
> -  ConsumableClass(ConsumableClass<T> &&other);
> +  ConsumableClass(nullptr_t p) RETURN_TYPESTATE(consumed);
> +  ConsumableClass(T val) RETURN_TYPESTATE(unconsumed);
> +  ConsumableClass(ConsumableClass<T> &other) RETURN_TYPESTATE(unconsumed);
> +  ConsumableClass(ConsumableClass<T> &&other) RETURN_TYPESTATE(unconsumed);
>
>    ConsumableClass<T>& operator=(ConsumableClass<T>  &other);
>    ConsumableClass<T>& operator=(ConsumableClass<T> &&other);
> Index: test/SemaCXX/warn-consumed-analysis.cpp
> ===================================================================
> --- test/SemaCXX/warn-consumed-analysis.cpp
> +++ test/SemaCXX/warn-consumed-analysis.cpp
> @@ -3,6 +3,7 @@
>  #define CALLABLE_WHEN_UNCONSUMED __attribute__ ((callable_when_unconsumed))
>  #define CONSUMABLE               __attribute__ ((consumable))
>  #define CONSUMES                 __attribute__ ((consumes))
> +#define RETURN_TYPESTATE(State)  __attribute__ ((return_typestate(State)))
>  #define TESTS_UNCONSUMED         __attribute__ ((tests_unconsumed))
>
>  typedef decltype(nullptr) nullptr_t;
> @@ -13,10 +14,10 @@
>
>    public:
>    ConsumableClass();
> -  ConsumableClass(nullptr_t p) CONSUMES;
> -  ConsumableClass(T val);
> -  ConsumableClass(ConsumableClass<T> &other);
> -  ConsumableClass(ConsumableClass<T> &&other);
> +  ConsumableClass(nullptr_t p) RETURN_TYPESTATE(consumed);
> +  ConsumableClass(T val) RETURN_TYPESTATE(unconsumed);
> +  ConsumableClass(ConsumableClass<T> &other) RETURN_TYPESTATE(unconsumed);
> +  ConsumableClass(ConsumableClass<T> &&other) RETURN_TYPESTATE(unconsumed);
>
>    ConsumableClass<T>& operator=(ConsumableClass<T>  &other);
>    ConsumableClass<T>& operator=(ConsumableClass<T> &&other);
> @@ -48,6 +49,16 @@
>
>  void baf3(ConsumableClass<int> &&var);
>
> +ConsumableClass<int> returnsUnconsumed() RETURN_TYPESTATE(unconsumed);
> +ConsumableClass<int> returnsUnconsumed() {
> +  return ConsumableClass<int>(); // expected-warning {{return value not in expected state; expected 'unconsumed', observed 'consumed'}}
> +}
> +
> +ConsumableClass<int> returnsConsumed() RETURN_TYPESTATE(consumed);
> +ConsumableClass<int> returnsConsumed() {
> +  return ConsumableClass<int>();
> +}
> +
>  void testInitialization() {
>    ConsumableClass<int> var0;
>    ConsumableClass<int> var1 = ConsumableClass<int>();
> @@ -253,6 +264,16 @@
>    *var; // expected-warning {{invocation of method 'operator*' on object 'var' while it is in the 'consumed' state}}
>  }
>
> +void testReturnStates() {
> +  ConsumableClass<int> var;
> +
> +  var = returnsUnconsumed();
> +  *var;
> +
> +  var = returnsConsumed();
> +  *var; // expected-warning {{invocation of method 'operator*' on object 'var' while it is in the 'consumed' state}}
> +}
> +
>  void testMoveAsignmentish() {
>    ConsumableClass<int>  var0;
>    ConsumableClass<long> var1(42);
> Index: test/SemaCXX/warn-consumed-parsing.cpp
> ===================================================================
> --- test/SemaCXX/warn-consumed-parsing.cpp
> +++ test/SemaCXX/warn-consumed-parsing.cpp
> @@ -1,21 +1,29 @@
>  // RUN: %clang_cc1 -fsyntax-only -verify -Wconsumed -std=c++11 %s
>
> -#define CONSUMABLE                __attribute__ ((consumable))
> -#define CONSUMES                  __attribute__ ((consumes))
> -#define TESTS_UNCONSUMED          __attribute__ ((tests_unconsumed))
> -#define CALLABLE_WHEN_UNCONSUMED  __attribute__ ((callable_when_unconsumed))
> +#define CALLABLE_WHEN_UNCONSUMED __attribute__ ((callable_when_unconsumed))
> +#define CONSUMABLE               __attribute__ ((consumable))
> +#define CONSUMES                 __attribute__ ((consumes))
> +#define RETURN_TYPESTATE(State)  __attribute__ ((return_typestate(State)))
> +#define TESTS_UNCONSUMED         __attribute__ ((tests_unconsumed))
> +
> +// FIXME: This warning is not generated if it appears bellow the AttrTester0
> +//        class declaration.  Why?
> +int returnTypestateForUnconsumable() RETURN_TYPESTATE(consumed); // expected-warning {{return state set for an unconsumable type 'int'}}
> +int returnTypestateForUnconsumable() {
> +  return 0;
> +}
>
>  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 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}}
>  };
>
>  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 var3 CONSUMABLE; // 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}}
> @@ -28,8 +36,11 @@
>    bool testsUnconsumed()        TESTS_UNCONSUMED;
>  };
>
> +AttrTester1 returnTypestateTester0() RETURN_TYPESTATE(not_a_state); // expected-warning {{unknown consumed analysis state '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 class 'AttrTester2' which isn't marked as consumable}}
> -  void consumes()               CONSUMES; // expected-warning {{consumed analysis attribute is attached to class 'AttrTester2' which isn't marked as consumable}}
> -  bool testsUnconsumed()        TESTS_UNCONSUMED; // expected-warning {{consumed analysis attribute is attached to class 'AttrTester2' which isn't marked as consumable}}
> +  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}}
>  };
>

~Aaron

On Mon, Sep 2, 2013 at 10:48 PM, Christian Wailes
<chriswailes at google.com> wrote:
>   Forgot a return statement.
>
> Hi delesley, dblaikie, aaron.ballman,
>
> http://llvm-reviews.chandlerc.com/D1567
>
> CHANGE SINCE LAST DIFF
>   http://llvm-reviews.chandlerc.com/D1567?vs=3974&id=3976#toc
>
> Files:
>   include/clang/Analysis/Analyses/Consumed.h
>   include/clang/Basic/Attr.td
>   include/clang/Basic/DiagnosticSemaKinds.td
>   lib/Analysis/Consumed.cpp
>   lib/Sema/AnalysisBasedWarnings.cpp
>   lib/Sema/SemaDeclAttr.cpp
>   test/SemaCXX/warn-consumed-analysis-strict.cpp
>   test/SemaCXX/warn-consumed-analysis.cpp
>   test/SemaCXX/warn-consumed-parsing.cpp



More information about the cfe-commits mailing list