ConsumedState ExpectedReturnState;
+ void determineExpectedReturnState(AnalysisDeclContext &AC,
+ const FunctionDecl *D);
bool hasConsumableAttributes(const CXXRecordDecl *RD);
bool splitState(const CFGBlock *CurrBlock,
const ConsumedStmtVisitor &Visitor);
def Consumable : InheritableAttr {
let Spellings = [GNU<"consumable">];
let Subjects = [CXXRecord];
+ let Args = [EnumArgument<"DefaultState", "ConsumedState",
+ ["unknown", "consumed", "unconsumed"],
+ ["Unknown", "Consumed", "Unconsumed"]>];
}
def CallableWhenUnconsumed : InheritableAttr {
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;
ReturnState = mapReturnTypestateAttrState(
Fun->getAttr<ReturnTypestateAttr>());
else
- ReturnState = CS_Unknown;
+ ReturnState = mapConsumableAttrState(ReturnType);
PropagationMap.insert(PairType(Call,
PropagationInfo(ReturnState)));
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) {
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) {
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());
}
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) <<
}
D->addAttr(::new (S.Context)
- ConsumableAttr(Attr.getRange(), S.Context,
+ ConsumableAttr(Attr.getRange(), S.Context, DefaultState,
Attr.getAttributeSpellingListIndex()));
}
// 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()
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);
*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);
}
}
-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;
// 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);
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'}}
}
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;
// 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
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;
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}}