--- /dev/null
+//===- Consumed.h ----------------------------------------------*- C++ --*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// A intra-procedural analysis for checking consumed properties. This is based,
+// in part, on research on linear types.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_CONSUMED_H
+#define LLVM_CLANG_CONSUMED_H
+
+#include "clang/AST/DeclCXX.h"
+#include "clang/AST/ExprCXX.h"
+#include "clang/AST/StmtCXX.h"
+#include "clang/Analysis/AnalysisContext.h"
+#include "clang/Analysis/Analyses/PostOrderCFGView.h"
+#include "clang/Basic/SourceLocation.h"
+#include "clang/Sema/ConsumedWarningsHandler.h"
+#include "clang/Sema/Sema.h"
+
+namespace clang {
+namespace consumed {
+
+ enum ConsumedState {
+ // No state information for the given variable.
+ CS_None,
+
+ CS_Unknown,
+ CS_Unconsumed,
+ CS_Consumed
+ };
+
+ class ConsumedStateMap {
+
+ typedef llvm::DenseMap<const VarDecl *, ConsumedState> MapType;
+ typedef std::pair<const VarDecl *, ConsumedState> PairType;
+
+ protected:
+
+ MapType Map;
+
+ public:
+ /// \brief Get the consumed state of a given variable.
+ ConsumedState getState(const VarDecl *Var);
+
+ /// \brief Merge this state map with another map.
+ void intersect(const ConsumedStateMap *Other);
+
+ /// \brief Mark all variables as unknown.
+ void makeUnknown();
+
+ /// \brief Set the consumed state of a given variable.
+ void setState(const VarDecl *Var, ConsumedState State);
+ };
+
+ class ConsumedBlockInfo {
+
+ ConsumedStateMap **StateMapsArray;
+ PostOrderCFGView::CFGBlockSet VisitedBlocks;
+
+ public:
+
+ ConsumedBlockInfo() : StateMapsArray(NULL) {}
+
+ ConsumedBlockInfo(const CFG *CFGraph)
+ : StateMapsArray(new ConsumedStateMap*[CFGraph->getNumBlockIDs()]()),
+ VisitedBlocks(CFGraph) {}
+
+ void addInfo(const CFGBlock *Block, ConsumedStateMap *StateMap,
+ bool &AlreadyOwned);
+ void addInfo(const CFGBlock *Block, ConsumedStateMap *StateMap);
+
+ ConsumedStateMap* getInfo(const CFGBlock *Block);
+
+ void markVisited(const CFGBlock *Block);
+ };
+
+ struct VarTestResult {
+ const VarDecl *Var;
+ SourceLocation Loc;
+ bool UnconsumedInTrueBranch;
+
+ VarTestResult() : Var(NULL), Loc(), UnconsumedInTrueBranch(true) {}
+
+ VarTestResult(const VarDecl *Var, SourceLocation Loc,
+ bool UnconsumedInTrueBranch)
+ : Var(Var), Loc(Loc), UnconsumedInTrueBranch(UnconsumedInTrueBranch) {}
+ };
+
+ /// A class that handles the analysis of uniqueness violations.
+ class ConsumedAnalyzer {
+
+ typedef llvm::DenseMap<const CXXRecordDecl *, bool> CacheMapType;
+ typedef std::pair<const CXXRecordDecl *, bool> CachePairType;
+
+ Sema &S;
+
+ ConsumedBlockInfo BlockInfo;
+ ConsumedStateMap *CurrStates;
+
+ CacheMapType ConsumableTypeCache;
+
+ bool hasConsumableAttributes(const CXXRecordDecl *RD);
+ void splitState(const CFGBlock *CurrBlock, const IfStmt *Terminator);
+
+ public:
+
+ ConsumedWarningsHandlerBase &WarningsHandler;
+
+ ConsumedAnalyzer(Sema &S, ConsumedWarningsHandlerBase &WarningsHandler)
+ : S(S), WarningsHandler(WarningsHandler) {}
+
+ /// \brief Get a constant reference to the Sema object.
+ const Sema & getSema(void);
+
+ /// \brief Check to see if the type is a consumable type.
+ bool isConsumableType(QualType Type);
+
+ /// \brief Check a function's CFG for consumed violations.
+ ///
+ /// We traverse the blocks in the CFG, keeping track of the state of each
+ /// value who's type has uniquness annotations. If methods are invoked in
+ /// the wrong state a warning is issued. Each block in the CFG is traversed
+ /// exactly once.
+ void run(AnalysisDeclContext &AC);
+ };
+
+ unsigned checkEnabled(DiagnosticsEngine &D);
+ /// \brief Check to see if a function tests an object's validity.
+ bool isTestingFunction(const CXXMethodDecl *MethodDecl);
+
+}} // end namespace clang::consumed
+
+#endif
let TemplateDependent = 1;
}
+// C/C++ consumed attributes.
+
+def CallableWhenUnconsumed : InheritableAttr {
+ let Spellings = [GNU<"callable_when_unconsumed">];
+ let Subjects = [CXXMethod];
+}
+
+def TestsUnconsumed : InheritableAttr {
+ let Spellings = [GNU<"tests_unconsumed">];
+ let Subjects = [CXXMethod];
+}
+
+def Consumes : InheritableAttr {
+ let Spellings = [GNU<"consumes">];
+ let Subjects = [CXXMethod];
+}
+
+def TestsConsumed : InheritableAttr {
+ let Spellings = [GNU<"tests_consumed">];
+ let Subjects = [CXXMethod];
+}
+
// Type safety attributes for `void *' pointers and type tags.
def ArgumentWithTypeTag : InheritableAttr {
ThreadSafetyPrecise]>;
def ThreadSafetyBeta : DiagGroup<"thread-safety-beta">;
+// Uniqueness Analysis warnings
+def Consumed : DiagGroup<"consumed">;
+def ConsumedStrict : DiagGroup<"consumed-strict", [Consumed]>;
+
// Note that putting warnings in -Wall will not disable them by default. If a
// warning should be active _only_ when -Wall is passed in, mark it as
// DefaultIgnore in addition to putting it here.
def warn_thread_safety_beta : Warning<
"Thread safety beta warning.">, InGroup<ThreadSafetyBeta>, DefaultIgnore;
+// Consumed warnings
+def warn_use_while_consumed : Warning<
+ "invocation of method '%0' on object '%1' while it is in the 'consumed' "
+ "state">, InGroup<Consumed>, DefaultIgnore;
+def warn_use_of_temp_while_consumed : Warning<
+ "invocation of method '%0' on a temporary object while it is in the "
+ "'consumed' state">, InGroup<Consumed>, DefaultIgnore;
+def warn_uniqueness_attribute_wrong_decl_type : Warning<
+ "%0 attribute only applies to methods">,
+ InGroup<Consumed>, DefaultIgnore;
+
+// ConsumedStrict warnings
+def warn_use_in_unknown_state : Warning<
+ "invocation of method '%0' on object '%1' while it is in an unknown state">,
+ InGroup<ConsumedStrict>, DefaultIgnore;
+def warn_use_of_temp_in_unknown_state : Warning<
+ "invocation of method '%0' on a temporary object while it is in an unknown "
+ "state">, InGroup<ConsumedStrict>, DefaultIgnore;
+def warn_unnecessary_test : Warning<
+ "unnecessary test. Variable '%0' is known to be in the '%1' state">,
+ InGroup<ConsumedStrict>, DefaultIgnore;
+
def warn_impcast_vector_scalar : Warning<
"implicit conversion turns vector to scalar: %0 to %1">,
InGroup<Conversion>, DefaultIgnore;
unsigned enableCheckFallThrough : 1;
unsigned enableCheckUnreachable : 1;
unsigned enableThreadSafetyAnalysis : 1;
+ unsigned enableConsumedAnalysis : 1;
public:
Policy();
void disableCheckFallThrough() { enableCheckFallThrough = 0; }
--- /dev/null
+//===- ConsumedWarningsHandler.h -------------------------------*- C++ --*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// A handler class for warnings issued by the consumed analysis.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_CONSUMED_WARNING_HANDLER_H
+#define LLVM_CLANG_CONSUMED_WARNING_HANDLER_H
+
+#include <list>
+#include <utility>
+
+#include "clang/Basic/SourceLocation.h"
+#include "clang/Sema/Sema.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/ADT/StringRef.h"
+
+namespace clang {
+namespace consumed {
+
+ typedef SmallVector<PartialDiagnosticAt, 1> OptionalNotes;
+ typedef std::pair<PartialDiagnosticAt, OptionalNotes> DelayedDiag;
+ typedef std::list<DelayedDiag> DiagList;
+
+ class ConsumedWarningsHandlerBase {
+
+ public:
+
+ virtual ~ConsumedWarningsHandlerBase();
+
+ /// \brief Emit the warnings and notes left by the analysis.
+ virtual void emitDiagnostics() {}
+
+ /// 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.
+ virtual void warnUnnecessaryTest(StringRef VariableName,
+ StringRef VariableState,
+ SourceLocation Loc) {}
+
+ /// 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.
+ virtual void warnUseOfTempWhileConsumed(StringRef MethodName,
+ SourceLocation Loc) {}
+
+ /// 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.
+ virtual void warnUseOfTempInUnknownState(StringRef MethodName,
+ SourceLocation Loc) {}
+
+ /// 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.
+ virtual void warnUseWhileConsumed(StringRef MethodName,
+ StringRef VariableName,
+ SourceLocation Loc) {}
+
+ /// 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.
+ virtual void warnUseInUnknownState(StringRef MethodName,
+ StringRef VariableName,
+ SourceLocation Loc) {}
+ };
+}} // end clang::consumed
+
+#endif
CFGStmtMap.cpp
CallGraph.cpp
CocoaConventions.cpp
+ Consumed.cpp
Dominators.cpp
FormatString.cpp
LiveVariables.cpp
--- /dev/null
+//===- Consumed.cpp --------------------------------------------*- C++ --*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// A intra-procedural analysis for checking consumed properties. This is based,
+// in part, on research on linear types.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/Attr.h"
+#include "clang/AST/DeclCXX.h"
+#include "clang/AST/ExprCXX.h"
+#include "clang/AST/RecursiveASTVisitor.h"
+#include "clang/AST/StmtVisitor.h"
+#include "clang/AST/StmtCXX.h"
+#include "clang/AST/Type.h"
+#include "clang/Analysis/Analyses/PostOrderCFGView.h"
+#include "clang/Analysis/AnalysisContext.h"
+#include "clang/Analysis/CFG.h"
+#include "clang/Analysis/Analyses/Consumed.h"
+#include "clang/Basic/OperatorKinds.h"
+#include "clang/Basic/SourceLocation.h"
+#include "clang/Sema/ConsumedWarningsHandler.h"
+#include "clang/Sema/SemaDiagnostic.h"
+#include "llvm/ADT/DenseMap.h"
+#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
+// transitions. (Deferred)
+// TODO: Take notes on state transitions to provide better warning messages.
+// (Deferred)
+// TODO: Test nested conditionals: A) Checking the same value multiple times,
+// and 2) Checking different values. (Deferred)
+// TODO: Test IsFalseVisitor with values in the unknown state. (Deferred)
+// TODO: Look into combining IsFalseVisitor and TestedVarsVisitor. (Deferred)
+
+using namespace clang;
+using namespace consumed;
+
+// Key method definition
+ConsumedWarningsHandlerBase::~ConsumedWarningsHandlerBase() {}
+
+static StringRef stateToString(ConsumedState State) {
+ switch (State) {
+ case consumed::CS_None:
+ return "none";
+
+ case consumed::CS_Unknown:
+ return "unknown";
+
+ case consumed::CS_Unconsumed:
+ return "unconsumed";
+
+ case consumed::CS_Consumed:
+ return "consumed";
+ }
+}
+
+namespace {
+class ConsumedStmtVisitor : public ConstStmtVisitor<ConsumedStmtVisitor> {
+
+ union PropagationUnion {
+ ConsumedState State;
+ const VarDecl *Var;
+ };
+
+ class PropagationInfo {
+ PropagationUnion StateOrVar;
+
+ public:
+ bool IsVar;
+
+ PropagationInfo() : IsVar(false) {
+ StateOrVar.State = consumed::CS_None;
+ }
+
+ PropagationInfo(ConsumedState State) : IsVar(false) {
+ StateOrVar.State = State;
+ }
+
+ PropagationInfo(const VarDecl *Var) : IsVar(true) {
+ StateOrVar.Var = Var;
+ }
+
+ ConsumedState getState() { return StateOrVar.State; };
+
+ const VarDecl * getVar() { return IsVar ? StateOrVar.Var : NULL; };
+ };
+
+ typedef llvm::DenseMap<const Stmt *, PropagationInfo> MapType;
+ typedef std::pair<const Stmt *, PropagationInfo> PairType;
+ typedef MapType::iterator InfoEntry;
+
+ ConsumedAnalyzer &Analyzer;
+ ConsumedStateMap *StateMap;
+ MapType PropagationMap;
+
+ void forwardInfo(const Stmt *From, const Stmt *To);
+ bool isLikeMoveAssignment(const CXXMethodDecl *MethodDecl);
+
+public:
+
+ void Visit(const Stmt *StmtNode);
+
+ void VisitBinaryOperator(const BinaryOperator *BinOp);
+ void VisitCallExpr(const CallExpr *Call);
+ void VisitCastExpr(const CastExpr *Cast);
+ void VisitCXXConstructExpr(const CXXConstructExpr *Call);
+ void VisitCXXMemberCallExpr(const CXXMemberCallExpr *Call);
+ void VisitCXXOperatorCallExpr(const CXXOperatorCallExpr *Call);
+ void VisitDeclRefExpr(const DeclRefExpr *DeclRef);
+ void VisitDeclStmt(const DeclStmt *DelcS);
+ void VisitMaterializeTemporaryExpr(const MaterializeTemporaryExpr *Temp);
+ void VisitMemberExpr(const MemberExpr *MExpr);
+ void VisitUnaryOperator(const UnaryOperator *UOp);
+ void VisitVarDecl(const VarDecl *Var);
+
+ ConsumedStmtVisitor(ConsumedAnalyzer &Analyzer, ConsumedStateMap *StateMap) :
+ Analyzer(Analyzer), StateMap(StateMap) {}
+
+ void reset() {
+ PropagationMap.clear();
+ }
+};
+
+void ConsumedStmtVisitor::forwardInfo(const Stmt *From, const Stmt *To) {
+ InfoEntry Entry = PropagationMap.find(From);
+
+ if (Entry != PropagationMap.end()) {
+ PropagationMap.insert(PairType(To, PropagationInfo(Entry->second)));
+ }
+}
+
+bool ConsumedStmtVisitor::isLikeMoveAssignment(
+ const CXXMethodDecl *MethodDecl) {
+
+ return MethodDecl->isMoveAssignmentOperator() ||
+ (MethodDecl->getOverloadedOperator() == OO_Equal &&
+ MethodDecl->getNumParams() == 1 &&
+ MethodDecl->getParamDecl(0)->getType()->isRValueReferenceType());
+}
+
+void ConsumedStmtVisitor::VisitBinaryOperator(const BinaryOperator *BinOp) {
+ switch (BinOp->getOpcode()) {
+ case BO_PtrMemD:
+ case BO_PtrMemI:
+ forwardInfo(BinOp->getLHS(), BinOp);
+ break;
+
+ default:
+ break;
+ }
+}
+
+void ConsumedStmtVisitor::Visit(const Stmt *StmtNode) {
+ ConstStmtVisitor::Visit(StmtNode);
+
+ for (Stmt::const_child_iterator CI = StmtNode->child_begin(),
+ CE = StmtNode->child_end(); CI != CE; ++CI) {
+
+ PropagationMap.erase(*CI);
+ }
+}
+
+void ConsumedStmtVisitor::VisitCallExpr(const CallExpr *Call) {
+ if (const FunctionDecl *FunDecl =
+ dyn_cast_or_null<FunctionDecl>(Call->getDirectCallee())) {
+
+ // Special case for the std::move function.
+ // TODO: Make this more specific. (Deferred)
+ if (FunDecl->getNameAsString() == "move") {
+ InfoEntry Entry = PropagationMap.find(Call->getArg(0));
+
+ if (Entry != PropagationMap.end()) {
+ PropagationMap.insert(PairType(Call, Entry->second));
+ }
+
+ return;
+ }
+
+ unsigned Offset = Call->getNumArgs() - FunDecl->getNumParams();
+
+ for (unsigned Index = Offset; Index < Call->getNumArgs(); ++Index) {
+ QualType ParamType = FunDecl->getParamDecl(Index - Offset)->getType();
+
+ InfoEntry Entry = PropagationMap.find(Call->getArg(Index));
+
+ if (Entry == PropagationMap.end() || !Entry->second.IsVar) {
+ continue;
+ }
+
+ PropagationInfo PState = Entry->second;
+
+ if (ParamType->isRValueReferenceType() ||
+ (ParamType->isLValueReferenceType() &&
+ !cast<LValueReferenceType>(*ParamType).isSpelledAsLValue())) {
+
+ StateMap->setState(PState.getVar(), consumed::CS_Consumed);
+
+ } else if (!(ParamType.isConstQualified() ||
+ ((ParamType->isReferenceType() ||
+ ParamType->isPointerType()) &&
+ ParamType->getPointeeType().isConstQualified()))) {
+
+ StateMap->setState(PState.getVar(), consumed::CS_Unknown);
+ }
+ }
+ }
+}
+
+void ConsumedStmtVisitor::VisitCastExpr(const CastExpr *Cast) {
+ InfoEntry Entry = PropagationMap.find(Cast->getSubExpr());
+
+ if (Entry != PropagationMap.end())
+ PropagationMap.insert(PairType(Cast, Entry->second));
+}
+
+void ConsumedStmtVisitor::VisitCXXConstructExpr(const CXXConstructExpr *Call) {
+ CXXConstructorDecl *Constructor = Call->getConstructor();
+
+ ASTContext &CurrContext = Analyzer.getSema().getASTContext();
+ QualType ThisType = Constructor->getThisType(CurrContext)->getPointeeType();
+
+ if (Analyzer.isConsumableType(ThisType)) {
+ if (Constructor->hasAttr<ConsumesAttr>() ||
+ Constructor->isDefaultConstructor()) {
+
+ PropagationMap.insert(PairType(Call,
+ PropagationInfo(consumed::CS_Consumed)));
+
+ } else if (Constructor->isMoveConstructor()) {
+
+ PropagationInfo PState =
+ PropagationMap.find(Call->getArg(0))->second;
+
+ if (PState.IsVar) {
+ const VarDecl* Var = PState.getVar();
+
+ PropagationMap.insert(PairType(Call,
+ PropagationInfo(StateMap->getState(Var))));
+
+ StateMap->setState(Var, consumed::CS_Consumed);
+
+ } else {
+ PropagationMap.insert(PairType(Call, PState));
+ }
+
+ } else if (Constructor->isCopyConstructor()) {
+ MapType::iterator Entry = PropagationMap.find(Call->getArg(0));
+
+ if (Entry != PropagationMap.end())
+ PropagationMap.insert(PairType(Call, Entry->second));
+
+ } else {
+ PropagationMap.insert(PairType(Call,
+ PropagationInfo(consumed::CS_Unconsumed)));
+ }
+ }
+}
+
+void ConsumedStmtVisitor::VisitCXXMemberCallExpr(
+ const CXXMemberCallExpr *Call) {
+
+ VisitCallExpr(Call);
+
+ InfoEntry Entry = PropagationMap.find(Call->getCallee()->IgnoreParens());
+
+ if (Entry != PropagationMap.end()) {
+ PropagationInfo PState = Entry->second;
+ if (!PState.IsVar) return;
+
+ const CXXMethodDecl *Method = Call->getMethodDecl();
+
+ if (Method->hasAttr<ConsumesAttr>())
+ StateMap->setState(PState.getVar(), consumed::CS_Consumed);
+ else if (!Method->isConst())
+ StateMap->setState(PState.getVar(), consumed::CS_Unknown);
+ }
+}
+
+void ConsumedStmtVisitor::VisitCXXOperatorCallExpr(
+ const CXXOperatorCallExpr *Call) {
+
+ const FunctionDecl *FunDecl =
+ dyn_cast_or_null<FunctionDecl>(Call->getDirectCallee());
+
+ if (!FunDecl) return;
+
+ if (isa<CXXMethodDecl>(FunDecl) &&
+ isLikeMoveAssignment(cast<CXXMethodDecl>(FunDecl))) {
+
+ InfoEntry LEntry = PropagationMap.find(Call->getArg(0));
+ InfoEntry REntry = PropagationMap.find(Call->getArg(1));
+
+ PropagationInfo LPState, RPState;
+
+ if (LEntry != PropagationMap.end() &&
+ REntry != PropagationMap.end()) {
+
+ LPState = LEntry->second;
+ RPState = REntry->second;
+
+ if (LPState.IsVar && RPState.IsVar) {
+ StateMap->setState(LPState.getVar(),
+ StateMap->getState(RPState.getVar()));
+
+ StateMap->setState(RPState.getVar(), consumed::CS_Consumed);
+
+ PropagationMap.insert(PairType(Call, LPState));
+
+ } else if (LPState.IsVar && !RPState.IsVar) {
+ StateMap->setState(LPState.getVar(), RPState.getState());
+
+ PropagationMap.insert(PairType(Call, LPState));
+
+ } else if (!LPState.IsVar && RPState.IsVar) {
+ PropagationMap.insert(PairType(Call,
+ PropagationInfo(StateMap->getState(RPState.getVar()))));
+
+ StateMap->setState(RPState.getVar(), consumed::CS_Consumed);
+
+ } else {
+ PropagationMap.insert(PairType(Call, RPState));
+ }
+
+ } else if (LEntry != PropagationMap.end() &&
+ REntry == PropagationMap.end()) {
+
+ LPState = LEntry->second;
+
+ if (LPState.IsVar) {
+ StateMap->setState(LPState.getVar(), consumed::CS_Unknown);
+
+ PropagationMap.insert(PairType(Call, LPState));
+
+ } else {
+ PropagationMap.insert(PairType(Call,
+ PropagationInfo(consumed::CS_Unknown)));
+ }
+
+ } else if (LEntry == PropagationMap.end() &&
+ REntry != PropagationMap.end()) {
+
+ RPState = REntry->second;
+
+ if (RPState.IsVar) {
+ const VarDecl *Var = RPState.getVar();
+
+ PropagationMap.insert(PairType(Call,
+ PropagationInfo(StateMap->getState(Var))));
+
+ StateMap->setState(Var, consumed::CS_Consumed);
+
+ } else {
+ PropagationMap.insert(PairType(Call, RPState));
+ }
+ }
+
+ } else {
+
+ VisitCallExpr(Call);
+
+ 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;
+
+ case CS_Unknown:
+ Analyzer.WarningsHandler.warnUseOfTempInUnknownState(
+ FunDecl->getNameAsString(), Call->getExprLoc());
+ break;
+
+ default:
+ break;
+ }
+ }
+ }
+
+ // 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);
+ }
+ }
+ }
+}
+
+void ConsumedStmtVisitor::VisitDeclRefExpr(const DeclRefExpr *DeclRef) {
+ if (const VarDecl *Var = dyn_cast_or_null<VarDecl>(DeclRef->getDecl()))
+ if (StateMap->getState(Var) != consumed::CS_None)
+ PropagationMap.insert(PairType(DeclRef, PropagationInfo(Var)));
+}
+
+void ConsumedStmtVisitor::VisitDeclStmt(const DeclStmt *DeclS) {
+ for (DeclStmt::const_decl_iterator DI = DeclS->decl_begin(),
+ DE = DeclS->decl_end(); DI != DE; ++DI) {
+
+ if (isa<VarDecl>(*DI)) VisitVarDecl(cast<VarDecl>(*DI));
+ }
+
+ if (DeclS->isSingleDecl())
+ if (const VarDecl *Var = dyn_cast_or_null<VarDecl>(DeclS->getSingleDecl()))
+ PropagationMap.insert(PairType(DeclS, PropagationInfo(Var)));
+}
+
+void ConsumedStmtVisitor::VisitMaterializeTemporaryExpr(
+ const MaterializeTemporaryExpr *Temp) {
+
+ InfoEntry Entry = PropagationMap.find(Temp->GetTemporaryExpr());
+
+ if (Entry != PropagationMap.end())
+ PropagationMap.insert(PairType(Temp, Entry->second));
+}
+
+void ConsumedStmtVisitor::VisitMemberExpr(const MemberExpr *MExpr) {
+ forwardInfo(MExpr->getBase(), MExpr);
+}
+
+void ConsumedStmtVisitor::VisitUnaryOperator(const UnaryOperator *UOp) {
+ if (UOp->getOpcode() == UO_AddrOf) {
+ InfoEntry Entry = PropagationMap.find(UOp->getSubExpr());
+
+ if (Entry != PropagationMap.end())
+ PropagationMap.insert(PairType(UOp, Entry->second));
+ }
+}
+
+void ConsumedStmtVisitor::VisitVarDecl(const VarDecl *Var) {
+ if (Analyzer.isConsumableType(Var->getType())) {
+ PropagationInfo PState =
+ PropagationMap.find(Var->getInit())->second;
+
+ StateMap->setState(Var, PState.IsVar ?
+ StateMap->getState(PState.getVar()) : PState.getState());
+ }
+}
+} // end anonymous::ConsumedStmtVisitor
+
+namespace {
+
+// TODO: Handle variable definitions, e.g. bool valid = x.isValid();
+// if (valid) ...; (Deferred)
+class TestedVarsVisitor : public RecursiveASTVisitor<TestedVarsVisitor> {
+
+ bool Invert;
+ SourceLocation CurrTestLoc;
+
+ ConsumedStateMap *StateMap;
+
+public:
+ bool IsUsefulConditional;
+ VarTestResult Test;
+
+ TestedVarsVisitor(ConsumedStateMap *StateMap) : Invert(false),
+ StateMap(StateMap), IsUsefulConditional(false) {}
+
+ bool VisitCallExpr(CallExpr *Call);
+ bool VisitDeclRefExpr(DeclRefExpr *DeclRef);
+ bool VisitUnaryOperator(UnaryOperator *UnaryOp);
+};
+
+bool TestedVarsVisitor::VisitCallExpr(CallExpr *Call) {
+ if (const CXXMethodDecl *Method =
+ dyn_cast_or_null<CXXMethodDecl>(Call->getDirectCallee())) {
+
+ if (isTestingFunction(Method)) {
+ CurrTestLoc = Call->getExprLoc();
+ IsUsefulConditional = true;
+ return true;
+ }
+
+ IsUsefulConditional = false;
+ }
+
+ return false;
+}
+
+bool TestedVarsVisitor::VisitDeclRefExpr(DeclRefExpr *DeclRef) {
+ 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;
+}
+
+bool TestedVarsVisitor::VisitUnaryOperator(UnaryOperator *UnaryOp) {
+ if (UnaryOp->getOpcode() == UO_LNot) {
+ Invert = true;
+ TraverseStmt(UnaryOp->getSubExpr());
+
+ } else {
+ IsUsefulConditional = false;
+ }
+
+ return false;
+}
+} // end anonymouse::TestedVarsVisitor
+
+namespace clang {
+namespace consumed {
+
+void ConsumedBlockInfo::addInfo(const CFGBlock *Block,
+ ConsumedStateMap *StateMap,
+ bool &AlreadyOwned) {
+
+ if (VisitedBlocks.alreadySet(Block)) return;
+
+ ConsumedStateMap *Entry = StateMapsArray[Block->getBlockID()];
+
+ if (Entry) {
+ Entry->intersect(StateMap);
+
+ } else if (AlreadyOwned) {
+ StateMapsArray[Block->getBlockID()] = new ConsumedStateMap(*StateMap);
+
+ } else {
+ StateMapsArray[Block->getBlockID()] = StateMap;
+ AlreadyOwned = true;
+ }
+}
+
+void ConsumedBlockInfo::addInfo(const CFGBlock *Block,
+ ConsumedStateMap *StateMap) {
+
+ if (VisitedBlocks.alreadySet(Block)) {
+ delete StateMap;
+ return;
+ }
+
+ ConsumedStateMap *Entry = StateMapsArray[Block->getBlockID()];
+
+ if (Entry) {
+ Entry->intersect(StateMap);
+ delete StateMap;
+
+ } else {
+ StateMapsArray[Block->getBlockID()] = StateMap;
+ }
+}
+
+ConsumedStateMap* ConsumedBlockInfo::getInfo(const CFGBlock *Block) {
+ return StateMapsArray[Block->getBlockID()];
+}
+
+void ConsumedBlockInfo::markVisited(const CFGBlock *Block) {
+ VisitedBlocks.insert(Block);
+}
+
+ConsumedState ConsumedStateMap::getState(const VarDecl *Var) {
+ MapType::const_iterator Entry = Map.find(Var);
+
+ if (Entry != Map.end()) {
+ return Entry->second;
+
+ } else {
+ return CS_None;
+ }
+}
+
+void ConsumedStateMap::intersect(const ConsumedStateMap *Other) {
+ ConsumedState LocalState;
+
+ for (MapType::const_iterator DMI = Other->Map.begin(),
+ DME = Other->Map.end(); DMI != DME; ++DMI) {
+
+ LocalState = this->getState(DMI->first);
+
+ if (LocalState != CS_None && LocalState != DMI->second)
+ setState(DMI->first, CS_Unknown);
+ }
+}
+
+void ConsumedStateMap::makeUnknown() {
+ PairType Pair;
+
+ for (MapType::const_iterator DMI = Map.begin(), DME = Map.end(); DMI != DME;
+ ++DMI) {
+
+ Pair = *DMI;
+
+ Map.erase(Pair.first);
+ Map.insert(PairType(Pair.first, CS_Unknown));
+ }
+}
+
+void ConsumedStateMap::setState(const VarDecl *Var, ConsumedState State) {
+ Map[Var] = State;
+}
+
+const Sema & ConsumedAnalyzer::getSema() {
+ return S;
+}
+
+
+bool ConsumedAnalyzer::isConsumableType(QualType Type) {
+ const CXXRecordDecl *RD =
+ dyn_cast_or_null<CXXRecordDecl>(Type->getAsCXXRecordDecl());
+
+ if (!RD) return false;
+
+ std::pair<CacheMapType::iterator, bool> Entry =
+ ConsumableTypeCache.insert(std::make_pair(RD, false));
+
+ if (Entry.second)
+ Entry.first->second = hasConsumableAttributes(RD);
+
+ return Entry.first->second;
+}
+
+// TODO: Walk the base classes to see if any of them are unique types.
+// (Deferred)
+bool ConsumedAnalyzer::hasConsumableAttributes(const CXXRecordDecl *RD) {
+ for (CXXRecordDecl::method_iterator MI = RD->method_begin(),
+ ME = RD->method_end(); MI != ME; ++MI) {
+
+ for (Decl::attr_iterator AI = (*MI)->attr_begin(), AE = (*MI)->attr_end();
+ AI != AE; ++AI) {
+
+ switch ((*AI)->getKind()) {
+ case attr::CallableWhenUnconsumed:
+ case attr::TestsUnconsumed:
+ return true;
+
+ default:
+ break;
+ }
+ }
+ }
+
+ return false;
+}
+
+// TODO: Handle other forms of branching with precision, including while- and
+// for-loops. (Deferred)
+void ConsumedAnalyzer::splitState(const CFGBlock *CurrBlock,
+ const IfStmt *Terminator) {
+
+ TestedVarsVisitor Visitor(CurrStates);
+ Visitor.TraverseStmt(const_cast<Expr*>(Terminator->getCond()));
+
+ bool HasElse = Terminator->getElse() != NULL;
+
+ ConsumedStateMap *ElseOrMergeStates = new ConsumedStateMap(*CurrStates);
+
+ if (Visitor.IsUsefulConditional) {
+ ConsumedState VarState = CurrStates->getState(Visitor.Test.Var);
+
+ if (VarState != CS_Unknown) {
+ // FIXME: Make this not warn if the test is from a macro expansion.
+ // (Deferred)
+ WarningsHandler.warnUnnecessaryTest(Visitor.Test.Var->getNameAsString(),
+ stateToString(VarState), Visitor.Test.Loc);
+ }
+
+ if (Visitor.Test.UnconsumedInTrueBranch) {
+ CurrStates->setState(Visitor.Test.Var, CS_Unconsumed);
+ if (HasElse) ElseOrMergeStates->setState(Visitor.Test.Var, CS_Consumed);
+
+ } else {
+ CurrStates->setState(Visitor.Test.Var, CS_Consumed);
+ if (HasElse) ElseOrMergeStates->setState(Visitor.Test.Var, CS_Unconsumed);
+ }
+ }
+
+ CFGBlock::const_succ_iterator SI = CurrBlock->succ_begin();
+
+ if (*SI) BlockInfo.addInfo(*SI, CurrStates);
+ if (*++SI) BlockInfo.addInfo(*SI, ElseOrMergeStates);
+}
+
+void ConsumedAnalyzer::run(AnalysisDeclContext &AC) {
+ const FunctionDecl *D = dyn_cast_or_null<FunctionDecl>(AC.getDecl());
+
+ if (!D) return;
+
+ BlockInfo = ConsumedBlockInfo(AC.getCFG());
+
+ PostOrderCFGView *SortedGraph = AC.getAnalysis<PostOrderCFGView>();
+
+ CurrStates = new ConsumedStateMap();
+
+ // Visit all of the function's basic blocks.
+ for (PostOrderCFGView::iterator I = SortedGraph->begin(),
+ E = SortedGraph->end(); I != E; ++I) {
+
+ const CFGBlock *CurrBlock = *I;
+ BlockInfo.markVisited(CurrBlock);
+
+ if (CurrStates == NULL)
+ CurrStates = BlockInfo.getInfo(CurrBlock);
+
+ ConsumedStmtVisitor Visitor(*this, CurrStates);
+
+ // Visit all of the basic block's statements.
+ for (CFGBlock::const_iterator BI = CurrBlock->begin(),
+ BE = CurrBlock->end(); BI != BE; ++BI) {
+
+ if (BI->getKind() == CFGElement::Statement)
+ Visitor.Visit(BI->castAs<CFGStmt>().getStmt());
+ }
+
+ // 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())) {
+
+ splitState(CurrBlock, Terminator);
+ CurrStates = NULL;
+
+ } else if (CurrBlock->succ_size() > 1) {
+ CurrStates->makeUnknown();
+
+ bool OwnershipTaken = false;
+
+ for (CFGBlock::const_succ_iterator SI = CurrBlock->succ_begin(),
+ SE = CurrBlock->succ_end(); SI != SE; ++SI) {
+
+ if (*SI) BlockInfo.addInfo(*SI, CurrStates, OwnershipTaken);
+ }
+
+ if (!OwnershipTaken)
+ delete CurrStates;
+
+ CurrStates = NULL;
+
+ } else if (CurrBlock->succ_size() == 1 &&
+ (*CurrBlock->succ_begin())->pred_size() > 1) {
+
+ BlockInfo.addInfo(*CurrBlock->succ_begin(), CurrStates);
+ CurrStates = NULL;
+ }
+
+ Visitor.reset();
+ } // End of block iterator.
+
+ // Delete the last existing state map.
+ delete CurrStates;
+
+ WarningsHandler.emitDiagnostics();
+}
+
+unsigned checkEnabled(DiagnosticsEngine &D) {
+ return (unsigned)
+ (D.getDiagnosticLevel(diag::warn_use_while_consumed, SourceLocation()) !=
+ DiagnosticsEngine::Ignored);
+}
+
+bool isTestingFunction(const CXXMethodDecl *Method) {
+ return Method->hasAttr<TestsUnconsumedAttr>();
+}
+
+}} // end namespace clang::consumed
#include "clang/AST/StmtObjC.h"
#include "clang/AST/StmtVisitor.h"
#include "clang/Analysis/Analyses/CFGReachabilityAnalysis.h"
+#include "clang/Analysis/Analyses/Consumed.h"
#include "clang/Analysis/Analyses/ReachableCode.h"
#include "clang/Analysis/Analyses/ThreadSafety.h"
#include "clang/Analysis/Analyses/UninitializedValues.h"
};
}
-
-//===----------------------------------------------------------------------===//
-// -Wthread-safety
-//===----------------------------------------------------------------------===//
namespace clang {
-namespace thread_safety {
+namespace {
typedef SmallVector<PartialDiagnosticAt, 1> OptionalNotes;
typedef std::pair<PartialDiagnosticAt, OptionalNotes> DelayedDiag;
typedef std::list<DelayedDiag> DiagList;
return SM.isBeforeInTranslationUnit(left.first.first, right.first.first);
}
};
+}}
+//===----------------------------------------------------------------------===//
+// -Wthread-safety
+//===----------------------------------------------------------------------===//
+namespace clang {
+namespace thread_safety {
namespace {
class ThreadSafetyReporter : public clang::thread_safety::ThreadSafetyHandler {
Sema &S;
}
}
+//===----------------------------------------------------------------------===//
+// -Wconsumed
+//===----------------------------------------------------------------------===//
+
+namespace clang {
+namespace consumed {
+namespace {
+class ConsumedWarningsHandler : public ConsumedWarningsHandlerBase {
+
+ Sema &S;
+ DiagList Warnings;
+
+public:
+
+ ConsumedWarningsHandler(Sema &S) : S(S) {}
+
+ void emitDiagnostics() {
+ Warnings.sort(SortDiagBySourceLocation(S.getSourceManager()));
+
+ for (DiagList::iterator I = Warnings.begin(), E = Warnings.end();
+ I != E; ++I) {
+
+ const OptionalNotes &Notes = I->second;
+ S.Diag(I->first.first, I->first.second);
+
+ for (unsigned NoteI = 0, NoteN = Notes.size(); NoteI != NoteN; ++NoteI) {
+ S.Diag(Notes[NoteI].first, Notes[NoteI].second);
+ }
+ }
+ }
+
+ /// 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 warnUnnecessaryTest(StringRef VariableName, StringRef VariableState,
+ SourceLocation Loc) {
+
+ PartialDiagnosticAt Warning(Loc, S.PDiag(diag::warn_unnecessary_test) <<
+ VariableName << VariableState);
+
+ 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 warnUseOfTempWhileConsumed(StringRef MethodName, SourceLocation Loc) {
+
+ PartialDiagnosticAt Warning(Loc, S.PDiag(
+ diag::warn_use_of_temp_while_consumed) << MethodName);
+
+ 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 warnUseOfTempInUnknownState(StringRef MethodName, SourceLocation Loc) {
+
+ PartialDiagnosticAt Warning(Loc, S.PDiag(
+ diag::warn_use_of_temp_in_unknown_state) << MethodName);
+
+ 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) {
+
+ PartialDiagnosticAt Warning(Loc, S.PDiag(diag::warn_use_while_consumed) <<
+ MethodName << VariableName);
+
+ 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) {
+
+ PartialDiagnosticAt Warning(Loc, S.PDiag(diag::warn_use_in_unknown_state) <<
+ MethodName << VariableName);
+
+ Warnings.push_back(DelayedDiag(Warning, OptionalNotes()));
+ }
+};
+}}}
+
//===----------------------------------------------------------------------===//
// AnalysisBasedWarnings - Worker object used by Sema to execute analysis-based
// warnings on a function, method, or block.
enableCheckFallThrough = 1;
enableCheckUnreachable = 0;
enableThreadSafetyAnalysis = 0;
+ enableConsumedAnalysis = 0;
}
clang::sema::AnalysisBasedWarnings::AnalysisBasedWarnings(Sema &s)
DefaultPolicy.enableThreadSafetyAnalysis = (unsigned)
(D.getDiagnosticLevel(diag::warn_double_lock, SourceLocation()) !=
DiagnosticsEngine::Ignored);
+ DefaultPolicy.enableConsumedAnalysis = consumed::checkEnabled(D);
}
// prototyping, but we need a way for analyses to say what expressions they
// expect to always be CFGElements and then fill in the BuildOptions
// appropriately. This is essentially a layering violation.
- if (P.enableCheckUnreachable || P.enableThreadSafetyAnalysis) {
+ if (P.enableCheckUnreachable || P.enableThreadSafetyAnalysis ||
+ P.enableConsumedAnalysis) {
// Unreachable code analysis and thread safety require a linearized CFG.
AC.getCFGBuildOptions().setAllAlwaysAdd();
}
Reporter.emitDiagnostics();
}
+ // Check for violations of consumed properties.
+ if (P.enableConsumedAnalysis) {
+ consumed::ConsumedWarningsHandler WarningHandler(S);
+ consumed::ConsumedAnalyzer Analyzer(S, WarningHandler);
+ Analyzer.run(AC);
+ }
+
if (Diags.getDiagnosticLevel(diag::warn_uninit_var, D->getLocStart())
!= DiagnosticsEngine::Ignored ||
Diags.getDiagnosticLevel(diag::warn_sometimes_uninit_var,D->getLocStart())
Attr.getAttributeSpellingListIndex()));
}
+static void handleConsumesAttr(Sema &S, Decl *D,
+ const AttributeList &Attr) {
+ assert(!Attr.isInvalid());
+ if (!checkAttributeNumArgs(S, Attr, 0)) return;
+
+ if (!(isa<CXXMethodDecl>(D) || isa<CXXConstructorDecl>(D))) {
+ S.Diag(Attr.getLoc(), diag::warn_uniqueness_attribute_wrong_decl_type) <<
+ Attr.getName();
+ return;
+ }
+
+ D->addAttr(::new (S.Context)
+ ConsumesAttr(Attr.getRange(), S.Context,
+ Attr.getAttributeSpellingListIndex()));
+}
+
+static void handleCallableWhenUnconsumedAttr(Sema &S, Decl *D,
+ const AttributeList &Attr) {
+ assert(!Attr.isInvalid());
+ if (!checkAttributeNumArgs(S, Attr, 0)) return;
+
+ if (!isa<CXXMethodDecl>(D)) {
+ S.Diag(Attr.getLoc(), diag::warn_uniqueness_attribute_wrong_decl_type) <<
+ Attr.getName();
+ return;
+ }
+
+ D->addAttr(::new (S.Context)
+ CallableWhenUnconsumedAttr(Attr.getRange(), S.Context,
+ Attr.getAttributeSpellingListIndex()));
+}
+
+static void handleTestsConsumedAttr(Sema &S, Decl *D,
+ const AttributeList &Attr) {
+ assert(!Attr.isInvalid());
+ if (!checkAttributeNumArgs(S, Attr, 0)) return;
+
+ if (!isa<CXXMethodDecl>(D)) {
+ S.Diag(Attr.getLoc(), diag::warn_uniqueness_attribute_wrong_decl_type) <<
+ Attr.getName();
+ return;
+ }
+
+ D->addAttr(::new (S.Context)
+ TestsConsumedAttr(Attr.getRange(), S.Context,
+ Attr.getAttributeSpellingListIndex()));
+}
+
+static void handleTestsUnconsumedAttr(Sema &S, Decl *D,
+ const AttributeList &Attr) {
+ assert(!Attr.isInvalid());
+ if (!checkAttributeNumArgs(S, Attr, 0)) return;
+
+ if (!isa<CXXMethodDecl>(D)) {
+ S.Diag(Attr.getLoc(), diag::warn_uniqueness_attribute_wrong_decl_type) <<
+ Attr.getName();
+ return;
+ }
+
+ D->addAttr(::new (S.Context)
+ TestsUnconsumedAttr(Attr.getRange(), S.Context,
+ Attr.getAttributeSpellingListIndex()));
+}
static void handleExtVectorTypeAttr(Sema &S, Scope *scope, Decl *D,
const AttributeList &Attr) {
handleAcquiredAfterAttr(S, D, Attr);
break;
+ // Uniqueness analysis attributes.
+ case AttributeList::AT_Consumes:
+ handleConsumesAttr(S, D, Attr);
+ break;
+ case AttributeList::AT_CallableWhenUnconsumed:
+ handleCallableWhenUnconsumedAttr(S, D, Attr);
+ break;
+ case AttributeList::AT_TestsConsumed:
+ handleTestsConsumedAttr(S, D, Attr);
+ break;
+ case AttributeList::AT_TestsUnconsumed:
+ handleTestsUnconsumedAttr(S, D, Attr);
+ break;
+
// Type safety attributes.
case AttributeList::AT_ArgumentWithTypeTag:
handleArgumentWithTypeTagAttr(S, D, Attr);
--- /dev/null
+// RUN: %clang_cc1 -fsyntax-only -verify -Wconsumed-strict -std=c++11 %s
+
+#define CALLABLE_WHEN_UNCONSUMED __attribute__ ((callable_when_unconsumed))
+#define CONSUMES __attribute__ ((consumes))
+#define TESTS_UNCONSUMED __attribute__ ((tests_unconsumed))
+
+typedef decltype(nullptr) nullptr_t;
+
+template <typename T>
+class Bar {
+ T var;
+
+ public:
+ Bar(void);
+ Bar(T val);
+ Bar(Bar<T> &other);
+ Bar(Bar<T> &&other);
+
+ Bar<T>& operator=(Bar<T> &other);
+ Bar<T>& operator=(Bar<T> &&other);
+ Bar<T>& operator=(nullptr_t);
+
+ template <typename U>
+ Bar<T>& operator=(Bar<U> &other);
+
+ template <typename U>
+ Bar<T>& operator=(Bar<U> &&other);
+
+ void operator*(void) const CALLABLE_WHEN_UNCONSUMED;
+
+ bool isValid(void) const TESTS_UNCONSUMED;
+
+ void constCall(void) const;
+ void nonconstCall(void);
+
+ void consume(void) CONSUMES;
+};
+
+void baf0(Bar<int> &var);
+void baf1(Bar<int> *var);
+
+void testIfStmt(void) {
+ Bar<int> var;
+
+ if (var.isValid()) { // expected-warning {{unnecessary test. Variable 'var' is known to be in the 'consumed' state}}
+
+ // Empty
+
+ } else {
+ // Empty
+ }
+}
+
+void testConditionalMerge(void) {
+ Bar<int> var;
+
+ if (var.isValid()) {// expected-warning {{unnecessary test. Variable 'var' is known to be in the 'consumed' state}}
+
+ // Empty
+ }
+
+ *var; // expected-warning {{invocation of method 'operator*' on object 'var' while it is in an unknown state}}
+
+ if (var.isValid()) {
+ // Empty
+
+ } else {
+ // Empty
+ }
+
+ *var; // expected-warning {{invocation of method 'operator*' on object 'var' while it is in an unknown state}}
+}
+
+void testCallingConventions(void) {
+ Bar<int> var(42);
+
+ baf0(var);
+ *var; // expected-warning {{invocation of method 'operator*' on object 'var' while it is in an unknown state}}
+
+ var = Bar<int>(42);
+ baf1(&var);
+ *var; // expected-warning {{invocation of method 'operator*' on object 'var' while it is in an unknown state}}
+}
+
+void testMoveAsignmentish(void) {
+ Bar<int> var;
+
+ var = nullptr;
+ *var; // expected-warning {{invocation of method 'operator*' on object 'var' while it is in an unknown state}}
+}
+
+void testConstAndNonConstMemberFunctions(void) {
+ Bar<int> var(42);
+
+ var.constCall();
+ *var;
+
+ var.nonconstCall();
+ *var; // expected-warning {{invocation of method 'operator*' on object 'var' while it is in an unknown state}}
+}
+
+void testSimpleForLoop(void) {
+ Bar<int> var;
+
+ for (int i = 0; i < 10; ++i) {
+ *var; // expected-warning {{invocation of method 'operator*' on object 'var' while it is in an unknown state}}
+ }
+
+ *var; // expected-warning {{invocation of method 'operator*' on object 'var' while it is in an unknown state}}
+}
+
+void testSimpleWhileLoop(void) {
+ int i = 0;
+
+ Bar<int> var;
+
+ while (i < 10) {
+ *var; // expected-warning {{invocation of method 'operator*' on object 'var' while it is in an unknown state}}
+ ++i;
+ }
+
+ *var; // expected-warning {{invocation of method 'operator*' on object 'var' while it is in an unknown state}}
+}
--- /dev/null
+// RUN: %clang_cc1 -fsyntax-only -verify -Wconsumed -std=c++11 %s
+
+#define CALLABLE_WHEN_UNCONSUMED __attribute__ ((callable_when_unconsumed))
+#define CONSUMES __attribute__ ((consumes))
+#define TESTS_UNCONSUMED __attribute__ ((tests_unconsumed))
+
+typedef decltype(nullptr) nullptr_t;
+
+template <typename T>
+class Bar {
+ T var;
+
+ public:
+ Bar(void);
+ Bar(nullptr_t p) CONSUMES;
+ Bar(T val);
+ Bar(Bar<T> &other);
+ Bar(Bar<T> &&other);
+
+ Bar<T>& operator=(Bar<T> &other);
+ Bar<T>& operator=(Bar<T> &&other);
+ Bar<T>& operator=(nullptr_t);
+
+ template <typename U>
+ Bar<T>& operator=(Bar<U> &other);
+
+ template <typename U>
+ Bar<T>& operator=(Bar<U> &&other);
+
+ void operator*(void) const CALLABLE_WHEN_UNCONSUMED;
+
+ bool isValid(void) const TESTS_UNCONSUMED;
+ operator bool() const TESTS_UNCONSUMED;
+
+ void constCall(void) const;
+ void nonconstCall(void);
+
+ void consume(void) CONSUMES;
+};
+
+void baf0(const Bar<int> var);
+void baf1(const Bar<int> &var);
+void baf2(const Bar<int> *var);
+
+void baf3(Bar<int> &&var);
+
+void testInitialization(void) {
+ Bar<int> var0;
+ Bar<int> var1 = Bar<int>();
+
+ var0 = Bar<int>();
+
+ *var0; // expected-warning {{invocation of method 'operator*' on object 'var0' while it is in the 'consumed' state}}
+ *var1; // expected-warning {{invocation of method 'operator*' on object 'var1' while it is in the 'consumed' state}}
+
+ if (var0.isValid()) {
+ *var0;
+ *var1; // expected-warning {{invocation of method 'operator*' on object 'var1' while it is in the 'consumed' state}}
+
+ } else {
+ *var0; // expected-warning {{invocation of method 'operator*' on object 'var0' while it is in the 'consumed' state}}
+ }
+}
+
+void testSimpleRValueRefs(void) {
+ Bar<int> var0;
+ Bar<int> var1(42);
+
+ *var0; // expected-warning {{invocation of method 'operator*' on object 'var0' while it is in the 'consumed' state}}
+ *var1;
+
+ var0 = static_cast<Bar<int>&&>(var1);
+
+ *var0;
+ *var1; // expected-warning {{invocation of method 'operator*' on object 'var1' while it is in the 'consumed' state}}
+}
+
+void testIfStmt(void) {
+ Bar<int> var;
+
+ if (var.isValid()) {
+ // Empty
+
+ } else {
+ *var; // expected-warning {{invocation of method 'operator*' on object 'var' while it is in the 'consumed' state}}
+ }
+
+ if (!var.isValid()) {
+ *var; // expected-warning {{invocation of method 'operator*' on object 'var' while it is in the 'consumed' state}}
+
+ } else {
+ *var;
+ }
+
+ if (var) {
+ // Empty
+
+ } else {
+ *var; // expected-warning {{invocation of method 'operator*' on object 'var' while it is in the 'consumed' state}}
+ }
+}
+
+void testCallingConventions(void) {
+ Bar<int> var(42);
+
+ baf0(var);
+ *var;
+
+ baf1(var);
+ *var;
+
+ baf2(&var);
+ *var;
+
+ baf3(static_cast<Bar<int>&&>(var));
+ *var; // expected-warning {{invocation of method 'operator*' on object 'var' while it is in the 'consumed' state}}
+}
+
+void testMoveAsignmentish(void) {
+ Bar<int> var0;
+ Bar<long> var1(42);
+
+ *var0; // expected-warning {{invocation of method 'operator*' on object 'var0' while it is in the 'consumed' state}}
+ *var1;
+
+ var0 = static_cast<Bar<long>&&>(var1);
+
+ *var0;
+ *var1; // expected-warning {{invocation of method 'operator*' on object 'var1' while it is in the 'consumed' state}}
+}
+
+void testConditionalMerge(void) {
+ Bar<int> var;
+
+ if (var.isValid()) {
+ // Empty
+ }
+
+ *var;
+
+ if (var.isValid()) {
+ // Empty
+
+ } else {
+ // Empty
+ }
+
+ *var;
+}
+
+void testConsumes0(void) {
+ Bar<int> var(42);
+
+ *var;
+
+ var.consume();
+
+ *var; // expected-warning {{invocation of method 'operator*' on object 'var' while it is in the 'consumed' state}}
+}
+
+void testConsumes1(void) {
+ Bar<int> var(nullptr);
+
+ *var; // expected-warning {{invocation of method 'operator*' on object 'var' while it is in the 'consumed' state}}
+}
+
+void testSimpleForLoop(void) {
+ Bar<int> var;
+
+ for (int i = 0; i < 10; ++i) {
+ *var;
+ }
+
+ *var;
+}
+
+void testSimpleWhileLoop(void) {
+ int i = 0;
+
+ Bar<int> var;
+
+ while (i < 10) {
+ *var;
+ ++i;
+ }
+
+ *var;
+}
--- /dev/null
+// RUN: %clang_cc1 -fsyntax-only -verify -Wconsumed -std=c++11 %s
+
+#define CONSUMES __attribute__ ((consumes))
+#define TESTS_UNCONSUMED __attribute__ ((tests_unconsumed))
+#define CALLABLE_WHEN_UNCONSUMED __attribute__ ((callable_when_unconsumed))
+
+class AttrTester0 {
+ void Consumes(void) __attribute__ ((consumes(42))); // expected-error {{attribute takes no arguments}}
+ bool TestsUnconsumed(void) __attribute__ ((tests_unconsumed(42))); // expected-error {{attribute takes no arguments}}
+ void CallableWhenUnconsumed(void)
+ __attribute__ ((callable_when_unconsumed(42))); // expected-error {{attribute takes no arguments}}
+};
+
+int var0 CONSUMES; // expected-warning {{'consumes' attribute only applies to methods}}
+int var1 TESTS_UNCONSUMED; // expected-warning {{'tests_unconsumed' attribute only applies to methods}}
+int var2 CALLABLE_WHEN_UNCONSUMED; // expected-warning {{'callable_when_unconsumed' attribute only applies to methods}}
+
+void function0(void) CONSUMES; // expected-warning {{'consumes' attribute only applies to methods}}
+void function1(void) TESTS_UNCONSUMED; // expected-warning {{'tests_unconsumed' attribute only applies to methods}}
+void function2(void) CALLABLE_WHEN_UNCONSUMED; // expected-warning {{'callable_when_unconsumed' attribute only applies to methods}}
+
+class AttrTester1 {
+ void consumes(void) CONSUMES;
+ bool testsUnconsumed(void) TESTS_UNCONSUMED;
+};
+
+class AttrTester2 {
+ void callableWhenUnconsumed(void) CALLABLE_WHEN_UNCONSUMED;
+};