llvm_unreachable("invalid enum");
}
-static bool isRValueRefish(QualType ParamType) {
- return ParamType->isRValueReferenceType(); /* ||
- (ParamType->isLValueReferenceType() &&
- !cast<LValueReferenceType>(
- ParamType.getCanonicalType())->isSpelledAsLValue()); */
+static bool isRValueRef(QualType ParamType) {
+ return ParamType->isRValueReferenceType();
}
static bool isTestingFunction(const FunctionDecl *FunDecl) {
bool ConsumedStmtVisitor::handleCall(const CallExpr *Call, const Expr *ObjArg,
const FunctionDecl *FunD) {
unsigned Offset = 0;
- if (isa<CXXMethodDecl>(FunD))
- Offset = 1; // First argument to call is 'this' parameter
+ if (isa<CXXOperatorCallExpr>(Call) && isa<CXXMethodDecl>(FunD))
+ Offset = 1; // first argument is 'this'
// check explicit parameters
for (unsigned Index = Offset; Index < Call->getNumArgs(); ++Index) {
continue;
// Adjust state on the caller side.
- if (isRValueRefish(ParamType))
+ if (isRValueRef(ParamType))
setStateForVarOrTmp(StateMap, PInfo, consumed::CS_Consumed);
else if (ReturnTypestateAttr *RT = Param->getAttr<ReturnTypestateAttr>())
setStateForVarOrTmp(StateMap, PInfo, mapReturnTypestateAttrState(RT));
- else if (isPointerOrRef(ParamType)) {
- if (!ParamType->getPointeeType().isConstQualified() ||
- isSetOnReadPtrType(ParamType))
- setStateForVarOrTmp(StateMap, PInfo, consumed::CS_Unknown);
- }
+ else if (isPointerOrRef(ParamType) &&
+ (!ParamType->getPointeeType().isConstQualified() ||
+ isSetOnReadPtrType(ParamType)))
+ setStateForVarOrTmp(StateMap, PInfo, consumed::CS_Unknown);
}
if (!ObjArg)
ParamState = mapParamTypestateAttrState(PTA);
else if (isConsumableType(ParamType))
ParamState = mapConsumableAttrState(ParamType);
- else if (isRValueRefish(ParamType) &&
- isConsumableType(ParamType->getPointeeType()))
+ else if (isRValueRef(ParamType) &&
+ isConsumableType(ParamType->getPointeeType()))
ParamState = mapConsumableAttrState(ParamType->getPointeeType());
else if (ParamType->isReferenceType() &&
- isConsumableType(ParamType->getPointeeType()))
+ isConsumableType(ParamType->getPointeeType()))
ParamState = consumed::CS_Unknown;
if (ParamState != CS_None)
class CONSUMABLE(unconsumed) DestructorTester {
public:
- DestructorTester() RETURN_TYPESTATE(unconsumed);
+ DestructorTester();
DestructorTester(int);
void operator*() CALLABLE_WHEN("unconsumed");
void testInitialization() {
ConsumableClass<int> var0;
ConsumableClass<int> var1 = ConsumableClass<int>();
-
- var0 = ConsumableClass<int>();
-
+ ConsumableClass<int> var2(42);
+ ConsumableClass<int> var3(var2); // copy constructor
+ ConsumableClass<int> var4(var0); // copy consumed value
+
*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;
+ *var3;
+ *var4; // expected-warning {{invalid invocation of method 'operator*' on object 'var4' while it is in the 'consumed' state}}
+
+ var0 = ConsumableClass<int>(42);
+ *var0;
+
+ var0 = var1;
+ *var0; // expected-warning {{invalid invocation of method 'operator*' on object 'var0' while it is in the 'consumed' state}}
if (var0.isValid()) {
*var0;
}
void testDestruction() {
- DestructorTester D0(42), D1(42);
+ DestructorTester D0(42), D1(42), D2;
*D0;
*D1;
-
- DestructorTester D2;
- *D2;
+ *D2; // expected-warning {{invalid invocation of method 'operator*' on object 'D2' while it is in the 'consumed' state}}
D0.~DestructorTester(); // expected-warning {{invalid invocation of method '~DestructorTester' on object 'D0' while it is in the 'unconsumed' state}}
return; // expected-warning {{invalid invocation of method '~DestructorTester' on object 'D0' while it is in the 'unconsumed' state}} \
- expected-warning {{invalid invocation of method '~DestructorTester' on object 'D1' while it is in the 'unconsumed' state}} \
- expected-warning {{invalid invocation of method '~DestructorTester' on object 'D2' while it is in the 'unconsumed' state}}
+ expected-warning {{invalid invocation of method '~DestructorTester' on object 'D1' while it is in the 'unconsumed' state}}
}
void testTempValue() {
testParamTypestateCallee(Var0, Var1); // expected-warning {{argument not in expected state; expected 'consumed', observed 'unconsumed'}}
}
+
+void consumeFunc(ConsumableClass<int> P PARAM_TYPESTATE(unconsumed));
+struct ParamTest {
+ static void consumeFuncStatic(ConsumableClass<int> P PARAM_TYPESTATE(unconsumed));
+ void consumeFuncMeth(ConsumableClass<int> P PARAM_TYPESTATE(unconsumed));
+ void operator<<(ConsumableClass<int> P PARAM_TYPESTATE(unconsumed));
+};
+
+void operator>>(ParamTest& pt, ConsumableClass<int> P PARAM_TYPESTATE(unconsumed));
+
+
+void testFunctionParams() {
+ // Make sure we handle the different kinds of functions.
+ ConsumableClass<int> P;
+
+ consumeFunc(P); // expected-warning {{argument not in expected state; expected 'unconsumed', observed 'consumed'}}
+ ParamTest::consumeFuncStatic(P); // expected-warning {{argument not in expected state; expected 'unconsumed', observed 'consumed'}}
+ ParamTest pt;
+ pt.consumeFuncMeth(P); // expected-warning {{argument not in expected state; expected 'unconsumed', observed 'consumed'}}
+ pt << P; // expected-warning {{argument not in expected state; expected 'unconsumed', observed 'consumed'}}
+ pt >> P; // expected-warning {{argument not in expected state; expected 'unconsumed', observed 'consumed'}}
+}
+
void baf3(ConsumableClass<int> var) {
*var;
}