diff options
author | Haojian Wu <hokein@google.com> | 2016-10-12 07:59:54 +0000 |
---|---|---|
committer | Haojian Wu <hokein@google.com> | 2016-10-12 07:59:54 +0000 |
commit | 7ba9450ffdef0a5b26f7d9791ff345d908d605bd (patch) | |
tree | 166215ad432000fa4ea071c63a5d4e8f2a3f759a /clang-tidy/utils | |
parent | 91cd765507a469fcf1685e3d6b8854b5802a6981 (diff) |
[ClangTidy] Add UsingInserter and NamespaceAliaser
Summary: This adds helper classes to add using declaractions and namespace aliases to function bodies. These help making function calls to deeply nested functions concise (e.g. when calling helpers in a refactoring)
Patch by Julian Bangert!
Reviewers: alexfh, hokein
Subscribers: cfe-commits
Tags: #clang-tools-extra
Differential Revision: https://reviews.llvm.org/D24997
git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@283981 91177308-0d34-0410-b5e6-96231b3b80d8
Diffstat (limited to 'clang-tidy/utils')
-rw-r--r-- | clang-tidy/utils/ASTUtils.cpp | 28 | ||||
-rw-r--r-- | clang-tidy/utils/ASTUtils.h | 25 | ||||
-rw-r--r-- | clang-tidy/utils/NamespaceAliaser.cpp | 99 | ||||
-rw-r--r-- | clang-tidy/utils/NamespaceAliaser.h | 52 | ||||
-rw-r--r-- | clang-tidy/utils/UsingInserter.cpp | 88 | ||||
-rw-r--r-- | clang-tidy/utils/UsingInserter.h | 50 |
6 files changed, 342 insertions, 0 deletions
diff --git a/clang-tidy/utils/ASTUtils.cpp b/clang-tidy/utils/ASTUtils.cpp new file mode 100644 index 00000000..b95cd54d --- /dev/null +++ b/clang-tidy/utils/ASTUtils.cpp @@ -0,0 +1,28 @@ +//===---------- ASTUtils.cpp - clang-tidy ----------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "ASTUtils.h" + +#include "clang/ASTMatchers/ASTMatchFinder.h" +#include "clang/ASTMatchers/ASTMatchers.h" + +namespace clang { +namespace tidy { +namespace utils { +using namespace ast_matchers; + +const FunctionDecl *getSurroundingFunction(ASTContext &Context, + const Stmt &Statement) { + return selectFirst<const FunctionDecl>( + "function", match(stmt(hasAncestor(functionDecl().bind("function"))), + Statement, Context)); +} +} // namespace utils +} // namespace tidy +} // namespace clang diff --git a/clang-tidy/utils/ASTUtils.h b/clang-tidy/utils/ASTUtils.h new file mode 100644 index 00000000..85ba7376 --- /dev/null +++ b/clang-tidy/utils/ASTUtils.h @@ -0,0 +1,25 @@ +//===---------- ASTUtils.h - clang-tidy ----------------------------===// +// +// 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_TIDY_ASTUTILS_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_ASTUTILS_H + +#include "clang/AST/AST.h" + +namespace clang { +namespace tidy { +namespace utils { +// Returns the (closest) Function declaration surrounding |Statement| or NULL. +const FunctionDecl *getSurroundingFunction(ASTContext &Context, + const Stmt &Statement); +} // namespace utils +} // namespace tidy +} // namespace clang + +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_ASTUTILS_H diff --git a/clang-tidy/utils/NamespaceAliaser.cpp b/clang-tidy/utils/NamespaceAliaser.cpp new file mode 100644 index 00000000..04fdb8cd --- /dev/null +++ b/clang-tidy/utils/NamespaceAliaser.cpp @@ -0,0 +1,99 @@ +//===---------- NamespaceAliaser.cpp - clang-tidy -------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "NamespaceAliaser.h" + +#include "ASTUtils.h" +#include "clang/ASTMatchers/ASTMatchFinder.h" +#include "clang/ASTMatchers/ASTMatchers.h" +#include "clang/Lex/Lexer.h" +namespace clang { +namespace tidy { +namespace utils { + +using namespace ast_matchers; + +NamespaceAliaser::NamespaceAliaser(const SourceManager &SourceMgr) + : SourceMgr(SourceMgr) {} + +AST_MATCHER_P(NamespaceAliasDecl, hasTargetNamespace, + ast_matchers::internal::Matcher<NamespaceDecl>, innerMatcher) { + return innerMatcher.matches(*Node.getNamespace(), Finder, Builder); +} + +Optional<FixItHint> +NamespaceAliaser::createAlias(ASTContext &Context, const Stmt &Statement, + StringRef Namespace, + const std::vector<std::string> &Abbreviations) { + const FunctionDecl *Function = getSurroundingFunction(Context, Statement); + if (!Function || !Function->hasBody()) + return None; + + + if (AddedAliases[Function].count(Namespace.str()) != 0) + return None; + + + // FIXME: Doesn't consider the order of declarations. + // If we accidentially pick an alias defined later in the function, + // the output won't compile. + // FIXME: Also doesn't consider file or class-scope aliases. + + const auto *ExistingAlias = selectFirst<NamedDecl>( + "alias", + match(functionDecl(hasBody(compoundStmt(has(declStmt( + has(namespaceAliasDecl(hasTargetNamespace(hasName(Namespace))) + .bind("alias"))))))), + *Function, Context)); + + if (ExistingAlias != nullptr) { + AddedAliases[Function][Namespace.str()] = ExistingAlias->getName().str(); + return None; + } + + for (const auto &Abbreviation : Abbreviations) { + DeclarationMatcher ConflictMatcher = namedDecl(hasName(Abbreviation)); + const auto HasConflictingChildren = + !match(findAll(ConflictMatcher), *Function, Context).empty(); + const auto HasConflictingAncestors = + !match(functionDecl(hasAncestor(decl(has(ConflictMatcher)))), *Function, + Context) + .empty(); + if (HasConflictingAncestors || HasConflictingChildren) + continue; + + std::string Declaration = + (llvm::Twine("\nnamespace ") + Abbreviation + " = " + Namespace + ";") + .str(); + SourceLocation Loc = + Lexer::getLocForEndOfToken(Function->getBody()->getLocStart(), 0, + SourceMgr, Context.getLangOpts()); + AddedAliases[Function][Namespace.str()] = Abbreviation; + return FixItHint::CreateInsertion(Loc, Declaration); + } + + return None; +} + +std::string NamespaceAliaser::getNamespaceName(ASTContext &Context, + const Stmt &Statement, + StringRef Namespace) const { + const auto *Function = getSurroundingFunction(Context, Statement); + auto FunctionAliases = AddedAliases.find(Function); + if (FunctionAliases != AddedAliases.end()) { + if (FunctionAliases->second.count(Namespace) != 0) { + return FunctionAliases->second.find(Namespace)->getValue(); + } + } + return Namespace.str(); +} + +} // namespace utils +} // namespace tidy +} // namespace clang diff --git a/clang-tidy/utils/NamespaceAliaser.h b/clang-tidy/utils/NamespaceAliaser.h new file mode 100644 index 00000000..e56d69d3 --- /dev/null +++ b/clang-tidy/utils/NamespaceAliaser.h @@ -0,0 +1,52 @@ +//===---------- NamespaceAliaser.h - clang-tidy ---------------------------===// +// +// 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_TIDY_NAMESPACEALIASER_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_NAMESPACEALIASER_H + +#include "clang/AST/ASTContext.h" +#include "clang/AST/Stmt.h" +#include "clang/Basic/Diagnostic.h" +#include "clang/Basic/SourceManager.h" +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/StringMap.h" +#include <map> + +namespace clang { +namespace tidy { +namespace utils { + +// This class creates function-level namespace aliases. +class NamespaceAliaser { +public: + explicit NamespaceAliaser(const SourceManager &SourceMgr); + // Adds a namespace alias for \p Namespace valid near \p + // Statement. Picks the first available name from \p Abbreviations. + // Returns ``llvm::None`` if an alias already exists or there is an error. + llvm::Optional<FixItHint> + createAlias(ASTContext &Context, const Stmt &Statement, + llvm::StringRef Namespace, + const std::vector<std::string> &Abbreviations); + + // Get an alias name for \p Namespace valid at \p Statement. Returns \p + // Namespace if there is no alias. + std::string getNamespaceName(ASTContext &Context, const Stmt &Statement, + llvm::StringRef Namespace) const; + +private: + const SourceManager &SourceMgr; + llvm::DenseMap<const FunctionDecl *, llvm::StringMap<std::string>> + AddedAliases; +}; + +} // namespace utils +} // namespace tidy +} // namespace clang + +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_NAMESPACEALIASER_H diff --git a/clang-tidy/utils/UsingInserter.cpp b/clang-tidy/utils/UsingInserter.cpp new file mode 100644 index 00000000..c0746afc --- /dev/null +++ b/clang-tidy/utils/UsingInserter.cpp @@ -0,0 +1,88 @@ +//===---------- UsingInserter.cpp - clang-tidy ----------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "UsingInserter.h" + +#include "ASTUtils.h" +#include "clang/ASTMatchers/ASTMatchFinder.h" +#include "clang/ASTMatchers/ASTMatchers.h" +#include "clang/Lex/Lexer.h" + +namespace clang { +namespace tidy { +namespace utils { + +using namespace ast_matchers; + +static StringRef getUnqualifiedName(StringRef QualifiedName) { + size_t LastSeparatorPos = QualifiedName.rfind("::"); + if (LastSeparatorPos == StringRef::npos) + return QualifiedName; + return QualifiedName.drop_front(LastSeparatorPos + 2); +} + +UsingInserter::UsingInserter(const SourceManager &SourceMgr) + : SourceMgr(SourceMgr) {} + +Optional<FixItHint> UsingInserter::createUsingDeclaration( + ASTContext &Context, const Stmt &Statement, StringRef QualifiedName) { + StringRef UnqualifiedName = getUnqualifiedName(QualifiedName); + const FunctionDecl *Function = getSurroundingFunction(Context, Statement); + if (!Function) + return None; + + if (AddedUsing.count(std::make_pair(Function, QualifiedName.str())) != 0) + return None; + + SourceLocation InsertLoc = Lexer::getLocForEndOfToken( + Function->getBody()->getLocStart(), 0, SourceMgr, Context.getLangOpts()); + + // Only use using declarations in the main file, not in includes. + if (SourceMgr.getFileID(InsertLoc) != SourceMgr.getMainFileID()) + return None; + + // FIXME: This declaration could be masked. Investigate if + // there is a way to avoid using Sema. + bool AlreadyHasUsingDecl = + !match(stmt(hasAncestor(decl(has(usingDecl(hasAnyUsingShadowDecl( + hasTargetDecl(hasName(QualifiedName.str())))))))), + Statement, Context) + .empty(); + if (AlreadyHasUsingDecl) { + AddedUsing.emplace(NameInFunction(Function, QualifiedName.str())); + return None; + } + // Find conflicting declarations and references. + auto ConflictingDecl = namedDecl(hasName(UnqualifiedName)); + bool HasConflictingDeclaration = + !match(findAll(ConflictingDecl), *Function, Context).empty(); + bool HasConflictingDeclRef = + !match(findAll(declRefExpr(to(ConflictingDecl))), *Function, Context) + .empty(); + if (HasConflictingDeclaration || HasConflictingDeclRef) + return None; + + std::string Declaration = (llvm::Twine("\nusing ") + QualifiedName + ";").str(); + + AddedUsing.emplace(std::make_pair(Function, QualifiedName.str())); + return FixItHint::CreateInsertion(InsertLoc, Declaration); +} + +StringRef UsingInserter::getShortName(ASTContext &Context, + const Stmt &Statement, + StringRef QualifiedName) { + const FunctionDecl *Function = getSurroundingFunction(Context, Statement); + if (AddedUsing.count(NameInFunction(Function, QualifiedName.str())) != 0) + return getUnqualifiedName(QualifiedName); + return QualifiedName; +} + +} // namespace utils +} // namespace tidy +} // namespace clang diff --git a/clang-tidy/utils/UsingInserter.h b/clang-tidy/utils/UsingInserter.h new file mode 100644 index 00000000..b1fb7db9 --- /dev/null +++ b/clang-tidy/utils/UsingInserter.h @@ -0,0 +1,50 @@ +//===---------- UsingInserter.h - clang-tidy ----------------------------===// +// +// 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_TIDY_USINGINSERTER_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_USINGINSERTER_H + +#include "clang/AST/Decl.h" +#include "clang/AST/Stmt.h" +#include "clang/Basic/Diagnostic.h" +#include "clang/Basic/SourceManager.h" +#include <set> + +namespace clang { +namespace tidy { +namespace utils { + +// UsingInserter adds using declarations for |QualifiedName| to the surrounding +// function. +// This allows using a shorter name without clobbering other scopes. +class UsingInserter { +public: + UsingInserter(const SourceManager &SourceMgr); + + // Creates a \p using declaration fixit. Returns ``llvm::None`` on error + // or if the using declaration already exists. + llvm::Optional<FixItHint> + createUsingDeclaration(ASTContext &Context, const Stmt &Statement, + llvm::StringRef QualifiedName); + + // Returns the unqualified version of the name if there is an + // appropriate using declaration and the qualified name otherwise. + llvm::StringRef getShortName(ASTContext &Context, const Stmt &Statement, + llvm::StringRef QualifiedName); + +private: + typedef NameInFunction std::pair<const FunctionDecl *, std::string>; + const SourceManager &SourceMgr; + std::set<NameInFunction> AddedUsing; +}; + +} // namespace utils +} // namespace tidy +} // namespace clang +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_USINGINSERTER_H |