Index: clang-move/CMakeLists.txt =================================================================== --- clang-move/CMakeLists.txt +++ clang-move/CMakeLists.txt @@ -4,8 +4,10 @@ add_clang_library(clangMove ClangMove.cpp + HelperDeclRefGraph.cpp LINK_LIBS + clangAnalysis clangAST clangASTMatchers clangBasic Index: clang-move/ClangMove.h =================================================================== --- clang-move/ClangMove.h +++ clang-move/ClangMove.h @@ -10,6 +10,7 @@ #ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_MOVE_CLANGMOVE_H #define LLVM_CLANG_TOOLS_EXTRA_CLANG_MOVE_CLANGMOVE_H +#include "HelperDeclRefGraph.h" #include "clang/ASTMatchers/ASTMatchFinder.h" #include "clang/Frontend/FrontendAction.h" #include "clang/Tooling/Core/Replacement.h" @@ -88,11 +89,18 @@ }; // This tool is used to move class/function definitions from the given source -// files (old.h/cc) to new files (new.h/cc). When moving a class, all its -// members are also moved. In addition, all helper functions (anonymous -// namespace declarations, static declarations, using declarations) in old.cc -// and forward class declarations in old.h are copied to the new files. -// The goal of this tool is to make the new files as compliable as possible. +// files (old.h/cc) to new files (new.h/cc). +// The goal of this tool is to make the new/old files as compilable as possible. +// +// When moving a symbol,all used helper declarations (e.g. static +// functions/variables definitions in global/named namespace, +// functions/variables/classes definitions in anonymous namespace) used by the +// moved symbol in old.cc are moved to the new.cc. In addition, all +// using-declarations in old.cc are also moved to new.cc; forward class +// declarations in old.h are also moved to new.h. +// +// The remaining helper declarations which are unused by non-moved symbols in +// old.cc will be removed. // // Note: When all declarations in old header are being moved, all code in // old.h/cc will be moved, which means old.h/cc are empty. This ignores symbols @@ -148,8 +156,10 @@ // Stores all MatchCallbacks created by this tool. std::vector> MatchCallbacks; - // All declarations (the class decl being moved, forward decls) that need to - // be moved/copy to the new files, saving in an AST-visited order. + // Store all potential declarations (decls being moved, forward decls) that + // might need to move to new.h/cc. It includes all helper declarations + // (include unused ones) by default. The unused ones will be filtered out in + // the last stage. Saving in an AST-visited order. std::vector MovedDecls; // The declarations that needs to be removed in old.cc/h. std::vector RemovedDecls; @@ -157,6 +167,10 @@ std::vector HeaderIncludes; // The #includes in old_cc.cc. std::vector CCIncludes; + // Records all helper declarations (function/variable/class definitions in + // anonymous namespaces, static function/variable definitions in global/named + // namespaces) in old.cc. saving in an AST-visited order. + std::vector HelperDeclarations; // The unmoved named declarations in old header. llvm::SmallPtrSet UnremovedDeclsInOldHeader; /// The source range for the written file name in #include (i.e. "old.h" for @@ -170,6 +184,8 @@ ClangMoveContext *const Context; /// A reporter to report all declarations from old header. It is not owned. DeclarationReporter *const Reporter; + /// Builder for helper declarations reference graph. + HelperDeclRGBuilder RGBuilder; }; class ClangMoveAction : public clang::ASTFrontendAction { Index: clang-move/ClangMove.cpp =================================================================== --- clang-move/ClangMove.cpp +++ clang-move/ClangMove.cpp @@ -8,6 +8,7 @@ //===----------------------------------------------------------------------===// #include "ClangMove.h" +#include "HelperDeclRefGraph.h" #include "clang/ASTMatchers/ASTMatchers.h" #include "clang/Basic/SourceManager.h" #include "clang/Format/Format.h" @@ -16,8 +17,11 @@ #include "clang/Lex/Preprocessor.h" #include "clang/Rewrite/Core/Rewriter.h" #include "clang/Tooling/Core/Replacement.h" +#include "llvm/Support/Debug.h" #include "llvm/Support/Path.h" +#define DEBUG_TYPE "clang-move" + using namespace clang::ast_matchers; namespace clang { @@ -394,6 +398,25 @@ clang::tooling::Replacement(FileName, 0, 0, NewCode)); } +// Return a set of all decls which are used/referenced by the given Decls. +// Specically, given a class member declaration, this method will return all +// decls which are used by the whole class. +llvm::DenseSet +getUsedDecls(const HelperDeclRefGraph *RG, + const std::vector &Decls) { + assert(RG); + llvm::DenseSet Nodes; + for (const auto *D : Decls) { + auto Result = RG->getReachableNodes( + HelperDeclRGBuilder::getOutmostClassOrFunDecl(D)); + Nodes.insert(Result.begin(), Result.end()); + } + llvm::DenseSet Results; + for (const auto *Node : Nodes) + Results.insert(Node->getDecl()); + return Results; +} + } // namespace std::unique_ptr @@ -455,24 +478,18 @@ //============================================================================ // Matchers for old cc //============================================================================ - auto InOldCCNamedOrGlobalNamespace = - allOf(hasParent(decl(anyOf(namespaceDecl(unless(isAnonymous())), - translationUnitDecl()))), - InOldCC); - // Matching using decls/type alias decls which are in named namespace or - // global namespace. Those in classes, functions and anonymous namespaces are - // covered in other matchers. + auto IsOldCCTopLevelDecl = allOf( + hasParent(decl(anyOf(namespaceDecl(), translationUnitDecl()))), InOldCC); + // Matching using decls/type alias decls which are in named/anonymous/global + // namespace, these decls are always copied to new.h/cc. Those in classes, + // functions are covered in other matchers. Finder->addMatcher( - namedDecl(anyOf(usingDecl(InOldCCNamedOrGlobalNamespace), - usingDirectiveDecl(InOldCCNamedOrGlobalNamespace), - typeAliasDecl( InOldCCNamedOrGlobalNamespace))) + namedDecl(anyOf(usingDecl(IsOldCCTopLevelDecl), + usingDirectiveDecl(IsOldCCTopLevelDecl), + typeAliasDecl(IsOldCCTopLevelDecl))) .bind("using_decl"), this); - // Match anonymous namespace decl in old cc. - Finder->addMatcher(namespaceDecl(isAnonymous(), InOldCC).bind("anonymous_ns"), - this); - // Match static functions/variable definitions which are defined in named // namespaces. Optional> HasAnySymbolNames; @@ -489,13 +506,37 @@ } auto InMovedClass = hasOutermostEnclosingClass(cxxRecordDecl(*HasAnySymbolNames)); - auto IsOldCCStaticDefinition = - allOf(isDefinition(), unless(InMovedClass), InOldCCNamedOrGlobalNamespace, - isStaticStorageClass()); - Finder->addMatcher(namedDecl(anyOf(functionDecl(IsOldCCStaticDefinition), - varDecl(IsOldCCStaticDefinition))) - .bind("static_decls"), - this); + + // Matchers for helper declarations in old.cc. + auto InAnonymousNS = hasParent(namespaceDecl(isAnonymous())); + auto DefinitionInOldCC = allOf(isDefinition(), unless(InMovedClass), InOldCC); + auto IsOldCCHelperDefinition = + allOf(DefinitionInOldCC, anyOf(isStaticStorageClass(), InAnonymousNS)); + // Match helper classes separately with helper functions/variables since we + // want to reuse these matchers in finding helpers usage below. + auto HelperFuncOrVar = namedDecl(anyOf(functionDecl(IsOldCCHelperDefinition), + varDecl(IsOldCCHelperDefinition))); + auto HelperClasses = cxxRecordDecl(DefinitionInOldCC, InAnonymousNS); + // Save all helper declarations in old.cc. + Finder->addMatcher( + namedDecl(anyOf(HelperFuncOrVar, HelperClasses)).bind("helper_decls"), + this); + + // Construct an AST-based call graph of helper declarations in old.cc. + // In the following matcheres, "dc" is a caller while "helper_decls" and + // "used_class" is a callee, so a new edge starting from caller to callee will + // be add in the graph. + // + // Find helper function/variable usages. + Finder->addMatcher( + declRefExpr(to(HelperFuncOrVar), hasAncestor(decl().bind("dc"))) + .bind("func_ref"), + &RGBuilder); + // Find helper class usages. + Finder->addMatcher( + typeLoc(loc(recordType(hasDeclaration(HelperClasses.bind("used_class")))), + hasAncestor(decl().bind("dc"))), + &RGBuilder); //============================================================================ // Matchers for old files, including old.h/old.cc @@ -543,12 +584,13 @@ else MovedDecls.push_back(FWD); } - } else if (const auto *ANS = - Result.Nodes.getNodeAs("anonymous_ns")) { - MovedDecls.push_back(ANS); } else if (const auto *ND = Result.Nodes.getNodeAs("static_decls")) { MovedDecls.push_back(ND); + } else if (const auto *ND = + Result.Nodes.getNodeAs("helper_decls")) { + MovedDecls.push_back(ND); + HelperDeclarations.push_back(ND); } else if (const auto *UD = Result.Nodes.getNodeAs("using_decl")) { MovedDecls.push_back(UD); @@ -567,9 +609,6 @@ SmallVector HeaderWithSearchPath; llvm::sys::path::append(HeaderWithSearchPath, SearchPath, IncludeHeader); std::string AbsoluteOldHeader = makeAbsolutePath(Context->Spec.OldHeader); - // FIXME: Add old.h to the new.cc/h when the new target has dependencies on - // old.h/c. For instance, when moved class uses another class defined in - // old.h, the old.h should be added in new.h. if (AbsoluteOldHeader == MakeAbsolutePath(SM, llvm::StringRef(HeaderWithSearchPath.data(), HeaderWithSearchPath.size()))) { @@ -591,6 +630,28 @@ void ClangMoveTool::removeDeclsInOldFiles() { if (RemovedDecls.empty()) return; + + // If old_header is not specified (only move declarations from old.cc), remain + // all the helper function declarations in old.cc as UnremovedDeclsInOldHeader + // is empty in this case, there is no way to verify unused/used helpers. + if (!Context->Spec.OldHeader.empty()) { + std::vector UnremovedDecls; + for (const auto *D : UnremovedDeclsInOldHeader) + UnremovedDecls.push_back(D); + + auto UsedDecls = getUsedDecls(RGBuilder.getGraph(), UnremovedDecls); + + // We remove the helper declarations which are not used in the old.cc after + // moving the given declarations. + for (const auto *D : HelperDeclarations) { + if (!UsedDecls.count(HelperDeclRGBuilder::getOutmostClassOrFunDecl(D))) { + DEBUG(llvm::dbgs() << "Helper removed in old.cc: " + << D->getNameAsString() << " " << D << "\n"); + RemovedDecls.push_back(D); + } + } + } + for (const auto *RemovedDecl : RemovedDecls) { const auto &SM = RemovedDecl->getASTContext().getSourceManager(); auto Range = getFullRange(RemovedDecl); @@ -650,6 +711,22 @@ NewCCDecls.push_back(MovedDecl); } + auto UsedDecls = getUsedDecls(RGBuilder.getGraph(), RemovedDecls); + std::vector ActualNewCCDecls; + + // Filter out all unused helpers in NewCCDecls. + // We only move the used helpers (including transively used helpers) and the + // given symbols being moved. + for (const auto *D : NewCCDecls) { + if (llvm::is_contained(HelperDeclarations, D) && + !UsedDecls.count(HelperDeclRGBuilder::getOutmostClassOrFunDecl(D))) + continue; + + DEBUG(llvm::dbgs() << "Helper used in new.cc: " << D->getNameAsString() + << " " << D << "\n"); + ActualNewCCDecls.push_back(D); + } + if (!Context->Spec.NewHeader.empty()) { std::string OldHeaderInclude = Context->Spec.NewDependOnOld @@ -662,7 +739,8 @@ } if (!Context->Spec.NewCC.empty()) Context->FileToReplacements[Context->Spec.NewCC] = - createInsertedReplacements(CCIncludes, NewCCDecls, Context->Spec.NewCC); + createInsertedReplacements(CCIncludes, ActualNewCCDecls, + Context->Spec.NewCC); } // Move all contents from OldFile to NewFile. @@ -737,8 +815,9 @@ moveAll(SM, Context->Spec.OldCC, Context->Spec.NewCC); return; } - removeDeclsInOldFiles(); + DEBUG(RGBuilder.getGraph()->dump()); moveDeclsToNewFiles(); + removeDeclsInOldFiles(); } } // namespace move Index: clang-move/HelperDeclRefGraph.h =================================================================== --- /dev/null +++ clang-move/HelperDeclRefGraph.h @@ -0,0 +1,99 @@ +//===-- UsedHelperDeclFinder.h - AST-based call graph for helper decls ----===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_MOVE_USED_HELPER_DECL_FINDER_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANG_MOVE_USED_HELPER_DECL_FINDER_H + +#include "clang/ASTMatchers/ASTMatchFinder.h" +#include "clang/Analysis/CallGraph.h" +#include "llvm/ADT/DenseSet.h" +#include +#include + +namespace clang { +namespace move { + +// A reference graph for finding used/unused helper declarations in a single +// translation unit (e.g. old.cc). We don't reuse CallGraph in clang/Analysis +// because that CallGraph only supports function declarations. +// +// Helper declarations include following types: +// * function/variable/class definitions in an anonymous namespace. +// * static function/variable definitions in a global/named namespace. +// +// The reference graph is a directed graph. Each node in the graph represents a +// helper declaration in old.cc or a non-moved/moved declaration (e.g. class, +// function) in old.h, which means each node is associated with a Decl. +// +// To construct the graph, we use AST matcher to find interesting Decls (usually +// a pair of Caller and Callee), and add an edge from the Caller node to the +// Callee node. +// +// Specially, for a class, it might have multiple declarations such methods +// and member variables. We only use a single node to present this class, and +// this node is associated with the class declaration (CXXRecordDecl). +// +// The graph has 3 types of edges: +// 1. moved_decl => helper_decl +// 2. non_moved_decl => helper_decl +// 3. helper_decl => helper_decl +class HelperDeclRefGraph { +public: + HelperDeclRefGraph() = default; + ~HelperDeclRefGraph() = default; + + // Add a directed edge from the caller node to the callee node. + // A new node will be created if the node for Caller/Callee doesn't exist. + // + // Note that, all class member declarations are represented by a single node + // in the graph. The corresponding Decl of this node is the class declaration. + void addEdge(const Decl *Caller, const Decl *Callee); + CallGraphNode *getNode(const Decl *D) const; + + // Get all reachable nodes in the graph from the given declaration D's node, + // including D. + llvm::DenseSet getReachableNodes(const Decl *D) const; + + // Dump the call graph for debug purpose. + void dump() const; + +private: + void print(raw_ostream &OS) const; + // Lookup a node for the given declaration D. If not found, insert a new + // node into the graph. + CallGraphNode *getOrInsertNode(Decl *D); + + typedef llvm::DenseMap> + DeclMapTy; + + // DeclMap owns all CallGraphNodes. + DeclMapTy DeclMap; +}; + +// A builder helps to construct a call graph of helper declarations. +class HelperDeclRGBuilder : public ast_matchers::MatchFinder::MatchCallback { +public: + HelperDeclRGBuilder() : RG(new HelperDeclRefGraph) {} + void run(const ast_matchers::MatchFinder::MatchResult &Result) override; + const HelperDeclRefGraph *getGraph() const { return RG.get(); } + + // Find out the outmost enclosing class/function declaration of a given D. + // For a CXXMethodDecl, get its CXXRecordDecl; For a VarDecl/FunctionDecl, get + // its outmost enclosing FunctionDecl or CXXRecordDecl. + // Return D if not found. + static const Decl *getOutmostClassOrFunDecl(const Decl *D); + +private: + std::unique_ptr RG; +}; + +} // namespace move +} // namespace clang + +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_MOVE_USED_HELPER_DECL_FINDER_H Index: clang-move/HelperDeclRefGraph.cpp =================================================================== --- /dev/null +++ clang-move/HelperDeclRefGraph.cpp @@ -0,0 +1,128 @@ +//===-- UsedHelperDeclFinder.cpp - AST-based call graph for helper decls --===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "HelperDeclRefGraph.h" +#include "ClangMove.h" +#include "clang/AST/Decl.h" +#include + +namespace clang { +namespace move { + +void HelperDeclRefGraph::print(raw_ostream &OS) const { + OS << " --- Call graph Dump --- \n"; + for (auto I = DeclMap.begin(); I != DeclMap.end(); ++I) { + const CallGraphNode *N = (I->second).get(); + + OS << " Declarations: "; + N->print(OS); + OS << " (" << N << ") "; + OS << " calls: "; + for (auto CI = N->begin(), CE = N->end(); CI != CE; ++CI) { + (*CI)->print(OS); + OS << " (" << CI << ") "; + } + OS << '\n'; + } + OS.flush(); +} + +void HelperDeclRefGraph::addEdge(const Decl *Caller, const Decl *Callee) { + assert(Caller); + assert(Callee); + + // Ignore the case where Caller equals Callee. This happens in the static + // class member definitions in global namespace like "int CLASS::static_var = + // 1;", its DC is a VarDel whose outmost enclosing declaration is the "CLASS" + // CXXRecordDecl. + if (Caller == Callee) return; + + // Allocate a new node, mark it as root, and process it's calls. + CallGraphNode *CallerNode = getOrInsertNode(const_cast(Caller)); + CallGraphNode *CalleeNode = getOrInsertNode(const_cast(Callee)); + CallerNode->addCallee(CalleeNode); +} + +void HelperDeclRefGraph::dump() const { print(llvm::errs()); } + +CallGraphNode *HelperDeclRefGraph::getOrInsertNode(Decl *F) { + F = F->getCanonicalDecl(); + std::unique_ptr &Node = DeclMap[F]; + if (Node) + return Node.get(); + + Node = llvm::make_unique(F); + return Node.get(); +} + +CallGraphNode *HelperDeclRefGraph::getNode(const Decl *D) const { + auto I = DeclMap.find(D->getCanonicalDecl()); + return I == DeclMap.end() ? nullptr : I->second.get(); +} + +llvm::DenseSet +HelperDeclRefGraph::getReachableNodes(const Decl *Root) const { + const auto *RootNode = getNode(Root); + if (!RootNode) + return {}; + llvm::DenseSet ConnectedNodes; + std::function VisitNode = + [&](const CallGraphNode *Node) { + if (ConnectedNodes.count(Node)) + return; + ConnectedNodes.insert(Node); + for (auto It = Node->begin(), End = Node->end(); It != End; ++It) + VisitNode(*It); + }; + + VisitNode(RootNode); + return ConnectedNodes; +} + +const Decl *HelperDeclRGBuilder::getOutmostClassOrFunDecl(const Decl *D) { + const auto *DC = D->getDeclContext(); + const auto *Result = D; + while (DC) { + if (const auto *RD = dyn_cast(DC)) + Result = RD; + else if (const auto *FD = dyn_cast(DC)) + Result = FD; + DC = DC->getParent(); + } + return Result; +} + +void HelperDeclRGBuilder::run( + const ast_matchers::MatchFinder::MatchResult &Result) { + // Construct the graph by adding a directed edge from caller to callee. + // + // "dc" is the closest ancestor declaration of "func_ref" or "used_class", it + // might be not the targetted Caller Decl, we always use the outmost enclosing + // FunctionDecl/CXXRecordDecl of "dc". For example, + // + // int MoveClass::F() { int a = helper(); return a; } + // + // The matched "dc" of "helper" DeclRefExpr is a VarDecl, we traverse up AST + // to find the outmost "MoveClass" CXXRecordDecl and use it as Caller. + if (const auto *FuncRef = Result.Nodes.getNodeAs("func_ref")) { + const auto *DC = Result.Nodes.getNodeAs("dc"); + assert(DC); + + RG->addEdge(getOutmostClassOrFunDecl(DC->getCanonicalDecl()), + getOutmostClassOrFunDecl(FuncRef->getDecl())); + } else if (const auto *UsedClass = + Result.Nodes.getNodeAs("used_class")) { + const auto *DC = Result.Nodes.getNodeAs("dc"); + assert(DC); + RG->addEdge(getOutmostClassOrFunDecl(DC->getCanonicalDecl()), UsedClass); + } +} + +} // namespace move +} // namespace clang Index: test/clang-move/Inputs/helper_decls_test.h =================================================================== --- /dev/null +++ test/clang-move/Inputs/helper_decls_test.h @@ -0,0 +1,30 @@ +namespace a { +class Class1 { + void f(); +}; + +class Class2 { + void f(); +}; + +class Class3 { + void f(); +}; + +class Class4 { + void f(); +}; + +class Class5 { + void f(); +}; + +class Class6 { + int f(); +}; + +void Fun1(); + +inline void Fun2() {} + +} // namespace a Index: test/clang-move/Inputs/helper_decls_test.cpp =================================================================== --- /dev/null +++ test/clang-move/Inputs/helper_decls_test.cpp @@ -0,0 +1,66 @@ +#include "helper_decls_test.h" + +namespace { +class HelperC1 { +public: + static int I; +}; + +int HelperC1::I = 0; + +class HelperC2 {}; + +class HelperC3 { + public: + static int I; +}; + +int HelperC3::I = 0; + +void HelperFun1() {} + +void HelperFun2() { HelperFun1(); } + +const int K1 = 1; +} // namespace + +static const int K2 = 2; +static void HelperFun3() { K2; } + +namespace a { + +static const int K3 = 3; +static const int K4 = HelperC3::I; +static const int K5 = 5; + +static void HelperFun4() {} + +void Class1::f() { HelperFun2(); } + +void Class2::f() { + HelperFun1(); + HelperFun3(); +} + +void Class3::f() { HelperC1::I; } + +void Class4::f() { HelperC2 c2; } + +void Class5::f() { + int Result = K1 + K2 + K3; + HelperFun4(); +} + +int Class6::f() { + int R = K4; + return R; +} + +static int HelperFun5() { + int R = K5; + return R; +} + +void Fun1() { HelperFun5(); } + +} // namespace a Index: test/clang-move/Inputs/multiple_class_test.cpp =================================================================== --- test/clang-move/Inputs/multiple_class_test.cpp +++ test/clang-move/Inputs/multiple_class_test.cpp @@ -15,7 +15,7 @@ using a::Move1; using namespace a; static int k = 0; -} // anonymous namespace +} // namespace namespace b { using a::Move1; @@ -34,19 +34,19 @@ } int Move4::f() { - return 0; + return k; } int EnclosingMove5::a = 1; int EnclosingMove5::Nested::f() { - return 0; + return g; } int EnclosingMove5::Nested::b = 1; int NoMove::f() { static int F = 0; - return 0; + return g; } } // namespace c Index: test/clang-move/move-multiple-classes.cpp =================================================================== --- test/clang-move/move-multiple-classes.cpp +++ test/clang-move/move-multiple-classes.cpp @@ -25,8 +25,7 @@ // CHECK-OLD-TEST-CPP: namespace { // CHECK-OLD-TEST-CPP: using a::Move1; // CHECK-OLD-TEST-CPP: using namespace a; -// CHECK-OLD-TEST-CPP: static int k = 0; -// CHECK-OLD-TEST-CPP: } // anonymous namespace +// CHECK-OLD-TEST-CPP: } // namespace // CHECK-OLD-TEST-CPP: namespace b { // CHECK-OLD-TEST-CPP: using a::Move1; // CHECK-OLD-TEST-CPP: using namespace a; @@ -35,7 +34,7 @@ // CHECK-OLD-TEST-CPP: namespace c { // CHECK-OLD-TEST-CPP: int NoMove::f() { // CHECK-OLD-TEST-CPP: static int F = 0; -// CHECK-OLD-TEST-CPP: return 0; +// CHECK-OLD-TEST-CPP: return g; // CHECK-OLD-TEST-CPP: } // CHECK-OLD-TEST-CPP: } // namespace c @@ -85,7 +84,7 @@ // CHECK-NEW-TEST-CPP: using a::Move1; // CHECK-NEW-TEST-CPP: using namespace a; // CHECK-NEW-TEST-CPP: static int k = 0; -// CHECK-NEW-TEST-CPP: } // anonymous namespace +// CHECK-NEW-TEST-CPP: } // namespace // CHECK-NEW-TEST-CPP: namespace b { // CHECK-NEW-TEST-CPP: using a::Move1; // CHECK-NEW-TEST-CPP: using namespace a; @@ -98,8 +97,8 @@ // CHECK-NEW-TEST-CPP: using namespace b; // CHECK-NEW-TEST-CPP: return 0; // CHECK-NEW-TEST-CPP: } -// CHECK-NEW-TEST-CPP: int Move4::f() { return 0; } +// CHECK-NEW-TEST-CPP: int Move4::f() { return k; } // CHECK-NEW-TEST-CPP: int EnclosingMove5::a = 1; -// CHECK-NEW-TEST-CPP: int EnclosingMove5::Nested::f() { return 0; } +// CHECK-NEW-TEST-CPP: int EnclosingMove5::Nested::f() { return g; } // CHECK-NEW-TEST-CPP: int EnclosingMove5::Nested::b = 1; // CHECK-NEW-TEST-CPP: } // namespace c Index: test/clang-move/move-used-helper-decls.cpp =================================================================== --- /dev/null +++ test/clang-move/move-used-helper-decls.cpp @@ -0,0 +1,202 @@ +// RUN: mkdir -p %T/used-helper-decls +// RUN: cp %S/Inputs/helper_decls_test* %T/used-helper-decls/ +// RUN: cd %T/used-helper-decls + +// RUN: clang-move -names="a::Class1" -new_cc=%T/used-helper-decls/new_helper_decls_test.cpp -new_header=%T/used-helper-decls/new_helper_decls_test.h -old_cc=%T/used-helper-decls/helper_decls_test.cpp -old_header=../used-helper-decls/helper_decls_test.h %T/used-helper-decls/helper_decls_test.cpp -- -std=c++11 +// RUN: FileCheck -input-file=%T/used-helper-decls/new_helper_decls_test.cpp -check-prefix=CHECK-NEW-CLASS1-CPP %s +// RUN: FileCheck -input-file=%T/used-helper-decls/helper_decls_test.cpp -check-prefix=CHECK-OLD-CLASS1-CPP %s + +// CHECK-NEW-CLASS1-CPP: #include "{{.*}}new_helper_decls_test.h" +// CHECK-NEW-CLASS1-CPP-SAME: {{[[:space:]]}} +// CHECK-NEW-CLASS1-CPP-NEXT: namespace { +// CHECK-NEW-CLASS1-CPP-NEXT: void HelperFun1() {} +// CHECK-NEW-CLASS1-CPP-SAME: {{[[:space:]]}} +// CHECK-NEW-CLASS1-CPP-NEXT: void HelperFun2() { HelperFun1(); } +// CHECK-NEW-CLASS1-CPP-NEXT: } // namespace +// CHECK-NEW-CLASS1-CPP-SAME: {{[[:space:]]}} +// CHECK-NEW-CLASS1-CPP-NEXT: namespace a { +// CHECK-NEW-CLASS1-CPP-NEXT: void Class1::f() { HelperFun2(); } +// CHECK-NEW-CLASS1-CPP-NEXT: } // namespace a +// +// CHECK-OLD-CLASS1-CPP: void HelperFun1() {} +// CHECK-OLD-CLASS1-CPP-NOT: void HelperFun2() { HelperFun1(); } +// CHECK-OLD-CLASS1-CPP-NOT: void Class1::f() { HelperFun2(); } +// CHECK-OLD-CLASS1-CPP: void Class2::f() { +// CHECK-OLD-CLASS1-CPP: HelperFun1(); + +// RUN: cp %S/Inputs/helper_decls_test* %T/used-helper-decls/ +// RUN: clang-move -names="a::Class2" -new_cc=%T/used-helper-decls/new_helper_decls_test.cpp -new_header=%T/used-helper-decls/new_helper_decls_test.h -old_cc=%T/used-helper-decls/helper_decls_test.cpp -old_header=../used-helper-decls/helper_decls_test.h %T/used-helper-decls/helper_decls_test.cpp -- -std=c++11 +// RUN: FileCheck -input-file=%T/used-helper-decls/new_helper_decls_test.cpp -check-prefix=CHECK-NEW-CLASS2-CPP %s +// RUN: FileCheck -input-file=%T/used-helper-decls/helper_decls_test.cpp -check-prefix=CHECK-OLD-CLASS2-CPP %s + +// CHECK-NEW-CLASS2-CPP: #include "{{.*}}new_helper_decls_test.h" +// CHECK-NEW-CLASS2-CPP-SAME: {{[[:space:]]}} +// CHECK-NEW-CLASS2-CPP-NEXT: namespace { +// CHECK-NEW-CLASS2-CPP-NEXT: void HelperFun1() {} +// CHECK-NEW-CLASS2-CPP-NEXT: } // namespace +// CHECK-NEW-CLASS2-CPP-SAME: {{[[:space:]]}} +// CHECK-NEW-CLASS2-CPP-NEXT: static const int K2 = 2; +// CHECK-NEW-CLASS2-CPP-SAME: {{[[:space:]]}} +// CHECK-NEW-CLASS2-CPP-NEXT: static void HelperFun3() { K2; } +// CHECK-NEW-CLASS2-CPP-NEXT: namespace a { +// CHECK-NEW-CLASS2-CPP-NEXT: void Class2::f() { +// CHECK-NEW-CLASS2-CPP-NEXT: HelperFun1(); +// CHECK-NEW-CLASS2-CPP-NEXT: HelperFun3(); +// CHECK-NEW-CLASS2-CPP-NEXT: } +// CHECK-NEW-CLASS2-CPP-NEXT: } // namespace a + +// CHECK-OLD-CLASS2-CPP: void HelperFun1() {} +// CHECK-OLD-CLASS2-CPP: void HelperFun2() { HelperFun1(); } +// CHECK-OLD-CLASS2-CPP: const int K1 = 1; +// CHECK-OLD-CLASS2-CPP: static const int K2 = 2; +// CHECK-OLD-CLASS2-CPP-NOT: static void HelperFun3() { K2; } +// CHECK-OLD-CLASS2-CPP-NOT: void Class2::f() { +// CHECK-OLD-CLASS2-CPP-NOT: HelperFun1(); +// CHECK-OLD-CLASS2-CPP-NOT: HelperFun3(); +// CHECK-OLD-CLASS2-CPP: void Class5::f() { +// CHECK-OLD-CLASS2-CPP-NEXT: int Result = K1 + K2 + K3; + +// RUN: cp %S/Inputs/helper_decls_test* %T/used-helper-decls/ +// RUN: clang-move -names="a::Class3" -new_cc=%T/used-helper-decls/new_helper_decls_test.cpp -new_header=%T/used-helper-decls/new_helper_decls_test.h -old_cc=%T/used-helper-decls/helper_decls_test.cpp -old_header=../used-helper-decls/helper_decls_test.h %T/used-helper-decls/helper_decls_test.cpp -- -std=c++11 +// RUN: FileCheck -input-file=%T/used-helper-decls/new_helper_decls_test.cpp -check-prefix=CHECK-NEW-CLASS3-CPP %s +// RUN: FileCheck -input-file=%T/used-helper-decls/helper_decls_test.cpp -check-prefix=CHECK-OLD-CLASS3-CPP %s + +// CHECK-NEW-CLASS3-CPP: #include "{{.*}}new_helper_decls_test.h" +// CHECK-NEW-CLASS3-CPP-SAME: {{[[:space:]]}} +// CHECK-NEW-CLASS3-CPP-NEXT: namespace { +// CHECK-NEW-CLASS3-CPP-NEXT: class HelperC1 { +// CHECK-NEW-CLASS3-CPP-NEXT: public: +// CHECK-NEW-CLASS3-CPP-NEXT: static int I; +// CHECK-NEW-CLASS3-CPP-NEXT: }; +// CHECK-NEW-CLASS3-CPP-SAME: {{[[:space:]]}} +// CHECK-NEW-CLASS3-CPP-NEXT: int HelperC1::I = 0; +// CHECK-NEW-CLASS3-CPP-NEXT: } // namespace +// CHECK-NEW-CLASS3-CPP-SAME: {{[[:space:]]}} +// CHECK-NEW-CLASS3-CPP-NEXT: namespace a { +// CHECK-NEW-CLASS3-CPP-NEXT: void Class3::f() { HelperC1::I; } +// CHECK-NEW-CLASS3-CPP-NEXT: } // namespace a + +// CHECK-OLD-CLASS3-CPP: namespace { +// CHECK-OLD-CLASS3-CPP-NOT: class HelperC1 { +// CHECK-OLD-CLASS3-CPP-NOT: public: +// CHECK-OLD-CLASS3-CPP-NOT: static int I; +// CHECK-OLD-CLASS3-CPP-NOT: }; +// CHECK-OLD-CLASS3-CPP-NOT: int HelperC1::I = 0; +// CHECK-OLD-CLASS3-CPP: class HelperC2 {}; + + +// RUN: cp %S/Inputs/helper_decls_test* %T/used-helper-decls/ +// RUN: clang-move -names="a::Class4" -new_cc=%T/used-helper-decls/new_helper_decls_test.cpp -new_header=%T/used-helper-decls/new_helper_decls_test.h -old_cc=%T/used-helper-decls/helper_decls_test.cpp -old_header=../used-helper-decls/helper_decls_test.h %T/used-helper-decls/helper_decls_test.cpp -- -std=c++11 +// RUN: FileCheck -input-file=%T/used-helper-decls/new_helper_decls_test.cpp -check-prefix=CHECK-NEW-CLASS4-CPP %s +// RUN: FileCheck -input-file=%T/used-helper-decls/helper_decls_test.cpp -check-prefix=CHECK-OLD-CLASS4-CPP %s + +// CHECK-NEW-CLASS4-CPP: #include "{{.*}}new_helper_decls_test.h" +// CHECK-NEW-CLASS4-CPP-SAME: {{[[:space:]]}} +// CHECK-NEW-CLASS4-CPP-NEXT: namespace { +// CHECK-NEW-CLASS4-CPP-NEXT: class HelperC2 {}; +// CHECK-NEW-CLASS4-CPP-NEXT: } // namespace +// CHECK-NEW-CLASS4-CPP-SAME: {{[[:space:]]}} +// CHECK-NEW-CLASS4-CPP-NEXT: namespace a { +// CHECK-NEW-CLASS4-CPP-NEXT: void Class4::f() { HelperC2 c2; } +// CHECK-NEW-CLASS4-CPP-NEXT: } // namespace a + +// CHECK-OLD-CLASS4-CPP-NOT: class HelperC2 {}; + + +// RUN: cp %S/Inputs/helper_decls_test* %T/used-helper-decls/ +// RUN: clang-move -names="a::Class5" -new_cc=%T/used-helper-decls/new_helper_decls_test.cpp -new_header=%T/used-helper-decls/new_helper_decls_test.h -old_cc=%T/used-helper-decls/helper_decls_test.cpp -old_header=../used-helper-decls/helper_decls_test.h %T/used-helper-decls/helper_decls_test.cpp -- -std=c++11 +// RUN: FileCheck -input-file=%T/used-helper-decls/new_helper_decls_test.cpp -check-prefix=CHECK-NEW-CLASS5-CPP %s +// RUN: FileCheck -input-file=%T/used-helper-decls/helper_decls_test.cpp -check-prefix=CHECK-OLD-CLASS5-CPP %s + +// CHECK-NEW-CLASS5-CPP: #include "{{.*}}new_helper_decls_test.h" +// CHECK-NEW-CLASS5-CPP-SAME: {{[[:space:]]}} +// CHECK-NEW-CLASS5-CPP-NEXT: namespace { +// CHECK-NEW-CLASS5-CPP-NEXT: const int K1 = 1; +// CHECK-NEW-CLASS5-CPP-NEXT: } // namespace +// CHECK-NEW-CLASS5-CPP-SAME: {{[[:space:]]}} +// CHECK-NEW-CLASS5-CPP-NEXT: static const int K2 = 2; +// CHECK-NEW-CLASS5-CPP-NEXT: namespace a { +// CHECK-NEW-CLASS5-CPP-NEXT: static const int K3 = 3; +// CHECK-NEW-CLASS5-CPP-SAME: {{[[:space:]]}} +// CHECK-NEW-CLASS5-CPP-NEXT: static void HelperFun4() {} +// CHECK-NEW-CLASS5-CPP-SAME: {{[[:space:]]}} +// CHECK-NEW-CLASS5-CPP-NEXT: void Class5::f() { +// CHECK-NEW-CLASS5-CPP-NEXT: int Result = K1 + K2 + K3; +// CHECK-NEW-CLASS5-CPP-NEXT: HelperFun4(); +// CHECK-NEW-CLASS5-CPP-NEXT: } +// CHECK-NEW-CLASS5-CPP-NEXT: } // namespace a + +// CHECK-OLD-CLASS5-CPP-NOT: const int K1 = 1; +// CHECK-OLD-CLASS5-CPP: static const int K2 = 2; +// CHECK-OLD-CLASS5-CPP: static void HelperFun3() { K2; } +// CHECK-OLD-CLASS5-CPP: static const int K4 = HelperC3::I; +// CHECK-OLD-CLASS5-CPP-NOT: void Class5::f() { + + +// RUN: cp %S/Inputs/helper_decls_test* %T/used-helper-decls/ +// RUN: clang-move -names="a::Class6" -new_cc=%T/used-helper-decls/new_helper_decls_test.cpp -new_header=%T/used-helper-decls/new_helper_decls_test.h -old_cc=%T/used-helper-decls/helper_decls_test.cpp -old_header=../used-helper-decls/helper_decls_test.h %T/used-helper-decls/helper_decls_test.cpp -- -std=c++11 +// RUN: FileCheck -input-file=%T/used-helper-decls/new_helper_decls_test.cpp -check-prefix=CHECK-NEW-CLASS6-CPP %s +// RUN: FileCheck -input-file=%T/used-helper-decls/helper_decls_test.cpp -check-prefix=CHECK-OLD-CLASS6-CPP %s + +// CHECK-NEW-CLASS6-CPP: #include "{{.*}}new_helper_decls_test.h" +// CHECK-NEW-CLASS6-CPP-SAME: {{[[:space:]]}} +// CHECK-NEW-CLASS6-CPP-NEXT: namespace { +// CHECK-NEW-CLASS6-CPP-NEXT: class HelperC3 { +// CHECK-NEW-CLASS6-CPP-NEXT: public: +// CHECK-NEW-CLASS6-CPP-NEXT: static int I; +// CHECK-NEW-CLASS6-CPP-NEXT: }; +// CHECK-NEW-CLASS6-CPP-SAME: {{[[:space:]]}} +// CHECK-NEW-CLASS6-CPP-NEXT: int HelperC3::I = 0; +// CHECK-NEW-CLASS6-CPP-NEXT: } // namespace +// CHECK-NEW-CLASS6-CPP-SAME: {{[[:space:]]}} +// CHECK-NEW-CLASS6-CPP-NEXT: namespace a { +// CHECK-NEW-CLASS6-CPP-NEXT: static const int K4 = HelperC3::I; +// CHECK-NEW-CLASS6-CPP-SAME: {{[[:space:]]}} +// CHECK-NEW-CLASS6-CPP-NEXT: int Class6::f() { +// CHECK-NEW-CLASS6-CPP-NEXT: int R = K4; +// CHECK-NEW-CLASS6-CPP-NEXT: return R; +// CHECK-NEW-CLASS6-CPP-NEXT: } +// CHECK-NEW-CLASS6-CPP-NEXT: } // namespace a + +// CHECK-OLD-CLASS6-CPP-NOT: class HelperC3 { +// CHECK-OLD-CLASS6-CPP-NOT: int HelperC3::I = 0; +// CHECK-OLD-CLASS6-CPP-NOT: static const int K4 = HelperC3::I; + + +// RUN: cp %S/Inputs/helper_decls_test* %T/used-helper-decls/ +// RUN: clang-move -names="a::Fun1" -new_cc=%T/used-helper-decls/new_helper_decls_test.cpp -new_header=%T/used-helper-decls/new_helper_decls_test.h -old_cc=%T/used-helper-decls/helper_decls_test.cpp -old_header=../used-helper-decls/helper_decls_test.h %T/used-helper-decls/helper_decls_test.cpp -- -std=c++11 +// RUN: FileCheck -input-file=%T/used-helper-decls/new_helper_decls_test.cpp -check-prefix=CHECK-NEW-FUN1-CPP %s +// RUN: FileCheck -input-file=%T/used-helper-decls/helper_decls_test.cpp -check-prefix=CHECK-OLD-FUN1-CPP %s + +// CHECK-NEW-FUN1-CPP: #include "{{.*}}new_helper_decls_test.h" +// CHECK-NEW-FUN1-CPP-SAME: {{[[:space:]]}} +// CHECK-NEW-FUN1-CPP-NEXT: namespace a { +// CHECK-NEW-FUN1-CPP-NEXT: static const int K5 = 5; +// CHECK-NEW-FUN1-CPP-SAME: {{[[:space:]]}} +// CHECK-NEW-FUN1-CPP-NEXT: static int HelperFun5() { +// CHECK-NEW-FUN1-CPP-NEXT: int R = K5; +// CHECK-NEW-FUN1-CPP-NEXT: return R; +// CHECK-NEW-FUN1-CPP-NEXT: } +// CHECK-NEW-FUN1-CPP-SAME: {{[[:space:]]}} +// CHECK-NEW-FUN1-CPP-NEXT: void Fun1() { HelperFun5(); } +// CHECK-NEW-FUN1-CPP-NEXT: } // namespace a + +// CHECK-OLD-FUN1-CPP-NOT: static const int K5 = 5; +// CHECK-OLD-FUN1-CPP-NOT: static int HelperFun5() { +// CHECK-OLD-FUN1-CPP-NOT: void Fun1() { HelperFun5(); } + + +// RUN: cp %S/Inputs/helper_decls_test* %T/used-helper-decls/ +// RUN: clang-move -names="a::Fun2" -new_cc=%T/used-helper-decls/new_helper_decls_test.cpp -new_header=%T/used-helper-decls/new_helper_decls_test.h -old_cc=%T/used-helper-decls/helper_decls_test.cpp -old_header=../used-helper-decls/helper_decls_test.h %T/used-helper-decls/helper_decls_test.cpp -- -std=c++11 +// RUN: FileCheck -input-file=%T/used-helper-decls/new_helper_decls_test.cpp -check-prefix=CHECK-NEW-FUN2-CPP %s +// RUN: FileCheck -input-file=%T/used-helper-decls/new_helper_decls_test.h -check-prefix=CHECK-NEW-FUN2-H %s +// RUN: FileCheck -input-file=%T/used-helper-decls/helper_decls_test.h -check-prefix=CHECK-OLD-FUN2-H %s + +// CHECK-NEW-FUN2-H: namespace a { +// CHECK-NEW-FUN2-H-NEXT: inline void Fun2() {} +// CHECK-NEW-FUN2-H-NEXT: } // namespace a + +// CHECK-NEW-FUN2-CPP: #include "{{.*}}new_helper_decls_test.h" +// CHECK-NEW-FUN2-CPP-SAME: {{[[:space:]]}} + +// CHECK-OLD-FUN2-H-NOT: inline void Fun2() {} Index: unittests/clang-move/ClangMoveTests.cpp =================================================================== --- unittests/clang-move/ClangMoveTests.cpp +++ unittests/clang-move/ClangMoveTests.cpp @@ -73,13 +73,21 @@ "\n" "// comment5\n" "// comment5\n" - "void Foo::f() { f1(); }\n" + "void Foo::f() {\n" + " f1();\n" + " kConstInt1;\n" + " kConstInt2;\n" + " help();\n" + "}\n" "\n" "/////////////\n" "// comment //\n" "/////////////\n" "int Foo::b = 2;\n" "int Foo2::f() {\n" + " kConstInt1;\n" + " kConstInt2;\n" + " help();\n" " f1();\n" " return 1;\n" "}\n" @@ -119,6 +127,9 @@ "}\n" "\n" "int Foo2::f() {\n" + " kConstInt1;\n" + " kConstInt2;\n" + " help();\n" " f1();\n" " return 1;\n" "}\n" @@ -154,6 +165,7 @@ "namespace {\n" "// comment1.\n" "void f1() {}\n" + "\n" "/// comment2.\n" "int kConstInt1 = 0;\n" "} // namespace\n" @@ -170,7 +182,12 @@ "\n" "// comment5\n" "// comment5\n" - "void Foo::f() { f1(); }\n" + "void Foo::f() {\n" + " f1();\n" + " kConstInt1;\n" + " kConstInt2;\n" + " help();\n" + "}\n" "\n" "/////////////\n" "// comment //\n"