diff options
-rw-r--r-- | clang/include/clang/Analysis/Analyses/Consumed.h | 2 | ||||
-rw-r--r-- | clang/include/clang/Basic/Attr.td | 3 | ||||
-rw-r--r-- | clang/lib/Analysis/Consumed.cpp | 110 | ||||
-rw-r--r-- | clang/lib/Sema/SemaDeclAttr.cpp | 27 | ||||
-rw-r--r-- | clang/test/SemaCXX/warn-consumed-analysis-strict.cpp | 20 | ||||
-rw-r--r-- | clang/test/SemaCXX/warn-consumed-analysis.cpp | 13 | ||||
-rw-r--r-- | clang/test/SemaCXX/warn-consumed-parsing.cpp | 12 |
7 files changed, 113 insertions, 74 deletions
diff --git a/clang/include/clang/Analysis/Analyses/Consumed.h b/clang/include/clang/Analysis/Analyses/Consumed.h index 082baad1767..e473d1e4275 100644 --- a/clang/include/clang/Analysis/Analyses/Consumed.h +++ b/clang/include/clang/Analysis/Analyses/Consumed.h @@ -190,6 +190,8 @@ namespace consumed { ConsumedState ExpectedReturnState; + void determineExpectedReturnState(AnalysisDeclContext &AC, + const FunctionDecl *D); bool hasConsumableAttributes(const CXXRecordDecl *RD); bool splitState(const CFGBlock *CurrBlock, const ConsumedStmtVisitor &Visitor); diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td index 5e0ecdb16ac..48cd640a864 100644 --- a/clang/include/clang/Basic/Attr.td +++ b/clang/include/clang/Basic/Attr.td @@ -931,6 +931,9 @@ def SharedLocksRequired : InheritableAttr { def Consumable : InheritableAttr { let Spellings = [GNU<"consumable">]; let Subjects = [CXXRecord]; + let Args = [EnumArgument<"DefaultState", "ConsumedState", + ["unknown", "consumed", "unconsumed"], + ["Unknown", "Consumed", "Unconsumed"]>]; } def CallableWhenUnconsumed : InheritableAttr { diff --git a/clang/lib/Analysis/Consumed.cpp b/clang/lib/Analysis/Consumed.cpp index 78153a0a684..b540d75898d 100644 --- a/clang/lib/Analysis/Consumed.cpp +++ b/clang/lib/Analysis/Consumed.cpp @@ -89,9 +89,25 @@ static bool isTestingFunction(const FunctionDecl *FunDecl) { return FunDecl->hasAttr<TestsUnconsumedAttr>(); } +static ConsumedState mapConsumableAttrState(const QualType QT) { + assert(isConsumableType(QT)); + + const ConsumableAttr *CAttr = + QT->getAsCXXRecordDecl()->getAttr<ConsumableAttr>(); + + switch (CAttr->getDefaultState()) { + case ConsumableAttr::Unknown: + return CS_Unknown; + case ConsumableAttr::Unconsumed: + return CS_Unconsumed; + case ConsumableAttr::Consumed: + return CS_Consumed; + } + llvm_unreachable("invalid enum"); +} + static ConsumedState mapReturnTypestateAttrState(const ReturnTypestateAttr *RTSAttr) { - switch (RTSAttr->getState()) { case ReturnTypestateAttr::Unknown: return CS_Unknown; @@ -402,7 +418,7 @@ void ConsumedStmtVisitor::propagateReturnType(const Stmt *Call, ReturnState = mapReturnTypestateAttrState( Fun->getAttr<ReturnTypestateAttr>()); else - ReturnState = CS_Unknown; + ReturnState = mapConsumableAttrState(ReturnType); PropagationMap.insert(PairType(Call, PropagationInfo(ReturnState))); @@ -709,8 +725,18 @@ void ConsumedStmtVisitor::VisitMemberExpr(const MemberExpr *MExpr) { void ConsumedStmtVisitor::VisitParmVarDecl(const ParmVarDecl *Param) { - if (isConsumableType(Param->getType())) - StateMap->setState(Param, consumed::CS_Unknown); + QualType ParamType = Param->getType(); + ConsumedState ParamState = consumed::CS_None; + + if (!(ParamType->isPointerType() || ParamType->isReferenceType()) && + isConsumableType(ParamType)) + ParamState = mapConsumableAttrState(ParamType); + else if (ParamType->isReferenceType() && + isConsumableType(ParamType->getPointeeType())) + ParamState = consumed::CS_Unknown; + + if (ParamState) + StateMap->setState(Param, ParamState); } void ConsumedStmtVisitor::VisitReturnStmt(const ReturnStmt *Ret) { @@ -952,6 +978,35 @@ void ConsumedStateMap::remove(const VarDecl *Var) { Map.erase(Var); } +void ConsumedAnalyzer::determineExpectedReturnState(AnalysisDeclContext &AC, + const FunctionDecl *D) { + QualType ReturnType; + if (const CXXConstructorDecl *Constructor = dyn_cast<CXXConstructorDecl>(D)) { + ASTContext &CurrContext = AC.getASTContext(); + ReturnType = Constructor->getThisType(CurrContext)->getPointeeType(); + } else + ReturnType = D->getCallResultType(); + + if (D->hasAttr<ReturnTypestateAttr>()) { + const ReturnTypestateAttr *RTSAttr = D->getAttr<ReturnTypestateAttr>(); + + const CXXRecordDecl *RD = ReturnType->getAsCXXRecordDecl(); + if (!RD || !RD->hasAttr<ConsumableAttr>()) { + // 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. + WarningsHandler.warnReturnTypestateForUnconsumableType( + RTSAttr->getLocation(), ReturnType.getAsString()); + ExpectedReturnState = CS_None; + } else + ExpectedReturnState = mapReturnTypestateAttrState(RTSAttr); + } else if (isConsumableType(ReturnType)) + ExpectedReturnState = mapConsumableAttrState(ReturnType); + else + ExpectedReturnState = CS_None; +} + bool ConsumedAnalyzer::splitState(const CFGBlock *CurrBlock, const ConsumedStmtVisitor &Visitor) { @@ -1051,52 +1106,7 @@ void ConsumedAnalyzer::run(AnalysisDeclContext &AC) { 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; - } + determineExpectedReturnState(AC, D); BlockInfo = ConsumedBlockInfo(AC.getCFG()); diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp index 97d12d57dcb..39347fa3949 100644 --- a/clang/lib/Sema/SemaDeclAttr.cpp +++ b/clang/lib/Sema/SemaDeclAttr.cpp @@ -968,7 +968,30 @@ static void handleLocksExcludedAttr(Sema &S, Decl *D, } static void handleConsumableAttr(Sema &S, Decl *D, const AttributeList &Attr) { - if (!checkAttributeNumArgs(S, Attr, 0)) return; + if (!checkAttributeNumArgs(S, Attr, 1)) + return; + + ConsumableAttr::ConsumedState DefaultState; + + if (Attr.isArgIdent(0)) { + StringRef Param = Attr.getArgAsIdent(0)->Ident->getName(); + + if (Param == "unknown") + DefaultState = ConsumableAttr::Unknown; + else if (Param == "consumed") + DefaultState = ConsumableAttr::Consumed; + else if (Param == "unconsumed") + DefaultState = ConsumableAttr::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<CXXRecordDecl>(D)) { S.Diag(Attr.getLoc(), diag::warn_attribute_wrong_decl_type) << @@ -977,7 +1000,7 @@ static void handleConsumableAttr(Sema &S, Decl *D, const AttributeList &Attr) { } D->addAttr(::new (S.Context) - ConsumableAttr(Attr.getRange(), S.Context, + ConsumableAttr(Attr.getRange(), S.Context, DefaultState, Attr.getAttributeSpellingListIndex())); } diff --git a/clang/test/SemaCXX/warn-consumed-analysis-strict.cpp b/clang/test/SemaCXX/warn-consumed-analysis-strict.cpp index 1bc604de479..98d6894261b 100644 --- a/clang/test/SemaCXX/warn-consumed-analysis-strict.cpp +++ b/clang/test/SemaCXX/warn-consumed-analysis-strict.cpp @@ -1,9 +1,9 @@ // RUN: %clang_cc1 -fsyntax-only -verify -Wconsumed-strict -std=c++11 %s #define CALLABLE_WHEN_UNCONSUMED __attribute__ ((callable_when_unconsumed)) -#define CONSUMABLE __attribute__ ((consumable)) +#define CONSUMABLE(state) __attribute__ ((consumable(state))) #define CONSUMES __attribute__ ((consumes)) -#define RETURN_TYPESTATE(State) __attribute__ ((return_typestate(State))) +#define RETURN_TYPESTATE(state) __attribute__ ((return_typestate(state))) #define TESTS_UNCONSUMED __attribute__ ((tests_unconsumed)) #define TEST_VAR(Var) Var.isValid() @@ -11,15 +11,15 @@ typedef decltype(nullptr) nullptr_t; template <typename T> -class CONSUMABLE ConsumableClass { +class CONSUMABLE(unconsumed) ConsumableClass { T var; public: ConsumableClass(); 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 val); + ConsumableClass(ConsumableClass<T> &other); + ConsumableClass(ConsumableClass<T> &&other); ConsumableClass<T>& operator=(ConsumableClass<T> &other); ConsumableClass<T>& operator=(ConsumableClass<T> &&other); @@ -187,6 +187,10 @@ void testConstAndNonConstMemberFunctions() { *var; } +void testFunctionParam0(ConsumableClass<int> ¶m) { + *param; // expected-warning {{invocation of method 'operator*' on object 'param' while it is in an unknown state}} +} + void testNoWarnTestFromMacroExpansion() { ConsumableClass<int> var(42); @@ -195,10 +199,6 @@ void testNoWarnTestFromMacroExpansion() { } } -void testFunctionParam(ConsumableClass<int> param) { - *param; // expected-warning {{invocation of method 'operator*' on object 'param' while it is in an unknown state}} -} - void testSimpleForLoop() { ConsumableClass<int> var; diff --git a/clang/test/SemaCXX/warn-consumed-analysis.cpp b/clang/test/SemaCXX/warn-consumed-analysis.cpp index 53a29b128d9..8d4bff705c8 100644 --- a/clang/test/SemaCXX/warn-consumed-analysis.cpp +++ b/clang/test/SemaCXX/warn-consumed-analysis.cpp @@ -1,23 +1,23 @@ // RUN: %clang_cc1 -fsyntax-only -verify -Wconsumed -std=c++11 %s #define CALLABLE_WHEN_UNCONSUMED __attribute__ ((callable_when_unconsumed)) -#define CONSUMABLE __attribute__ ((consumable)) +#define CONSUMABLE(state) __attribute__ ((consumable(state))) #define CONSUMES __attribute__ ((consumes)) -#define RETURN_TYPESTATE(State) __attribute__ ((return_typestate(State))) +#define RETURN_TYPESTATE(state) __attribute__ ((return_typestate(state))) #define TESTS_UNCONSUMED __attribute__ ((tests_unconsumed)) typedef decltype(nullptr) nullptr_t; template <typename T> -class CONSUMABLE ConsumableClass { +class CONSUMABLE(unconsumed) ConsumableClass { T var; public: ConsumableClass(); 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(ConsumableClass<T> &other); + ConsumableClass(ConsumableClass<T> &&other); ConsumableClass<T>& operator=(ConsumableClass<T> &other); ConsumableClass<T>& operator=(ConsumableClass<T> &&other); @@ -49,7 +49,6 @@ void baf2(const ConsumableClass<int> *var); 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'}} } @@ -241,7 +240,7 @@ void testFunctionParam(ConsumableClass<int> param) { if (param.isValid()) { *param; } else { - *param; // expected-warning {{invocation of method 'operator*' on object 'param' while it is in the 'consumed' state}} + *param; } param = nullptr; diff --git a/clang/test/SemaCXX/warn-consumed-parsing.cpp b/clang/test/SemaCXX/warn-consumed-parsing.cpp index 4408dfc7d71..001fb866b89 100644 --- a/clang/test/SemaCXX/warn-consumed-parsing.cpp +++ b/clang/test/SemaCXX/warn-consumed-parsing.cpp @@ -1,9 +1,9 @@ // RUN: %clang_cc1 -fsyntax-only -verify -Wconsumed -std=c++11 %s #define CALLABLE_WHEN_UNCONSUMED __attribute__ ((callable_when_unconsumed)) -#define CONSUMABLE __attribute__ ((consumable)) +#define CONSUMABLE(state) __attribute__ ((consumable(state))) #define CONSUMES __attribute__ ((consumes)) -#define RETURN_TYPESTATE(State) __attribute__ ((return_typestate(State))) +#define RETURN_TYPESTATE(state) __attribute__ ((return_typestate(state))) #define TESTS_UNCONSUMED __attribute__ ((tests_unconsumed)) // FIXME: This test is here because the warning is issued by the Consumed @@ -25,15 +25,15 @@ class AttrTester0 { 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 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 function3() CONSUMABLE; // expected-warning {{'consumable' attribute only applies to classes}} +void function3() CONSUMABLE(consumed); // expected-warning {{'consumable' attribute only applies to classes}} -class CONSUMABLE AttrTester1 { +class CONSUMABLE(unknown) AttrTester1 { void callableWhenUnconsumed() CALLABLE_WHEN_UNCONSUMED; void consumes() CONSUMES; bool testsUnconsumed() TESTS_UNCONSUMED; @@ -47,3 +47,5 @@ class AttrTester2 { 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}} |