r189059 - Update to consumed analysis.
DeLesley Hutchins
delesley at google.com
Thu Aug 22 13:44:47 PDT 2013
Author: delesley
Date: Thu Aug 22 15:44:47 2013
New Revision: 189059
URL: http://llvm.org/viewvc/llvm-project?rev=189059&view=rev
Log:
Update to consumed analysis.
Patch by chris.wailes at gmail.com. The following functionality was added:
* The same functionality is now supported for both CXXOperatorCallExprs and CXXMemberCallExprs.
* Factored out some code in StmtVisitor.
* Removed variables from the state map when their destructors are encountered.
* Started adding documentation for the consumed analysis attributes.
Modified:
cfe/trunk/docs/LanguageExtensions.rst
cfe/trunk/include/clang/Analysis/Analyses/Consumed.h
cfe/trunk/include/clang/Basic/Attr.td
cfe/trunk/lib/Analysis/Consumed.cpp
cfe/trunk/lib/Sema/AnalysisBasedWarnings.cpp
cfe/trunk/test/SemaCXX/warn-consumed-analysis.cpp
Modified: cfe/trunk/docs/LanguageExtensions.rst
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/docs/LanguageExtensions.rst?rev=189059&r1=189058&r2=189059&view=diff
==============================================================================
--- cfe/trunk/docs/LanguageExtensions.rst (original)
+++ cfe/trunk/docs/LanguageExtensions.rst Thu Aug 22 15:44:47 2013
@@ -2022,6 +2022,33 @@ to specify that the function must be cal
locks. Arguments must be lockable type, and there must be at least one
argument.
+Consumed Annotation Checking
+============================
+
+Clang supports additional attributes for checking basic resource management
+properties, specifically for unique objects that have a single owning reference.
+The following attributes are currently supported, although **the implementation
+for these annotations is currently in development and are subject to change.**
+
+``consumes``
+------------
+
+Use ``__attribute__((consumes))`` on a method that transitions an object into
+the consumed state.
+
+``callable_when_unconsumed``
+----------------------------
+
+Use ``__attribute__((callable_when_unconsumed))`` to indicate that a method may
+only be called when the object is not in the consumed state.
+
+``tests_unconsumed``
+--------------------
+
+Use `__attribute__((tests_unconsumed))`` to indicate that a method returns true
+if the object is in the unconsumed state.
+
+
Type Safety Checking
====================
Modified: cfe/trunk/include/clang/Analysis/Analyses/Consumed.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Analysis/Analyses/Consumed.h?rev=189059&r1=189058&r2=189059&view=diff
==============================================================================
--- cfe/trunk/include/clang/Analysis/Analyses/Consumed.h (original)
+++ cfe/trunk/include/clang/Analysis/Analyses/Consumed.h Thu Aug 22 15:44:47 2013
@@ -118,6 +118,9 @@ namespace consumed {
/// \brief Set the consumed state of a given variable.
void setState(const VarDecl *Var, ConsumedState State);
+
+ /// \brief Remove the variable from our state map.
+ void remove(const VarDecl *Var);
};
class ConsumedBlockInfo {
@@ -186,10 +189,6 @@ namespace consumed {
/// exactly once.
void run(AnalysisDeclContext &AC);
};
-
- /// \brief Check to see if a function tests an object's validity.
- bool isTestingFunction(const CXXMethodDecl *MethodDecl);
-
}} // end namespace clang::consumed
#endif
Modified: cfe/trunk/include/clang/Basic/Attr.td
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/Attr.td?rev=189059&r1=189058&r2=189059&view=diff
==============================================================================
--- cfe/trunk/include/clang/Basic/Attr.td (original)
+++ cfe/trunk/include/clang/Basic/Attr.td Thu Aug 22 15:44:47 2013
@@ -932,7 +932,7 @@ def TestsUnconsumed : InheritableAttr {
def Consumes : InheritableAttr {
let Spellings = [GNU<"consumes">];
- let Subjects = [CXXMethod];
+ let Subjects = [CXXMethod, CXXConstructor];
}
def TestsConsumed : InheritableAttr {
Modified: cfe/trunk/lib/Analysis/Consumed.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Analysis/Consumed.cpp?rev=189059&r1=189058&r2=189059&view=diff
==============================================================================
--- cfe/trunk/lib/Analysis/Consumed.cpp (original)
+++ cfe/trunk/lib/Analysis/Consumed.cpp Thu Aug 22 15:44:47 2013
@@ -30,7 +30,6 @@
#include "llvm/ADT/SmallVector.h"
#include "llvm/Support/raw_ostream.h"
-// TODO: Add support for methods with CallableWhenUnconsumed.
// TODO: Mark variables as Unknown going into while- or for-loops only if they
// are referenced inside that block. (Deferred)
// TODO: Add a method(s) to identify which method calls perform what state
@@ -48,6 +47,10 @@ using namespace consumed;
// Key method definition
ConsumedWarningsHandlerBase::~ConsumedWarningsHandlerBase() {}
+static bool isTestingFunction(const FunctionDecl *FunDecl) {
+ return FunDecl->hasAttr<TestsUnconsumedAttr>();
+}
+
static StringRef stateToString(ConsumedState State) {
switch (State) {
case consumed::CS_None:
@@ -91,9 +94,9 @@ class ConsumedStmtVisitor : public Const
StateOrVar.Var = Var;
}
- ConsumedState getState() { return StateOrVar.State; };
+ ConsumedState getState() const { return StateOrVar.State; }
- const VarDecl * getVar() { return IsVar ? StateOrVar.Var : NULL; };
+ const VarDecl * getVar() const { return IsVar ? StateOrVar.Var : NULL; }
};
typedef llvm::DenseMap<const Stmt *, PropagationInfo> MapType;
@@ -105,6 +108,9 @@ class ConsumedStmtVisitor : public Const
ConsumedStateMap *StateMap;
MapType PropagationMap;
+ void checkCallability(const PropagationInfo &PState,
+ const FunctionDecl *FunDecl,
+ const CallExpr *Call);
void forwardInfo(const Stmt *From, const Stmt *To);
bool isLikeMoveAssignment(const CXXMethodDecl *MethodDecl);
@@ -134,6 +140,54 @@ public:
}
};
+// TODO: When we support CallableWhenConsumed this will have to check for
+// the different attributes and change the behavior bellow. (Deferred)
+void ConsumedStmtVisitor::checkCallability(const PropagationInfo &PState,
+ const FunctionDecl *FunDecl,
+ const CallExpr *Call) {
+
+ if (!FunDecl->hasAttr<CallableWhenUnconsumedAttr>()) return;
+
+ if (PState.IsVar) {
+ const VarDecl *Var = PState.getVar();
+
+ switch (StateMap->getState(Var)) {
+ case CS_Consumed:
+ Analyzer.WarningsHandler.warnUseWhileConsumed(
+ FunDecl->getNameAsString(), Var->getNameAsString(),
+ Call->getExprLoc());
+ break;
+
+ case CS_Unknown:
+ Analyzer.WarningsHandler.warnUseInUnknownState(
+ FunDecl->getNameAsString(), Var->getNameAsString(),
+ Call->getExprLoc());
+ break;
+
+ case CS_None:
+ case CS_Unconsumed:
+ break;
+ }
+
+ } else {
+ switch (PState.getState()) {
+ case CS_Consumed:
+ Analyzer.WarningsHandler.warnUseOfTempWhileConsumed(
+ FunDecl->getNameAsString(), Call->getExprLoc());
+ break;
+
+ case CS_Unknown:
+ Analyzer.WarningsHandler.warnUseOfTempInUnknownState(
+ FunDecl->getNameAsString(), Call->getExprLoc());
+ break;
+
+ case CS_None:
+ case CS_Unconsumed:
+ break;
+ }
+ }
+}
+
void ConsumedStmtVisitor::forwardInfo(const Stmt *From, const Stmt *To) {
InfoEntry Entry = PropagationMap.find(From);
@@ -278,14 +332,16 @@ void ConsumedStmtVisitor::VisitCXXMember
if (Entry != PropagationMap.end()) {
PropagationInfo PState = Entry->second;
- if (!PState.IsVar) return;
+ const CXXMethodDecl *MethodDecl = Call->getMethodDecl();
- const CXXMethodDecl *Method = Call->getMethodDecl();
+ checkCallability(PState, MethodDecl, Call);
- if (Method->hasAttr<ConsumesAttr>())
- StateMap->setState(PState.getVar(), consumed::CS_Consumed);
- else if (!Method->isConst())
- StateMap->setState(PState.getVar(), consumed::CS_Unknown);
+ if (PState.IsVar) {
+ if (MethodDecl->hasAttr<ConsumesAttr>())
+ StateMap->setState(PState.getVar(), consumed::CS_Consumed);
+ else if (!MethodDecl->isConst())
+ StateMap->setState(PState.getVar(), consumed::CS_Unknown);
+ }
}
}
@@ -374,58 +430,22 @@ void ConsumedStmtVisitor::VisitCXXOperat
InfoEntry Entry = PropagationMap.find(Call->getArg(0));
if (Entry != PropagationMap.end()) {
-
PropagationInfo PState = Entry->second;
- // TODO: When we support CallableWhenConsumed this will have to check for
- // the different attributes and change the behavior bellow.
- // (Deferred)
- if (FunDecl->hasAttr<CallableWhenUnconsumedAttr>()) {
- if (PState.IsVar) {
- const VarDecl *Var = PState.getVar();
-
- switch (StateMap->getState(Var)) {
- case CS_Consumed:
- Analyzer.WarningsHandler.warnUseWhileConsumed(
- FunDecl->getNameAsString(), Var->getNameAsString(),
- Call->getExprLoc());
- break;
-
- case CS_Unknown:
- Analyzer.WarningsHandler.warnUseInUnknownState(
- FunDecl->getNameAsString(), Var->getNameAsString(),
- Call->getExprLoc());
- break;
-
- default:
- break;
- }
-
- } else {
- switch (PState.getState()) {
- case CS_Consumed:
- Analyzer.WarningsHandler.warnUseOfTempWhileConsumed(
- FunDecl->getNameAsString(), Call->getExprLoc());
- break;
+ checkCallability(PState, FunDecl, Call);
+
+ if (PState.IsVar) {
+ if (FunDecl->hasAttr<ConsumesAttr>()) {
+ // Handle consuming operators.
+ StateMap->setState(PState.getVar(), consumed::CS_Consumed);
+ } else if (const CXXMethodDecl *MethodDecl =
+ dyn_cast_or_null<CXXMethodDecl>(FunDecl)) {
- case CS_Unknown:
- Analyzer.WarningsHandler.warnUseOfTempInUnknownState(
- FunDecl->getNameAsString(), Call->getExprLoc());
- break;
-
- default:
- break;
- }
+ // Handle non-constant member operators.
+ if (!MethodDecl->isConst())
+ StateMap->setState(PState.getVar(), consumed::CS_Unknown);
}
}
-
- // Handle non-constant member operators.
- if (const CXXMethodDecl *MethodDecl =
- dyn_cast_or_null<CXXMethodDecl>(FunDecl)) {
-
- if (!MethodDecl->isConst() && PState.IsVar)
- StateMap->setState(PState.getVar(), consumed::CS_Unknown);
- }
}
}
}
@@ -505,10 +525,10 @@ public:
};
bool TestedVarsVisitor::VisitCallExpr(CallExpr *Call) {
- if (const CXXMethodDecl *Method =
- dyn_cast_or_null<CXXMethodDecl>(Call->getDirectCallee())) {
+ if (const FunctionDecl *FunDecl =
+ dyn_cast_or_null<FunctionDecl>(Call->getDirectCallee())) {
- if (isTestingFunction(Method)) {
+ if (isTestingFunction(FunDecl)) {
CurrTestLoc = Call->getExprLoc();
IsUsefulConditional = true;
return true;
@@ -521,16 +541,11 @@ bool TestedVarsVisitor::VisitCallExpr(Ca
}
bool TestedVarsVisitor::VisitDeclRefExpr(DeclRefExpr *DeclRef) {
- if (const VarDecl *Var = dyn_cast_or_null<VarDecl>(DeclRef->getDecl())) {
- if (StateMap->getState(Var) != consumed::CS_None) {
+ if (const VarDecl *Var = dyn_cast_or_null<VarDecl>(DeclRef->getDecl()))
+ if (StateMap->getState(Var) != consumed::CS_None)
Test = VarTestResult(Var, CurrTestLoc, !Invert);
- }
-
- } else {
- IsUsefulConditional = false;
- }
- return IsUsefulConditional;
+ return true;
}
bool TestedVarsVisitor::VisitUnaryOperator(UnaryOperator *UnaryOp) {
@@ -637,6 +652,9 @@ void ConsumedStateMap::setState(const Va
Map[Var] = State;
}
+void ConsumedStateMap::remove(const VarDecl *Var) {
+ Map.erase(Var);
+}
bool ConsumedAnalyzer::isConsumableType(QualType Type) {
const CXXRecordDecl *RD =
@@ -741,13 +759,17 @@ void ConsumedAnalyzer::run(AnalysisDeclC
for (CFGBlock::const_iterator BI = CurrBlock->begin(),
BE = CurrBlock->end(); BI != BE; ++BI) {
- if (BI->getKind() == CFGElement::Statement)
+ switch (BI->getKind()) {
+ case CFGElement::Statement:
Visitor.Visit(BI->castAs<CFGStmt>().getStmt());
+ break;
+ case CFGElement::AutomaticObjectDtor:
+ CurrStates->remove(BI->castAs<CFGAutomaticObjDtor>().getVarDecl());
+ default:
+ break;
+ }
}
- // TODO: Remove any variables that have reached the end of their
- // lifetimes from the state map. (Deferred)
-
if (const IfStmt *Terminator =
dyn_cast_or_null<IfStmt>(CurrBlock->getTerminator().getStmt())) {
@@ -785,9 +807,4 @@ void ConsumedAnalyzer::run(AnalysisDeclC
WarningsHandler.emitDiagnostics();
}
-
-bool isTestingFunction(const CXXMethodDecl *Method) {
- return Method->hasAttr<TestsUnconsumedAttr>();
-}
-
}} // end namespace clang::consumed
Modified: cfe/trunk/lib/Sema/AnalysisBasedWarnings.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/AnalysisBasedWarnings.cpp?rev=189059&r1=189058&r2=189059&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/AnalysisBasedWarnings.cpp (original)
+++ cfe/trunk/lib/Sema/AnalysisBasedWarnings.cpp Thu Aug 22 15:44:47 2013
@@ -1551,10 +1551,9 @@ clang::sema::AnalysisBasedWarnings::Anal
DefaultPolicy.enableThreadSafetyAnalysis = (unsigned)
(D.getDiagnosticLevel(diag::warn_double_lock, SourceLocation()) !=
DiagnosticsEngine::Ignored);
- DefaultPolicy.enableConsumedAnalysis =
- (unsigned)(D.getDiagnosticLevel(diag::warn_use_while_consumed,
- SourceLocation()) !=
- DiagnosticsEngine::Ignored);
+ DefaultPolicy.enableConsumedAnalysis = (unsigned)
+ (D.getDiagnosticLevel(diag::warn_use_while_consumed, SourceLocation()) !=
+ DiagnosticsEngine::Ignored);
}
static void flushDiagnostics(Sema &S, sema::FunctionScopeInfo *fscope) {
Modified: cfe/trunk/test/SemaCXX/warn-consumed-analysis.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaCXX/warn-consumed-analysis.cpp?rev=189059&r1=189058&r2=189059&view=diff
==============================================================================
--- cfe/trunk/test/SemaCXX/warn-consumed-analysis.cpp (original)
+++ cfe/trunk/test/SemaCXX/warn-consumed-analysis.cpp Thu Aug 22 15:44:47 2013
@@ -27,10 +27,13 @@ class Bar {
template <typename U>
Bar<T>& operator=(Bar<U> &&other);
+ void operator()(int a) CONSUMES;
void operator*(void) const CALLABLE_WHEN_UNCONSUMED;
+ void unconsumedCall(void) const CALLABLE_WHEN_UNCONSUMED;
bool isValid(void) const TESTS_UNCONSUMED;
operator bool() const TESTS_UNCONSUMED;
+ bool operator!=(nullptr_t) const TESTS_UNCONSUMED;
void constCall(void) const;
void nonconstCall(void);
@@ -62,6 +65,10 @@ void testInitialization(void) {
}
}
+void testTempValue(void) {
+ *Bar<int>(); // expected-warning {{invocation of method 'operator*' on a temporary object while it is in the 'consumed' state}}
+}
+
void testSimpleRValueRefs(void) {
Bar<int> var0;
Bar<int> var1(42);
@@ -98,6 +105,13 @@ void testIfStmt(void) {
} else {
*var; // expected-warning {{invocation of method 'operator*' on object 'var' while it is in the 'consumed' state}}
}
+
+ if (var != nullptr) {
+ // Empty
+
+ } else {
+ *var; // expected-warning {{invocation of method 'operator*' on object 'var' while it is in the 'consumed' state}}
+ }
}
void testCallingConventions(void) {
@@ -164,6 +178,15 @@ void testConsumes1(void) {
*var; // expected-warning {{invocation of method 'operator*' on object 'var' while it is in the 'consumed' state}}
}
+void testConsumes2(void) {
+ Bar<int> var(42);
+
+ var.unconsumedCall();
+ var(6);
+
+ var.unconsumedCall(); // expected-warning {{invocation of method 'unconsumedCall' on object 'var' while it is in the 'consumed' state}}
+}
+
void testSimpleForLoop(void) {
Bar<int> var;
More information about the cfe-commits
mailing list