summaryrefslogtreecommitdiffstats
path: root/clang/lib/Analysis
diff options
context:
space:
mode:
authorDeLesley Hutchins <delesley@google.com>2013-09-03 20:11:38 +0000
committerDeLesley Hutchins <delesley@google.com>2013-09-03 20:11:38 +0000
commitfc368259af1a580b1843174cae3067d1b3c9aafc (patch)
tree2732d2c5da4cb77df8c39009f0077f78387c6382 /clang/lib/Analysis
parent362bf98ec68b63f8abcd347e48db2551436de75d (diff)
downloadbcm5719-llvm-fc368259af1a580b1843174cae3067d1b3c9aafc.tar.gz
bcm5719-llvm-fc368259af1a580b1843174cae3067d1b3c9aafc.zip
Consumed analysis: add return_typestate attribute.
Patch by chris.wailes@gmail.com Functions can now declare what state the consumable type the are returning will be in. This is then used on the caller side and checked on the callee side. Constructors now use this attribute instead of the 'consumes' attribute. llvm-svn: 189843
Diffstat (limited to 'clang/lib/Analysis')
-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