<div dir="ltr">Added missing dependency in rL281460</div><div class="gmail_extra"><br><div class="gmail_quote">On 14 September 2016 at 15:36, Martin Böhme <span dir="ltr"><<a href="mailto:mboehme@google.com" target="_blank">mboehme@google.com</a>></span> wrote:<br><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div dir="ltr">clang-tidy/misc/CMakeLists.txt was missing a dependency on clangAnalysis. For some reason, the Linux build wasn't affected by this and still built correctly. I'll submit a patch to correct this.</div><div class="HOEnZb"><div class="h5"><div class="gmail_extra"><br><div class="gmail_quote">On 14 September 2016 at 15:20, Martin Böhme <span dir="ltr"><<a href="mailto:mboehme@google.com" target="_blank">mboehme@google.com</a>></span> wrote:<br><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div dir="ltr">This has also caused the PPC build bot to fail:<div><br></div><div><a href="http://lab.llvm.org:8011/builders/clang-ppc64le-linux-multistage/builds/2488/steps/build%20stage%201/logs/stdio" target="_blank">http://lab.llvm.org:8011/build<wbr>ers/clang-ppc64le-linux-multis<wbr>tage/builds/2488/steps/build%<wbr>20stage%201/logs/stdio</a><br></div><div><br></div><div>It's complaining about undefined symbols from CFG. I hope it's an obvious missing dependency.</div></div><div class="gmail_extra"><br><div class="gmail_quote"><span>On 14 September 2016 at 12:29, Martin Bohme via cfe-commits <span dir="ltr"><<a href="mailto:cfe-commits@lists.llvm.org" target="_blank">cfe-commits@lists.llvm.org</a>></span> wrote:<br></span><div><div class="m_1189535854236998011h5"><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">Author: mboehme<br>
Date: Wed Sep 14 05:29:32 2016<br>
New Revision: 281453<br>
<br>
URL: <a href="http://llvm.org/viewvc/llvm-project?rev=281453&view=rev" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-pr<wbr>oject?rev=281453&view=rev</a><br>
Log:<br>
[clang-tidy] Add check 'misc-use-after-move'<br>
<br>
Summary:<br>
The check warns if an object is used after it has been moved, without an<br>
intervening reinitialization.<br>
<br>
See user-facing documentation for details.<br>
<br>
Reviewers: sbenza, Prazek, alexfh<br>
<br>
Subscribers: beanz, mgorny, shadeware, omtcyfz, Eugene.Zelenko, Prazek, fowles, ioeric, cfe-commits<br>
<br>
Differential Revision: <a href="https://reviews.llvm.org/D23353" rel="noreferrer" target="_blank">https://reviews.llvm.org/D2335<wbr>3</a><br>
<br>
Added:<br>
clang-tools-extra/trunk/clang-<wbr>tidy/misc/UseAfterMoveCheck.cp<wbr>p<br>
clang-tools-extra/trunk/clang-<wbr>tidy/misc/UseAfterMoveCheck.h<br>
clang-tools-extra/trunk/docs/c<wbr>lang-tidy/checks/misc-use-afte<wbr>r-move.rst<br>
clang-tools-extra/trunk/test/c<wbr>lang-tidy/misc-use-after-move.<wbr>cpp<br>
Modified:<br>
clang-tools-extra/trunk/clang-<wbr>tidy/misc/CMakeLists.txt<br>
clang-tools-extra/trunk/clang-<wbr>tidy/misc/MiscTidyModule.cpp<br>
clang-tools-extra/trunk/docs/R<wbr>eleaseNotes.rst<br>
clang-tools-extra/trunk/docs/c<wbr>lang-tidy/checks/list.rst<br>
<br>
Modified: clang-tools-extra/trunk/clang-<wbr>tidy/misc/CMakeLists.txt<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clang-tidy/misc/CMakeLists.txt?rev=281453&r1=281452&r2=281453&view=diff" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-pr<wbr>oject/clang-tools-extra/trunk/<wbr>clang-tidy/misc/CMakeLists.txt<wbr>?rev=281453&r1=281452&r2=28145<wbr>3&view=diff</a><br>
==============================<wbr>==============================<wbr>==================<br>
--- clang-tools-extra/trunk/clang-<wbr>tidy/misc/CMakeLists.txt (original)<br>
+++ clang-tools-extra/trunk/clang-<wbr>tidy/misc/CMakeLists.txt Wed Sep 14 05:29:32 2016<br>
@@ -43,6 +43,7 @@ add_clang_library(clangTidyMis<wbr>cModule<br>
UnusedParametersCheck.cpp<br>
UnusedRAIICheck.cpp<br>
UnusedUsingDeclsCheck.cpp<br>
+ UseAfterMoveCheck.cpp<br>
VirtualNearMissCheck.cpp<br>
<br>
LINK_LIBS<br>
<br>
Modified: clang-tools-extra/trunk/clang-<wbr>tidy/misc/MiscTidyModule.cpp<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clang-tidy/misc/MiscTidyModule.cpp?rev=281453&r1=281452&r2=281453&view=diff" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-pr<wbr>oject/clang-tools-extra/trunk/<wbr>clang-tidy/misc/MiscTidyModule<wbr>.cpp?rev=281453&r1=281452&r2=2<wbr>81453&view=diff</a><br>
==============================<wbr>==============================<wbr>==================<br>
--- clang-tools-extra/trunk/clang-<wbr>tidy/misc/MiscTidyModule.cpp (original)<br>
+++ clang-tools-extra/trunk/clang-<wbr>tidy/misc/MiscTidyModule.cpp Wed Sep 14 05:29:32 2016<br>
@@ -51,6 +51,7 @@<br>
#include "UnusedParametersCheck.h"<br>
#include "UnusedRAIICheck.h"<br>
#include "UnusedUsingDeclsCheck.h"<br>
+#include "UseAfterMoveCheck.h"<br>
#include "VirtualNearMissCheck.h"<br>
<br>
namespace clang {<br>
@@ -139,6 +140,7 @@ public:<br>
CheckFactories.registerCheck<<wbr>UnusedRAIICheck>("misc-unused-<wbr>raii");<br>
CheckFactories.registerCheck<<wbr>UnusedUsingDeclsCheck>(<br>
"misc-unused-using-decls");<br>
+ CheckFactories.registerCheck<U<wbr>seAfterMoveCheck>("misc-use-af<wbr>ter-move");<br>
CheckFactories.registerCheck<<wbr>VirtualNearMissCheck>(<br>
"misc-virtual-near-miss");<br>
}<br>
<br>
Added: clang-tools-extra/trunk/clang-<wbr>tidy/misc/UseAfterMoveCheck.cp<wbr>p<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clang-tidy/misc/UseAfterMoveCheck.cpp?rev=281453&view=auto" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-pr<wbr>oject/clang-tools-extra/trunk/<wbr>clang-tidy/misc/UseAfterMoveCh<wbr>eck.cpp?rev=281453&view=auto</a><br>
==============================<wbr>==============================<wbr>==================<br>
--- clang-tools-extra/trunk/clang-<wbr>tidy/misc/UseAfterMoveCheck.cp<wbr>p (added)<br>
+++ clang-tools-extra/trunk/clang-<wbr>tidy/misc/UseAfterMoveCheck.cp<wbr>p Wed Sep 14 05:29:32 2016<br>
@@ -0,0 +1,643 @@<br>
+//===--- UseAfterMoveCheck.cpp - clang-tidy ------------------------------<wbr>-===//<br>
+//<br>
+// The LLVM Compiler Infrastructure<br>
+//<br>
+// This file is distributed under the University of Illinois Open Source<br>
+// License. See LICENSE.TXT for details.<br>
+//<br>
+//===------------------------<wbr>------------------------------<wbr>----------------===//<br>
+<br>
+#include "UseAfterMoveCheck.h"<br>
+<br>
+#include "clang/Analysis/CFG.h"<br>
+#include "clang/Lex/Lexer.h"<br>
+#include "llvm/ADT/DenseMap.h"<br>
+#include "llvm/ADT/SmallPtrSet.h"<br>
+#include "llvm/ADT/SmallVector.h"<br>
+<br>
+#include <algorithm><br>
+<br>
+using namespace clang::ast_matchers;<br>
+<br>
+namespace clang {<br>
+namespace tidy {<br>
+namespace misc {<br>
+<br>
+namespace {<br>
+<br>
+/// Provides information about the evaluation order of (sub-)expressions within<br>
+/// a `CFGBlock`.<br>
+///<br>
+/// While a `CFGBlock` does contain individual `CFGElement`s for some<br>
+/// sub-expressions, the order in which those `CFGElement`s appear reflects<br>
+/// only one possible order in which the sub-expressions may be evaluated.<br>
+/// However, we want to warn if any of the potential evaluation orders can lead<br>
+/// to a use-after-move, not just the one contained in the `CFGBlock`.<br>
+///<br>
+/// This class implements only a simplified version of the C++ sequencing rules<br>
+/// that is, however, sufficient for the purposes of this check. The main<br>
+/// limitation is that we do not distinguish between value computation and side<br>
+/// effect -- see the "Implementation" section for more details.<br>
+///<br>
+/// Note: `SequenceChecker` from SemaChecking.cpp does a similar job (and much<br>
+/// more thoroughly), but using it would require<br>
+/// - Pulling `SequenceChecker` out into a header file (i.e. making it part of<br>
+/// the API),<br>
+/// - Removing the dependency of `SequenceChecker` on `Sema`, and<br>
+/// - (Probably) modifying `SequenceChecker` to make it suitable to be used in<br>
+/// this context.<br>
+/// For the moment, it seems preferable to re-implement our own version of<br>
+/// sequence checking that is special-cased to what we need here.<br>
+///<br>
+/// Implementation<br>
+/// --------------<br>
+///<br>
+/// `ExprSequence` uses two types of sequencing edges between nodes in the AST:<br>
+///<br>
+/// - Every `Stmt` is assumed to be sequenced after its children. This is<br>
+/// overly optimistic because the standard only states that value computations<br>
+/// of operands are sequenced before the value computation of the operator,<br>
+/// making no guarantees about side effects (in general).<br>
+///<br>
+/// For our purposes, this rule is sufficient, however, because this check is<br>
+/// interested in operations on objects, which are generally performed through<br>
+/// function calls (whether explicit and implicit). Function calls guarantee<br>
+/// that the value computations and side effects for all function arguments<br>
+/// are sequenced before the execution fo the function.<br>
+///<br>
+/// - In addition, some `Stmt`s are known to be sequenced before or after<br>
+/// their siblings. For example, the `Stmt`s that make up a `CompoundStmt`are<br>
+/// all sequenced relative to each other. The function<br>
+/// `getSequenceSuccessor()` implements these sequencing rules.<br>
+class ExprSequence {<br>
+public:<br>
+ /// Initializes this `ExprSequence` with sequence information for the given<br>
+ /// `CFG`.<br>
+ ExprSequence(const CFG *TheCFG, ASTContext *TheContext);<br>
+<br>
+ /// Returns whether \p Before is sequenced before \p After.<br>
+ bool inSequence(const Stmt *Before, const Stmt *After) const;<br>
+<br>
+ /// Returns whether \p After can potentially be evaluated after \p Before.<br>
+ /// This is exactly equivalent to `!inSequence(After, Before)` but makes some<br>
+ /// conditions read more naturally.<br>
+ bool potentiallyAfter(const Stmt *After, const Stmt *Before) const;<br>
+<br>
+private:<br>
+ // Returns the sibling of \p S (if any) that is directly sequenced after \p S,<br>
+ // or nullptr if no such sibling exists. For example, if \p S is the child of<br>
+ // a `CompoundStmt`, this would return the Stmt that directly follows \p S in<br>
+ // the `CompoundStmt`.<br>
+ //<br>
+ // As the sequencing of many constructs that change control flow is already<br>
+ // encoded in the `CFG`, this function only implements the sequencing rules<br>
+ // for those constructs where sequencing cannot be inferred from the `CFG`.<br>
+ const Stmt *getSequenceSuccessor(const Stmt *S) const;<br>
+<br>
+ const Stmt *resolveSyntheticStmt(const Stmt *S) const;<br>
+<br>
+ ASTContext *Context;<br>
+<br>
+ llvm::DenseMap<const Stmt *, const Stmt *> SyntheticStmtSourceMap;<br>
+};<br>
+<br>
+/// Maps `Stmt`s to the `CFGBlock` that contains them. Some `Stmt`s may be<br>
+/// contained in more than one `CFGBlock`; in this case, they are mapped to the<br>
+/// innermost block (i.e. the one that is furthest from the root of the tree).<br>
+class StmtToBlockMap {<br>
+public:<br>
+ /// Initializes the map for the given `CFG`.<br>
+ StmtToBlockMap(const CFG *TheCFG, ASTContext *TheContext);<br>
+<br>
+ /// Returns the block that \p S is contained in. Some `Stmt`s may be contained<br>
+ /// in more than one `CFGBlock`; in this case, this function returns the<br>
+ /// innermost block (i.e. the one that is furthest from the root of the tree).<br>
+ const CFGBlock *blockContainingStmt(const Stmt *S) const;<br>
+<br>
+private:<br>
+ ASTContext *Context;<br>
+<br>
+ llvm::DenseMap<const Stmt *, const CFGBlock *> Map;<br>
+};<br>
+<br>
+/// Contains information about a use-after-move.<br>
+struct UseAfterMove {<br>
+ // The DeclRefExpr that constituted the use of the object.<br>
+ const DeclRefExpr *DeclRef;<br>
+<br>
+ // Is the order in which the move and the use are evaluated undefined?<br>
+ bool EvaluationOrderUndefined;<br>
+};<br>
+<br>
+/// Finds uses of a variable after a move (and maintains state required by the<br>
+/// various internal helper functions).<br>
+class UseAfterMoveFinder {<br>
+public:<br>
+ UseAfterMoveFinder(ASTContext *TheContext);<br>
+<br>
+ // Within the given function body, finds the first use of 'MovedVariable' that<br>
+ // occurs after 'MovingCall' (the expression that performs the move). If a<br>
+ // use-after-move is found, writes information about it to 'TheUseAfterMove'.<br>
+ // Returns whether a use-after-move was found.<br>
+ bool find(Stmt *FunctionBody, const Expr *MovingCall,<br>
+ const ValueDecl *MovedVariable, UseAfterMove *TheUseAfterMove);<br>
+<br>
+private:<br>
+ bool findInternal(const CFGBlock *Block, const Expr *MovingCall,<br>
+ const ValueDecl *MovedVariable,<br>
+ UseAfterMove *TheUseAfterMove);<br>
+ void getUsesAndReinits(const CFGBlock *Block, const ValueDecl *MovedVariable,<br>
+ llvm::SmallVectorImpl<const DeclRefExpr *> *Uses,<br>
+ llvm::SmallPtrSetImpl<const Stmt *> *Reinits);<br>
+ void getDeclRefs(const CFGBlock *Block, const Decl *MovedVariable,<br>
+ llvm::SmallPtrSetImpl<const DeclRefExpr *> *DeclRefs);<br>
+ void getReinits(const CFGBlock *Block, const ValueDecl *MovedVariable,<br>
+ llvm::SmallPtrSetImpl<const Stmt *> *Stmts,<br>
+ llvm::SmallPtrSetImpl<const DeclRefExpr *> *DeclRefs);<br>
+<br>
+ ASTContext *Context;<br>
+ std::unique_ptr<ExprSequence> Sequence;<br>
+ std::unique_ptr<StmtToBlockMap<wbr>> BlockMap;<br>
+ llvm::SmallPtrSet<const CFGBlock *, 8> Visited;<br>
+};<br>
+<br>
+} // namespace<br>
+<br>
+// Returns the Stmt nodes that are parents of 'S', skipping any potential<br>
+// intermediate non-Stmt nodes.<br>
+//<br>
+// In almost all cases, this function returns a single parent or no parents at<br>
+// all.<br>
+//<br>
+// The case that a Stmt has multiple parents is rare but does actually occur in<br>
+// the parts of the AST that we're interested in. Specifically, InitListExpr<br>
+// nodes cause ASTContext::getParent() to return multiple parents for certain<br>
+// nodes in their subtree because RecursiveASTVisitor visits both the syntactic<br>
+// and semantic forms of InitListExpr, and the parent-child relationships are<br>
+// different between the two forms.<br>
+static SmallVector<const Stmt *, 1> getParentStmts(const Stmt *S,<br>
+ ASTContext *Context) {<br>
+ SmallVector<const Stmt *, 1> Result;<br>
+<br>
+ ASTContext::DynTypedNodeList Parents = Context->getParents(*S);<br>
+<br>
+ SmallVector<ast_type_traits::D<wbr>ynTypedNode, 1> NodesToProcess(Parents.begin()<wbr>,<br>
+ Parents.end());<br>
+<br>
+ while (!NodesToProcess.empty()) {<br>
+ ast_type_traits::DynTypedNode Node = NodesToProcess.back();<br>
+ NodesToProcess.pop_back();<br>
+<br>
+ if (const auto *S = Node.get<Stmt>()) {<br>
+ Result.push_back(S);<br>
+ } else {<br>
+ Parents = Context->getParents(Node);<br>
+ NodesToProcess.append(Parents.<wbr>begin(), Parents.end());<br>
+ }<br>
+ }<br>
+<br>
+ return Result;<br>
+}<br>
+<br>
+bool isDescendantOrEqual(const Stmt *Descendant, const Stmt *Ancestor,<br>
+ ASTContext *Context) {<br>
+ if (Descendant == Ancestor)<br>
+ return true;<br>
+ for (const Stmt *Parent : getParentStmts(Descendant, Context)) {<br>
+ if (isDescendantOrEqual(Parent, Ancestor, Context))<br>
+ return true;<br>
+ }<br>
+<br>
+ return false;<br>
+}<br>
+<br>
+ExprSequence::ExprSequence(co<wbr>nst CFG *TheCFG, ASTContext *TheContext)<br>
+ : Context(TheContext) {<br>
+ for (const auto &SyntheticStmt : TheCFG->synthetic_stmts()) {<br>
+ SyntheticStmtSourceMap[Synthet<wbr>icStmt.first] = SyntheticStmt.second;<br>
+ }<br>
+}<br>
+<br>
+bool ExprSequence::inSequence(const Stmt *Before, const Stmt *After) const {<br>
+ Before = resolveSyntheticStmt(Before);<br>
+ After = resolveSyntheticStmt(After);<br>
+<br>
+ // If 'After' is in the subtree of the siblings that follow 'Before' in the<br>
+ // chain of successors, we know that 'After' is sequenced after 'Before'.<br>
+ for (const Stmt *Successor = getSequenceSuccessor(Before); Successor;<br>
+ Successor = getSequenceSuccessor(Successor<wbr>)) {<br>
+ if (isDescendantOrEqual(After, Successor, Context))<br>
+ return true;<br>
+ }<br>
+<br>
+ // If 'After' is a parent of 'Before' or is sequenced after one of these<br>
+ // parents, we know that it is sequenced after 'Before'.<br>
+ for (const Stmt *Parent : getParentStmts(Before, Context)) {<br>
+ if (Parent == After || inSequence(Parent, After))<br>
+ return true;<br>
+ }<br>
+<br>
+ return false;<br>
+}<br>
+<br>
+bool ExprSequence::potentiallyAfter<wbr>(const Stmt *After,<br>
+ const Stmt *Before) const {<br>
+ return !inSequence(After, Before);<br>
+}<br>
+<br>
+const Stmt *ExprSequence::getSequenceSucc<wbr>essor(const Stmt *S) const {<br>
+ for (const Stmt *Parent : getParentStmts(S, Context)) {<br>
+ if (const auto *BO = dyn_cast<BinaryOperator>(Paren<wbr>t)) {<br>
+ // Comma operator: Right-hand side is sequenced after the left-hand side.<br>
+ if (BO->getLHS() == S && BO->getOpcode() == BO_Comma)<br>
+ return BO->getRHS();<br>
+ } else if (const auto *InitList = dyn_cast<InitListExpr>(Parent)<wbr>) {<br>
+ // Initializer list: Each initializer clause is sequenced after the<br>
+ // clauses that precede it.<br>
+ for (unsigned I = 1; I < InitList->getNumInits(); ++I) {<br>
+ if (InitList->getInit(I - 1) == S)<br>
+ return InitList->getInit(I);<br>
+ }<br>
+ } else if (const auto *Compound = dyn_cast<CompoundStmt>(Parent)<wbr>) {<br>
+ // Compound statement: Each sub-statement is sequenced after the<br>
+ // statements that precede it.<br>
+ const Stmt *Previous = nullptr;<br>
+ for (const auto *Child : Compound->body()) {<br>
+ if (Previous == S)<br>
+ return Child;<br>
+ Previous = Child;<br>
+ }<br>
+ } else if (const auto *TheDeclStmt = dyn_cast<DeclStmt>(Parent)) {<br>
+ // Declaration: Every initializer expression is sequenced after the<br>
+ // initializer expressions that precede it.<br>
+ const Expr *PreviousInit = nullptr;<br>
+ for (const Decl *TheDecl : TheDeclStmt->decls()) {<br>
+ if (const auto *TheVarDecl = dyn_cast<VarDecl>(TheDecl)) {<br>
+ if (const Expr *Init = TheVarDecl->getInit()) {<br>
+ if (PreviousInit == S)<br>
+ return Init;<br>
+ PreviousInit = Init;<br>
+ }<br>
+ }<br>
+ }<br>
+ } else if (const auto *ForRange = dyn_cast<CXXForRangeStmt>(Pare<wbr>nt)) {<br>
+ // Range-based for: Loop variable declaration is sequenced before the<br>
+ // body. (We need this rule because these get placed in the same<br>
+ // CFGBlock.)<br>
+ if (S == ForRange->getLoopVarStmt())<br>
+ return ForRange->getBody();<br>
+ } else if (const auto *TheIfStmt = dyn_cast<IfStmt>(Parent)) {<br>
+ // If statement: If a variable is declared inside the condition, the<br>
+ // expression used to initialize the variable is sequenced before the<br>
+ // evaluation of the condition.<br>
+ if (S == TheIfStmt->getConditionVariabl<wbr>eDeclStmt())<br>
+ return TheIfStmt->getCond();<br>
+ }<br>
+ }<br>
+<br>
+ return nullptr;<br>
+}<br>
+<br>
+const Stmt *ExprSequence::resolveSyntheti<wbr>cStmt(const Stmt *S) const {<br>
+ if (SyntheticStmtSourceMap.count(<wbr>S))<br>
+ return SyntheticStmtSourceMap.lookup(<wbr>S);<br>
+ else<br>
+ return S;<br>
+}<br>
+<br>
+StmtToBlockMap::StmtToBlockMa<wbr>p(const CFG *TheCFG, ASTContext *TheContext)<br>
+ : Context(TheContext) {<br>
+ for (const auto *B : *TheCFG) {<br>
+ for (const auto &Elem : *B) {<br>
+ if (Optional<CFGStmt> S = Elem.getAs<CFGStmt>())<br>
+ Map[S->getStmt()] = B;<br>
+ }<br>
+ }<br>
+}<br>
+<br>
+const CFGBlock *StmtToBlockMap::blockContaini<wbr>ngStmt(const Stmt *S) const {<br>
+ while (!Map.count(S)) {<br>
+ SmallVector<const Stmt *, 1> Parents = getParentStmts(S, Context);<br>
+ if (Parents.empty())<br>
+ return nullptr;<br>
+ S = Parents[0];<br>
+ }<br>
+<br>
+ return Map.lookup(S);<br>
+}<br>
+<br>
+// Matches nodes that are<br>
+// - Part of a decltype argument or class template argument (we check this by<br>
+// seeing if they are children of a TypeLoc), or<br>
+// - Part of a function template argument (we check this by seeing if they are<br>
+// children of a DeclRefExpr that references a function template).<br>
+// DeclRefExprs that fulfill these conditions should not be counted as a use or<br>
+// move.<br>
+static StatementMatcher inDecltypeOrTemplateArg() {<br>
+ return anyOf(hasAncestor(typeLoc()),<br>
+ hasAncestor(declRefExpr(<br>
+ to(functionDecl(ast_matchers:<wbr>:isTemplateInstantiation()))))<wbr>);<br>
+}<br>
+<br>
+UseAfterMoveFinder::UseAfterM<wbr>oveFinder(ASTContext *TheContext)<br>
+ : Context(TheContext) {}<br>
+<br>
+bool UseAfterMoveFinder::find(Stmt *FunctionBody, const Expr *MovingCall,<br>
+ const ValueDecl *MovedVariable,<br>
+ UseAfterMove *TheUseAfterMove) {<br>
+ // Generate the CFG manually instead of through an AnalysisDeclContext because<br>
+ // it seems the latter can't be used to generate a CFG for the body of a<br>
+ // labmda.<br>
+ //<br>
+ // We include implicit and temporary destructors in the CFG so that<br>
+ // destructors marked [[noreturn]] are handled correctly in the control flow<br>
+ // analysis. (These are used in some styles of assertion macros.)<br>
+ CFG::BuildOptions Options;<br>
+ Options.AddImplicitDtors = true;<br>
+ Options.AddTemporaryDtors = true;<br>
+ std::unique_ptr<CFG> TheCFG =<br>
+ CFG::buildCFG(nullptr, FunctionBody, Context, Options);<br>
+ if (!TheCFG)<br>
+ return false;<br>
+<br>
+ Sequence.reset(new ExprSequence(TheCFG.get(), Context));<br>
+ BlockMap.reset(new StmtToBlockMap(TheCFG.get(), Context));<br>
+ Visited.clear();<br>
+<br>
+ const CFGBlock *Block = BlockMap->blockContainingStmt(<wbr>MovingCall);<br>
+ if (!Block)<br>
+ return false;<br>
+<br>
+ return findInternal(Block, MovingCall, MovedVariable, TheUseAfterMove);<br>
+}<br>
+<br>
+bool UseAfterMoveFinder::findIntern<wbr>al(const CFGBlock *Block,<br>
+ const Expr *MovingCall,<br>
+ const ValueDecl *MovedVariable,<br>
+ UseAfterMove *TheUseAfterMove) {<br>
+ if (Visited.count(Block))<br>
+ return false;<br>
+<br>
+ // Mark the block as visited (except if this is the block containing the<br>
+ // std::move() and it's being visited the first time).<br>
+ if (!MovingCall)<br>
+ Visited.insert(Block);<br>
+<br>
+ // Get all uses and reinits in the block.<br>
+ llvm::SmallVector<const DeclRefExpr *, 1> Uses;<br>
+ llvm::SmallPtrSet<const Stmt *, 1> Reinits;<br>
+ getUsesAndReinits(Block, MovedVariable, &Uses, &Reinits);<br>
+<br>
+ // Ignore all reinitializations where the move potentially comes after the<br>
+ // reinit.<br>
+ llvm::SmallVector<const Stmt *, 1> ReinitsToDelete;<br>
+ for (const Stmt *Reinit : Reinits) {<br>
+ if (MovingCall && Sequence->potentiallyAfter(Mov<wbr>ingCall, Reinit))<br>
+ ReinitsToDelete.push_back(Rein<wbr>it);<br>
+ }<br>
+ for (const Stmt *Reinit : ReinitsToDelete) {<br>
+ Reinits.erase(Reinit);<br>
+ }<br>
+<br>
+ // Find all uses that potentially come after the move.<br>
+ for (const DeclRefExpr *Use : Uses) {<br>
+ if (!MovingCall || Sequence->potentiallyAfter(Use<wbr>, MovingCall)) {<br>
+ // Does the use have a saving reinit? A reinit is saving if it definitely<br>
+ // comes before the use, i.e. if there's no potential that the reinit is<br>
+ // after the use.<br>
+ bool HaveSavingReinit = false;<br>
+ for (const Stmt *Reinit : Reinits) {<br>
+ if (!Sequence->potentiallyAfter(R<wbr>einit, Use))<br>
+ HaveSavingReinit = true;<br>
+ }<br>
+<br>
+ if (!HaveSavingReinit) {<br>
+ TheUseAfterMove->DeclRef = Use;<br>
+<br>
+ // Is this a use-after-move that depends on order of evaluation?<br>
+ // This is the case if the move potentially comes after the use (and we<br>
+ // already know that use potentially comes after the move, which taken<br>
+ // together tells us that the ordering is unclear).<br>
+ TheUseAfterMove->EvaluationOrd<wbr>erUndefined =<br>
+ MovingCall != nullptr &&<br>
+ Sequence->potentiallyAfter(Mov<wbr>ingCall, Use);<br>
+<br>
+ return true;<br>
+ }<br>
+ }<br>
+ }<br>
+<br>
+ // If the object wasn't reinitialized, call ourselves recursively on all<br>
+ // successors.<br>
+ if (Reinits.empty()) {<br>
+ for (const auto &Succ : Block->succs()) {<br>
+ if (Succ && findInternal(Succ, nullptr, MovedVariable, TheUseAfterMove))<br>
+ return true;<br>
+ }<br>
+ }<br>
+<br>
+ return false;<br>
+}<br>
+<br>
+void UseAfterMoveFinder::getUsesAnd<wbr>Reinits(<br>
+ const CFGBlock *Block, const ValueDecl *MovedVariable,<br>
+ llvm::SmallVectorImpl<const DeclRefExpr *> *Uses,<br>
+ llvm::SmallPtrSetImpl<const Stmt *> *Reinits) {<br>
+ llvm::SmallPtrSet<const DeclRefExpr *, 1> DeclRefs;<br>
+ llvm::SmallPtrSet<const DeclRefExpr *, 1> ReinitDeclRefs;<br>
+<br>
+ getDeclRefs(Block, MovedVariable, &DeclRefs);<br>
+ getReinits(Block, MovedVariable, Reinits, &ReinitDeclRefs);<br>
+<br>
+ // All references to the variable that aren't reinitializations are uses.<br>
+ Uses->clear();<br>
+ for (const DeclRefExpr *DeclRef : DeclRefs) {<br>
+ if (!ReinitDeclRefs.count(DeclRef<wbr>))<br>
+ Uses->push_back(DeclRef);<br>
+ }<br>
+<br>
+ // Sort the uses by their occurrence in the source code.<br>
+ std::sort(Uses->begin(), Uses->end(),<br>
+ [](const DeclRefExpr *D1, const DeclRefExpr *D2) {<br>
+ return D1->getExprLoc() < D2->getExprLoc();<br>
+ });<br>
+}<br>
+<br>
+void UseAfterMoveFinder::getDeclRef<wbr>s(<br>
+ const CFGBlock *Block, const Decl *MovedVariable,<br>
+ llvm::SmallPtrSetImpl<const DeclRefExpr *> *DeclRefs) {<br>
+ DeclRefs->clear();<br>
+ for (const auto &Elem : *Block) {<br>
+ Optional<CFGStmt> S = Elem.getAs<CFGStmt>();<br>
+ if (!S)<br>
+ continue;<br>
+<br>
+ SmallVector<BoundNodes, 1> Matches =<br>
+ match(findAll(declRefExpr(hasD<wbr>eclaration(equalsNode(MovedVar<wbr>iable)),<br>
+ unless(inDecltypeOrTemplateArg<wbr>()))<br>
+ .bind("declref")),<br>
+ *S->getStmt(), *Context);<br>
+<br>
+ for (const auto &Match : Matches) {<br>
+ const auto *DeclRef = Match.getNodeAs<DeclRefExpr>("<wbr>declref");<br>
+ if (DeclRef && BlockMap->blockContainingStmt(<wbr>DeclRef) == Block)<br>
+ DeclRefs->insert(DeclRef);<br>
+ }<br>
+ }<br>
+}<br>
+<br>
+void UseAfterMoveFinder::getReinits<wbr>(<br>
+ const CFGBlock *Block, const ValueDecl *MovedVariable,<br>
+ llvm::SmallPtrSetImpl<const Stmt *> *Stmts,<br>
+ llvm::SmallPtrSetImpl<const DeclRefExpr *> *DeclRefs) {<br>
+ auto DeclRefMatcher =<br>
+ declRefExpr(hasDeclaration(equ<wbr>alsNode(MovedVariable))).bind(<wbr>"declref");<br>
+<br>
+ auto StandardContainerTypeMatcher = hasType(cxxRecordDecl(<br>
+ hasAnyName("::std::basic_strin<wbr>g", "::std::vector", "::std::deque",<br>
+ "::std::forward_list", "::std::list", "::std::set",<br>
+ "::std::map", "::std::multiset", "::std::multimap",<br>
+ "::std::unordered_set", "::std::unordered_map",<br>
+ "::std::unordered_multiset", "::std::unordered_multimap")))<wbr>;<br>
+<br>
+ // Matches different types of reinitialization.<br>
+ auto ReinitMatcher =<br>
+ stmt(anyOf(<br>
+ // Assignment. In addition to the overloaded assignment operator,<br>
+ // test for built-in assignment as well, since template functions<br>
+ // may be instantiated to use std::move() on built-in types.<br>
+ binaryOperator(hasOperatorNam<wbr>e("="), hasLHS(DeclRefMatcher)),<br>
+ cxxOperatorCallExpr(hasOverlo<wbr>adedOperatorName("="),<br>
+ hasArgument(0, DeclRefMatcher)),<br>
+ // Declaration. We treat this as a type of reinitialization too,<br>
+ // so we don't need to treat it separately.<br>
+ declStmt(hasDescendant(equals<wbr>Node(MovedVariable))),<br>
+ // clear() and assign() on standard containers.<br>
+ cxxMemberCallExpr(<br>
+ on(allOf(DeclRefMatcher, StandardContainerTypeMatcher))<wbr>,<br>
+ // To keep the matcher simple, we check for assign() calls<br>
+ // on all standard containers, even though only vector,<br>
+ // deque, forward_list and list have assign(). If assign()<br>
+ // is called on any of the other containers, this will be<br>
+ // flagged by a compile error anyway.<br>
+ callee(cxxMethodDecl(hasAnyNa<wbr>me("clear", "assign")))),<br>
+ // Passing variable to a function as a non-const pointer.<br>
+ callExpr(forEachArgumentWithP<wbr>aram(<br>
+ unaryOperator(hasOperatorName<wbr>("&"),<br>
+ hasUnaryOperand(DeclRefMatche<wbr>r)),<br>
+ unless(parmVarDecl(hasType(po<wbr>intsTo(isConstQualified())))))<wbr>),<br>
+ // Passing variable to a function as a non-const lvalue reference<br>
+ // (unless that function is std::move()).<br>
+ callExpr(forEachArgumentWithP<wbr>aram(<br>
+ DeclRefMatcher,<br>
+ unless(parmVarDecl(hasType(<br>
+ references(qualType(isConstQua<wbr>lified())))))),<br>
+ unless(callee(functionDecl(has<wbr>Name("::std::move")))))))<br>
+ .bind("reinit");<br>
+<br>
+ Stmts->clear();<br>
+ DeclRefs->clear();<br>
+ for (const auto &Elem : *Block) {<br>
+ Optional<CFGStmt> S = Elem.getAs<CFGStmt>();<br>
+ if (!S)<br>
+ continue;<br>
+<br>
+ SmallVector<BoundNodes, 1> Matches =<br>
+ match(findAll(ReinitMatcher), *S->getStmt(), *Context);<br>
+<br>
+ for (const auto &Match : Matches) {<br>
+ const auto *TheStmt = Match.getNodeAs<Stmt>("reinit"<wbr>);<br>
+ const auto *TheDeclRef = Match.getNodeAs<DeclRefExpr>("<wbr>declref");<br>
+ if (TheStmt && BlockMap->blockContainingStmt(<wbr>TheStmt) == Block) {<br>
+ Stmts->insert(TheStmt);<br>
+<br>
+ // We count DeclStmts as reinitializations, but they don't have a<br>
+ // DeclRefExpr associated with them -- so we need to check 'TheDeclRef'<br>
+ // before adding it to the set.<br>
+ if (TheDeclRef)<br>
+ DeclRefs->insert(TheDeclRef);<br>
+ }<br>
+ }<br>
+ }<br>
+}<br>
+<br>
+static void emitDiagnostic(const Expr *MovingCall,<br>
+ const ValueDecl *MovedVariable,<br>
+ const UseAfterMove &Use, ClangTidyCheck *Check,<br>
+ ASTContext *Context) {<br>
+ Check->diag(Use.DeclRef->getEx<wbr>prLoc(), "'%0' used after it was moved")<br>
+ << MovedVariable->getName();<br>
+ Check->diag(MovingCall->getExp<wbr>rLoc(), "move occurred here",<br>
+ DiagnosticIDs::Note);<br>
+ if (Use.EvaluationOrderUndefined) {<br>
+ Check->diag(Use.DeclRef->getEx<wbr>prLoc(),<br>
+ "the use and move are unsequenced, i.e. there is no guarantee "<br>
+ "about the order in which they are evaluated",<br>
+ DiagnosticIDs::Note);<br>
+ }<br>
+}<br>
+<br>
+void UseAfterMoveCheck::registerMat<wbr>chers(MatchFinder *Finder) {<br>
+ if (!getLangOpts().CPlusPlus11)<br>
+ return;<br>
+<br>
+ auto StandardSmartPointerTypeMatche<wbr>r = hasType(<br>
+ cxxRecordDecl(hasAnyName("::st<wbr>d::unique_ptr", "::std::shared_ptr")));<br>
+<br>
+ auto CallMoveMatcher =<br>
+ callExpr(<br>
+ callee(functionDecl(hasName(":<wbr>:std::move"))), argumentCountIs(1),<br>
+ hasArgument(<br>
+ 0,<br>
+ declRefExpr(unless(StandardSma<wbr>rtPointerTypeMatcher)).bind("a<wbr>rg")),<br>
+ anyOf(hasAncestor(lambdaExpr()<wbr>.bind("containing-lambda")),<br>
+ hasAncestor(functionDecl().bin<wbr>d("containing-func"))),<br>
+ unless(inDecltypeOrTemplateArg<wbr>()))<br>
+ .bind("call-move");<br>
+<br>
+ Finder->addMatcher(<br>
+ // To find the Stmt that we assume performs the actual move, we look for<br>
+ // the direct ancestor of the std::move() that isn't one of the node<br>
+ // types ignored by ignoringParenImpCasts().<br>
+ stmt(forEach(expr(ignoringPare<wbr>nImpCasts(CallMoveMatcher))),<br>
+ unless(expr(ignoringParenImpC<wbr>asts(equalsBoundNode("call-mov<wbr>e")))))<br>
+ .bind("moving-call"),<br>
+ this);<br>
+}<br>
+<br>
+void UseAfterMoveCheck::check(const MatchFinder::MatchResult &Result) {<br>
+ const auto *ContainingLambda =<br>
+ Result.Nodes.getNodeAs<LambdaE<wbr>xpr>("containing-lambda");<br>
+ const auto *ContainingFunc =<br>
+ Result.Nodes.getNodeAs<Functio<wbr>nDecl>("containing-func");<br>
+ const auto *CallMove = Result.Nodes.getNodeAs<CallExp<wbr>r>("call-move");<br>
+ const auto *MovingCall = Result.Nodes.getNodeAs<Expr>("<wbr>moving-call");<br>
+ const auto *Arg = Result.Nodes.getNodeAs<DeclRef<wbr>Expr>("arg");<br>
+<br>
+ if (!MovingCall)<br>
+ MovingCall = CallMove;<br>
+<br>
+ Stmt *FunctionBody = nullptr;<br>
+ if (ContainingLambda)<br>
+ FunctionBody = ContainingLambda->getBody();<br>
+ else if (ContainingFunc)<br>
+ FunctionBody = ContainingFunc->getBody();<br>
+ else<br>
+ return;<br>
+<br>
+ const ValueDecl *MovedVariable = Arg->getDecl();<br>
+<br>
+ // Ignore the std::move if the variable that was passed to it isn't a local<br>
+ // variable.<br>
+ if (!Arg->getDecl()->getDeclConte<wbr>xt()->isFunctionOrMethod())<br>
+ return;<br>
+<br>
+ UseAfterMoveFinder finder(Result.Context);<br>
+ UseAfterMove Use;<br>
+ if (finder.find(FunctionBody, MovingCall, MovedVariable, &Use))<br>
+ emitDiagnostic(MovingCall, MovedVariable, Use, this, Result.Context);<br>
+}<br>
+<br>
+} // namespace misc<br>
+} // namespace tidy<br>
+} // namespace clang<br>
<br>
Added: clang-tools-extra/trunk/clang-<wbr>tidy/misc/UseAfterMoveCheck.h<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clang-tidy/misc/UseAfterMoveCheck.h?rev=281453&view=auto" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-pr<wbr>oject/clang-tools-extra/trunk/<wbr>clang-tidy/misc/UseAfterMoveCh<wbr>eck.h?rev=281453&view=auto</a><br>
==============================<wbr>==============================<wbr>==================<br>
--- clang-tools-extra/trunk/clang-<wbr>tidy/misc/UseAfterMoveCheck.h (added)<br>
+++ clang-tools-extra/trunk/clang-<wbr>tidy/misc/UseAfterMoveCheck.h Wed Sep 14 05:29:32 2016<br>
@@ -0,0 +1,36 @@<br>
+//===--- UseAfterMoveCheck.h - clang-tidy ------------------------------<wbr>---===//<br>
+//<br>
+// The LLVM Compiler Infrastructure<br>
+//<br>
+// This file is distributed under the University of Illinois Open Source<br>
+// License. See LICENSE.TXT for details.<br>
+//<br>
+//===------------------------<wbr>------------------------------<wbr>----------------===//<br>
+<br>
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_T<wbr>IDY_MISC_USEAFTERMOVECHECK_H<br>
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_T<wbr>IDY_MISC_USEAFTERMOVECHECK_H<br>
+<br>
+#include "../ClangTidy.h"<br>
+<br>
+namespace clang {<br>
+namespace tidy {<br>
+namespace misc {<br>
+<br>
+/// The check warns if an object is used after it has been moved, without an<br>
+/// intervening reinitialization.<br>
+///<br>
+/// For details, see the user-facing documentation:<br>
+/// <a href="http://clang.llvm.org/extra/clang-tidy/checks/misc-use-after-move.html" rel="noreferrer" target="_blank">http://clang.llvm.org/extra/cl<wbr>ang-tidy/checks/misc-use-after<wbr>-move.html</a><br>
+class UseAfterMoveCheck : public ClangTidyCheck {<br>
+public:<br>
+ UseAfterMoveCheck(StringRef Name, ClangTidyContext *Context)<br>
+ : ClangTidyCheck(Name, Context) {}<br>
+ void registerMatchers(ast_matchers:<wbr>:MatchFinder *Finder) override;<br>
+ void check(const ast_matchers::MatchFinder::Mat<wbr>chResult &Result) override;<br>
+};<br>
+<br>
+} // namespace misc<br>
+} // namespace tidy<br>
+} // namespace clang<br>
+<br>
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_T<wbr>IDY_MISC_USEAFTERMOVECHECK_H<br>
<br>
Modified: clang-tools-extra/trunk/docs/R<wbr>eleaseNotes.rst<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/docs/ReleaseNotes.rst?rev=281453&r1=281452&r2=281453&view=diff" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-pr<wbr>oject/clang-tools-extra/trunk/<wbr>docs/ReleaseNotes.rst?rev=2814<wbr>53&r1=281452&r2=281453&view=<wbr>diff</a><br>
==============================<wbr>==============================<wbr>==================<br>
--- clang-tools-extra/trunk/docs/R<wbr>eleaseNotes.rst (original)<br>
+++ clang-tools-extra/trunk/docs/R<wbr>eleaseNotes.rst Wed Sep 14 05:29:32 2016<br>
@@ -73,6 +73,12 @@ Improvements to clang-tidy<br>
Warns when ``std::move`` is applied to a forwarding reference instead of<br>
``std::forward``.<br>
<br>
+- New `misc-use-after-move<br>
+ <<a href="http://clang.llvm.org/extra/clang-tidy/checks/misc-use-after-move.html" rel="noreferrer" target="_blank">http://clang.llvm.org/extra/c<wbr>lang-tidy/checks/misc-use-afte<wbr>r-move.html</a>>`_ check<br>
+<br>
+ Warns if an object is used after it has been moved, without an intervening<br>
+ reinitialization.<br>
+<br>
- New `mpi-buffer-deref<br>
<<a href="http://clang.llvm.org/extra/clang-tidy/checks/mpi-buffer-deref.html" rel="noreferrer" target="_blank">http://clang.llvm.org/extra/<wbr>clang-tidy/checks/mpi-buffer-d<wbr>eref.html</a>>`_ check<br>
<br>
<br>
Modified: clang-tools-extra/trunk/docs/c<wbr>lang-tidy/checks/list.rst<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/docs/clang-tidy/checks/list.rst?rev=281453&r1=281452&r2=281453&view=diff" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-pr<wbr>oject/clang-tools-extra/trunk/<wbr>docs/clang-tidy/checks/list.rs<wbr>t?rev=281453&r1=281452&r2=2814<wbr>53&view=diff</a><br>
==============================<wbr>==============================<wbr>==================<br>
--- clang-tools-extra/trunk/docs/c<wbr>lang-tidy/checks/list.rst (original)<br>
+++ clang-tools-extra/trunk/docs/c<wbr>lang-tidy/checks/list.rst Wed Sep 14 05:29:32 2016<br>
@@ -93,6 +93,7 @@ Clang-Tidy Checks<br>
misc-unused-parameters<br>
misc-unused-raii<br>
misc-unused-using-decls<br>
+ misc-use-after-move<br>
misc-virtual-near-miss<br>
modernize-avoid-bind<br>
modernize-deprecated-headers<br>
<br>
Added: clang-tools-extra/trunk/docs/c<wbr>lang-tidy/checks/misc-use-afte<wbr>r-move.rst<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/docs/clang-tidy/checks/misc-use-after-move.rst?rev=281453&view=auto" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-pr<wbr>oject/clang-tools-extra/trunk/<wbr>docs/clang-tidy/checks/misc-us<wbr>e-after-move.rst?rev=281453&vi<wbr>ew=auto</a><br>
==============================<wbr>==============================<wbr>==================<br>
--- clang-tools-extra/trunk/docs/c<wbr>lang-tidy/checks/misc-use-afte<wbr>r-move.rst (added)<br>
+++ clang-tools-extra/trunk/docs/c<wbr>lang-tidy/checks/misc-use-afte<wbr>r-move.rst Wed Sep 14 05:29:32 2016<br>
@@ -0,0 +1,197 @@<br>
+.. title:: clang-tidy - misc-use-after-move<br>
+<br>
+misc-use-after-move<br>
+===================<br>
+<br>
+Warns if an object is used after it has been moved, for example:<br>
+<br>
+ .. code-block:: c++<br>
+<br>
+ std::string str = "Hello, world!\n";<br>
+ std::vector<std::string> messages;<br>
+ messages.emplace_back(std::mov<wbr>e(str));<br>
+ std::cout << str;<br>
+<br>
+The last line will trigger a warning that ``str`` is used after it has been<br>
+moved.<br>
+<br>
+The check does not trigger a warning if the object is reinitialized after the<br>
+move and before the use. For example, no warning will be output for this code:<br>
+<br>
+ .. code-block:: c++<br>
+<br>
+ messages.emplace_back(std::mov<wbr>e(str));<br>
+ str = "Greetings, stranger!\n";<br>
+ std::cout << str;<br>
+<br>
+The check takes control flow into account. A warning is only emitted if the use<br>
+can be reached from the move. This means that the following code does not<br>
+produce a warning:<br>
+<br>
+ .. code-block:: c++<br>
+<br>
+ if (condition) {<br>
+ messages.emplace_back(std::mov<wbr>e(str));<br>
+ } else {<br>
+ std::cout << str;<br>
+ }<br>
+<br>
+On the other hand, the following code does produce a warning:<br>
+<br>
+ .. code-block:: c++<br>
+<br>
+ for (int i = 0; i < 10; ++i) {<br>
+ std::cout << str;<br>
+ messages.emplace_back(std::mov<wbr>e(str));<br>
+ }<br>
+<br>
+(The use-after-move happens on the second iteration of the loop.)<br>
+<br>
+In some cases, the check may not be able to detect that two branches are<br>
+mutually exclusive. For example (assuming that ``i`` is an int):<br>
+<br>
+ .. code-block:: c++<br>
+<br>
+ if (i == 1) {<br>
+ messages.emplace_back(std::mov<wbr>e(str));<br>
+ }<br>
+ if (i == 2) {<br>
+ std::cout << str;<br>
+ }<br>
+<br>
+In this case, the check will erroneously produce a warning, even though it is<br>
+not possible for both the move and the use to be executed.<br>
+<br>
+An erroneous warning can be silenced by reinitializing the object after the<br>
+move:<br>
+<br>
+ .. code-block:: c++<br>
+<br>
+ if (i == 1) {<br>
+ messages.emplace_back(std::mov<wbr>e(str));<br>
+ str = "";<br>
+ }<br>
+ if (i == 2) {<br>
+ std::cout << str;<br>
+ }<br>
+<br>
+No warnings are emitted for objects of type ``std::unique_ptr`` and<br>
+``std::shared_ptr``, as they have defined move behavior. (Objects of these<br>
+classes are guaranteed to be empty after they have been moved from.)<br>
+<br>
+Subsections below explain more precisely what exactly the check considers to be<br>
+a move, use, and reinitialization.<br>
+<br>
+Unsequenced moves, uses, and reinitializations<br>
+-----------------------------<wbr>-----------------<br>
+<br>
+In many cases, C++ does not make any guarantees about the order in which<br>
+sub-expressions of a statement are evaluated. This means that in code like the<br>
+following, it is not guaranteed whether the use will happen before or after the<br>
+move:<br>
+<br>
+ .. code-block:: c++<br>
+<br>
+ void f(int i, std::vector<int> v);<br>
+ std::vector<int> v = { 1, 2, 3 };<br>
+ f(v[1], std::move(v));<br>
+<br>
+In this kind of situation, the check will note that the use and move are<br>
+unsequenced.<br>
+<br>
+The check will also take sequencing rules into account when reinitializations<br>
+occur in the same statement as moves or uses. A reinitialization is only<br>
+considered to reinitialize a variable if it is guaranteed to be evaluated after<br>
+the move and before the use.<br>
+<br>
+Move<br>
+----<br>
+<br>
+The check currently only considers calls of ``std::move`` on local variables or<br>
+function parameters. It does not check moves of member variables or global<br>
+variables.<br>
+<br>
+Any call of ``std::move`` on a variable is considered to cause a move of that<br>
+variable, even if the result of ``std::move`` is not passed to an rvalue<br>
+reference parameter.<br>
+<br>
+This means that the check will flag a use-after-move even on a type that does<br>
+not define a move constructor or move assignment operator. This is intentional.<br>
+Developers may use ``std::move`` on such a type in the expectation that the type<br>
+will add move semantics in the future. If such a ``std::move`` has the potential<br>
+to cause a use-after-move, we want to warn about it even if the type does not<br>
+implement move semantics yet.<br>
+<br>
+Furthermore, if the result of ``std::move`` *is* passed to an rvalue reference<br>
+parameter, this will always be considered to cause a move, even if the function<br>
+that consumes this parameter does not move from it, or if it does so only<br>
+conditionally. For example, in the following situation, the check will assume<br>
+that a move always takes place:<br>
+<br>
+ .. code-block:: c++<br>
+<br>
+ std::vector<std::string> messages;<br>
+ void f(std::string &&str) {<br>
+ // Only remember the message if it isn't empty.<br>
+ if (!str.empty()) {<br>
+ messages.emplace_back(std::mov<wbr>e(str));<br>
+ }<br>
+ }<br>
+ std::string str = "";<br>
+ f(std::move(str));<br>
+<br>
+The check will assume that the last line causes a move, even though, in this<br>
+particular case, it does not. Again, this is intentional.<br>
+<br>
+When analyzing the order in which moves, uses and reinitializations happen (see<br>
+section `Unsequenced moves, uses, and reinitializations`_), the move is assumed<br>
+to occur in whichever function the result of the ``std::move`` is passed to.<br>
+<br>
+Use<br>
+---<br>
+<br>
+Any occurrence of the moved variable that is not a reinitialization (see below)<br>
+is considered to be a use.<br>
+<br>
+If multiple uses occur after a move, only the first of these is flagged.<br>
+<br>
+Reinitialization<br>
+----------------<br>
+<br>
+The check considers a variable to be reinitialized in the following cases:<br>
+<br>
+ - The variable occurs on the left-hand side of an assignment.<br>
+<br>
+ - The variable is passed to a function as a non-const pointer or non-const<br>
+ lvalue reference. (It is assumed that the variable may be an out-parameter<br>
+ for the function.)<br>
+<br>
+ - ``clear()`` or ``assign()`` is called on the variable and the variable is of<br>
+ one of the standard container types ``basic_string``, ``vector``, ``deque``,<br>
+ ``forward_list``, ``list``, ``set``, ``map``, ``multiset``, ``multimap``,<br>
+ ``unordered_set``, ``unordered_map``, ``unordered_multiset``,<br>
+ ``unordered_multimap``.<br>
+<br>
+If the variable in question is a struct and an individual member variable of<br>
+that struct is written to, the check does not consider this to be a<br>
+reinitialization -- even if, eventually, all member variables of the struct are<br>
+written to. For example:<br>
+<br>
+ .. code-block:: c++<br>
+<br>
+ struct S {<br>
+ std::string str;<br>
+ int i;<br>
+ };<br>
+ S s = { "Hello, world!\n", 42 };<br>
+ S s_other = std::move(s);<br>
+ s.str = "Lorem ipsum";<br>
+ s.i = 99;<br>
+<br>
+The check will not consider ``s`` to be reinitialized after the last line;<br>
+instead, the line that assigns to ``s.str`` will be flagged as a use-after-move.<br>
+This is intentional as this pattern of reinitializing a struct is error-prone.<br>
+For example, if an additional member variable is added to ``S``, it is easy to<br>
+forget to add the reinitialization for this additional member. Instead, it is<br>
+safer to assign to the entire struct in one go, and this will also avoid the<br>
+use-after-move warning.<br>
<br>
Added: clang-tools-extra/trunk/test/c<wbr>lang-tidy/misc-use-after-move.<wbr>cpp<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/test/clang-tidy/misc-use-after-move.cpp?rev=281453&view=auto" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-pr<wbr>oject/clang-tools-extra/trunk/<wbr>test/clang-tidy/misc-use-after<wbr>-move.cpp?rev=281453&view=auto</a><br>
==============================<wbr>==============================<wbr>==================<br>
--- clang-tools-extra/trunk/test/c<wbr>lang-tidy/misc-use-after-move.<wbr>cpp (added)<br>
+++ clang-tools-extra/trunk/test/c<wbr>lang-tidy/misc-use-after-move.<wbr>cpp Wed Sep 14 05:29:32 2016<br>
@@ -0,0 +1,1039 @@<br>
+// RUN: %check_clang_tidy %s misc-use-after-move %t<br>
+<br>
+typedef decltype(nullptr) nullptr_t;<br>
+<br>
+namespace std {<br>
+typedef unsigned size_t;<br>
+<br>
+template <typename T><br>
+struct unique_ptr {<br>
+ unique_ptr();<br>
+ T *get() const;<br>
+};<br>
+<br>
+template <typename T><br>
+struct shared_ptr {<br>
+ shared_ptr();<br>
+ T *get() const;<br>
+};<br>
+<br>
+#define DECLARE_STANDARD_CONTAINER(nam<wbr>e) \<br>
+ template <typename T> \<br>
+ struct name { \<br>
+ name(); \<br>
+ void clear(); \<br>
+ bool empty(); \<br>
+ }<br>
+<br>
+#define DECLARE_STANDARD_CONTAINER_WIT<wbr>H_ASSIGN(name) \<br>
+ template <typename T> \<br>
+ struct name { \<br>
+ name(); \<br>
+ void clear(); \<br>
+ bool empty(); \<br>
+ void assign(size_t, const T &); \<br>
+ }<br>
+<br>
+DECLARE_STANDARD_CONTAINER_WI<wbr>TH_ASSIGN(basic_string);<br>
+DECLARE_STANDARD_CONTAINER_WI<wbr>TH_ASSIGN(vector);<br>
+DECLARE_STANDARD_CONTAINER_WI<wbr>TH_ASSIGN(deque);<br>
+DECLARE_STANDARD_CONTAINER_WI<wbr>TH_ASSIGN(forward_list);<br>
+DECLARE_STANDARD_CONTAINER_WI<wbr>TH_ASSIGN(list);<br>
+DECLARE_STANDARD_CONTAINER(se<wbr>t);<br>
+DECLARE_STANDARD_CONTAINER(ma<wbr>p);<br>
+DECLARE_STANDARD_CONTAINER(mu<wbr>ltiset);<br>
+DECLARE_STANDARD_CONTAINER(mu<wbr>ltimap);<br>
+DECLARE_STANDARD_CONTAINER(un<wbr>ordered_set);<br>
+DECLARE_STANDARD_CONTAINER(un<wbr>ordered_map);<br>
+DECLARE_STANDARD_CONTAINER(un<wbr>ordered_multiset);<br>
+DECLARE_STANDARD_CONTAINER(un<wbr>ordered_multimap);<br>
+<br>
+typedef basic_string<char> string;<br>
+<br>
+template <typename><br>
+struct remove_reference;<br>
+<br>
+template <typename _Tp><br>
+struct remove_reference {<br>
+ typedef _Tp type;<br>
+};<br>
+<br>
+template <typename _Tp><br>
+struct remove_reference<_Tp &> {<br>
+ typedef _Tp type;<br>
+};<br>
+<br>
+template <typename _Tp><br>
+struct remove_reference<_Tp &&> {<br>
+ typedef _Tp type;<br>
+};<br>
+<br>
+template <typename _Tp><br>
+constexpr typename std::remove_reference<_Tp>::ty<wbr>pe &&move(_Tp &&__t) noexcept {<br>
+ return static_cast<typename remove_reference<_Tp>::type &&>(__t);<br>
+}<br>
+<br>
+} // namespace std<br>
+<br>
+class A {<br>
+public:<br>
+ A();<br>
+ A(const A &);<br>
+ A(A &&);<br>
+<br>
+ A &operator=(const A &);<br>
+ A &operator=(A &&);<br>
+<br>
+ void foo() const;<br>
+ int getInt() const;<br>
+<br>
+ operator bool() const;<br>
+<br>
+ int i;<br>
+};<br>
+<br>
+/////////////////////////////<wbr>//////////////////////////////<wbr>/////////////////////<br>
+// General tests.<br>
+<br>
+// Simple case.<br>
+void simple() {<br>
+ A a;<br>
+ a.foo();<br>
+ A other_a = std::move(a);<br>
+ a.foo();<br>
+ // CHECK-MESSAGES: [[@LINE-1]]:3: warning: 'a' used after it was moved<br>
+ // CHECK-MESSAGES: [[@LINE-3]]:15: note: move occurred here<br>
+}<br>
+<br>
+// A warning should only be emitted for one use-after-move.<br>
+void onlyFlagOneUseAfterMove() {<br>
+ A a;<br>
+ a.foo();<br>
+ A other_a = std::move(a);<br>
+ a.foo();<br>
+ // CHECK-MESSAGES: [[@LINE-1]]:3: warning: 'a' used after it was moved<br>
+ // CHECK-MESSAGES: [[@LINE-3]]:15: note: move occurred here<br>
+ a.foo();<br>
+}<br>
+<br>
+void moveAfterMove() {<br>
+ // Move-after-move also counts as a use.<br>
+ {<br>
+ A a;<br>
+ std::move(a);<br>
+ std::move(a);<br>
+ // CHECK-MESSAGES: [[@LINE-1]]:15: warning: 'a' used after it was moved<br>
+ // CHECK-MESSAGES: [[@LINE-3]]:5: note: move occurred here<br>
+ }<br>
+ // This is also true if the move itself turns into the use on the second loop<br>
+ // iteration.<br>
+ {<br>
+ A a;<br>
+ for (int i = 0; i < 10; ++i) {<br>
+ std::move(a);<br>
+ // CHECK-MESSAGES: [[@LINE-1]]:17: warning: 'a' used after it was moved<br>
+ // CHECK-MESSAGES: [[@LINE-2]]:7: note: move occurred here<br>
+ }<br>
+ }<br>
+}<br>
+<br>
+// Checks also works on function parameters that have a use-after move.<br>
+void parameters(A a) {<br>
+ std::move(a);<br>
+ a.foo();<br>
+ // CHECK-MESSAGES: [[@LINE-1]]:3: warning: 'a' used after it was moved<br>
+ // CHECK-MESSAGES: [[@LINE-3]]:3: note: move occurred here<br>
+}<br>
+<br>
+void uniquePtrAndSharedPtr() {<br>
+ // Use-after-moves on std::unique_ptr<> or std::shared_ptr<> aren't flagged.<br>
+ {<br>
+ std::unique_ptr<A> ptr;<br>
+ std::move(ptr);<br>
+ ptr.get();<br>
+ }<br>
+ {<br>
+ std::shared_ptr<A> ptr;<br>
+ std::move(ptr);<br>
+ ptr.get();<br>
+ }<br>
+ // This is also true if the std::unique_ptr<> or std::shared_ptr<> is wrapped<br>
+ // in a typedef.<br>
+ {<br>
+ typedef std::unique_ptr<A> PtrToA;<br>
+ PtrToA ptr;<br>
+ std::move(ptr);<br>
+ ptr.get();<br>
+ }<br>
+ {<br>
+ typedef std::shared_ptr<A> PtrToA;<br>
+ PtrToA ptr;<br>
+ std::move(ptr);<br>
+ ptr.get();<br>
+ }<br>
+ // And it's also true if the template argument is a little more involved.<br>
+ {<br>
+ struct B {<br>
+ typedef A AnotherNameForA;<br>
+ };<br>
+ std::unique_ptr<B::AnotherName<wbr>ForA> ptr;<br>
+ std::move(ptr);<br>
+ ptr.get();<br>
+ }<br>
+}<br>
+<br>
+// The check also works in member functions.<br>
+class Container {<br>
+ void useAfterMoveInMemberFunction() {<br>
+ A a;<br>
+ std::move(a);<br>
+ a.foo();<br>
+ // CHECK-MESSAGES: [[@LINE-1]]:5: warning: 'a' used after it was moved<br>
+ // CHECK-MESSAGES: [[@LINE-3]]:5: note: move occurred here<br>
+ }<br>
+};<br>
+<br>
+// We see the std::move() if it's inside a declaration.<br>
+void moveInDeclaration() {<br>
+ A a;<br>
+ A another_a(std::move(a));<br>
+ a.foo();<br>
+ // CHECK-MESSAGES: [[@LINE-1]]:3: warning: 'a' used after it was moved<br>
+ // CHECK-MESSAGES: [[@LINE-3]]:5: note: move occurred here<br>
+}<br>
+<br>
+// We see the std::move if it's inside an initializer list. Initializer lists<br>
+// are a special case because they cause ASTContext::getParents() to return<br>
+// multiple parents for certain nodes in their subtree. This is because<br>
+// RecursiveASTVisitor visits both the syntactic and semantic forms of<br>
+// InitListExpr, and the parent-child relationships are different between the<br>
+// two forms.<br>
+void moveInInitList() {<br>
+ struct S {<br>
+ A a;<br>
+ };<br>
+ A a;<br>
+ S s{std::move(a)};<br>
+ a.foo();<br>
+ // CHECK-MESSAGES: [[@LINE-1]]:3: warning: 'a' used after it was moved<br>
+ // CHECK-MESSAGES: [[@LINE-3]]:6: note: move occurred here<br>
+}<br>
+<br>
+void lambdas() {<br>
+ // Use-after-moves inside a lambda should be detected.<br>
+ {<br>
+ A a;<br>
+ auto lambda = [a] {<br>
+ std::move(a);<br>
+ a.foo();<br>
+ // CHECK-MESSAGES: [[@LINE-1]]:7: warning: 'a' used after it was moved<br>
+ // CHECK-MESSAGES: [[@LINE-3]]:7: note: move occurred here<br>
+ };<br>
+ }<br>
+ // This is just as true if the variable was declared inside the lambda.<br>
+ {<br>
+ auto lambda = [] {<br>
+ A a;<br>
+ std::move(a);<br>
+ a.foo();<br>
+ // CHECK-MESSAGES: [[@LINE-1]]:7: warning: 'a' used after it was moved<br>
+ // CHECK-MESSAGES: [[@LINE-3]]:7: note: move occurred here<br>
+ };<br>
+ }<br>
+ // But don't warn if the move happened inside the lambda but the use happened<br>
+ // outside -- because<br>
+ // - the 'a' inside the lambda is a copy, and<br>
+ // - we don't know when the lambda will get called anyway<br>
+ {<br>
+ A a;<br>
+ auto lambda = [a] {<br>
+ std::move(a);<br>
+ };<br>
+ a.foo();<br>
+ }<br>
+ // Warn if the use consists of a capture that happens after a move.<br>
+ {<br>
+ A a;<br>
+ std::move(a);<br>
+ auto lambda = [a]() { a.foo(); };<br>
+ // CHECK-MESSAGES: [[@LINE-1]]:20: warning: 'a' used after it was moved<br>
+ // CHECK-MESSAGES: [[@LINE-3]]:5: note: move occurred here<br>
+ }<br>
+ // ...even if the capture was implicit.<br>
+ {<br>
+ A a;<br>
+ std::move(a);<br>
+ auto lambda = [=]() { a.foo(); };<br>
+ // CHECK-MESSAGES: [[@LINE-1]]:27: warning: 'a' used after it was moved<br>
+ // CHECK-MESSAGES: [[@LINE-3]]:5: note: move occurred here<br>
+ }<br>
+ // Same tests but for capture by reference.<br>
+ {<br>
+ A a;<br>
+ std::move(a);<br>
+ auto lambda = [&a]() { a.foo(); };<br>
+ // CHECK-MESSAGES: [[@LINE-1]]:21: warning: 'a' used after it was moved<br>
+ // CHECK-MESSAGES: [[@LINE-3]]:5: note: move occurred here<br>
+ }<br>
+ {<br>
+ A a;<br>
+ std::move(a);<br>
+ auto lambda = [&]() { a.foo(); };<br>
+ // CHECK-MESSAGES: [[@LINE-1]]:27: warning: 'a' used after it was moved<br>
+ // CHECK-MESSAGES: [[@LINE-3]]:5: note: move occurred here<br>
+ }<br>
+ // But don't warn if the move happened after the capture.<br>
+ {<br>
+ A a;<br>
+ auto lambda = [a]() { a.foo(); };<br>
+ std::move(a);<br>
+ }<br>
+ // ...and again, same thing with an implicit move.<br>
+ {<br>
+ A a;<br>
+ auto lambda = [=]() { a.foo(); };<br>
+ std::move(a);<br>
+ }<br>
+ // Same tests but for capture by reference.<br>
+ {<br>
+ A a;<br>
+ auto lambda = [&a]() { a.foo(); };<br>
+ std::move(a);<br>
+ }<br>
+ {<br>
+ A a;<br>
+ auto lambda = [&]() { a.foo(); };<br>
+ std::move(a);<br>
+ }<br>
+}<br>
+<br>
+// Use-after-moves are detected in uninstantiated templates if the moved type<br>
+// is not a dependent type.<br>
+template <class T><br>
+void movedTypeIsNotDependentType() {<br>
+ T t;<br>
+ A a;<br>
+ std::move(a);<br>
+ a.foo();<br>
+ // CHECK-MESSAGES: [[@LINE-1]]:3: warning: 'a' used after it was moved<br>
+ // CHECK-MESSAGES: [[@LINE-3]]:3: note: move occurred here<br>
+}<br>
+<br>
+// And if the moved type is a dependent type, the use-after-move is detected if<br>
+// the template is instantiated.<br>
+template <class T><br>
+void movedTypeIsDependentType() {<br>
+ T t;<br>
+ std::move(t);<br>
+ t.foo();<br>
+ // CHECK-MESSAGES: [[@LINE-1]]:3: warning: 't' used after it was moved<br>
+ // CHECK-MESSAGES: [[@LINE-3]]:3: note: move occurred here<br>
+}<br>
+template void movedTypeIsDependentType<A>();<br>
+<br>
+// Using decltype on an expression is not a use.<br>
+void decltypeIsNotUse() {<br>
+ A a;<br>
+ std::move(a);<br>
+ decltype(a) other_a;<br>
+}<br>
+<br>
+// Ignore moves or uses that occur as part of template arguments.<br>
+template <int><br>
+class ClassTemplate {<br>
+public:<br>
+ void foo(A a);<br>
+};<br>
+template <int><br>
+void functionTemplate(A a);<br>
+void templateArgIsNotUse() {<br>
+ {<br>
+ // A pattern like this occurs in the EXPECT_EQ and ASSERT_EQ macros in<br>
+ // Google Test.<br>
+ A a;<br>
+ ClassTemplate<sizeof(A(std::mo<wbr>ve(a)))>().foo(std::move(a));<br>
+ }<br>
+ {<br>
+ A a;<br>
+ functionTemplate<sizeof(A(std:<wbr>:move(a)))>(std::move(a));<br>
+ }<br>
+}<br>
+<br>
+// Ignore moves of global variables.<br>
+A global_a;<br>
+void ignoreGlobalVariables() {<br>
+ std::move(global_a);<br>
+ global_a.foo();<br>
+}<br>
+<br>
+// Ignore moves of member variables.<br>
+class IgnoreMemberVariables {<br>
+ A a;<br>
+ static A static_a;<br>
+<br>
+ void f() {<br>
+ std::move(a);<br>
+ a.foo();<br>
+<br>
+ std::move(static_a);<br>
+ static_a.foo();<br>
+ }<br>
+};<br>
+<br>
+/////////////////////////////<wbr>//////////////////////////////<wbr>/////////////////////<br>
+// Tests involving control flow.<br>
+<br>
+void useAndMoveInLoop() {<br>
+ // Warn about use-after-moves if they happen in a later loop iteration than<br>
+ // the std::move().<br>
+ {<br>
+ A a;<br>
+ for (int i = 0; i < 10; ++i) {<br>
+ a.foo();<br>
+ // CHECK-MESSAGES: [[@LINE-1]]:7: warning: 'a' used after it was moved<br>
+ // CHECK-MESSAGES: [[@LINE+1]]:7: note: move occurred here<br>
+ std::move(a);<br>
+ }<br>
+ }<br>
+ // However, this case shouldn't be flagged -- the scope of the declaration of<br>
+ // 'a' is important.<br>
+ {<br>
+ for (int i = 0; i < 10; ++i) {<br>
+ A a;<br>
+ a.foo();<br>
+ std::move(a);<br>
+ }<br>
+ }<br>
+ // Same as above, except that we have an unrelated variable being declared in<br>
+ // the same declaration as 'a'. This case is interesting because it tests that<br>
+ // the synthetic DeclStmts generated by the CFG are sequenced correctly<br>
+ // relative to the other statements.<br>
+ {<br>
+ for (int i = 0; i < 10; ++i) {<br>
+ A a, other;<br>
+ a.foo();<br>
+ std::move(a);<br>
+ }<br>
+ }<br>
+ // Don't warn if we return after the move.<br>
+ {<br>
+ A a;<br>
+ for (int i = 0; i < 10; ++i) {<br>
+ a.foo();<br>
+ if (a.getInt() > 0) {<br>
+ std::move(a);<br>
+ return;<br>
+ }<br>
+ }<br>
+ }<br>
+}<br>
+<br>
+void differentBranches(int i) {<br>
+ // Don't warn if the use is in a different branch from the move.<br>
+ {<br>
+ A a;<br>
+ if (i > 0) {<br>
+ std::move(a);<br>
+ } else {<br>
+ a.foo();<br>
+ }<br>
+ }<br>
+ // Same thing, but with a ternary operator.<br>
+ {<br>
+ A a;<br>
+ i > 0 ? (void)std::move(a) : a.foo();<br>
+ }<br>
+ // A variation on the theme above.<br>
+ {<br>
+ A a;<br>
+ a.getInt() > 0 ? a.getInt() : A(std::move(a)).getInt();<br>
+ }<br>
+ // Same thing, but with a switch statement.<br>
+ {<br>
+ A a;<br>
+ switch (i) {<br>
+ case 1:<br>
+ std::move(a);<br>
+ break;<br>
+ case 2:<br>
+ a.foo();<br>
+ break;<br>
+ }<br>
+ }<br>
+ // However, if there's a fallthrough, we do warn.<br>
+ {<br>
+ A a;<br>
+ switch (i) {<br>
+ case 1:<br>
+ std::move(a);<br>
+ case 2:<br>
+ a.foo();<br>
+ // CHECK-MESSAGES: [[@LINE-1]]:7: warning: 'a' used after it was moved<br>
+ // CHECK-MESSAGES: [[@LINE-4]]:7: note: move occurred here<br>
+ break;<br>
+ }<br>
+ }<br>
+}<br>
+<br>
+// False positive: A use-after-move is flagged even though the "if (b)" and<br>
+// "if (!b)" are mutually exclusive.<br>
+void mutuallyExclusiveBranchesFalse<wbr>Positive(bool b) {<br>
+ A a;<br>
+ if (b) {<br>
+ std::move(a);<br>
+ }<br>
+ if (!b) {<br>
+ a.foo();<br>
+ // CHECK-MESSAGES: [[@LINE-1]]:5: warning: 'a' used after it was moved<br>
+ // CHECK-MESSAGES: [[@LINE-5]]:5: note: move occurred here<br>
+ }<br>
+}<br>
+<br>
+// Destructors marked [[noreturn]] are handled correctly in the control flow<br>
+// analysis. (These are used in some styles of assertion macros.)<br>
+class FailureLogger {<br>
+public:<br>
+ FailureLogger();<br>
+ [[noreturn]] ~FailureLogger();<br>
+ void log(const char *);<br>
+};<br>
+#define ASSERT(x) \<br>
+ while (x) \<br>
+ FailureLogger().log(#x)<br>
+bool operationOnA(A);<br>
+void noreturnDestructor() {<br>
+ A a;<br>
+ // The while loop in the ASSERT() would ordinarily have the potential to cause<br>
+ // a use-after-move because the second iteration of the loop would be using a<br>
+ // variable that had been moved from in the first iteration. Check that the<br>
+ // CFG knows that the second iteration of the loop is never reached because<br>
+ // the FailureLogger destructor is marked [[noreturn]].<br>
+ ASSERT(operationOnA(std::move(<wbr>a)));<br>
+}<br>
+#undef ASSERT<br>
+<br>
+/////////////////////////////<wbr>//////////////////////////////<wbr>/////////////////////<br>
+// Tests for reinitializations<br>
+<br>
+template <class T><br>
+void swap(T &a, T &b) {<br>
+ T tmp = std::move(a);<br>
+ a = std::move(b);<br>
+ b = std::move(tmp);<br>
+}<br>
+void assignments(int i) {<br>
+ // Don't report a use-after-move if the variable was assigned to in the<br>
+ // meantime.<br>
+ {<br>
+ A a;<br>
+ std::move(a);<br>
+ a = A();<br>
+ a.foo();<br>
+ }<br>
+ // The assignment should also be recognized if move, assignment and use don't<br>
+ // all happen in the same block (but the assignment is still guaranteed to<br>
+ // prevent a use-after-move).<br>
+ {<br>
+ A a;<br>
+ if (i == 1) {<br>
+ std::move(a);<br>
+ a = A();<br>
+ }<br>
+ if (i == 2) {<br>
+ a.foo();<br>
+ }<br>
+ }<br>
+ {<br>
+ A a;<br>
+ if (i == 1) {<br>
+ std::move(a);<br>
+ }<br>
+ if (i == 2) {<br>
+ a = A();<br>
+ a.foo();<br>
+ }<br>
+ }<br>
+ // The built-in assignment operator should also be recognized as a<br>
+ // reinitialization. (std::move() may be called on built-in types in template<br>
+ // code.)<br>
+ {<br>
+ int a1 = 1, a2 = 2;<br>
+ swap(a1, a2);<br>
+ }<br>
+ // A std::move() after the assignment makes the variable invalid again.<br>
+ {<br>
+ A a;<br>
+ std::move(a);<br>
+ a = A();<br>
+ std::move(a);<br>
+ a.foo();<br>
+ // CHECK-MESSAGES: [[@LINE-1]]:5: warning: 'a' used after it was moved<br>
+ // CHECK-MESSAGES: [[@LINE-3]]:5: note: move occurred here<br>
+ }<br>
+ // Report a use-after-move if we can't be sure that the variable was assigned<br>
+ // to.<br>
+ {<br>
+ A a;<br>
+ std::move(a);<br>
+ if (i < 10) {<br>
+ a = A();<br>
+ }<br>
+ if (i > 5) {<br>
+ a.foo();<br>
+ // CHECK-MESSAGES: [[@LINE-1]]:7: warning: 'a' used after it was moved<br>
+ // CHECK-MESSAGES: [[@LINE-7]]:5: note: move occurred here<br>
+ }<br>
+ }<br>
+}<br>
+<br>
+// Passing the object to a function through a non-const pointer or refernce<br>
+// counts as a re-initialization.<br>
+void passByNonConstPointer(A *);<br>
+void passByNonConstReference(A &);<br>
+void passByNonConstPointerIsReinit(<wbr>) {<br>
+ {<br>
+ A a;<br>
+ std::move(a);<br>
+ passByNonConstPointer(&a);<br>
+ a.foo();<br>
+ }<br>
+ {<br>
+ A a;<br>
+ std::move(a);<br>
+ passByNonConstReference(a);<br>
+ a.foo();<br>
+ }<br>
+}<br>
+<br>
+// Passing the object through a const pointer or reference counts as a use --<br>
+// since the called function cannot reinitialize the object.<br>
+void passByConstPointer(const A *);<br>
+void passByConstReference(const A &);<br>
+void passByConstPointerIsUse() {<br>
+ {<br>
+ // Declaring 'a' as const so that no ImplicitCastExpr is inserted into the<br>
+ // AST -- we wouldn't want the check to rely solely on that to detect a<br>
+ // const pointer argument.<br>
+ const A a;<br>
+ std::move(a);<br>
+ passByConstPointer(&a);<br>
+ // CHECK-MESSAGES: [[@LINE-1]]:25: warning: 'a' used after it was moved<br>
+ // CHECK-MESSAGES: [[@LINE-3]]:5: note: move occurred here<br>
+ }<br>
+ const A a;<br>
+ std::move(a);<br>
+ passByConstReference(a);<br>
+ // CHECK-MESSAGES: [[@LINE-1]]:24: warning: 'a' used after it was moved<br>
+ // CHECK-MESSAGES: [[@LINE-3]]:3: note: move occurred here<br>
+}<br>
+<br>
+// Clearing a standard container using clear() is treated as a<br>
+// re-initialization.<br>
+void standardContainerClearIsReinit<wbr>() {<br>
+ {<br>
+ std::string container;<br>
+ std::move(container);<br>
+ container.clear();<br>
+ container.empty();<br>
+ }<br>
+ {<br>
+ std::vector<int> container;<br>
+ std::move(container);<br>
+ container.clear();<br>
+ container.empty();<br>
+ }<br>
+ {<br>
+ std::deque<int> container;<br>
+ std::move(container);<br>
+ container.clear();<br>
+ container.empty();<br>
+ }<br>
+ {<br>
+ std::forward_list<int> container;<br>
+ std::move(container);<br>
+ container.clear();<br>
+ container.empty();<br>
+ }<br>
+ {<br>
+ std::list<int> container;<br>
+ std::move(container);<br>
+ container.clear();<br>
+ container.empty();<br>
+ }<br>
+ {<br>
+ std::set<int> container;<br>
+ std::move(container);<br>
+ container.clear();<br>
+ container.empty();<br>
+ }<br>
+ {<br>
+ std::map<int> container;<br>
+ std::move(container);<br>
+ container.clear();<br>
+ container.empty();<br>
+ }<br>
+ {<br>
+ std::multiset<int> container;<br>
+ std::move(container);<br>
+ container.clear();<br>
+ container.empty();<br>
+ }<br>
+ {<br>
+ std::multimap<int> container;<br>
+ std::move(container);<br>
+ container.clear();<br>
+ container.empty();<br>
+ }<br>
+ {<br>
+ std::unordered_set<int> container;<br>
+ std::move(container);<br>
+ container.clear();<br>
+ container.empty();<br>
+ }<br>
+ {<br>
+ std::unordered_map<int> container;<br>
+ std::move(container);<br>
+ container.clear();<br>
+ container.empty();<br>
+ }<br>
+ {<br>
+ std::unordered_multiset<int> container;<br>
+ std::move(container);<br>
+ container.clear();<br>
+ container.empty();<br>
+ }<br>
+ {<br>
+ std::unordered_multimap<int> container;<br>
+ std::move(container);<br>
+ container.clear();<br>
+ container.empty();<br>
+ }<br>
+ // This should also work for typedefs of standard containers.<br>
+ {<br>
+ typedef std::vector<int> IntVector;<br>
+ IntVector container;<br>
+ std::move(container);<br>
+ container.clear();<br>
+ container.empty();<br>
+ }<br>
+ // But it shouldn't work for non-standard containers.<br>
+ {<br>
+ // This might be called "vector", but it's not in namespace "std".<br>
+ struct vector {<br>
+ void clear() {}<br>
+ } container;<br>
+ std::move(container);<br>
+ container.clear();<br>
+ // CHECK-MESSAGES: [[@LINE-1]]:5: warning: 'container' used after it was<br>
+ // CHECK-MESSAGES: [[@LINE-3]]:5: note: move occurred here<br>
+ }<br>
+ // An intervening clear() on a different container does not reinitialize.<br>
+ {<br>
+ std::vector<int> container1, container2;<br>
+ std::move(container1);<br>
+ container2.clear();<br>
+ container1.empty();<br>
+ // CHECK-MESSAGES: [[@LINE-1]]:5: warning: 'container1' used after it was<br>
+ // CHECK-MESSAGES: [[@LINE-4]]:5: note: move occurred here<br>
+ }<br>
+}<br>
+<br>
+// Clearing a standard container using assign() is treated as a<br>
+// re-initialization.<br>
+void standardContainerAssignIsReini<wbr>t() {<br>
+ {<br>
+ std::string container;<br>
+ std::move(container);<br>
+ container.assign(0, ' ');<br>
+ container.empty();<br>
+ }<br>
+ {<br>
+ std::vector<int> container;<br>
+ std::move(container);<br>
+ container.assign(0, 0);<br>
+ container.empty();<br>
+ }<br>
+ {<br>
+ std::deque<int> container;<br>
+ std::move(container);<br>
+ container.assign(0, 0);<br>
+ container.empty();<br>
+ }<br>
+ {<br>
+ std::forward_list<int> container;<br>
+ std::move(container);<br>
+ container.assign(0, 0);<br>
+ container.empty();<br>
+ }<br>
+ {<br>
+ std::list<int> container;<br>
+ std::move(container);<br>
+ container.clear();<br>
+ container.empty();<br>
+ }<br>
+ // But it doesn't work for non-standard containers.<br>
+ {<br>
+ // This might be called "vector", but it's not in namespace "std".<br>
+ struct vector {<br>
+ void assign(std::size_t, int) {}<br>
+ } container;<br>
+ std::move(container);<br>
+ container.assign(0, 0);<br>
+ // CHECK-MESSAGES: [[@LINE-1]]:5: warning: 'container' used after it was<br>
+ // CHECK-MESSAGES: [[@LINE-3]]:5: note: move occurred here<br>
+ }<br>
+ // An intervening assign() on a different container does not reinitialize.<br>
+ {<br>
+ std::vector<int> container1, container2;<br>
+ std::move(container1);<br>
+ container2.assign(0, 0);<br>
+ container1.empty();<br>
+ // CHECK-MESSAGES: [[@LINE-1]]:5: warning: 'container1' used after it was<br>
+ // CHECK-MESSAGES: [[@LINE-4]]:5: note: move occurred here<br>
+ }<br>
+}<br>
+<br>
+/////////////////////////////<wbr>//////////////////////////////<wbr>/////////////////////<br>
+// Tests related to order of evaluation within expressions<br>
+<br>
+// Relative sequencing of move and use.<br>
+void passByRvalueReference(int i, A &&a);<br>
+void passByValue(int i, A a);<br>
+void passByValue(A a, int i);<br>
+A g(A, A &&);<br>
+int intFromA(A &&);<br>
+int intFromInt(int);<br>
+void sequencingOfMoveAndUse() {<br>
+ // This case is fine because the move only happens inside<br>
+ // passByRvalueReference(). At this point, a.getInt() is guaranteed to have<br>
+ // been evaluated.<br>
+ {<br>
+ A a;<br>
+ passByRvalueReference(a.getInt<wbr>(), std::move(a));<br>
+ }<br>
+ // However, if we pass by value, the move happens when the move constructor is<br>
+ // called to create a temporary, and this happens before the call to<br>
+ // passByValue(). Because the order in which arguments are evaluated isn't<br>
+ // defined, the move may happen before the call to a.getInt().<br>
+ //<br>
+ // Check that we warn about a potential use-after move for both orderings of<br>
+ // a.getInt() and std::move(a), independent of the order in which the<br>
+ // arguments happen to get evaluated by the compiler.<br>
+ {<br>
+ A a;<br>
+ passByValue(a.getInt(), std::move(a));<br>
+ // CHECK-MESSAGES: [[@LINE-1]]:17: warning: 'a' used after it was moved<br>
+ // CHECK-MESSAGES: [[@LINE-2]]:29: note: move occurred here<br>
+ // CHECK-MESSAGES: [[@LINE-3]]:17: note: the use and move are unsequenced<br>
+ }<br>
+ {<br>
+ A a;<br>
+ passByValue(std::move(a), a.getInt());<br>
+ // CHECK-MESSAGES: [[@LINE-1]]:31: warning: 'a' used after it was moved<br>
+ // CHECK-MESSAGES: [[@LINE-2]]:17: note: move occurred here<br>
+ // CHECK-MESSAGES: [[@LINE-3]]:31: note: the use and move are unsequenced<br>
+ }<br>
+ // An even more convoluted example.<br>
+ {<br>
+ A a;<br>
+ g(g(a, std::move(a)), g(a, std::move(a)));<br>
+ // CHECK-MESSAGES: [[@LINE-1]]:9: warning: 'a' used after it was moved<br>
+ // CHECK-MESSAGES: [[@LINE-2]]:27: note: move occurred here<br>
+ // CHECK-MESSAGES: [[@LINE-3]]:9: note: the use and move are unsequenced<br>
+ // CHECK-MESSAGES: [[@LINE-4]]:29: warning: 'a' used after it was moved<br>
+ // CHECK-MESSAGES: [[@LINE-5]]:7: note: move occurred here<br>
+ // CHECK-MESSAGES: [[@LINE-6]]:29: note: the use and move are unsequenced<br>
+ }<br>
+ // This case is fine because the actual move only happens inside the call to<br>
+ // operator=(). a.getInt(), by necessity, is evaluated before that call.<br>
+ {<br>
+ A a;<br>
+ A vec[1];<br>
+ vec[a.getInt()] = std::move(a);<br>
+ }<br>
+ // However, in the following case, the move happens before the assignment, and<br>
+ // so the order of evaluation is not guaranteed.<br>
+ {<br>
+ A a;<br>
+ int v[3];<br>
+ v[a.getInt()] = intFromA(std::move(a));<br>
+ // CHECK-MESSAGES: [[@LINE-1]]:7: warning: 'a' used after it was moved<br>
+ // CHECK-MESSAGES: [[@LINE-2]]:21: note: move occurred here<br>
+ // CHECK-MESSAGES: [[@LINE-3]]:7: note: the use and move are unsequenced<br>
+ }<br>
+ {<br>
+ A a;<br>
+ int v[3];<br>
+ v[intFromA(std::move(a))] = intFromInt(a.i);<br>
+ // CHECK-MESSAGES: [[@LINE-1]]:44: warning: 'a' used after it was moved<br>
+ // CHECK-MESSAGES: [[@LINE-2]]:7: note: move occurred here<br>
+ // CHECK-MESSAGES: [[@LINE-3]]:44: note: the use and move are unsequenced<br>
+ }<br>
+}<br>
+<br>
+// Relative sequencing of move and reinitialization. If the two are unsequenced,<br>
+// we conservatively assume that the move happens after the reinitialization,<br>
+// i.e. the that object does not get reinitialized after the move.<br>
+A MutateA(A a);<br>
+void passByValue(A a1, A a2);<br>
+void sequencingOfMoveAndReinit() {<br>
+ // Move and reinitialization as function arguments (which are indeterminately<br>
+ // sequenced). Again, check that we warn for both orderings.<br>
+ {<br>
+ A a;<br>
+ passByValue(std::move(a), (a = A()));<br>
+ a.foo();<br>
+ // CHECK-MESSAGES: [[@LINE-1]]:5: warning: 'a' used after it was moved<br>
+ // CHECK-MESSAGES: [[@LINE-3]]:17: note: move occurred here<br>
+ }<br>
+ {<br>
+ A a;<br>
+ passByValue((a = A()), std::move(a));<br>
+ a.foo();<br>
+ // CHECK-MESSAGES: [[@LINE-1]]:5: warning: 'a' used after it was moved<br>
+ // CHECK-MESSAGES: [[@LINE-3]]:28: note: move occurred here<br>
+ }<br>
+ // Common usage pattern: Move the object to a function that mutates it in some<br>
+ // way, then reassign the result to the object. This pattern is fine.<br>
+ {<br>
+ A a;<br>
+ a = MutateA(std::move(a));<br>
+ a.foo();<br>
+ }<br>
+}<br>
+<br>
+// Relative sequencing of reinitialization and use. If the two are unsequenced,<br>
+// we conservatively assume that the reinitialization happens after the use,<br>
+// i.e. that the object is not reinitialized at the point in time when it is<br>
+// used.<br>
+void sequencingOfReinitAndUse() {<br>
+ // Reinitialization and use in function arguments. Again, check both possible<br>
+ // orderings.<br>
+ {<br>
+ A a;<br>
+ std::move(a);<br>
+ passByValue(a.getInt(), (a = A()));<br>
+ // CHECK-MESSAGES: [[@LINE-1]]:17: warning: 'a' used after it was moved<br>
+ // CHECK-MESSAGES: [[@LINE-3]]:5: note: move occurred here<br>
+ }<br>
+ {<br>
+ A a;<br>
+ std::move(a);<br>
+ passByValue((a = A()), a.getInt());<br>
+ // CHECK-MESSAGES: [[@LINE-1]]:28: warning: 'a' used after it was moved<br>
+ // CHECK-MESSAGES: [[@LINE-3]]:5: note: move occurred here<br>
+ }<br>
+}<br>
+<br>
+// The comma operator sequences its operands.<br>
+void commaOperatorSequences() {<br>
+ {<br>
+ A a;<br>
+ A(std::move(a))<br>
+ , (a = A());<br>
+ a.foo();<br>
+ }<br>
+ {<br>
+ A a;<br>
+ (a = A()), A(std::move(a));<br>
+ a.foo();<br>
+ // CHECK-MESSAGES: [[@LINE-1]]:5: warning: 'a' used after it was moved<br>
+ // CHECK-MESSAGES: [[@LINE-3]]:16: note: move occurred here<br>
+ }<br>
+}<br>
+<br>
+// An initializer list sequences its initialization clauses.<br>
+void initializerListSequences() {<br>
+ {<br>
+ struct S1 {<br>
+ int i;<br>
+ A a;<br>
+ };<br>
+ A a;<br>
+ S1 s1{a.getInt(), std::move(a)};<br>
+ }<br>
+ {<br>
+ struct S2 {<br>
+ A a;<br>
+ int i;<br>
+ };<br>
+ A a;<br>
+ S2 s2{std::move(a), a.getInt()};<br>
+ // CHECK-MESSAGES: [[@LINE-1]]:25: warning: 'a' used after it was moved<br>
+ // CHECK-MESSAGES: [[@LINE-2]]:11: note: move occurred here<br>
+ }<br>
+}<br>
+<br>
+// A declaration statement containing multiple declarations sequences the<br>
+// initializer expressions.<br>
+void declarationSequences() {<br>
+ {<br>
+ A a;<br>
+ A a1 = a, a2 = std::move(a);<br>
+ }<br>
+ {<br>
+ A a;<br>
+ A a1 = std::move(a), a2 = a;<br>
+ // CHECK-MESSAGES: [[@LINE-1]]:31: warning: 'a' used after it was moved<br>
+ // CHECK-MESSAGES: [[@LINE-2]]:12: note: move occurred here<br>
+ }<br>
+}<br>
+<br>
+// The logical operators && and || sequence their operands.<br>
+void logicalOperatorsSequence() {<br>
+ {<br>
+ A a;<br>
+ if (a.getInt() > 0 && A(std::move(a)).getInt() > 0) {<br>
+ A().foo();<br>
+ }<br>
+ }<br>
+ // A variation: Negate the result of the && (which pushes the && further down<br>
+ // into the AST).<br>
+ {<br>
+ A a;<br>
+ if (!(a.getInt() > 0 && A(std::move(a)).getInt() > 0)) {<br>
+ A().foo();<br>
+ }<br>
+ }<br>
+ {<br>
+ A a;<br>
+ if (A(std::move(a)).getInt() > 0 && a.getInt() > 0) {<br>
+ // CHECK-MESSAGES: [[@LINE-1]]:41: warning: 'a' used after it was moved<br>
+ // CHECK-MESSAGES: [[@LINE-2]]:9: note: move occurred here<br>
+ A().foo();<br>
+ }<br>
+ }<br>
+ {<br>
+ A a;<br>
+ if (a.getInt() > 0 || A(std::move(a)).getInt() > 0) {<br>
+ A().foo();<br>
+ }<br>
+ }<br>
+ {<br>
+ A a;<br>
+ if (A(std::move(a)).getInt() > 0 || a.getInt() > 0) {<br>
+ // CHECK-MESSAGES: [[@LINE-1]]:41: warning: 'a' used after it was moved<br>
+ // CHECK-MESSAGES: [[@LINE-2]]:9: note: move occurred here<br>
+ A().foo();<br>
+ }<br>
+ }<br>
+}<br>
+<br>
+// A range-based for sequences the loop variable declaration before the body.<br>
+void forRangeSequences() {<br>
+ A v[2] = {A(), A()};<br>
+ for (A &a : v) {<br>
+ std::move(a);<br>
+ }<br>
+}<br>
+<br>
+// If a variable is declared in an if statement, the declaration of the variable<br>
+// (which is treated like a reinitialization by the check) is sequenced before<br>
+// the evaluation of the condition (which constitutes a use).<br>
+void ifStmtSequencesDeclAndConditio<wbr>n() {<br>
+ for (int i = 0; i < 10; ++i) {<br>
+ if (A a = A()) {<br>
+ std::move(a);<br>
+ }<br>
+ }<br>
+}<br>
<br>
<br>
______________________________<wbr>_________________<br>
cfe-commits mailing list<br>
<a href="mailto:cfe-commits@lists.llvm.org" target="_blank">cfe-commits@lists.llvm.org</a><br>
<a href="http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits" rel="noreferrer" target="_blank">http://lists.llvm.org/cgi-bin/<wbr>mailman/listinfo/cfe-commits</a><br>
</blockquote></div></div></div><br><br clear="all"><span><div><br></div>-- <br><div class="m_1189535854236998011m_2389028132087517620gmail_signature" data-smartmail="gmail_signature"><div dir="ltr"><div><div dir="ltr"><div dir="ltr"><div dir="ltr"><span><p dir="ltr" style="line-height:1.38;margin-top:0pt;margin-bottom:0pt"><span style="font-size:16px;font-family:Arial;color:rgb(0,0,0);font-weight:700;vertical-align:baseline;white-space:pre-wrap;background-color:transparent">Martin Böhme</span></p><p dir="ltr" style="line-height:1.38;margin-top:0pt;margin-bottom:0pt"><span style="font-size:13.3333333333333px;font-family:Arial;color:rgb(102,102,102);vertical-align:baseline;white-space:pre-wrap;background-color:transparent">Software Engineer</span></p><p dir="ltr" style="line-height:1.38;margin-top:0pt;margin-bottom:0pt"><span style="font-size:13.3333333333333px;font-family:Arial;color:rgb(102,102,102);vertical-align:baseline;white-space:pre-wrap;background-color:transparent"><a href="mailto:mboehme@google.com" target="_blank">mboehme@google.com</a></span><br></p><span style="font-size:13.3333333333333px;font-family:Arial;color:rgb(102,102,102);vertical-align:baseline;white-space:pre-wrap;background-color:transparent"><img src="https://lh3.googleusercontent.com/jArLagMFiBykGEq2mtMjIS9RO3ydtGbI1KRMmdY7daBuMP4HQbM7p92vdv8yolHBV9sE4YXu7ORFjsy6mcF6LGJOjQhqkd4PrG43u1U2toF3BQIitx8YMjbU9hqX9u3U8MU=s1600" width="100px;" height="58px;" style="border:none"></span></span><br><div><div style="font-size:13px"><span style="font-family:Arial,Verdana,sans-serif"><font color="#666666">Google Germany GmbH</font></span></div><div><div style="font-size:13px;font-family:Arial,Verdana,sans-serif"><font color="#666666">Erika-Mann-Straße 33</font></div><div style="font-size:13px;font-family:Arial,Verdana,sans-serif"><font color="#666666">80363 München<br></font></div><div style="font-size:13px;font-family:Arial,Verdana,sans-serif"><font color="#666666"><br></font></div><div style="font-family:Arial,Verdana,sans-serif"><span><p dir="ltr" style="line-height:1.38;margin-top:0pt;margin-bottom:0pt"><span style="font-size:10.6666666666667px;font-family:Arial;color:rgb(183,183,183);vertical-align:baseline;white-space:pre-wrap;background-color:transparent">Geschäftsführer: Matthew Scott Sucherman, Paul Terence Manicle</span></p><p dir="ltr" style="line-height:1.38;margin-top:0pt;margin-bottom:0pt"><span style="font-size:10.6666666666667px;font-family:Arial;color:rgb(183,183,183);vertical-align:baseline;white-space:pre-wrap;background-color:transparent">Registergericht und -nummer: Hamburg, HRB 86891</span></p><p dir="ltr" style="line-height:1.38;margin-top:0pt;margin-bottom:0pt"><span style="font-size:10.6666666666667px;font-family:Arial;color:rgb(183,183,183);vertical-align:baseline;white-space:pre-wrap;background-color:transparent">Sitz der Gesellschaft: Hamburg</span></p><br><p dir="ltr" style="line-height:1.38;margin-top:0pt;margin-bottom:0pt"><span style="font-size:10.6666666666667px;font-family:Arial;color:rgb(183,183,183);vertical-align:baseline;white-space:pre-wrap;background-color:transparent">Diese E-Mail ist vertraulich. Wenn Sie nicht der richtige Adressat sind, leiten Sie diese bitte nicht weiter, informieren Sie den Absender und löschen Sie die E-Mail und alle Anhänge. Vielen Dank.</span></p><p dir="ltr" style="line-height:1.38;margin-top:0pt;margin-bottom:0pt"><span style="font-size:10.6666666666667px;font-family:Arial;color:rgb(183,183,183);vertical-align:baseline;white-space:pre-wrap;background-color:transparent"> </span></p><p dir="ltr" style="line-height:1.38;margin-top:0pt;margin-bottom:0pt"><span style="font-size:10.6666666666667px;font-family:Arial;color:rgb(183,183,183);vertical-align:baseline;white-space:pre-wrap;background-color:transparent">This e-mail is confidential. If you are not the right addressee please do not forward it, please inform the sender, and please erase this e-mail including any attachments. Thanks.</span></p></span></div></div></div></div></div></div></div></div></div>
</span></div>
</blockquote></div><br><br clear="all"><div><br></div>-- <br><div class="m_1189535854236998011gmail_signature" data-smartmail="gmail_signature"><div dir="ltr"><div><div dir="ltr"><div dir="ltr"><div dir="ltr"><span><p dir="ltr" style="line-height:1.38;margin-top:0pt;margin-bottom:0pt"><span style="font-size:16px;font-family:Arial;color:rgb(0,0,0);font-weight:700;vertical-align:baseline;white-space:pre-wrap;background-color:transparent">Martin Böhme</span></p><p dir="ltr" style="line-height:1.38;margin-top:0pt;margin-bottom:0pt"><span style="font-size:13.3333333333333px;font-family:Arial;color:rgb(102,102,102);vertical-align:baseline;white-space:pre-wrap;background-color:transparent">Software Engineer</span></p><p dir="ltr" style="line-height:1.38;margin-top:0pt;margin-bottom:0pt"><span style="font-size:13.3333333333333px;font-family:Arial;color:rgb(102,102,102);vertical-align:baseline;white-space:pre-wrap;background-color:transparent"><a href="mailto:mboehme@google.com" target="_blank">mboehme@google.com</a></span><br></p><span style="font-size:13.3333333333333px;font-family:Arial;color:rgb(102,102,102);vertical-align:baseline;white-space:pre-wrap;background-color:transparent"><img src="https://lh3.googleusercontent.com/jArLagMFiBykGEq2mtMjIS9RO3ydtGbI1KRMmdY7daBuMP4HQbM7p92vdv8yolHBV9sE4YXu7ORFjsy6mcF6LGJOjQhqkd4PrG43u1U2toF3BQIitx8YMjbU9hqX9u3U8MU=s1600" width="100px;" height="58px;" style="border:none"></span></span><br><div><div style="font-size:13px"><span style="font-family:Arial,Verdana,sans-serif"><font color="#666666">Google Germany GmbH</font></span></div><div><div style="font-size:13px;font-family:Arial,Verdana,sans-serif"><font color="#666666">Erika-Mann-Straße 33</font></div><div style="font-size:13px;font-family:Arial,Verdana,sans-serif"><font color="#666666">80363 München<br></font></div><div style="font-size:13px;font-family:Arial,Verdana,sans-serif"><font color="#666666"><br></font></div><div style="font-family:Arial,Verdana,sans-serif"><span><p dir="ltr" style="line-height:1.38;margin-top:0pt;margin-bottom:0pt"><span style="font-size:10.6666666666667px;font-family:Arial;color:rgb(183,183,183);vertical-align:baseline;white-space:pre-wrap;background-color:transparent">Geschäftsführer: Matthew Scott Sucherman, Paul Terence Manicle</span></p><p dir="ltr" style="line-height:1.38;margin-top:0pt;margin-bottom:0pt"><span style="font-size:10.6666666666667px;font-family:Arial;color:rgb(183,183,183);vertical-align:baseline;white-space:pre-wrap;background-color:transparent">Registergericht und -nummer: Hamburg, HRB 86891</span></p><p dir="ltr" style="line-height:1.38;margin-top:0pt;margin-bottom:0pt"><span style="font-size:10.6666666666667px;font-family:Arial;color:rgb(183,183,183);vertical-align:baseline;white-space:pre-wrap;background-color:transparent">Sitz der Gesellschaft: Hamburg</span></p><br><p dir="ltr" style="line-height:1.38;margin-top:0pt;margin-bottom:0pt"><span style="font-size:10.6666666666667px;font-family:Arial;color:rgb(183,183,183);vertical-align:baseline;white-space:pre-wrap;background-color:transparent">Diese E-Mail ist vertraulich. Wenn Sie nicht der richtige Adressat sind, leiten Sie diese bitte nicht weiter, informieren Sie den Absender und löschen Sie die E-Mail und alle Anhänge. Vielen Dank.</span></p><p dir="ltr" style="line-height:1.38;margin-top:0pt;margin-bottom:0pt"><span style="font-size:10.6666666666667px;font-family:Arial;color:rgb(183,183,183);vertical-align:baseline;white-space:pre-wrap;background-color:transparent"> </span></p><p dir="ltr" style="line-height:1.38;margin-top:0pt;margin-bottom:0pt"><span style="font-size:10.6666666666667px;font-family:Arial;color:rgb(183,183,183);vertical-align:baseline;white-space:pre-wrap;background-color:transparent">This e-mail is confidential. If you are not the right addressee please do not forward it, please inform the sender, and please erase this e-mail including any attachments. Thanks.</span></p></span></div></div></div></div></div></div></div></div></div>
</div>
</div></div></blockquote></div><br><br clear="all"><div><br></div>-- <br><div class="gmail_signature" data-smartmail="gmail_signature"><div dir="ltr"><div><div dir="ltr"><div dir="ltr"><div dir="ltr"><span><p dir="ltr" style="line-height:1.38;margin-top:0pt;margin-bottom:0pt"><span style="font-size:16px;font-family:Arial;color:rgb(0,0,0);font-weight:700;vertical-align:baseline;white-space:pre-wrap;background-color:transparent">Martin Böhme</span></p><p dir="ltr" style="line-height:1.38;margin-top:0pt;margin-bottom:0pt"><span style="font-size:13.3333333333333px;font-family:Arial;color:rgb(102,102,102);vertical-align:baseline;white-space:pre-wrap;background-color:transparent">Software Engineer</span></p><p dir="ltr" style="line-height:1.38;margin-top:0pt;margin-bottom:0pt"><span style="font-size:13.3333333333333px;font-family:Arial;color:rgb(102,102,102);vertical-align:baseline;white-space:pre-wrap;background-color:transparent"><a href="mailto:mboehme@google.com" target="_blank">mboehme@google.com</a></span><br></p><span style="font-size:13.3333333333333px;font-family:Arial;color:rgb(102,102,102);vertical-align:baseline;white-space:pre-wrap;background-color:transparent"><img src="https://lh3.googleusercontent.com/jArLagMFiBykGEq2mtMjIS9RO3ydtGbI1KRMmdY7daBuMP4HQbM7p92vdv8yolHBV9sE4YXu7ORFjsy6mcF6LGJOjQhqkd4PrG43u1U2toF3BQIitx8YMjbU9hqX9u3U8MU=s1600" width="100px;" height="58px;" style="border:none"></span></span><br><div><div style="font-size:13px"><span style="font-family:Arial,Verdana,sans-serif"><font color="#666666">Google Germany GmbH</font></span></div><div><div style="font-size:13px;font-family:Arial,Verdana,sans-serif"><font color="#666666">Erika-Mann-Straße 33</font></div><div style="font-size:13px;font-family:Arial,Verdana,sans-serif"><font color="#666666">80363 München<br></font></div><div style="font-size:13px;font-family:Arial,Verdana,sans-serif"><font color="#666666"><br></font></div><div style="font-family:Arial,Verdana,sans-serif"><span><p dir="ltr" style="line-height:1.38;margin-top:0pt;margin-bottom:0pt"><span style="font-size:10.6666666666667px;font-family:Arial;color:rgb(183,183,183);vertical-align:baseline;white-space:pre-wrap;background-color:transparent">Geschäftsführer: Matthew Scott Sucherman, Paul Terence Manicle</span></p><p dir="ltr" style="line-height:1.38;margin-top:0pt;margin-bottom:0pt"><span style="font-size:10.6666666666667px;font-family:Arial;color:rgb(183,183,183);vertical-align:baseline;white-space:pre-wrap;background-color:transparent">Registergericht und -nummer: Hamburg, HRB 86891</span></p><p dir="ltr" style="line-height:1.38;margin-top:0pt;margin-bottom:0pt"><span style="font-size:10.6666666666667px;font-family:Arial;color:rgb(183,183,183);vertical-align:baseline;white-space:pre-wrap;background-color:transparent">Sitz der Gesellschaft: Hamburg</span></p><br><p dir="ltr" style="line-height:1.38;margin-top:0pt;margin-bottom:0pt"><span style="font-size:10.6666666666667px;font-family:Arial;color:rgb(183,183,183);vertical-align:baseline;white-space:pre-wrap;background-color:transparent">Diese E-Mail ist vertraulich. Wenn Sie nicht der richtige Adressat sind, leiten Sie diese bitte nicht weiter, informieren Sie den Absender und löschen Sie die E-Mail und alle Anhänge. Vielen Dank.</span></p><p dir="ltr" style="line-height:1.38;margin-top:0pt;margin-bottom:0pt"><span style="font-size:10.6666666666667px;font-family:Arial;color:rgb(183,183,183);vertical-align:baseline;white-space:pre-wrap;background-color:transparent"> </span></p><p dir="ltr" style="line-height:1.38;margin-top:0pt;margin-bottom:0pt"><span style="font-size:10.6666666666667px;font-family:Arial;color:rgb(183,183,183);vertical-align:baseline;white-space:pre-wrap;background-color:transparent">This e-mail is confidential. If you are not the right addressee please do not forward it, please inform the sender, and please erase this e-mail including any attachments. Thanks.</span></p></span></div></div></div></div></div></div></div></div></div>
</div>