r191983 - Consumed Analysis: Change callable_when so that it can take a list of states
DeLesley Hutchins
delesley at google.com
Fri Oct 4 14:28:06 PDT 2013
Author: delesley
Date: Fri Oct 4 16:28:06 2013
New Revision: 191983
URL: http://llvm.org/viewvc/llvm-project?rev=191983&view=rev
Log:
Consumed Analysis: Change callable_when so that it can take a list of states
that a function can be called in. This reduced the total number of annotations
needed and makes writing more complicated behaviour less burdensome.
Patch by chriswails at gmail.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-strict.cpp
cfe/trunk/test/SemaCXX/warn-consumed-analysis.cpp
cfe/trunk/test/SemaCXX/warn-consumed-parsing.cpp
cfe/trunk/utils/TableGen/ClangAttrEmitter.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=191983&r1=191982&r2=191983&view=diff
==============================================================================
--- cfe/trunk/include/clang/Analysis/Analyses/Consumed.h (original)
+++ cfe/trunk/include/clang/Analysis/Analyses/Consumed.h Fri Oct 4 16:28:06 2013
@@ -25,6 +25,15 @@
namespace clang {
namespace consumed {
+ enum ConsumedState {
+ // No state information for the given variable.
+ CS_None,
+
+ CS_Unknown,
+ CS_Unconsumed,
+ CS_Consumed
+ };
+
class ConsumedStmtVisitor;
typedef SmallVector<PartialDiagnosticAt, 1> OptionalNotes;
@@ -71,52 +80,29 @@ namespace consumed {
/// \param MethodName -- The name of the method that was incorrectly
/// invoked.
///
- /// \param Loc -- The SourceLocation of the method invocation.
- virtual void warnUseOfTempWhileConsumed(StringRef MethodName,
- SourceLocation Loc) {}
-
- /// \brief Warn about use-in-unknown-state errors.
- /// \param MethodName -- The name of the method that was incorrectly
- /// invoked.
+ /// \param State -- The state the object was used in.
///
/// \param Loc -- The SourceLocation of the method invocation.
- virtual void warnUseOfTempInUnknownState(StringRef MethodName,
+ virtual void warnUseOfTempInInvalidState(StringRef MethodName,
+ StringRef State,
SourceLocation Loc) {}
/// \brief 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.
- virtual void warnUseWhileConsumed(StringRef MethodName,
- StringRef VariableName,
- SourceLocation Loc) {}
-
- /// \brief Warn about use-in-unknown-state errors.
- /// \param MethodName -- The name of the method that was incorrectly
- /// invoked.
+ /// \param State -- The state the object was used in.
///
/// \param VariableName -- The name of the variable that holds the unique
/// value.
///
/// \param Loc -- The SourceLocation of the method invocation.
- virtual void warnUseInUnknownState(StringRef MethodName,
+ virtual void warnUseInInvalidState(StringRef MethodName,
StringRef VariableName,
+ StringRef State,
SourceLocation Loc) {}
};
- enum ConsumedState {
- // No state information for the given variable.
- CS_None,
-
- CS_Unknown,
- CS_Unconsumed,
- CS_Consumed
- };
-
class ConsumedStateMap {
typedef llvm::DenseMap<const VarDecl *, ConsumedState> MapType;
Modified: cfe/trunk/include/clang/Basic/Attr.td
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/Attr.td?rev=191983&r1=191982&r2=191983&view=diff
==============================================================================
--- cfe/trunk/include/clang/Basic/Attr.td (original)
+++ cfe/trunk/include/clang/Basic/Attr.td Fri Oct 4 16:28:06 2013
@@ -81,6 +81,15 @@ class EnumArgument<string name, string t
list<string> Enums = enums;
}
+// FIXME: There should be a VariadicArgument type that takes any other type
+// of argument and generates the appropriate type.
+class VariadicEnumArgument<string name, string type, list<string> values,
+ list<string> enums> : Argument<name, 1> {
+ string Type = type;
+ list<string> Values = values;
+ list<string> Enums = enums;
+}
+
// This handles one spelling of an attribute.
class Spelling<string name, string variety> {
string Name = name;
@@ -950,9 +959,12 @@ def Consumable : InheritableAttr {
["Unknown", "Consumed", "Unconsumed"]>];
}
-def CallableWhenUnconsumed : InheritableAttr {
- let Spellings = [GNU<"callable_when_unconsumed">];
+def CallableWhen : InheritableAttr {
+ let Spellings = [GNU<"callable_when">];
let Subjects = [CXXMethod];
+ let Args = [VariadicEnumArgument<"CallableState", "ConsumedState",
+ ["unknown", "consumed", "unconsumed"],
+ ["Unknown", "Consumed", "Unconsumed"]>];
}
def TestsUnconsumed : InheritableAttr {
Modified: cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td?rev=191983&r1=191982&r2=191983&view=diff
==============================================================================
--- cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td (original)
+++ cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td Fri Oct 4 16:28:06 2013
@@ -2195,12 +2195,12 @@ def warn_thread_safety_beta : Warning<
"Thread safety beta warning.">, InGroup<ThreadSafetyBeta>, DefaultIgnore;
// Consumed warnings
-def warn_use_while_consumed : Warning<
- "invocation of method '%0' on object '%1' while it is in the 'consumed' "
+def warn_use_in_invalid_state : Warning<
+ "invalid invocation of method '%0' on object '%1' while it is in the '%2' "
"state">, InGroup<Consumed>, DefaultIgnore;
-def warn_use_of_temp_while_consumed : Warning<
- "invocation of method '%0' on a temporary object while it is in the "
- "'consumed' state">, InGroup<Consumed>, DefaultIgnore;
+def warn_use_of_temp_in_invalid_state : Warning<
+ "invalid invocation of method '%0' on a temporary object while it is in the "
+ "'%1' state">, InGroup<Consumed>, DefaultIgnore;
def warn_attr_on_unconsumable_class : Warning<
"consumed analysis attribute is attached to member of class '%0' which isn't "
"marked as consumable">, InGroup<Consumed>, DefaultIgnore;
@@ -2212,12 +2212,6 @@ def warn_return_typestate_mismatch : War
InGroup<Consumed>, DefaultIgnore;
// ConsumedStrict warnings
-def warn_use_in_unknown_state : Warning<
- "invocation of method '%0' on object '%1' while it is in an unknown state">,
- InGroup<ConsumedStrict>, DefaultIgnore;
-def warn_use_of_temp_in_unknown_state : Warning<
- "invocation of method '%0' on a temporary object while it is in an unknown "
- "state">, InGroup<ConsumedStrict>, DefaultIgnore;
def warn_unnecessary_test : Warning<
"unnecessary test. Variable '%0' is known to be in the '%1' state">,
InGroup<ConsumedStrict>, DefaultIgnore;
Modified: cfe/trunk/lib/Analysis/Consumed.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Analysis/Consumed.cpp?rev=191983&r1=191982&r2=191983&view=diff
==============================================================================
--- cfe/trunk/lib/Analysis/Consumed.cpp (original)
+++ cfe/trunk/lib/Analysis/Consumed.cpp Fri Oct 4 16:28:06 2013
@@ -33,6 +33,8 @@
// TODO: Add notes about the actual and expected state for
// TODO: Correctly identify unreachable blocks when chaining boolean operators.
+// TODO: Adjust the parser and AttributesList class to support lists of
+// identifiers.
// TODO: Warn about unreachable code.
// TODO: Switch to using a bitmap to track unreachable blocks.
// TODO: Mark variables as Unknown going into while- or for-loops only if they
@@ -66,6 +68,37 @@ static ConsumedState invertConsumedUncon
llvm_unreachable("invalid enum");
}
+static bool isCallableInState(const CallableWhenAttr *CWAttr,
+ ConsumedState State) {
+
+ CallableWhenAttr::callableState_iterator I = CWAttr->callableState_begin(),
+ E = CWAttr->callableState_end();
+
+ for (; I != E; ++I) {
+
+ ConsumedState MappedAttrState = CS_None;
+
+ switch (*I) {
+ case CallableWhenAttr::Unknown:
+ MappedAttrState = CS_Unknown;
+ break;
+
+ case CallableWhenAttr::Unconsumed:
+ MappedAttrState = CS_Unconsumed;
+ break;
+
+ case CallableWhenAttr::Consumed:
+ MappedAttrState = CS_Consumed;
+ break;
+ }
+
+ if (MappedAttrState == State)
+ return true;
+ }
+
+ return false;
+}
+
static bool isConsumableType(const QualType &QT) {
if (const CXXRecordDecl *RD = QT->getAsCXXRecordDecl())
return RD->hasAttr<ConsumableAttr>();
@@ -174,6 +207,8 @@ class PropagationInfo {
BinTestTy BinTest;
};
+ QualType TempType;
+
public:
PropagationInfo() : InfoType(IT_None) {}
@@ -208,7 +243,9 @@ public:
BinTest.RTest.TestsFor = RTestsFor;
}
- PropagationInfo(ConsumedState State) : InfoType(IT_State), State(State) {}
+ PropagationInfo(ConsumedState State, QualType TempType)
+ : InfoType(IT_State), State(State), TempType(TempType) {}
+
PropagationInfo(const VarDecl *Var) : InfoType(IT_Var), Var(Var) {}
const ConsumedState & getState() const {
@@ -216,6 +253,11 @@ public:
return State;
}
+ const QualType & getTempType() const {
+ assert(InfoType == IT_State);
+ return TempType;
+ }
+
const VarTestResult & getTest() const {
assert(InfoType == IT_Test);
return Test;
@@ -327,51 +369,38 @@ public:
}
};
-// TODO: When we support CallableWhenConsumed this will have to check for
-// the different attributes and change the behavior bellow. (Deferred)
void ConsumedStmtVisitor::checkCallability(const PropagationInfo &PInfo,
const FunctionDecl *FunDecl,
const CallExpr *Call) {
- if (!FunDecl->hasAttr<CallableWhenUnconsumedAttr>()) return;
+ if (!FunDecl->hasAttr<CallableWhenAttr>())
+ return;
+
+ const CallableWhenAttr *CWAttr = FunDecl->getAttr<CallableWhenAttr>();
if (PInfo.isVar()) {
const VarDecl *Var = PInfo.getVar();
+ ConsumedState VarState = StateMap->getState(Var);
- switch (StateMap->getState(Var)) {
- case CS_Consumed:
- Analyzer.WarningsHandler.warnUseWhileConsumed(
- FunDecl->getNameAsString(), Var->getNameAsString(),
- Call->getExprLoc());
- break;
+ assert(VarState != CS_None && "Invalid state");
- case CS_Unknown:
- Analyzer.WarningsHandler.warnUseInUnknownState(
- FunDecl->getNameAsString(), Var->getNameAsString(),
- Call->getExprLoc());
- break;
-
- case CS_None:
- case CS_Unconsumed:
- break;
- }
+ if (isCallableInState(CWAttr, VarState))
+ return;
- } else {
- switch (PInfo.getState()) {
- case CS_Consumed:
- Analyzer.WarningsHandler.warnUseOfTempWhileConsumed(
- FunDecl->getNameAsString(), Call->getExprLoc());
- break;
+ Analyzer.WarningsHandler.warnUseInInvalidState(
+ FunDecl->getNameAsString(), Var->getNameAsString(),
+ stateToString(VarState), Call->getExprLoc());
- case CS_Unknown:
- Analyzer.WarningsHandler.warnUseOfTempInUnknownState(
- FunDecl->getNameAsString(), Call->getExprLoc());
- break;
-
- case CS_None:
- case CS_Unconsumed:
- break;
- }
+ } else if (PInfo.isState()) {
+
+ assert(PInfo.getState() != CS_None && "Invalid state");
+
+ if (isCallableInState(CWAttr, PInfo.getState()))
+ return;
+
+ Analyzer.WarningsHandler.warnUseOfTempInInvalidState(
+ FunDecl->getNameAsString(), stateToString(PInfo.getState()),
+ Call->getExprLoc());
}
}
@@ -421,7 +450,7 @@ void ConsumedStmtVisitor::propagateRetur
ReturnState = mapConsumableAttrState(ReturnType);
PropagationMap.insert(PairType(Call,
- PropagationInfo(ReturnState)));
+ PropagationInfo(ReturnState, ReturnType)));
}
}
@@ -522,7 +551,11 @@ void ConsumedStmtVisitor::VisitCallExpr(
}
}
- propagateReturnType(Call, FunDecl, FunDecl->getCallResultType());
+ QualType RetType = FunDecl->getCallResultType();
+ if (RetType->isReferenceType())
+ RetType = RetType->getPointeeType();
+
+ propagateReturnType(Call, FunDecl, RetType);
}
}
@@ -540,7 +573,7 @@ void ConsumedStmtVisitor::VisitCXXConstr
if (Constructor->isDefaultConstructor()) {
PropagationMap.insert(PairType(Call,
- PropagationInfo(consumed::CS_Consumed)));
+ PropagationInfo(consumed::CS_Consumed, ThisType)));
} else if (Constructor->isMoveConstructor()) {
@@ -551,7 +584,7 @@ void ConsumedStmtVisitor::VisitCXXConstr
const VarDecl* Var = PInfo.getVar();
PropagationMap.insert(PairType(Call,
- PropagationInfo(StateMap->getState(Var))));
+ PropagationInfo(StateMap->getState(Var), ThisType)));
StateMap->setState(Var, consumed::CS_Consumed);
@@ -630,7 +663,8 @@ void ConsumedStmtVisitor::VisitCXXOperat
} else if (!LPInfo.isVar() && RPInfo.isVar()) {
PropagationMap.insert(PairType(Call,
- PropagationInfo(StateMap->getState(RPInfo.getVar()))));
+ PropagationInfo(StateMap->getState(RPInfo.getVar()),
+ LPInfo.getTempType())));
StateMap->setState(RPInfo.getVar(), consumed::CS_Consumed);
@@ -648,27 +682,16 @@ void ConsumedStmtVisitor::VisitCXXOperat
PropagationMap.insert(PairType(Call, LPInfo));
- } else {
+ } else if (LPInfo.isState()) {
PropagationMap.insert(PairType(Call,
- PropagationInfo(consumed::CS_Unknown)));
+ PropagationInfo(consumed::CS_Unknown, LPInfo.getTempType())));
}
} else if (LEntry == PropagationMap.end() &&
REntry != PropagationMap.end()) {
- RPInfo = REntry->second;
-
- if (RPInfo.isVar()) {
- const VarDecl *Var = RPInfo.getVar();
-
- PropagationMap.insert(PairType(Call,
- PropagationInfo(StateMap->getState(Var))));
-
- StateMap->setState(Var, consumed::CS_Consumed);
-
- } else {
- PropagationMap.insert(PairType(Call, RPInfo));
- }
+ if (REntry->second.isVar())
+ StateMap->setState(REntry->second.getVar(), consumed::CS_Consumed);
}
} else {
@@ -776,6 +799,7 @@ void ConsumedStmtVisitor::VisitUnaryOper
}
}
+// TODO: See if I need to check for reference types here.
void ConsumedStmtVisitor::VisitVarDecl(const VarDecl *Var) {
if (isConsumableType(Var->getType())) {
if (Var->hasInit()) {
@@ -803,13 +827,12 @@ void splitVarStateForIf(const IfStmt * I
if (VarState == CS_Unknown) {
ThenStates->setState(Test.Var, Test.TestsFor);
- if (ElseStates)
- ElseStates->setState(Test.Var, invertConsumedUnconsumed(Test.TestsFor));
+ ElseStates->setState(Test.Var, invertConsumedUnconsumed(Test.TestsFor));
} else if (VarState == invertConsumedUnconsumed(Test.TestsFor)) {
ThenStates->markUnreachable();
- } else if (VarState == Test.TestsFor && ElseStates) {
+ } else if (VarState == Test.TestsFor) {
ElseStates->markUnreachable();
}
}
@@ -832,31 +855,27 @@ void splitVarStateForIfBinOp(const Propa
ThenStates->markUnreachable();
} else if (LState == LTest.TestsFor && isKnownState(RState)) {
- if (RState == RTest.TestsFor) {
- if (ElseStates)
- ElseStates->markUnreachable();
- } else {
+ if (RState == RTest.TestsFor)
+ ElseStates->markUnreachable();
+ else
ThenStates->markUnreachable();
- }
}
} else {
- if (LState == CS_Unknown && ElseStates) {
+ if (LState == CS_Unknown) {
ElseStates->setState(LTest.Var,
invertConsumedUnconsumed(LTest.TestsFor));
- } else if (LState == LTest.TestsFor && ElseStates) {
+ } else if (LState == LTest.TestsFor) {
ElseStates->markUnreachable();
} else if (LState == invertConsumedUnconsumed(LTest.TestsFor) &&
isKnownState(RState)) {
- if (RState == RTest.TestsFor) {
- if (ElseStates)
- ElseStates->markUnreachable();
- } else {
+ if (RState == RTest.TestsFor)
+ ElseStates->markUnreachable();
+ else
ThenStates->markUnreachable();
- }
}
}
}
@@ -868,7 +887,7 @@ void splitVarStateForIfBinOp(const Propa
else if (RState == invertConsumedUnconsumed(RTest.TestsFor))
ThenStates->markUnreachable();
- } else if (ElseStates) {
+ } else {
if (RState == CS_Unknown)
ElseStates->setState(RTest.Var,
invertConsumedUnconsumed(RTest.TestsFor));
@@ -1016,7 +1035,6 @@ bool ConsumedAnalyzer::splitState(const
if (const IfStmt *IfNode =
dyn_cast_or_null<IfStmt>(CurrBlock->getTerminator().getStmt())) {
- bool HasElse = IfNode->getElse() != NULL;
const Stmt *Cond = IfNode->getCond();
PInfo = Visitor.getInfo(Cond);
@@ -1026,15 +1044,12 @@ bool ConsumedAnalyzer::splitState(const
if (PInfo.isTest()) {
CurrStates->setSource(Cond);
FalseStates->setSource(Cond);
-
- splitVarStateForIf(IfNode, PInfo.getTest(), CurrStates,
- HasElse ? FalseStates : NULL);
+ splitVarStateForIf(IfNode, PInfo.getTest(), CurrStates, FalseStates);
} else if (PInfo.isBinTest()) {
CurrStates->setSource(PInfo.testSourceNode());
FalseStates->setSource(PInfo.testSourceNode());
-
- splitVarStateForIfBinOp(PInfo, CurrStates, HasElse ? FalseStates : NULL);
+ splitVarStateForIfBinOp(PInfo, CurrStates, FalseStates);
} else {
delete FalseStates;
Modified: cfe/trunk/lib/Sema/AnalysisBasedWarnings.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/AnalysisBasedWarnings.cpp?rev=191983&r1=191982&r2=191983&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/AnalysisBasedWarnings.cpp (original)
+++ cfe/trunk/lib/Sema/AnalysisBasedWarnings.cpp Fri Oct 4 16:28:06 2013
@@ -1503,36 +1503,20 @@ public:
Warnings.push_back(DelayedDiag(Warning, OptionalNotes()));
}
- void warnUseOfTempWhileConsumed(StringRef MethodName, SourceLocation Loc) {
+ void warnUseOfTempInInvalidState(StringRef MethodName, StringRef State,
+ SourceLocation Loc) {
PartialDiagnosticAt Warning(Loc, S.PDiag(
- diag::warn_use_of_temp_while_consumed) << MethodName);
+ diag::warn_use_of_temp_in_invalid_state) << MethodName << State);
Warnings.push_back(DelayedDiag(Warning, OptionalNotes()));
}
- void warnUseOfTempInUnknownState(StringRef MethodName, SourceLocation Loc) {
+ void warnUseInInvalidState(StringRef MethodName, StringRef VariableName,
+ StringRef State, SourceLocation Loc) {
- PartialDiagnosticAt Warning(Loc, S.PDiag(
- diag::warn_use_of_temp_in_unknown_state) << MethodName);
-
- Warnings.push_back(DelayedDiag(Warning, OptionalNotes()));
- }
-
- void warnUseWhileConsumed(StringRef MethodName, StringRef VariableName,
- SourceLocation Loc) {
-
- PartialDiagnosticAt Warning(Loc, S.PDiag(diag::warn_use_while_consumed) <<
- MethodName << VariableName);
-
- Warnings.push_back(DelayedDiag(Warning, OptionalNotes()));
- }
-
- void warnUseInUnknownState(StringRef MethodName, StringRef VariableName,
- SourceLocation Loc) {
-
- PartialDiagnosticAt Warning(Loc, S.PDiag(diag::warn_use_in_unknown_state) <<
- MethodName << VariableName);
+ PartialDiagnosticAt Warning(Loc, S.PDiag(diag::warn_use_in_invalid_state) <<
+ MethodName << VariableName << State);
Warnings.push_back(DelayedDiag(Warning, OptionalNotes()));
}
@@ -1570,7 +1554,7 @@ clang::sema::AnalysisBasedWarnings::Anal
(D.getDiagnosticLevel(diag::warn_double_lock, SourceLocation()) !=
DiagnosticsEngine::Ignored);
DefaultPolicy.enableConsumedAnalysis = (unsigned)
- (D.getDiagnosticLevel(diag::warn_use_while_consumed, SourceLocation()) !=
+ (D.getDiagnosticLevel(diag::warn_use_in_invalid_state, SourceLocation()) !=
DiagnosticsEngine::Ignored);
}
Modified: cfe/trunk/lib/Sema/SemaDeclAttr.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaDeclAttr.cpp?rev=191983&r1=191982&r2=191983&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaDeclAttr.cpp (original)
+++ cfe/trunk/lib/Sema/SemaDeclAttr.cpp Fri Oct 4 16:28:06 2013
@@ -1041,8 +1041,11 @@ static void handleConsumesAttr(Sema &S,
Attr.getAttributeSpellingListIndex()));
}
-static void handleCallableWhenUnconsumedAttr(Sema &S, Decl *D,
- const AttributeList &Attr) {
+static void handleCallableWhenAttr(Sema &S, Decl *D,
+ const AttributeList &Attr) {
+
+ if (!checkAttributeAtLeastNumArgs(S, Attr, 1)) return;
+
if (!isa<CXXMethodDecl>(D)) {
S.Diag(Attr.getLoc(), diag::warn_attribute_wrong_decl_type) <<
Attr.getName() << ExpectedMethod;
@@ -1052,9 +1055,34 @@ static void handleCallableWhenUnconsumed
if (!checkForConsumableClass(S, cast<CXXMethodDecl>(D), Attr))
return;
+ SmallVector<CallableWhenAttr::ConsumedState, 3> States;
+ for (unsigned ArgIndex = 0; ArgIndex < Attr.getNumArgs(); ++ArgIndex) {
+ CallableWhenAttr::ConsumedState CallableState;
+
+ if (Attr.isArgExpr(ArgIndex) &&
+ isa<StringLiteral>(Attr.getArgAsExpr(ArgIndex))) {
+
+ Expr *Arg = Attr.getArgAsExpr(ArgIndex);
+ StringRef StateString = cast<StringLiteral>(Arg)->getString();
+
+ if (!CallableWhenAttr::ConvertStrToConsumedState(StateString,
+ CallableState)) {
+ S.Diag(Arg->getExprLoc(), diag::warn_attribute_type_not_supported)
+ << Attr.getName() << StateString;
+ return;
+ }
+
+ States.push_back(CallableState);
+ } else {
+ S.Diag(Attr.getLoc(), diag::err_attribute_argument_type) << Attr.getName()
+ << AANT_ArgumentString;
+ return;
+ }
+ }
+
D->addAttr(::new (S.Context)
- CallableWhenUnconsumedAttr(Attr.getRange(), S.Context,
- Attr.getAttributeSpellingListIndex()));
+ CallableWhenAttr(Attr.getRange(), S.Context, States.data(),
+ States.size(), Attr.getAttributeSpellingListIndex()));
}
static void handleTestsConsumedAttr(Sema &S, Decl *D,
@@ -4767,8 +4795,8 @@ static void ProcessDeclAttribute(Sema &S
case AttributeList::AT_Consumes:
handleConsumesAttr(S, D, Attr);
break;
- case AttributeList::AT_CallableWhenUnconsumed:
- handleCallableWhenUnconsumedAttr(S, D, Attr);
+ case AttributeList::AT_CallableWhen:
+ handleCallableWhenAttr(S, D, Attr);
break;
case AttributeList::AT_TestsConsumed:
handleTestsConsumedAttr(S, D, Attr);
Modified: cfe/trunk/test/SemaCXX/warn-consumed-analysis-strict.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaCXX/warn-consumed-analysis-strict.cpp?rev=191983&r1=191982&r2=191983&view=diff
==============================================================================
--- cfe/trunk/test/SemaCXX/warn-consumed-analysis-strict.cpp (original)
+++ cfe/trunk/test/SemaCXX/warn-consumed-analysis-strict.cpp Fri Oct 4 16:28:06 2013
@@ -1,10 +1,10 @@
// RUN: %clang_cc1 -fsyntax-only -verify -Wconsumed-strict -std=c++11 %s
-#define CALLABLE_WHEN_UNCONSUMED __attribute__ ((callable_when_unconsumed))
-#define CONSUMABLE(state) __attribute__ ((consumable(state)))
-#define CONSUMES __attribute__ ((consumes))
-#define RETURN_TYPESTATE(state) __attribute__ ((return_typestate(state)))
-#define TESTS_UNCONSUMED __attribute__ ((tests_unconsumed))
+#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_UNCONSUMED __attribute__ ((tests_unconsumed))
#define TEST_VAR(Var) Var.isValid()
@@ -32,8 +32,8 @@ class CONSUMABLE(unconsumed) ConsumableC
ConsumableClass<T>& operator=(ConsumableClass<U> &&other);
void operator()(int a) CONSUMES;
- void operator*() const CALLABLE_WHEN_UNCONSUMED;
- void unconsumedCall() const CALLABLE_WHEN_UNCONSUMED;
+ void operator*() const CALLABLE_WHEN("unconsumed");
+ void unconsumedCall() const CALLABLE_WHEN("unconsumed");
bool isValid() const TESTS_UNCONSUMED;
operator bool() const TESTS_UNCONSUMED;
@@ -45,9 +45,6 @@ class CONSUMABLE(unconsumed) ConsumableC
void consume() CONSUMES;
};
-void baf0(ConsumableClass<int> &var);
-void baf1(ConsumableClass<int> *var);
-
void testIfStmt() {
ConsumableClass<int> var;
@@ -60,137 +57,6 @@ void testIfStmt() {
}
}
-void testComplexConditionals() {
- ConsumableClass<int> var0, var1, var2;
-
- // Coerce all variables into the unknown state.
- baf0(var0);
- baf0(var1);
- baf0(var2);
-
- if (var0 && var1) {
- *var0;
- *var1;
-
- } else {
- *var0; // expected-warning {{invocation of method 'operator*' on object 'var0' while it is in an unknown state}}
- *var1; // expected-warning {{invocation of method 'operator*' on object 'var1' while it is in an unknown state}}
- }
-
- if (var0 || var1) {
- *var0; // expected-warning {{invocation of method 'operator*' on object 'var0' while it is in an unknown state}}
- *var1; // expected-warning {{invocation of method 'operator*' on object 'var1' while it is in an unknown state}}
-
- } else {
- *var0; // expected-warning {{invocation of method 'operator*' on object 'var0' while it is in the 'consumed' state}}
- *var1; // expected-warning {{invocation of method 'operator*' on object 'var1' while it is in the 'consumed' state}}
- }
-
- if (var0 && !var1) {
- *var0;
- *var1; // expected-warning {{invocation of method 'operator*' on object 'var1' while it is in the 'consumed' state}}
-
- } else {
- *var0; // expected-warning {{invocation of method 'operator*' on object 'var0' while it is in an unknown state}}
- *var1; // expected-warning {{invocation of method 'operator*' on object 'var1' while it is in an unknown state}}
- }
-
- if (var0 || !var1) {
- *var0; // expected-warning {{invocation of method 'operator*' on object 'var0' while it is in an unknown state}}
- *var1; // expected-warning {{invocation of method 'operator*' on object 'var1' while it is in an unknown state}}
-
- } else {
- *var0; // expected-warning {{invocation of method 'operator*' on object 'var0' while it is in the 'consumed' state}}
- *var1;
- }
-
- if (!var0 && !var1) {
- *var0; // expected-warning {{invocation of method 'operator*' on object 'var0' while it is in the 'consumed' state}}
- *var1; // expected-warning {{invocation of method 'operator*' on object 'var1' while it is in the 'consumed' state}}
-
- } else {
- *var0; // expected-warning {{invocation of method 'operator*' on object 'var0' while it is in an unknown state}}
- *var1; // expected-warning {{invocation of method 'operator*' on object 'var1' while it is in an unknown state}}
- }
-
- if (!(var0 || var1)) {
- *var0; // expected-warning {{invocation of method 'operator*' on object 'var0' while it is in the 'consumed' state}}
- *var1; // expected-warning {{invocation of method 'operator*' on object 'var1' while it is in the 'consumed' state}}
-
- } else {
- *var0; // expected-warning {{invocation of method 'operator*' on object 'var0' while it is in an unknown state}}
- *var1; // expected-warning {{invocation of method 'operator*' on object 'var1' while it is in an unknown state}}
- }
-
- if (!var0 || !var1) {
- *var0; // expected-warning {{invocation of method 'operator*' on object 'var0' while it is in an unknown state}}
- *var1; // expected-warning {{invocation of method 'operator*' on object 'var1' while it is in an unknown state}}
-
- } else {
- *var0;
- *var1;
- }
-
- if (!(var0 && var1)) {
- *var0; // expected-warning {{invocation of method 'operator*' on object 'var0' while it is in an unknown state}}
- *var1; // expected-warning {{invocation of method 'operator*' on object 'var1' while it is in an unknown state}}
-
- } else {
- *var0;
- *var1;
- }
-
- if (var0 && var1 && var2) {
- *var0;
- *var1;
- *var2;
-
- } else {
- *var0; // expected-warning {{invocation of method 'operator*' on object 'var0' while it is in an unknown state}}
- *var1; // expected-warning {{invocation of method 'operator*' on object 'var1' while it is in an unknown state}}
- *var2; // expected-warning {{invocation of method 'operator*' on object 'var2' while it is in an unknown state}}
- }
-
-#if 0
- // FIXME: Get this test to pass.
- if (var0 || var1 || var2) {
- *var0; // expected-warning {{invocation of method 'operator*' on object 'var0' while it is in an unknown state}}
- *var1; // expected-warning {{invocation of method 'operator*' on object 'var1' while it is in an unknown state}}
- *var2; // expected-warning {{invocation of method 'operator*' on object 'var2' while it is in an unknown state}}
-
- } else {
- *var0; // expected-warning {{invocation of method 'operator*' on object 'var0' while it is in the 'consumed' state}}
- *var1; // expected-warning {{invocation of method 'operator*' on object 'var1' while it is in the 'consumed' state}}
- *var2; // expected-warning {{invocation of method 'operator*' on object 'var2' while it is in the 'consumed' state}}
- }
-#endif
-}
-
-void testCallingConventions() {
- ConsumableClass<int> var(42);
-
- baf0(var);
- *var; // expected-warning {{invocation of method 'operator*' on object 'var' while it is in an unknown state}}
-
- var = ConsumableClass<int>(42);
- baf1(&var);
- *var; // expected-warning {{invocation of method 'operator*' on object 'var' while it is in an unknown state}}
-}
-
-void testConstAndNonConstMemberFunctions() {
- ConsumableClass<int> var(42);
-
- var.constCall();
- *var;
-
- var.nonconstCall();
- *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);
@@ -198,26 +64,3 @@ void testNoWarnTestFromMacroExpansion()
*var;
}
}
-
-void testSimpleForLoop() {
- ConsumableClass<int> var;
-
- for (int i = 0; i < 10; ++i) {
- *var; // expected-warning {{invocation of method 'operator*' on object 'var' while it is in an unknown state}}
- }
-
- *var; // expected-warning {{invocation of method 'operator*' on object 'var' while it is in an unknown state}}
-}
-
-void testSimpleWhileLoop() {
- int i = 0;
-
- ConsumableClass<int> var;
-
- while (i < 10) {
- *var; // expected-warning {{invocation of method 'operator*' on object 'var' while it is in an unknown state}}
- ++i;
- }
-
- *var; // expected-warning {{invocation of method 'operator*' on object 'var' while it is in an unknown state}}
-}
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=191983&r1=191982&r2=191983&view=diff
==============================================================================
--- cfe/trunk/test/SemaCXX/warn-consumed-analysis.cpp (original)
+++ cfe/trunk/test/SemaCXX/warn-consumed-analysis.cpp Fri Oct 4 16:28:06 2013
@@ -1,10 +1,12 @@
// RUN: %clang_cc1 -fsyntax-only -verify -Wconsumed -std=c++11 %s
-#define CALLABLE_WHEN_UNCONSUMED __attribute__ ((callable_when_unconsumed))
-#define CONSUMABLE(state) __attribute__ ((consumable(state)))
-#define CONSUMES __attribute__ ((consumes))
-#define RETURN_TYPESTATE(state) __attribute__ ((return_typestate(state)))
-#define TESTS_UNCONSUMED __attribute__ ((tests_unconsumed))
+// TODO: Switch to using macros for the expected warnings.
+
+#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_UNCONSUMED __attribute__ ((tests_unconsumed))
typedef decltype(nullptr) nullptr_t;
@@ -30,8 +32,9 @@ class CONSUMABLE(unconsumed) ConsumableC
ConsumableClass<T>& operator=(ConsumableClass<U> &&other);
void operator()(int a) CONSUMES;
- void operator*() const CALLABLE_WHEN_UNCONSUMED;
- void unconsumedCall() const CALLABLE_WHEN_UNCONSUMED;
+ void operator*() const CALLABLE_WHEN("unconsumed");
+ void unconsumedCall() const CALLABLE_WHEN("unconsumed");
+ void callableWhenUnknown() const CALLABLE_WHEN("unconsumed", "unknown");
bool isValid() const TESTS_UNCONSUMED;
operator bool() const TESTS_UNCONSUMED;
@@ -47,7 +50,9 @@ void baf0(const ConsumableClass<int> va
void baf1(const ConsumableClass<int> &var);
void baf2(const ConsumableClass<int> *var);
-void baf3(ConsumableClass<int> &&var);
+void baf3(ConsumableClass<int> &var);
+void baf4(ConsumableClass<int> *var);
+void baf5(ConsumableClass<int> &&var);
ConsumableClass<int> returnsUnconsumed() {
return ConsumableClass<int>(); // expected-warning {{return value not in expected state; expected 'unconsumed', observed 'consumed'}}
@@ -58,39 +63,41 @@ ConsumableClass<int> returnsConsumed() {
return ConsumableClass<int>();
}
+ConsumableClass<int> returnsUnknown() RETURN_TYPESTATE(unknown);
+
void testInitialization() {
ConsumableClass<int> var0;
ConsumableClass<int> var1 = ConsumableClass<int>();
var0 = ConsumableClass<int>();
- *var0; // expected-warning {{invocation of method 'operator*' on object 'var0' while it is in the 'consumed' state}}
- *var1; // expected-warning {{invocation of method 'operator*' on object 'var1' while it is in the 'consumed' state}}
+ *var0; // expected-warning {{invalid invocation of method 'operator*' on object 'var0' while it is in the 'consumed' state}}
+ *var1; // expected-warning {{invalid invocation of method 'operator*' on object 'var1' while it is in the 'consumed' state}}
if (var0.isValid()) {
*var0;
*var1;
} else {
- *var0; // expected-warning {{invocation of method 'operator*' on object 'var0' while it is in the 'consumed' state}}
+ *var0; // expected-warning {{invalid invocation of method 'operator*' on object 'var0' while it is in the 'consumed' state}}
}
}
void testTempValue() {
- *ConsumableClass<int>(); // expected-warning {{invocation of method 'operator*' on a temporary object while it is in the 'consumed' state}}
+ *ConsumableClass<int>(); // expected-warning {{invalid invocation of method 'operator*' on a temporary object while it is in the 'consumed' state}}
}
void testSimpleRValueRefs() {
ConsumableClass<int> var0;
ConsumableClass<int> var1(42);
- *var0; // expected-warning {{invocation of method 'operator*' on object 'var0' while it is in the 'consumed' state}}
+ *var0; // expected-warning {{invalid invocation of method 'operator*' on object 'var0' while it is in the 'consumed' state}}
*var1;
var0 = static_cast<ConsumableClass<int>&&>(var1);
*var0;
- *var1; // expected-warning {{invocation of method 'operator*' on object 'var1' while it is in the 'consumed' state}}
+ *var1; // expected-warning {{invalid invocation of method 'operator*' on object 'var1' while it is in the 'consumed' state}}
}
void testIfStmt() {
@@ -99,11 +106,11 @@ void testIfStmt() {
if (var.isValid()) {
*var;
} else {
- *var; // expected-warning {{invocation of method 'operator*' on object 'var' while it is in the 'consumed' state}}
+ *var; // expected-warning {{invalid invocation of method 'operator*' on object 'var' while it is in the 'consumed' state}}
}
if (!var.isValid()) {
- *var; // expected-warning {{invocation of method 'operator*' on object 'var' while it is in the 'consumed' state}}
+ *var; // expected-warning {{invalid invocation of method 'operator*' on object 'var' while it is in the 'consumed' state}}
} else {
*var;
}
@@ -111,17 +118,17 @@ void testIfStmt() {
if (var) {
// Empty
} else {
- *var; // expected-warning {{invocation of method 'operator*' on object 'var' while it is in the 'consumed' state}}
+ *var; // expected-warning {{invalid invocation of method 'operator*' on object 'var' while it is in the 'consumed' state}}
}
if (var != nullptr) {
// Empty
} else {
- *var; // expected-warning {{invocation of method 'operator*' on object 'var' while it is in the 'consumed' state}}
+ *var; // expected-warning {{invalid invocation of method 'operator*' on object 'var' while it is in the 'consumed' state}}
}
}
-void testComplexConditionals() {
+void testComplexConditionals0() {
ConsumableClass<int> var0, var1, var2;
if (var0 && var1) {
@@ -129,8 +136,8 @@ void testComplexConditionals() {
*var1;
} else {
- *var0; // expected-warning {{invocation of method 'operator*' on object 'var0' while it is in the 'consumed' state}}
- *var1; // expected-warning {{invocation of method 'operator*' on object 'var1' while it is in the 'consumed' state}}
+ *var0; // expected-warning {{invalid invocation of method 'operator*' on object 'var0' while it is in the 'consumed' state}}
+ *var1; // expected-warning {{invalid invocation of method 'operator*' on object 'var1' while it is in the 'consumed' state}}
}
if (var0 || var1) {
@@ -138,8 +145,8 @@ void testComplexConditionals() {
*var1;
} else {
- *var0; // expected-warning {{invocation of method 'operator*' on object 'var0' while it is in the 'consumed' state}}
- *var1; // expected-warning {{invocation of method 'operator*' on object 'var1' while it is in the 'consumed' state}}
+ *var0; // expected-warning {{invalid invocation of method 'operator*' on object 'var0' while it is in the 'consumed' state}}
+ *var1; // expected-warning {{invalid invocation of method 'operator*' on object 'var1' while it is in the 'consumed' state}}
}
if (var0 && !var1) {
@@ -147,13 +154,13 @@ void testComplexConditionals() {
*var1;
} else {
- *var0; // expected-warning {{invocation of method 'operator*' on object 'var0' while it is in the 'consumed' state}}
- *var1; // expected-warning {{invocation of method 'operator*' on object 'var1' while it is in the 'consumed' state}}
+ *var0; // expected-warning {{invalid invocation of method 'operator*' on object 'var0' while it is in the 'consumed' state}}
+ *var1; // expected-warning {{invalid invocation of method 'operator*' on object 'var1' while it is in the 'consumed' state}}
}
if (var0 || !var1) {
- *var0; // expected-warning {{invocation of method 'operator*' on object 'var0' while it is in the 'consumed' state}}
- *var1; // expected-warning {{invocation of method 'operator*' on object 'var1' while it is in the 'consumed' state}}
+ *var0; // expected-warning {{invalid invocation of method 'operator*' on object 'var0' while it is in the 'consumed' state}}
+ *var1; // expected-warning {{invalid invocation of method 'operator*' on object 'var1' while it is in the 'consumed' state}}
} else {
*var0;
@@ -161,8 +168,8 @@ void testComplexConditionals() {
}
if (!var0 && !var1) {
- *var0; // expected-warning {{invocation of method 'operator*' on object 'var0' while it is in the 'consumed' state}}
- *var1; // expected-warning {{invocation of method 'operator*' on object 'var1' while it is in the 'consumed' state}}
+ *var0; // expected-warning {{invalid invocation of method 'operator*' on object 'var0' while it is in the 'consumed' state}}
+ *var1; // expected-warning {{invalid invocation of method 'operator*' on object 'var1' while it is in the 'consumed' state}}
} else {
*var0;
@@ -170,8 +177,8 @@ void testComplexConditionals() {
}
if (!var0 || !var1) {
- *var0; // expected-warning {{invocation of method 'operator*' on object 'var0' while it is in the 'consumed' state}}
- *var1; // expected-warning {{invocation of method 'operator*' on object 'var1' while it is in the 'consumed' state}}
+ *var0; // expected-warning {{invalid invocation of method 'operator*' on object 'var0' while it is in the 'consumed' state}}
+ *var1; // expected-warning {{invalid invocation of method 'operator*' on object 'var1' while it is in the 'consumed' state}}
} else {
*var0;
@@ -179,8 +186,8 @@ void testComplexConditionals() {
}
if (!(var0 && var1)) {
- *var0; // expected-warning {{invocation of method 'operator*' on object 'var0' while it is in the 'consumed' state}}
- *var1; // expected-warning {{invocation of method 'operator*' on object 'var1' while it is in the 'consumed' state}}
+ *var0; // expected-warning {{invalid invocation of method 'operator*' on object 'var0' while it is in the 'consumed' state}}
+ *var1; // expected-warning {{invalid invocation of method 'operator*' on object 'var1' while it is in the 'consumed' state}}
} else {
*var0;
@@ -188,8 +195,8 @@ void testComplexConditionals() {
}
if (!(var0 || var1)) {
- *var0; // expected-warning {{invocation of method 'operator*' on object 'var0' while it is in the 'consumed' state}}
- *var1; // expected-warning {{invocation of method 'operator*' on object 'var1' while it is in the 'consumed' state}}
+ *var0; // expected-warning {{invalid invocation of method 'operator*' on object 'var0' while it is in the 'consumed' state}}
+ *var1; // expected-warning {{invalid invocation of method 'operator*' on object 'var1' while it is in the 'consumed' state}}
} else {
*var0;
@@ -202,9 +209,9 @@ void testComplexConditionals() {
*var2;
} else {
- *var0; // expected-warning {{invocation of method 'operator*' on object 'var0' while it is in the 'consumed' state}}
- *var1; // expected-warning {{invocation of method 'operator*' on object 'var1' while it is in the 'consumed' state}}
- *var2; // expected-warning {{invocation of method 'operator*' on object 'var2' while it is in the 'consumed' state}}
+ *var0; // expected-warning {{invalid invocation of method 'operator*' on object 'var0' while it is in the 'consumed' state}}
+ *var1; // expected-warning {{invalid invocation of method 'operator*' on object 'var1' while it is in the 'consumed' state}}
+ *var2; // expected-warning {{invalid invocation of method 'operator*' on object 'var2' while it is in the 'consumed' state}}
}
#if 0
@@ -215,9 +222,115 @@ void testComplexConditionals() {
*var2;
} else {
- *var0; // expected-warning {{invocation of method 'operator*' on object 'var0' while it is in the 'consumed' state}}
- *var1; // expected-warning {{invocation of method 'operator*' on object 'var1' while it is in the 'consumed' state}}
- *var2; // expected-warning {{invocation of method 'operator*' on object 'var2' while it is in the 'consumed' state}}
+ *var0; // expected-warning {{invalid invocation of method 'operator*' on object 'var0' while it is in the 'consumed' state}}
+ *var1; // expected-warning {{invalid invocation of method 'operator*' on object 'var1' while it is in the 'consumed' state}}
+ *var2; // expected-warning {{invalid invocation of method 'operator*' on object 'var2' while it is in the 'consumed' state}}
+ }
+#endif
+}
+
+void testComplexConditionals1() {
+ ConsumableClass<int> var0, var1, var2;
+
+ // Coerce all variables into the unknown state.
+ baf3(var0);
+ baf3(var1);
+ baf3(var2);
+
+ if (var0 && var1) {
+ *var0;
+ *var1;
+
+ } else {
+ *var0; // expected-warning {{invalid invocation of method 'operator*' on object 'var0' while it is in the 'unknown' state}}
+ *var1; // expected-warning {{invalid invocation of method 'operator*' on object 'var1' while it is in the 'unknown' state}}
+ }
+
+ if (var0 || var1) {
+ *var0; // expected-warning {{invalid invocation of method 'operator*' on object 'var0' while it is in the 'unknown' state}}
+ *var1; // expected-warning {{invalid invocation of method 'operator*' on object 'var1' while it is in the 'unknown' state}}
+
+ } else {
+ *var0; // expected-warning {{invalid invocation of method 'operator*' on object 'var0' while it is in the 'consumed' state}}
+ *var1; // expected-warning {{invalid invocation of method 'operator*' on object 'var1' while it is in the 'consumed' state}}
+ }
+
+ if (var0 && !var1) {
+ *var0;
+ *var1; // expected-warning {{invalid invocation of method 'operator*' on object 'var1' while it is in the 'consumed' state}}
+
+ } else {
+ *var0; // expected-warning {{invalid invocation of method 'operator*' on object 'var0' while it is in the 'unknown' state}}
+ *var1; // expected-warning {{invalid invocation of method 'operator*' on object 'var1' while it is in the 'unknown' state}}
+ }
+
+ if (var0 || !var1) {
+ *var0; // expected-warning {{invalid invocation of method 'operator*' on object 'var0' while it is in the 'unknown' state}}
+ *var1; // expected-warning {{invalid invocation of method 'operator*' on object 'var1' while it is in the 'unknown' state}}
+
+ } else {
+ *var0; // expected-warning {{invalid invocation of method 'operator*' on object 'var0' while it is in the 'consumed' state}}
+ *var1;
+ }
+
+ if (!var0 && !var1) {
+ *var0; // expected-warning {{invalid invocation of method 'operator*' on object 'var0' while it is in the 'consumed' state}}
+ *var1; // expected-warning {{invalid invocation of method 'operator*' on object 'var1' while it is in the 'consumed' state}}
+
+ } else {
+ *var0; // expected-warning {{invalid invocation of method 'operator*' on object 'var0' while it is in the 'unknown' state}}
+ *var1; // expected-warning {{invalid invocation of method 'operator*' on object 'var1' while it is in the 'unknown' state}}
+ }
+
+ if (!(var0 || var1)) {
+ *var0; // expected-warning {{invalid invocation of method 'operator*' on object 'var0' while it is in the 'consumed' state}}
+ *var1; // expected-warning {{invalid invocation of method 'operator*' on object 'var1' while it is in the 'consumed' state}}
+
+ } else {
+ *var0; // expected-warning {{invalid invocation of method 'operator*' on object 'var0' while it is in the 'unknown' state}}
+ *var1; // expected-warning {{invalid invocation of method 'operator*' on object 'var1' while it is in the 'unknown' state}}
+ }
+
+ if (!var0 || !var1) {
+ *var0; // expected-warning {{invalid invocation of method 'operator*' on object 'var0' while it is in the 'unknown' state}}
+ *var1; // expected-warning {{invalid invocation of method 'operator*' on object 'var1' while it is in the 'unknown' state}}
+
+ } else {
+ *var0;
+ *var1;
+ }
+
+ if (!(var0 && var1)) {
+ *var0; // expected-warning {{invalid invocation of method 'operator*' on object 'var0' while it is in the 'unknown' state}}
+ *var1; // expected-warning {{invalid invocation of method 'operator*' on object 'var1' while it is in the 'unknown' state}}
+
+ } else {
+ *var0;
+ *var1;
+ }
+
+ if (var0 && var1 && var2) {
+ *var0;
+ *var1;
+ *var2;
+
+ } else {
+ *var0; // expected-warning {{invalid invocation of method 'operator*' on object 'var0' while it is in the 'unknown' state}}
+ *var1; // expected-warning {{invalid invocation of method 'operator*' on object 'var1' while it is in the 'unknown' state}}
+ *var2; // expected-warning {{invalid invocation of method 'operator*' on object 'var2' while it is in the 'unknown' state}}
+ }
+
+#if 0
+ // FIXME: Get this test to pass.
+ if (var0 || var1 || var2) {
+ *var0; // expected-warning {{invalid invocation of method 'operator*' on object 'var0' while it is in the 'unknown' state}}
+ *var1; // expected-warning {{invalid invocation of method 'operator*' on object 'var1' while it is in the 'unknown' state}}
+ *var2; // expected-warning {{invalid invocation of method 'operator*' on object 'var2' while it is in the 'unknown' state}}
+
+ } else {
+ *var0; // expected-warning {{invalid invocation of method 'operator*' on object 'var0' while it is in the 'consumed' state}}
+ *var1; // expected-warning {{invalid invocation of method 'operator*' on object 'var1' while it is in the 'consumed' state}}
+ *var2; // expected-warning {{invalid invocation of method 'operator*' on object 'var2' while it is in the 'consumed' state}}
}
#endif
}
@@ -226,7 +339,7 @@ void testStateChangeInBranch() {
ConsumableClass<int> var;
// Make var enter the 'unknown' state.
- baf1(var);
+ baf3(var);
if (!var) {
var = ConsumableClass<int>(42);
@@ -259,8 +372,34 @@ void testCallingConventions() {
baf2(&var);
*var;
- baf3(static_cast<ConsumableClass<int>&&>(var));
- *var; // expected-warning {{invocation of method 'operator*' on object 'var' while it is in the 'consumed' state}}
+ baf3(var);
+ *var; // expected-warning {{invalid invocation of method 'operator*' on object 'var' while it is in the 'unknown' state}}
+
+ var = ConsumableClass<int>(42);
+ baf4(&var);
+ *var; // expected-warning {{invalid invocation of method 'operator*' on object 'var' while it is in the 'unknown' state}}
+
+ var = ConsumableClass<int>(42);
+ baf5(static_cast<ConsumableClass<int>&&>(var));
+ *var; // expected-warning {{invalid invocation of method 'operator*' on object 'var' while it is in the 'consumed' state}}
+}
+
+void testConstAndNonConstMemberFunctions() {
+ ConsumableClass<int> var(42);
+
+ var.constCall();
+ *var;
+
+ var.nonconstCall();
+ *var;
+}
+
+void testFunctionParam0(ConsumableClass<int> param) {
+ *param;
+}
+
+void testFunctionParam1(ConsumableClass<int> ¶m) {
+ *param; // expected-warning {{invalid invocation of method 'operator*' on object 'param' while it is in the 'unknown' state}}
}
void testReturnStates() {
@@ -270,24 +409,34 @@ void testReturnStates() {
*var;
var = returnsConsumed();
- *var; // expected-warning {{invocation of method 'operator*' on object 'var' while it is in the 'consumed' state}}
+ *var; // expected-warning {{invalid invocation of method 'operator*' on object 'var' while it is in the 'consumed' state}}
+}
+
+void testCallableWhen() {
+ ConsumableClass<int> var(42);
+
+ *var;
+
+ baf3(var);
+
+ var.callableWhenUnknown();
}
void testMoveAsignmentish() {
ConsumableClass<int> var0;
ConsumableClass<long> var1(42);
- *var0; // expected-warning {{invocation of method 'operator*' on object 'var0' while it is in the 'consumed' state}}
+ *var0; // expected-warning {{invalid invocation of method 'operator*' on object 'var0' while it is in the 'consumed' state}}
*var1;
var0 = static_cast<ConsumableClass<long>&&>(var1);
*var0;
- *var1; // expected-warning {{invocation of method 'operator*' on object 'var1' while it is in the 'consumed' state}}
+ *var1; // expected-warning {{invalid invocation of method 'operator*' on object 'var1' while it is in the 'consumed' state}}
var1 = ConsumableClass<long>(42);
var1 = nullptr;
- *var1; // expected-warning {{invocation of method 'operator*' on object 'var1' while it is in the 'consumed' state}}
+ *var1; // expected-warning {{invalid invocation of method 'operator*' on object 'var1' while it is in the 'consumed' state}}
}
void testConditionalMerge() {
@@ -297,7 +446,7 @@ void testConditionalMerge() {
// Empty
}
- *var; // expected-warning {{invocation of method 'operator*' on object 'var' while it is in the 'consumed' state}}
+ *var; // expected-warning {{invalid invocation of method 'operator*' on object 'var' while it is in the 'consumed' state}}
if (var.isValid()) {
// Empty
@@ -305,7 +454,7 @@ void testConditionalMerge() {
// Empty
}
- *var; // expected-warning {{invocation of method 'operator*' on object 'var' while it is in the 'consumed' state}}
+ *var; // expected-warning {{invalid invocation of method 'operator*' on object 'var' while it is in the 'consumed' state}}
}
void testConsumes0() {
@@ -315,13 +464,13 @@ void testConsumes0() {
var.consume();
- *var; // expected-warning {{invocation of method 'operator*' on object 'var' while it is in the 'consumed' state}}
+ *var; // expected-warning {{invalid invocation of method 'operator*' on object 'var' while it is in the 'consumed' state}}
}
void testConsumes1() {
ConsumableClass<int> var(nullptr);
- *var; // expected-warning {{invocation of method 'operator*' on object 'var' while it is in the 'consumed' state}}
+ *var; // expected-warning {{invalid invocation of method 'operator*' on object 'var' while it is in the 'consumed' state}}
}
void testConsumes2() {
@@ -330,10 +479,10 @@ void testConsumes2() {
var.unconsumedCall();
var(6);
- var.unconsumedCall(); // expected-warning {{invocation of method 'unconsumedCall' on object 'var' while it is in the 'consumed' state}}
+ var.unconsumedCall(); // expected-warning {{invalid invocation of method 'unconsumedCall' on object 'var' while it is in the 'consumed' state}}
}
-void testNonsenseState() {
+void testUnreachableBlock() {
ConsumableClass<int> var(42);
if (var) {
@@ -349,10 +498,10 @@ void testSimpleForLoop() {
ConsumableClass<int> var;
for (int i = 0; i < 10; ++i) {
- *var;
+ *var; // expected-warning {{invalid invocation of method 'operator*' on object 'var' while it is in the 'unknown' state}}
}
- *var;
+ *var; // expected-warning {{invalid invocation of method 'operator*' on object 'var' while it is in the 'unknown' state}}
}
void testSimpleWhileLoop() {
@@ -361,9 +510,9 @@ void testSimpleWhileLoop() {
ConsumableClass<int> var;
while (i < 10) {
- *var;
+ *var; // expected-warning {{invalid invocation of method 'operator*' on object 'var' while it is in the 'unknown' state}}
++i;
}
- *var;
+ *var; // expected-warning {{invalid invocation of method 'operator*' on object 'var' while it is in the 'unknown' state}}
}
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=191983&r1=191982&r2=191983&view=diff
==============================================================================
--- cfe/trunk/test/SemaCXX/warn-consumed-parsing.cpp (original)
+++ cfe/trunk/test/SemaCXX/warn-consumed-parsing.cpp Fri Oct 4 16:28:06 2013
@@ -1,6 +1,6 @@
// RUN: %clang_cc1 -fsyntax-only -verify -Wconsumed -std=c++11 %s
-#define CALLABLE_WHEN_UNCONSUMED __attribute__ ((callable_when_unconsumed))
+#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)))
@@ -19,33 +19,35 @@ int returnTypestateForUnconsumable() {
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 callableWhen() __attribute__ ((callable_when())); // expected-error {{attribute takes at least 1 argument}}
};
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 var2 CALLABLE_WHEN(42); // 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 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 function2() CALLABLE_WHEN(42); // 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 callableWhenUnconsumed() CALLABLE_WHEN_UNCONSUMED;
- void consumes() CONSUMES;
- bool testsUnconsumed() TESTS_UNCONSUMED;
+ 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;
+ bool testsUnconsumed() TESTS_UNCONSUMED;
};
AttrTester1 returnTypestateTester0() RETURN_TYPESTATE(not_a_state); // expected-warning {{'return_typestate' attribute argument not supported: '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 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}}
+ 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}}
+ 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}}
Modified: cfe/trunk/utils/TableGen/ClangAttrEmitter.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/utils/TableGen/ClangAttrEmitter.cpp?rev=191983&r1=191982&r2=191983&view=diff
==============================================================================
--- cfe/trunk/utils/TableGen/ClangAttrEmitter.cpp (original)
+++ cfe/trunk/utils/TableGen/ClangAttrEmitter.cpp Fri Oct 4 16:28:06 2013
@@ -136,6 +136,7 @@ namespace {
virtual void writeHasChildren(raw_ostream &OS) const { OS << "false"; }
virtual bool isEnumArg() const { return false; }
+ virtual bool isVariadicEnumArg() const { return false; }
};
class SimpleArgument : public Argument {
@@ -474,7 +475,7 @@ namespace {
<< ";\n";
OS << " " << getLowerName() << ".reserve(" << getLowerName()
<< "Size);\n";
- OS << " for (unsigned i = " << getLowerName() << "Size; i; --i)\n";
+ OS << " for (unsigned i = " << getLowerName() << "Size; i; --i)\n";
std::string read = ReadPCHRecord(type);
OS << " " << getLowerName() << ".push_back(" << read << ");\n";
@@ -604,6 +605,93 @@ namespace {
OS << " }\n";
}
};
+
+ class VariadicEnumArgument: public VariadicArgument {
+ std::string type, QualifiedTypeName;
+ std::vector<StringRef> values, enums, uniques;
+ public:
+ VariadicEnumArgument(Record &Arg, StringRef Attr)
+ : VariadicArgument(Arg, Attr, Arg.getValueAsString("Type")),
+ type(Arg.getValueAsString("Type")),
+ values(getValueAsListOfStrings(Arg, "Values")),
+ enums(getValueAsListOfStrings(Arg, "Enums")),
+ uniques(enums)
+ {
+ // Calculate the various enum values
+ std::sort(uniques.begin(), uniques.end());
+ uniques.erase(std::unique(uniques.begin(), uniques.end()), uniques.end());
+
+ QualifiedTypeName = getAttrName().str() + "Attr::" + type;
+
+ // FIXME: Emit a proper error
+ assert(!uniques.empty());
+ }
+
+ bool isVariadicEnumArg() const { return true; }
+
+ void writeDeclarations(raw_ostream &OS) const {
+ std::vector<StringRef>::const_iterator i = uniques.begin(),
+ e = uniques.end();
+ // The last one needs to not have a comma.
+ --e;
+
+ OS << "public:\n";
+ OS << " enum " << type << " {\n";
+ for (; i != e; ++i)
+ OS << " " << *i << ",\n";
+ OS << " " << *e << "\n";
+ OS << " };\n";
+ OS << "private:\n";
+
+ VariadicArgument::writeDeclarations(OS);
+ }
+ void writeDump(raw_ostream &OS) const {
+ OS << " for (" << getAttrName() << "Attr::" << getLowerName()
+ << "_iterator I = SA->" << getLowerName() << "_begin(), E = SA->"
+ << getLowerName() << "_end(); I != E; ++I) {\n";
+ OS << " switch(*I) {\n";
+ for (std::vector<StringRef>::const_iterator UI = uniques.begin(),
+ UE = uniques.end(); UI != UE; ++UI) {
+ OS << " case " << getAttrName() << "Attr::" << *UI << ":\n";
+ OS << " OS << \" " << *UI << "\";\n";
+ OS << " break;\n";
+ }
+ OS << " }\n";
+ OS << " }\n";
+ }
+ void writePCHReadDecls(raw_ostream &OS) const {
+ OS << " unsigned " << getLowerName() << "Size = Record[Idx++];\n";
+ OS << " SmallVector<" << QualifiedTypeName << ", 4> " << getLowerName()
+ << ";\n";
+ OS << " " << getLowerName() << ".reserve(" << getLowerName()
+ << "Size);\n";
+ OS << " for (unsigned i = " << getLowerName() << "Size; i; --i)\n";
+ OS << " " << getLowerName() << ".push_back(" << "static_cast<"
+ << QualifiedTypeName << ">(Record[Idx++]));\n";
+ }
+ void writePCHWrite(raw_ostream &OS) const{
+ OS << " Record.push_back(SA->" << getLowerName() << "_size());\n";
+ OS << " for (" << getAttrName() << "Attr::" << getLowerName()
+ << "_iterator i = SA->" << getLowerName() << "_begin(), e = SA->"
+ << getLowerName() << "_end(); i != e; ++i)\n";
+ OS << " " << WritePCHRecord(QualifiedTypeName, "(*i)");
+ }
+ void writeConversion(raw_ostream &OS) const {
+ OS << " static bool ConvertStrTo" << type << "(StringRef Val, ";
+ OS << type << " &Out) {\n";
+ OS << " Optional<" << type << "> R = llvm::StringSwitch<Optional<";
+ OS << type << "> >(Val)\n";
+ for (size_t I = 0; I < enums.size(); ++I) {
+ OS << " .Case(\"" << values[I] << "\", ";
+ OS << getAttrName() << "Attr::" << enums[I] << ")\n";
+ }
+ OS << " .Default(Optional<" << type << ">());\n";
+ OS << " if (R) {\n";
+ OS << " Out = *R;\n return true;\n }\n";
+ OS << " return false;\n";
+ OS << " }\n";
+ }
+ };
class VersionArgument : public Argument {
public:
@@ -768,6 +856,8 @@ static Argument *createArgument(Record &
Ptr = new SimpleArgument(Arg, Attr, "SourceLocation");
else if (ArgName == "VariadicUnsignedArgument")
Ptr = new VariadicArgument(Arg, Attr, "unsigned");
+ else if (ArgName == "VariadicEnumArgument")
+ Ptr = new VariadicEnumArgument(Arg, Attr);
else if (ArgName == "VariadicExprArgument")
Ptr = new VariadicExprArgument(Arg, Attr);
else if (ArgName == "VersionArgument")
@@ -1051,6 +1141,9 @@ void EmitClangAttrClass(RecordKeeper &Re
if ((*ai)->isEnumArg()) {
EnumArgument *EA = (EnumArgument *)*ai;
EA->writeConversion(OS);
+ } else if ((*ai)->isVariadicEnumArg()) {
+ VariadicEnumArgument *VEA = (VariadicEnumArgument *)*ai;
+ VEA->writeConversion(OS);
}
}
More information about the cfe-commits
mailing list