summaryrefslogtreecommitdiffstats
path: root/clang/lib/Analysis/Consumed.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'clang/lib/Analysis/Consumed.cpp')
-rw-r--r--clang/lib/Analysis/Consumed.cpp108
1 files changed, 104 insertions, 4 deletions
diff --git a/clang/lib/Analysis/Consumed.cpp b/clang/lib/Analysis/Consumed.cpp
index 59ebbb2fc05..6ffdb23f2e9 100644
--- a/clang/lib/Analysis/Consumed.cpp
+++ b/clang/lib/Analysis/Consumed.cpp
@@ -31,6 +31,7 @@
#include "llvm/Support/Compiler.h"
#include "llvm/Support/raw_ostream.h"
+// TODO: Add notes about the actual and expected state for
// TODO: Correctly identify unreachable blocks when chaining boolean operators.
// TODO: Warn about unreachable code.
// TODO: Switch to using a bitmap to track unreachable blocks.
@@ -88,6 +89,19 @@ static bool isTestingFunction(const FunctionDecl *FunDecl) {
return FunDecl->hasAttr<TestsUnconsumedAttr>();
}
+static ConsumedState mapReturnTypestateAttrState(
+ const ReturnTypestateAttr *RTSAttr) {
+
+ switch (RTSAttr->getState()) {
+ case ReturnTypestateAttr::Unknown:
+ return CS_Unknown;
+ case ReturnTypestateAttr::Unconsumed:
+ return CS_Unconsumed;
+ case ReturnTypestateAttr::Consumed:
+ return CS_Consumed;
+ }
+}
+
static StringRef stateToString(ConsumedState State) {
switch (State) {
case consumed::CS_None:
@@ -256,6 +270,8 @@ class ConsumedStmtVisitor : public ConstStmtVisitor<ConsumedStmtVisitor> {
void forwardInfo(const Stmt *From, const Stmt *To);
void handleTestingFunctionCall(const CallExpr *Call, const VarDecl *Var);
bool isLikeMoveAssignment(const CXXMethodDecl *MethodDecl);
+ void propagateReturnType(const Stmt *Call, const FunctionDecl *Fun,
+ QualType ReturnType);
public:
@@ -272,6 +288,7 @@ public:
void VisitMaterializeTemporaryExpr(const MaterializeTemporaryExpr *Temp);
void VisitMemberExpr(const MemberExpr *MExpr);
void VisitParmVarDecl(const ParmVarDecl *Param);
+ void VisitReturnStmt(const ReturnStmt *Ret);
void VisitUnaryOperator(const UnaryOperator *UOp);
void VisitVarDecl(const VarDecl *Var);
@@ -373,6 +390,24 @@ bool ConsumedStmtVisitor::isLikeMoveAssignment(
MethodDecl->getParamDecl(0)->getType()->isRValueReferenceType());
}
+void ConsumedStmtVisitor::propagateReturnType(const Stmt *Call,
+ const FunctionDecl *Fun,
+ QualType ReturnType) {
+ if (isConsumableType(ReturnType)) {
+
+ ConsumedState ReturnState;
+
+ if (Fun->hasAttr<ReturnTypestateAttr>())
+ ReturnState = mapReturnTypestateAttrState(
+ Fun->getAttr<ReturnTypestateAttr>());
+ else
+ ReturnState = CS_Unknown;
+
+ PropagationMap.insert(PairType(Call,
+ PropagationInfo(ReturnState)));
+ }
+}
+
void ConsumedStmtVisitor::Visit(const Stmt *StmtNode) {
ConstStmtVisitor<ConsumedStmtVisitor>::Visit(StmtNode);
@@ -469,6 +504,8 @@ void ConsumedStmtVisitor::VisitCallExpr(const CallExpr *Call) {
StateMap->setState(PInfo.getVar(), consumed::CS_Unknown);
}
}
+
+ propagateReturnType(Call, FunDecl, FunDecl->getCallResultType());
}
}
@@ -483,8 +520,7 @@ void ConsumedStmtVisitor::VisitCXXConstructExpr(const CXXConstructExpr *Call) {
QualType ThisType = Constructor->getThisType(CurrContext)->getPointeeType();
if (isConsumableType(ThisType)) {
- if (Constructor->hasAttr<ConsumesAttr>() ||
- Constructor->isDefaultConstructor()) {
+ if (Constructor->isDefaultConstructor()) {
PropagationMap.insert(PairType(Call,
PropagationInfo(consumed::CS_Consumed)));
@@ -513,8 +549,7 @@ void ConsumedStmtVisitor::VisitCXXConstructExpr(const CXXConstructExpr *Call) {
PropagationMap.insert(PairType(Call, Entry->second));
} else {
- PropagationMap.insert(PairType(Call,
- PropagationInfo(consumed::CS_Unconsumed)));
+ propagateReturnType(Call, Constructor, ThisType);
}
}
}
@@ -677,6 +712,24 @@ void ConsumedStmtVisitor::VisitParmVarDecl(const ParmVarDecl *Param) {
StateMap->setState(Param, consumed::CS_Unknown);
}
+void ConsumedStmtVisitor::VisitReturnStmt(const ReturnStmt *Ret) {
+ if (ConsumedState ExpectedState = Analyzer.getExpectedReturnState()) {
+ InfoEntry Entry = PropagationMap.find(Ret->getRetValue());
+
+ if (Entry != PropagationMap.end()) {
+ assert(Entry->second.isState() || Entry->second.isVar());
+
+ ConsumedState RetState = Entry->second.isState() ?
+ Entry->second.getState() : StateMap->getState(Entry->second.getVar());
+
+ if (RetState != ExpectedState)
+ Analyzer.WarningsHandler.warnReturnTypestateMismatch(
+ Ret->getReturnLoc(), stateToString(ExpectedState),
+ stateToString(RetState));
+ }
+ }
+}
+
void ConsumedStmtVisitor::VisitUnaryOperator(const UnaryOperator *UOp) {
InfoEntry Entry = PropagationMap.find(UOp->getSubExpr()->IgnoreParens());
if (Entry == PropagationMap.end()) return;
@@ -997,6 +1050,53 @@ void ConsumedAnalyzer::run(AnalysisDeclContext &AC) {
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;
+ }
+
BlockInfo = ConsumedBlockInfo(AC.getCFG());
PostOrderCFGView *SortedGraph = AC.getAnalysis<PostOrderCFGView>();
OpenPOWER on IntegriCloud