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 [email protected]. 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; _______________________________________________ cfe-commits mailing list [email protected] http://lists.cs.uiuc.edu/mailman/listinfo/cfe-commits
