llvm-project/clang/unittests/Tooling/RecursiveASTVisitorTests/TraversalScope.cpp

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

51 lines
1.3 KiB
C++
Raw Normal View History

[AST] Allow limiting the scope of common AST traversals (getParents, RAV). Summary: The goal is to allow analyses such as clang-tidy checks to run on a subset of the AST, e.g. "only on main-file decls" for interactive tools. Today, these become "problematically global" by running RecursiveASTVisitors rooted at the TUDecl, or by navigating up via ASTContext::getParent(). The scope is restricted using a set of top-level-decls that RecursiveASTVisitors should be rooted at. This also applies to the visitor that populates the parent map, and so the top-level-decls are considered to have no parents. This patch makes the traversal scope a mutable property of ASTContext. The more obvious way to do this is to pass the top-level decls to relevant functions directly, but this has some problems: - it's error-prone: accidentally mixing restricted and unrestricted scopes is a performance trap. Interleaving multiple analyses is common (many clang-tidy checks run matchers or RAVs from matcher callbacks) - it doesn't map well to the actual use cases, where we really do want *all* traversals to be restricted. - it involves a lot of plumbing in parts of the code that don't care about traversals. This approach was tried out in D54259 and D54261, I wanted to like it but it feels pretty awful in practice. Caveats: to get scope-limiting behavior of RecursiveASTVisitors, callers have to call the new TraverseAST(Ctx) function instead of TraverseDecl(TU). I think this is an improvement to the API regardless. Reviewers: klimek, ioeric Subscribers: mgorny, cfe-commits Differential Revision: https://reviews.llvm.org/D54309 llvm-svn: 346847
2018-11-14 18:33:30 +08:00
//===- unittest/Tooling/RecursiveASTVisitorTests/TraversalScope.cpp -------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
[AST] Allow limiting the scope of common AST traversals (getParents, RAV). Summary: The goal is to allow analyses such as clang-tidy checks to run on a subset of the AST, e.g. "only on main-file decls" for interactive tools. Today, these become "problematically global" by running RecursiveASTVisitors rooted at the TUDecl, or by navigating up via ASTContext::getParent(). The scope is restricted using a set of top-level-decls that RecursiveASTVisitors should be rooted at. This also applies to the visitor that populates the parent map, and so the top-level-decls are considered to have no parents. This patch makes the traversal scope a mutable property of ASTContext. The more obvious way to do this is to pass the top-level decls to relevant functions directly, but this has some problems: - it's error-prone: accidentally mixing restricted and unrestricted scopes is a performance trap. Interleaving multiple analyses is common (many clang-tidy checks run matchers or RAVs from matcher callbacks) - it doesn't map well to the actual use cases, where we really do want *all* traversals to be restricted. - it involves a lot of plumbing in parts of the code that don't care about traversals. This approach was tried out in D54259 and D54261, I wanted to like it but it feels pretty awful in practice. Caveats: to get scope-limiting behavior of RecursiveASTVisitors, callers have to call the new TraverseAST(Ctx) function instead of TraverseDecl(TU). I think this is an improvement to the API regardless. Reviewers: klimek, ioeric Subscribers: mgorny, cfe-commits Differential Revision: https://reviews.llvm.org/D54309 llvm-svn: 346847
2018-11-14 18:33:30 +08:00
//
//===----------------------------------------------------------------------===//
#include "TestVisitor.h"
using namespace clang;
namespace {
class Visitor : public ExpectedLocationVisitor<Visitor, clang::TestVisitor> {
public:
Visitor(ASTContext *Context) { this->Context = Context; }
bool VisitNamedDecl(NamedDecl *D) {
if (!D->isImplicit())
Match(D->getName(), D->getLocation());
return true;
}
};
TEST(RecursiveASTVisitor, RespectsTraversalScope) {
auto AST = tooling::buildASTFromCode(
R"cpp(
struct foo {
struct bar {
struct baz {};
};
};
)cpp",
"foo.cpp", std::make_shared<PCHContainerOperations>());
auto &Ctx = AST->getASTContext();
auto &TU = *Ctx.getTranslationUnitDecl();
auto &Foo = *TU.lookup(&Ctx.Idents.get("foo")).front();
auto &Bar = *cast<DeclContext>(Foo).lookup(&Ctx.Idents.get("bar")).front();
Ctx.setTraversalScope({&Bar});
Visitor V(&Ctx);
V.DisallowMatch("foo", 2, 8);
V.ExpectMatch("bar", 3, 10);
V.ExpectMatch("baz", 4, 12);
V.TraverseAST(Ctx);
}
} // end anonymous namespace