summaryrefslogtreecommitdiffstats
path: root/clang-tools-extra/clang-move/ClangMove.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'clang-tools-extra/clang-move/ClangMove.cpp')
-rw-r--r--clang-tools-extra/clang-move/ClangMove.cpp137
1 files changed, 108 insertions, 29 deletions
diff --git a/clang-tools-extra/clang-move/ClangMove.cpp b/clang-tools-extra/clang-move/ClangMove.cpp
index 9219c7ff4a8..78176808f33 100644
--- a/clang-tools-extra/clang-move/ClangMove.cpp
+++ b/clang-tools-extra/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 @@ createInsertedReplacements(const std::vector<std::string> &Includes,
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<const Decl *>
+getUsedDecls(const HelperDeclRefGraph *RG,
+ const std::vector<const NamedDecl *> &Decls) {
+ assert(RG);
+ llvm::DenseSet<const CallGraphNode *> Nodes;
+ for (const auto *D : Decls) {
+ auto Result = RG->getReachableNodes(
+ HelperDeclRGBuilder::getOutmostClassOrFunDecl(D));
+ Nodes.insert(Result.begin(), Result.end());
+ }
+ llvm::DenseSet<const Decl *> Results;
+ for (const auto *Node : Nodes)
+ Results.insert(Node->getDecl());
+ return Results;
+}
+
} // namespace
std::unique_ptr<clang::ASTConsumer>
@@ -455,24 +478,18 @@ void ClangMoveTool::registerMatchers(ast_matchers::MatchFinder *Finder) {
//============================================================================
// 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<ast_matchers::internal::Matcher<NamedDecl>> HasAnySymbolNames;
@@ -489,13 +506,37 @@ void ClangMoveTool::registerMatchers(ast_matchers::MatchFinder *Finder) {
}
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 @@ void ClangMoveTool::run(const ast_matchers::MatchFinder::MatchResult &Result) {
else
MovedDecls.push_back(FWD);
}
- } else if (const auto *ANS =
- Result.Nodes.getNodeAs<clang::NamespaceDecl>("anonymous_ns")) {
- MovedDecls.push_back(ANS);
} else if (const auto *ND =
Result.Nodes.getNodeAs<clang::NamedDecl>("static_decls")) {
MovedDecls.push_back(ND);
+ } else if (const auto *ND =
+ Result.Nodes.getNodeAs<clang::NamedDecl>("helper_decls")) {
+ MovedDecls.push_back(ND);
+ HelperDeclarations.push_back(ND);
} else if (const auto *UD =
Result.Nodes.getNodeAs<clang::NamedDecl>("using_decl")) {
MovedDecls.push_back(UD);
@@ -567,9 +609,6 @@ void ClangMoveTool::addIncludes(llvm::StringRef IncludeHeader, bool IsAngled,
SmallVector<char, 128> 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::addIncludes(llvm::StringRef IncludeHeader, bool IsAngled,
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<const NamedDecl *> 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 @@ void ClangMoveTool::moveDeclsToNewFiles() {
NewCCDecls.push_back(MovedDecl);
}
+ auto UsedDecls = getUsedDecls(RGBuilder.getGraph(), RemovedDecls);
+ std::vector<const NamedDecl *> 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 @@ void ClangMoveTool::moveDeclsToNewFiles() {
}
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 @@ void ClangMoveTool::onEndOfTranslationUnit() {
moveAll(SM, Context->Spec.OldCC, Context->Spec.NewCC);
return;
}
- removeDeclsInOldFiles();
+ DEBUG(RGBuilder.getGraph()->dump());
moveDeclsToNewFiles();
+ removeDeclsInOldFiles();
}
} // namespace move
OpenPOWER on IntegriCloud