r192934 - Consumed analysis: Add param_typestate attribute, which specifies that
DeLesley Hutchins
delesley at google.com
Thu Oct 17 16:23:54 PDT 2013
Author: delesley
Date: Thu Oct 17 18:23:53 2013
New Revision: 192934
URL: http://llvm.org/viewvc/llvm-project?rev=192934&view=rev
Log:
Consumed analysis: Add param_typestate attribute, which specifies that
function parameters must be in a particular state. Patch by
chris.wailes at gmail.com. Reviewed by delesley at google.com.
Modified:
cfe/trunk/include/clang/Analysis/Analyses/Consumed.h
cfe/trunk/include/clang/Basic/Attr.td
cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td
cfe/trunk/lib/Analysis/Consumed.cpp
cfe/trunk/lib/Sema/AnalysisBasedWarnings.cpp
cfe/trunk/lib/Sema/SemaDeclAttr.cpp
cfe/trunk/test/SemaCXX/warn-consumed-analysis.cpp
Modified: cfe/trunk/include/clang/Analysis/Analyses/Consumed.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Analysis/Analyses/Consumed.h?rev=192934&r1=192933&r2=192934&view=diff
==============================================================================
--- cfe/trunk/include/clang/Analysis/Analyses/Consumed.h (original)
+++ cfe/trunk/include/clang/Analysis/Analyses/Consumed.h Thu Oct 17 18:23:53 2013
@@ -73,6 +73,11 @@ namespace consumed {
StringRef ExpectedState,
StringRef ObservedState) {};
+ // FIXME: Add documentation.
+ virtual void warnParamTypestateMismatch(SourceLocation LOC,
+ StringRef ExpectedState,
+ StringRef ObservedState) {}
+
// FIXME: This can be removed when the attr propagation fix for templated
// classes lands.
/// \brief Warn about return typestates set for unconsumable types.
Modified: cfe/trunk/include/clang/Basic/Attr.td
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/Attr.td?rev=192934&r1=192933&r2=192934&view=diff
==============================================================================
--- cfe/trunk/include/clang/Basic/Attr.td (original)
+++ cfe/trunk/include/clang/Basic/Attr.td Thu Oct 17 18:23:53 2013
@@ -967,6 +967,14 @@ def CallableWhen : InheritableAttr {
["Unknown", "Consumed", "Unconsumed"]>];
}
+def ParamTypestate : InheritableAttr {
+ let Spellings = [GNU<"param_typestate">];
+ let Subjects = [ParmVar];
+ let Args = [EnumArgument<"ParamState", "ConsumedState",
+ ["unknown", "consumed", "unconsumed"],
+ ["Unknown", "Consumed", "Unconsumed"]>];
+}
+
def ReturnTypestate : InheritableAttr {
let Spellings = [GNU<"return_typestate">];
let Subjects = [Function, ParmVar];
Modified: cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td?rev=192934&r1=192933&r2=192934&view=diff
==============================================================================
--- cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td (original)
+++ cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td Thu Oct 17 18:23:53 2013
@@ -2225,6 +2225,9 @@ def warn_loop_state_mismatch : Warning<
def warn_param_return_typestate_mismatch : Warning<
"parameter '%0' not in expected state when the function returns: expected "
"'%1', observed '%2'">, InGroup<Consumed>, DefaultIgnore;
+def warn_param_typestate_mismatch : Warning<
+ "argument not in expected state; expected '%0', observed '%1'">,
+ InGroup<Consumed>, DefaultIgnore;
def warn_impcast_vector_scalar : Warning<
"implicit conversion turns vector to scalar: %0 to %1">,
Modified: cfe/trunk/lib/Analysis/Consumed.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Analysis/Consumed.cpp?rev=192934&r1=192933&r2=192934&view=diff
==============================================================================
--- cfe/trunk/lib/Analysis/Consumed.cpp (original)
+++ cfe/trunk/lib/Analysis/Consumed.cpp Thu Oct 17 18:23:53 2013
@@ -180,13 +180,14 @@ static ConsumedState mapConsumableAttrSt
llvm_unreachable("invalid enum");
}
-static ConsumedState mapSetTypestateAttrState(const SetTypestateAttr *STAttr) {
- switch (STAttr->getNewState()) {
- case SetTypestateAttr::Unknown:
+static ConsumedState
+mapParamTypestateAttrState(const ParamTypestateAttr *PTAttr) {
+ switch (PTAttr->getParamState()) {
+ case ParamTypestateAttr::Unknown:
return CS_Unknown;
- case SetTypestateAttr::Unconsumed:
+ case ParamTypestateAttr::Unconsumed:
return CS_Unconsumed;
- case SetTypestateAttr::Consumed:
+ case ParamTypestateAttr::Consumed:
return CS_Consumed;
}
llvm_unreachable("invalid_enum");
@@ -205,6 +206,18 @@ mapReturnTypestateAttrState(const Return
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 StringRef stateToString(ConsumedState State) {
switch (State) {
case consumed::CS_None:
@@ -577,12 +590,33 @@ void ConsumedStmtVisitor::VisitCallExpr(
InfoEntry Entry = PropagationMap.find(Call->getArg(Index));
- if (Entry == PropagationMap.end() || !Entry->second.isVar()) {
+ if (Entry == PropagationMap.end() ||
+ !(Entry->second.isState() || Entry->second.isVar()))
continue;
- }
PropagationInfo PInfo = Entry->second;
+ // Check that the parameter is in the correct state.
+
+ if (Param->hasAttr<ParamTypestateAttr>()) {
+ ConsumedState ParamState =
+ PInfo.isState() ? PInfo.getState() :
+ StateMap->getState(PInfo.getVar());
+
+ ConsumedState ExpectedState =
+ mapParamTypestateAttrState(Param->getAttr<ParamTypestateAttr>());
+
+ if (ParamState != ExpectedState)
+ Analyzer.WarningsHandler.warnParamTypestateMismatch(
+ Call->getArg(Index - Offset)->getExprLoc(),
+ stateToString(ExpectedState), stateToString(ParamState));
+ }
+
+ if (!Entry->second.isVar())
+ continue;
+
+ // Adjust state on the caller side.
+
if (ParamType->isRValueReferenceType() ||
(ParamType->isLValueReferenceType() &&
!cast<LValueReferenceType>(*ParamType).isSpelledAsLValue())) {
@@ -812,15 +846,22 @@ void ConsumedStmtVisitor::VisitMemberExp
void ConsumedStmtVisitor::VisitParmVarDecl(const ParmVarDecl *Param) {
QualType ParamType = Param->getType();
ConsumedState ParamState = consumed::CS_None;
-
- if (!(ParamType->isPointerType() || ParamType->isReferenceType()) &&
- isConsumableType(ParamType))
+
+ if (Param->hasAttr<ParamTypestateAttr>()) {
+ ParamState =
+ mapParamTypestateAttrState(Param->getAttr<ParamTypestateAttr>());
+
+ } else if (!(ParamType->isPointerType() || ParamType->isReferenceType()) &&
+ isConsumableType(ParamType)) {
+
ParamState = mapConsumableAttrState(ParamType);
- else if (ParamType->isReferenceType() &&
- isConsumableType(ParamType->getPointeeType()))
+
+ } else if (ParamType->isReferenceType() &&
+ isConsumableType(ParamType->getPointeeType())) {
ParamState = consumed::CS_Unknown;
-
- if (ParamState)
+ }
+
+ if (ParamState != CS_None)
StateMap->setState(Param, ParamState);
}
Modified: cfe/trunk/lib/Sema/AnalysisBasedWarnings.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/AnalysisBasedWarnings.cpp?rev=192934&r1=192933&r2=192934&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/AnalysisBasedWarnings.cpp (original)
+++ cfe/trunk/lib/Sema/AnalysisBasedWarnings.cpp Thu Oct 17 18:23:53 2013
@@ -1496,6 +1496,15 @@ public:
Warnings.push_back(DelayedDiag(Warning, OptionalNotes()));
}
+ void warnParamTypestateMismatch(SourceLocation Loc, StringRef ExpectedState,
+ StringRef ObservedState) {
+
+ PartialDiagnosticAt Warning(Loc, S.PDiag(
+ diag::warn_param_typestate_mismatch) << ExpectedState << ObservedState);
+
+ Warnings.push_back(DelayedDiag(Warning, OptionalNotes()));
+ }
+
void warnReturnTypestateForUnconsumableType(SourceLocation Loc,
StringRef TypeName) {
PartialDiagnosticAt Warning(Loc, S.PDiag(
Modified: cfe/trunk/lib/Sema/SemaDeclAttr.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaDeclAttr.cpp?rev=192934&r1=192933&r2=192934&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaDeclAttr.cpp (original)
+++ cfe/trunk/lib/Sema/SemaDeclAttr.cpp Thu Oct 17 18:23:53 2013
@@ -1051,7 +1051,7 @@ static void handleCallableWhenAttr(Sema
return;
if (!CallableWhenAttr::ConvertStrToConsumedState(StateString,
- CallableState)) {
+ CallableState)) {
S.Diag(Loc, diag::warn_attribute_type_not_supported)
<< Attr.getName() << StateString;
return;
@@ -1066,6 +1066,52 @@ static void handleCallableWhenAttr(Sema
}
+static void handleParamTypestateAttr(Sema &S, Decl *D,
+ const AttributeList &Attr) {
+ if (!checkAttributeNumArgs(S, Attr, 1)) return;
+
+ if (!isa<ParmVarDecl>(D)) {
+ S.Diag(Attr.getLoc(), diag::warn_attribute_wrong_decl_type) <<
+ Attr.getName() << ExpectedParameter;
+ return;
+ }
+
+ ParamTypestateAttr::ConsumedState ParamState;
+
+ if (Attr.isArgIdent(0)) {
+ IdentifierLoc *Ident = Attr.getArgAsIdent(0);
+ StringRef StateString = Ident->Ident->getName();
+
+ if (!ParamTypestateAttr::ConvertStrToConsumedState(StateString,
+ ParamState)) {
+ S.Diag(Ident->Loc, diag::warn_attribute_type_not_supported)
+ << Attr.getName() << StateString;
+ return;
+ }
+ } else {
+ S.Diag(Attr.getLoc(), diag::err_attribute_argument_type) <<
+ Attr.getName() << AANT_ArgumentIdentifier;
+ 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 = cast<ParmVarDecl>(D)->getType();
+ //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)
+ ParamTypestateAttr(Attr.getRange(), S.Context, ParamState,
+ Attr.getAttributeSpellingListIndex()));
+}
+
+
static void handleReturnTypestateAttr(Sema &S, Decl *D,
const AttributeList &Attr) {
if (!checkAttributeNumArgs(S, Attr, 1)) return;
@@ -4818,6 +4864,9 @@ static void ProcessDeclAttribute(Sema &S
case AttributeList::AT_CallableWhen:
handleCallableWhenAttr(S, D, Attr);
break;
+ case AttributeList::AT_ParamTypestate:
+ handleParamTypestateAttr(S, D, Attr);
+ break;
case AttributeList::AT_ReturnTypestate:
handleReturnTypestateAttr(S, D, Attr);
break;
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=192934&r1=192933&r2=192934&view=diff
==============================================================================
--- cfe/trunk/test/SemaCXX/warn-consumed-analysis.cpp (original)
+++ cfe/trunk/test/SemaCXX/warn-consumed-analysis.cpp Thu Oct 17 18:23:53 2013
@@ -4,8 +4,9 @@
#define CALLABLE_WHEN(...) __attribute__ ((callable_when(__VA_ARGS__)))
#define CONSUMABLE(state) __attribute__ ((consumable(state)))
-#define SET_TYPESTATE(state) __attribute__ ((set_typestate(state)))
+#define PARAM_TYPESTATE(state) __attribute__ ((param_typestate(state)))
#define RETURN_TYPESTATE(state) __attribute__ ((return_typestate(state)))
+#define SET_TYPESTATE(state) __attribute__ ((set_typestate(state)))
#define TESTS_TYPESTATE(state) __attribute__ ((tests_typestate(state)))
typedef decltype(nullptr) nullptr_t;
@@ -406,6 +407,19 @@ void testParamReturnTypestateCaller() {
*var;
}
+void testParamTypestateCallee(ConsumableClass<int> Param0 PARAM_TYPESTATE(consumed),
+ ConsumableClass<int> &Param1 PARAM_TYPESTATE(consumed)) {
+
+ *Param0; // expected-warning {{invalid invocation of method 'operator*' on object 'Param0' while it is in the 'consumed' state}}
+ *Param1; // expected-warning {{invalid invocation of method 'operator*' on object 'Param1' while it is in the 'consumed' state}}
+}
+
+void testParamTypestateCaller() {
+ ConsumableClass<int> Var0, Var1(42);
+
+ testParamTypestateCallee(Var0, Var1); // expected-warning {{argument not in expected state; expected 'consumed', observed 'unconsumed'}}
+}
+
void testCallingConventions() {
ConsumableClass<int> var(42);
More information about the cfe-commits
mailing list