Skip to content

Commit 4b03364

Browse files
committed
[AST] Traverse attributes inside DEF_TRAVERSE_DECL macro
Summary: Instead of traversing inside the TraverseDecl() function. Previously the attributes were traversed after Travese(Some)Decl returns. Logically attributes are properties of particular Decls and should be traversed alongside other "child" nodes. None of the tests relied on this behavior, hopefully this is an indication that the change is relatively safe. This change started with a discussion on cfe-dev, for details see: https://lists.llvm.org/pipermail/cfe-dev/2019-July/062899.html Reviewers: rsmith, gribozavr Reviewed By: gribozavr Subscribers: mgorny, cfe-commits Tags: #clang Differential Revision: https://reviews.llvm.org/D64907 llvm-svn: 368052
1 parent 8442252 commit 4b03364

File tree

3 files changed

+112
-6
lines changed

3 files changed

+112
-6
lines changed

clang/include/clang/AST/RecursiveASTVisitor.h

+5-6
Original file line numberDiff line numberDiff line change
@@ -722,12 +722,6 @@ bool RecursiveASTVisitor<Derived>::TraverseDecl(Decl *D) {
722722
break;
723723
#include "clang/AST/DeclNodes.inc"
724724
}
725-
726-
// Visit any attributes attached to this declaration.
727-
for (auto *I : D->attrs()) {
728-
if (!getDerived().TraverseAttr(I))
729-
return false;
730-
}
731725
return true;
732726
}
733727

@@ -1407,6 +1401,11 @@ bool RecursiveASTVisitor<Derived>::TraverseDeclContextHelper(DeclContext *DC) {
14071401
{ CODE; } \
14081402
if (ReturnValue && ShouldVisitChildren) \
14091403
TRY_TO(TraverseDeclContextHelper(dyn_cast<DeclContext>(D))); \
1404+
if (ReturnValue) { \
1405+
/* Visit any attributes attached to this declaration. */ \
1406+
for (auto *I : D->attrs()) \
1407+
TRY_TO(getDerived().TraverseAttr(I)); \
1408+
} \
14101409
if (ReturnValue && getDerived().shouldTraversePostOrder()) \
14111410
TRY_TO(WalkUpFrom##DECL(D)); \
14121411
return ReturnValue; \

clang/unittests/AST/CMakeLists.txt

+1
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ add_clang_unittest(ASTTests
2626
Language.cpp
2727
NamedDeclPrinterTest.cpp
2828
OMPStructuredBlockTest.cpp
29+
RecursiveASTVisitorTest.cpp
2930
SourceLocationTest.cpp
3031
StmtPrinterTest.cpp
3132
StructuralEquivalenceTest.cpp
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
//===- unittest/AST/RecursiveASTVisitorTest.cpp ---------------------------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#include "clang/AST/RecursiveASTVisitor.h"
10+
#include "clang/AST/ASTConsumer.h"
11+
#include "clang/AST/ASTContext.h"
12+
#include "clang/AST/Attr.h"
13+
#include "clang/Frontend/FrontendAction.h"
14+
#include "clang/Tooling/Tooling.h"
15+
#include "llvm/ADT/FunctionExtras.h"
16+
#include "llvm/ADT/STLExtras.h"
17+
#include "gmock/gmock-generated-matchers.h"
18+
#include "gmock/gmock.h"
19+
#include "gtest/gtest.h"
20+
#include <cassert>
21+
22+
using namespace clang;
23+
using ::testing::ElementsAre;
24+
25+
namespace {
26+
class ProcessASTAction : public clang::ASTFrontendAction {
27+
public:
28+
ProcessASTAction(llvm::unique_function<void(clang::ASTContext &)> Process)
29+
: Process(std::move(Process)) {
30+
assert(this->Process);
31+
}
32+
33+
std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &CI,
34+
StringRef InFile) {
35+
class Consumer : public ASTConsumer {
36+
public:
37+
Consumer(llvm::function_ref<void(ASTContext &CTx)> Process)
38+
: Process(Process) {}
39+
40+
void HandleTranslationUnit(ASTContext &Ctx) override { Process(Ctx); }
41+
42+
private:
43+
llvm::function_ref<void(ASTContext &CTx)> Process;
44+
};
45+
46+
return llvm::make_unique<Consumer>(Process);
47+
}
48+
49+
private:
50+
llvm::unique_function<void(clang::ASTContext &)> Process;
51+
};
52+
53+
enum class VisitEvent {
54+
StartTraverseFunction,
55+
EndTraverseFunction,
56+
StartTraverseAttr,
57+
EndTraverseAttr
58+
};
59+
60+
class CollectInterestingEvents
61+
: public RecursiveASTVisitor<CollectInterestingEvents> {
62+
public:
63+
bool TraverseFunctionDecl(FunctionDecl *D) {
64+
Events.push_back(VisitEvent::StartTraverseFunction);
65+
bool Ret = RecursiveASTVisitor::TraverseFunctionDecl(D);
66+
Events.push_back(VisitEvent::EndTraverseFunction);
67+
68+
return Ret;
69+
}
70+
71+
bool TraverseAttr(Attr *A) {
72+
Events.push_back(VisitEvent::StartTraverseAttr);
73+
bool Ret = RecursiveASTVisitor::TraverseAttr(A);
74+
Events.push_back(VisitEvent::EndTraverseAttr);
75+
76+
return Ret;
77+
}
78+
79+
std::vector<VisitEvent> takeEvents() && { return std::move(Events); }
80+
81+
private:
82+
std::vector<VisitEvent> Events;
83+
};
84+
85+
std::vector<VisitEvent> collectEvents(llvm::StringRef Code) {
86+
CollectInterestingEvents Visitor;
87+
clang::tooling::runToolOnCode(
88+
new ProcessASTAction(
89+
[&](clang::ASTContext &Ctx) { Visitor.TraverseAST(Ctx); }),
90+
Code);
91+
return std::move(Visitor).takeEvents();
92+
}
93+
} // namespace
94+
95+
TEST(RecursiveASTVisitorTest, AttributesInsideDecls) {
96+
/// Check attributes are traversed inside TraverseFunctionDecl.
97+
llvm::StringRef Code = R"cpp(
98+
__attribute__((annotate("something"))) int foo() { return 10; }
99+
)cpp";
100+
101+
EXPECT_THAT(collectEvents(Code),
102+
ElementsAre(VisitEvent::StartTraverseFunction,
103+
VisitEvent::StartTraverseAttr,
104+
VisitEvent::EndTraverseAttr,
105+
VisitEvent::EndTraverseFunction));
106+
}

0 commit comments

Comments
 (0)