summaryrefslogtreecommitdiffstats
path: root/clang/lib
diff options
context:
space:
mode:
Diffstat (limited to 'clang/lib')
-rw-r--r--clang/lib/Analysis/Consumed.cpp108
-rw-r--r--clang/lib/Sema/AnalysisBasedWarnings.cpp48
-rw-r--r--clang/lib/Sema/SemaDeclAttr.cpp61
3 files changed, 182 insertions, 35 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>();
diff --git a/clang/lib/Sema/AnalysisBasedWarnings.cpp b/clang/lib/Sema/AnalysisBasedWarnings.cpp
index 74de09a9566..d4997429edc 100644
--- a/clang/lib/Sema/AnalysisBasedWarnings.cpp
+++ b/clang/lib/Sema/AnalysisBasedWarnings.cpp
@@ -1445,11 +1445,23 @@ public:
}
}
- /// Warn about unnecessary-test errors.
- /// \param VariableName -- The name of the variable that holds the unique
- /// value.
- ///
- /// \param Loc -- The SourceLocation of the unnecessary test.
+ void warnReturnTypestateForUnconsumableType(SourceLocation Loc,
+ StringRef TypeName) {
+ PartialDiagnosticAt Warning(Loc, S.PDiag(
+ diag::warn_return_typestate_for_unconsumable_type) << TypeName);
+
+ Warnings.push_back(DelayedDiag(Warning, OptionalNotes()));
+ }
+
+ void warnReturnTypestateMismatch(SourceLocation Loc, StringRef ExpectedState,
+ StringRef ObservedState) {
+
+ PartialDiagnosticAt Warning(Loc, S.PDiag(
+ diag::warn_return_typestate_mismatch) << ExpectedState << ObservedState);
+
+ Warnings.push_back(DelayedDiag(Warning, OptionalNotes()));
+ }
+
void warnUnnecessaryTest(StringRef VariableName, StringRef VariableState,
SourceLocation Loc) {
@@ -1459,11 +1471,6 @@ public:
Warnings.push_back(DelayedDiag(Warning, OptionalNotes()));
}
- /// Warn about use-while-consumed errors.
- /// \param MethodName -- The name of the method that was incorrectly
- /// invoked.
- ///
- /// \param Loc -- The SourceLocation of the method invocation.
void warnUseOfTempWhileConsumed(StringRef MethodName, SourceLocation Loc) {
PartialDiagnosticAt Warning(Loc, S.PDiag(
@@ -1472,11 +1479,6 @@ public:
Warnings.push_back(DelayedDiag(Warning, OptionalNotes()));
}
- /// Warn about use-in-unknown-state errors.
- /// \param MethodName -- The name of the method that was incorrectly
- /// invoked.
- ///
- /// \param Loc -- The SourceLocation of the method invocation.
void warnUseOfTempInUnknownState(StringRef MethodName, SourceLocation Loc) {
PartialDiagnosticAt Warning(Loc, S.PDiag(
@@ -1485,14 +1487,6 @@ public:
Warnings.push_back(DelayedDiag(Warning, OptionalNotes()));
}
- /// 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.
void warnUseWhileConsumed(StringRef MethodName, StringRef VariableName,
SourceLocation Loc) {
@@ -1502,14 +1496,6 @@ public:
Warnings.push_back(DelayedDiag(Warning, OptionalNotes()));
}
- /// Warn about use-in-unknown-state 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.
void warnUseInUnknownState(StringRef MethodName, StringRef VariableName,
SourceLocation Loc) {
diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp
index 00f8af9382b..cc4f107aeb5 100644
--- a/clang/lib/Sema/SemaDeclAttr.cpp
+++ b/clang/lib/Sema/SemaDeclAttr.cpp
@@ -1069,6 +1069,64 @@ static void handleTestsUnconsumedAttr(Sema &S, Decl *D,
Attr.getAttributeSpellingListIndex()));
}
+static void handleReturnTypestateAttr(Sema &S, Decl *D,
+ const AttributeList &Attr) {
+ if (!checkAttributeNumArgs(S, Attr, 1)) return;
+
+ ReturnTypestateAttr::ConsumedState ReturnState;
+
+ if (Attr.isArgIdent(0)) {
+ StringRef Param = Attr.getArgAsIdent(0)->Ident->getName();
+
+ if (Param == "unknown") {
+ ReturnState = ReturnTypestateAttr::Unknown;
+ } else if (Param == "consumed") {
+ ReturnState = ReturnTypestateAttr::Consumed;
+ } else if (Param == "unconsumed") {
+ ReturnState = ReturnTypestateAttr::Unconsumed;
+ } else {
+ S.Diag(Attr.getLoc(), diag::warn_unknown_consumed_state) << Param;
+ return;
+ }
+
+ } else {
+ S.Diag(Attr.getLoc(), diag::err_attribute_argument_type) <<
+ Attr.getName() << AANT_ArgumentIdentifier;
+ 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)) {
+ // ReturnType = Constructor->getThisType(S.getASTContext())->getPointeeType();
+ //
+ //} else {
+ //
+ // ReturnType = cast<FunctionDecl>(D)->getCallResultType();
+ //}
+ //
+ //const CXXRecordDecl *RD = ReturnType->getAsCXXRecordDecl();
+ //
+ //if (!RD || !RD->hasAttr<ConsumableAttr>()) {
+ // S.Diag(Attr.getLoc(), diag::warn_return_state_for_unconsumable_type) <<
+ // ReturnType.getAsString();
+ // return;
+ //}
+
+ D->addAttr(::new (S.Context)
+ ReturnTypestateAttr(Attr.getRange(), S.Context, ReturnState,
+ Attr.getAttributeSpellingListIndex()));
+}
+
static void handleExtVectorTypeAttr(Sema &S, Scope *scope, Decl *D,
const AttributeList &Attr) {
TypedefNameDecl *TD = dyn_cast<TypedefNameDecl>(D);
@@ -5024,6 +5082,9 @@ static void ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D,
case AttributeList::AT_TestsUnconsumed:
handleTestsUnconsumedAttr(S, D, Attr);
break;
+ case AttributeList::AT_ReturnTypestate:
+ handleReturnTypestateAttr(S, D, Attr);
+ break;
// Type safety attributes.
case AttributeList::AT_ArgumentWithTypeTag:
OpenPOWER on IntegriCloud