mirror of
https://github.com/capstone-engine/llvm-capstone.git
synced 2025-01-16 13:08:42 +00:00
Consumed Analysis: Allow parameters that are passed by non-const reference
to be treated as return values, and marked with the "returned_typestate" attribute. Patch by chris.wailes@gmail.com; reviewed by delesley@google.com. llvm-svn: 192932
This commit is contained in:
parent
c1d5471291
commit
36ea1dd4fc
@ -59,6 +59,20 @@ namespace consumed {
|
||||
virtual void warnLoopStateMismatch(SourceLocation Loc,
|
||||
StringRef VariableName) {}
|
||||
|
||||
/// \brief Warn about parameter typestate mismatches upon return.
|
||||
///
|
||||
/// \param Loc -- The SourceLocation of the return statement.
|
||||
///
|
||||
/// \param ExpectedState -- The state the return value was expected to be
|
||||
/// in.
|
||||
///
|
||||
/// \param ObservedState -- The state the return value was observed to be
|
||||
/// in.
|
||||
virtual void warnParamReturnTypestateMismatch(SourceLocation Loc,
|
||||
StringRef VariableName,
|
||||
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.
|
||||
@ -70,7 +84,14 @@ namespace consumed {
|
||||
StringRef TypeName) {}
|
||||
|
||||
/// \brief Warn about return typestate mismatches.
|
||||
///
|
||||
/// \param Loc -- The SourceLocation of the return statement.
|
||||
///
|
||||
/// \param ExpectedState -- The state the return value was expected to be
|
||||
/// in.
|
||||
///
|
||||
/// \param ObservedState -- The state the return value was observed to be
|
||||
/// in.
|
||||
virtual void warnReturnTypestateMismatch(SourceLocation Loc,
|
||||
StringRef ExpectedState,
|
||||
StringRef ObservedState) {}
|
||||
@ -118,6 +139,11 @@ namespace consumed {
|
||||
ConsumedStateMap(const ConsumedStateMap &Other)
|
||||
: Reachable(Other.Reachable), From(Other.From), Map(Other.Map) {}
|
||||
|
||||
/// \brief Warn if any of the parameters being tracked are not in the state
|
||||
/// they were declared to be in upon return from a function.
|
||||
void checkParamsForReturnTypestate(SourceLocation BlameLoc,
|
||||
ConsumedWarningsHandlerBase &WarningsHandler) const;
|
||||
|
||||
/// \brief Get the consumed state of a given variable.
|
||||
ConsumedState getState(const VarDecl *Var) const;
|
||||
|
||||
|
@ -969,7 +969,7 @@ def CallableWhen : InheritableAttr {
|
||||
|
||||
def ReturnTypestate : InheritableAttr {
|
||||
let Spellings = [GNU<"return_typestate">];
|
||||
let Subjects = [Function];
|
||||
let Subjects = [Function, ParmVar];
|
||||
let Args = [EnumArgument<"State", "ConsumedState",
|
||||
["unknown", "consumed", "unconsumed"],
|
||||
["Unknown", "Consumed", "Unconsumed"]>];
|
||||
|
@ -2222,6 +2222,9 @@ def warn_return_typestate_mismatch : Warning<
|
||||
def warn_loop_state_mismatch : Warning<
|
||||
"state of variable '%0' must match at the entry and exit of loop">,
|
||||
InGroup<Consumed>, DefaultIgnore;
|
||||
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_impcast_vector_scalar : Warning<
|
||||
"implicit conversion turns vector to scalar: %0 to %1">,
|
||||
|
@ -87,7 +87,7 @@ static SourceLocation getLastStmtLoc(const CFGBlock *Block) {
|
||||
Loc = getFirstStmtLoc(*Block->succ_begin());
|
||||
if (Loc.isValid())
|
||||
return Loc;
|
||||
|
||||
|
||||
// If we have one predecessor, return the last statement in that block
|
||||
if (Block->pred_size() == 1 && *Block->pred_begin())
|
||||
return getLastStmtLoc(*Block->pred_begin());
|
||||
@ -572,7 +572,8 @@ void ConsumedStmtVisitor::VisitCallExpr(const CallExpr *Call) {
|
||||
unsigned Offset = Call->getNumArgs() - FunDecl->getNumParams();
|
||||
|
||||
for (unsigned Index = Offset; Index < Call->getNumArgs(); ++Index) {
|
||||
QualType ParamType = FunDecl->getParamDecl(Index - Offset)->getType();
|
||||
const ParmVarDecl *Param = FunDecl->getParamDecl(Index - Offset);
|
||||
QualType ParamType = Param->getType();
|
||||
|
||||
InfoEntry Entry = PropagationMap.find(Call->getArg(Index));
|
||||
|
||||
@ -588,6 +589,10 @@ void ConsumedStmtVisitor::VisitCallExpr(const CallExpr *Call) {
|
||||
|
||||
StateMap->setState(PInfo.getVar(), consumed::CS_Consumed);
|
||||
|
||||
} else if (Param->hasAttr<ReturnTypestateAttr>()) {
|
||||
StateMap->setState(PInfo.getVar(),
|
||||
mapReturnTypestateAttrState(Param->getAttr<ReturnTypestateAttr>()));
|
||||
|
||||
} else if (!(ParamType.isConstQualified() ||
|
||||
((ParamType->isReferenceType() ||
|
||||
ParamType->isPointerType()) &&
|
||||
@ -820,7 +825,9 @@ void ConsumedStmtVisitor::VisitParmVarDecl(const ParmVarDecl *Param) {
|
||||
}
|
||||
|
||||
void ConsumedStmtVisitor::VisitReturnStmt(const ReturnStmt *Ret) {
|
||||
if (ConsumedState ExpectedState = Analyzer.getExpectedReturnState()) {
|
||||
ConsumedState ExpectedState = Analyzer.getExpectedReturnState();
|
||||
|
||||
if (ExpectedState != CS_None) {
|
||||
InfoEntry Entry = PropagationMap.find(Ret->getRetValue());
|
||||
|
||||
if (Entry != PropagationMap.end()) {
|
||||
@ -835,6 +842,9 @@ void ConsumedStmtVisitor::VisitReturnStmt(const ReturnStmt *Ret) {
|
||||
stateToString(RetState));
|
||||
}
|
||||
}
|
||||
|
||||
StateMap->checkParamsForReturnTypestate(Ret->getLocStart(),
|
||||
Analyzer.WarningsHandler);
|
||||
}
|
||||
|
||||
void ConsumedStmtVisitor::VisitUnaryOperator(const UnaryOperator *UOp) {
|
||||
@ -1054,6 +1064,31 @@ bool ConsumedBlockInfo::isBackEdgeTarget(const CFGBlock *Block) {
|
||||
return false;
|
||||
}
|
||||
|
||||
void ConsumedStateMap::checkParamsForReturnTypestate(SourceLocation BlameLoc,
|
||||
ConsumedWarningsHandlerBase &WarningsHandler) const {
|
||||
|
||||
ConsumedState ExpectedState;
|
||||
|
||||
for (MapType::const_iterator DMI = Map.begin(), DME = Map.end(); DMI != DME;
|
||||
++DMI) {
|
||||
|
||||
if (isa<ParmVarDecl>(DMI->first)) {
|
||||
const ParmVarDecl *Param = cast<ParmVarDecl>(DMI->first);
|
||||
|
||||
if (!Param->hasAttr<ReturnTypestateAttr>()) continue;
|
||||
|
||||
ExpectedState =
|
||||
mapReturnTypestateAttrState(Param->getAttr<ReturnTypestateAttr>());
|
||||
|
||||
if (DMI->second != ExpectedState) {
|
||||
WarningsHandler.warnParamReturnTypestateMismatch(BlameLoc,
|
||||
Param->getNameAsString(), stateToString(ExpectedState),
|
||||
stateToString(DMI->second));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ConsumedState ConsumedStateMap::getState(const VarDecl *Var) const {
|
||||
MapType::const_iterator Entry = Map.find(Var);
|
||||
|
||||
@ -1375,6 +1410,11 @@ void ConsumedAnalyzer::run(AnalysisDeclContext &AC) {
|
||||
CurrStates = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if (CurrBlock == &AC.getCFG()->getExit() &&
|
||||
D->getCallResultType()->isVoidType())
|
||||
CurrStates->checkParamsForReturnTypestate(D->getLocation(),
|
||||
WarningsHandler);
|
||||
} // End of block iterator.
|
||||
|
||||
// Delete the last existing state map.
|
||||
|
@ -1484,6 +1484,18 @@ public:
|
||||
Warnings.push_back(DelayedDiag(Warning, OptionalNotes()));
|
||||
}
|
||||
|
||||
void warnParamReturnTypestateMismatch(SourceLocation Loc,
|
||||
StringRef VariableName,
|
||||
StringRef ExpectedState,
|
||||
StringRef ObservedState) {
|
||||
|
||||
PartialDiagnosticAt Warning(Loc, S.PDiag(
|
||||
diag::warn_param_return_typestate_mismatch) << VariableName <<
|
||||
ExpectedState << ObservedState);
|
||||
|
||||
Warnings.push_back(DelayedDiag(Warning, OptionalNotes()));
|
||||
}
|
||||
|
||||
void warnReturnTypestateForUnconsumableType(SourceLocation Loc,
|
||||
StringRef TypeName) {
|
||||
PartialDiagnosticAt Warning(Loc, S.PDiag(
|
||||
|
@ -1068,6 +1068,14 @@ static void handleCallableWhenAttr(Sema &S, Decl *D,
|
||||
|
||||
static void handleReturnTypestateAttr(Sema &S, Decl *D,
|
||||
const AttributeList &Attr) {
|
||||
if (!checkAttributeNumArgs(S, Attr, 1)) return;
|
||||
|
||||
if (!(isa<FunctionDecl>(D) || isa<ParmVarDecl>(D))) {
|
||||
S.Diag(Attr.getLoc(), diag::warn_attribute_wrong_decl_type) <<
|
||||
Attr.getName() << ExpectedFunctionMethodOrParameter;
|
||||
return;
|
||||
}
|
||||
|
||||
ReturnTypestateAttr::ConsumedState ReturnState;
|
||||
|
||||
if (Attr.isArgIdent(0)) {
|
||||
@ -1084,18 +1092,16 @@ static void handleReturnTypestateAttr(Sema &S, Decl *D,
|
||||
return;
|
||||
}
|
||||
|
||||
if (!isa<FunctionDecl>(D)) {
|
||||
S.Diag(Attr.getLoc(), diag::warn_attribute_wrong_decl_type) <<
|
||||
Attr.getName() << ExpectedFunction;
|
||||
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;
|
||||
//
|
||||
//if (const CXXConstructorDecl *Constructor = dyn_cast<CXXConstructorDecl>(D)) {
|
||||
//if (const ParmVarDecl *Param = dyn_cast<ParmVarDecl>(D)) {
|
||||
// ReturnType = Param->getType();
|
||||
//
|
||||
//} else if (const CXXConstructorDecl *Constructor =
|
||||
// dyn_cast<CXXConstructorDecl>(D)) {
|
||||
// ReturnType = Constructor->getThisType(S.getASTContext())->getPointeeType();
|
||||
//
|
||||
//} else {
|
||||
|
@ -388,6 +388,24 @@ void testFunctionParam(ConsumableClass<int> param) {
|
||||
*param; // expected-warning {{invocation of method 'operator*' on object 'param' while it is in the 'consumed' state}}
|
||||
}
|
||||
|
||||
void testParamReturnTypestateCallee(bool cond, ConsumableClass<int> &Param RETURN_TYPESTATE(unconsumed)) { // expected-warning {{parameter 'Param' not in expected state when the function returns: expected 'unconsumed', observed 'consumed'}}
|
||||
|
||||
if (cond) {
|
||||
Param.consume();
|
||||
return; // expected-warning {{parameter 'Param' not in expected state when the function returns: expected 'unconsumed', observed 'consumed'}}
|
||||
}
|
||||
|
||||
Param.consume();
|
||||
}
|
||||
|
||||
void testParamReturnTypestateCaller() {
|
||||
ConsumableClass<int> var;
|
||||
|
||||
testParamReturnTypestateCallee(true, var);
|
||||
|
||||
*var;
|
||||
}
|
||||
|
||||
void testCallingConventions() {
|
||||
ConsumableClass<int> var(42);
|
||||
|
||||
|
@ -44,6 +44,8 @@ class CONSUMABLE(unknown) AttrTester1 {
|
||||
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}}
|
||||
|
||||
void returnTypestateTester2(AttrTester1 &Param RETURN_TYPESTATE(unconsumed));
|
||||
|
||||
class AttrTester2 {
|
||||
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() SET_TYPESTATE(consumed); // expected-warning {{consumed analysis attribute is attached to member of class 'AttrTester2' which isn't marked as consumable}}
|
||||
|
Loading…
x
Reference in New Issue
Block a user