Skip to content

Commit d8081d1

Browse files
ilya-biryukovkrishna2803
authored andcommitted
[Clang] Dump minimization hints for namespaces (llvm#151534)
Unlike other declarations, these cover two ranges: - from `namespace/inline namespace` to the opening `{`, - the closing `}`. This allows to mark the declarations inside the namespace itself independently.
1 parent 5656b46 commit d8081d1

File tree

2 files changed

+118
-17
lines changed

2 files changed

+118
-17
lines changed

clang/lib/Frontend/FrontendAction.cpp

Lines changed: 71 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
#include "clang/Frontend/FrontendAction.h"
1010
#include "clang/AST/ASTConsumer.h"
1111
#include "clang/AST/ASTContext.h"
12+
#include "clang/AST/Decl.h"
1213
#include "clang/AST/DeclGroup.h"
1314
#include "clang/Basic/Builtins.h"
1415
#include "clang/Basic/DiagnosticOptions.h"
@@ -39,6 +40,7 @@
3940
#include "clang/Serialization/ASTReader.h"
4041
#include "clang/Serialization/GlobalModuleIndex.h"
4142
#include "llvm/ADT/ScopeExit.h"
43+
#include "llvm/ADT/SmallPtrSet.h"
4244
#include "llvm/ADT/StringRef.h"
4345
#include "llvm/Support/BuryPointer.h"
4446
#include "llvm/Support/ErrorHandling.h"
@@ -87,12 +89,25 @@ class DeserializedDeclsSourceRangePrinter : public ASTConsumer,
8789
// reducing the granularity and making the output less useful.
8890
return;
8991
}
90-
if (auto *DC = D->getDeclContext(); !DC || !DC->isFileContext()) {
92+
auto *DC = D->getLexicalDeclContext();
93+
if (!DC || !DC->isFileContext()) {
9194
// We choose to work at namespace level to reduce complexity and the
9295
// number of cases we care about.
9396
return;
9497
}
98+
9599
PendingDecls.push_back(D);
100+
if (auto *NS = dyn_cast<NamespaceDecl>(DC)) {
101+
// Add any namespaces we have not seen before.
102+
// Note that we filter out namespaces from DeclRead as it includes too
103+
// all redeclarations and we only want the ones that had other used
104+
// declarations.
105+
while (NS && ProcessedNamespaces.insert(NS).second) {
106+
PendingDecls.push_back(NS);
107+
108+
NS = dyn_cast<NamespaceDecl>(NS->getLexicalParent());
109+
}
110+
}
96111
}
97112

98113
struct Position {
@@ -141,23 +156,25 @@ class DeserializedDeclsSourceRangePrinter : public ASTConsumer,
141156
OptionalFileEntryRef Ref;
142157
};
143158
llvm::DenseMap<const FileEntry *, FileData> FileToRanges;
159+
144160
for (const Decl *D : PendingDecls) {
145-
CharSourceRange R = SM.getExpansionRange(D->getSourceRange());
146-
if (!R.isValid())
147-
continue;
161+
for (CharSourceRange R : getRangesToMark(D)) {
162+
if (!R.isValid())
163+
continue;
148164

149-
auto *F = SM.getFileEntryForID(SM.getFileID(R.getBegin()));
150-
if (F != SM.getFileEntryForID(SM.getFileID(R.getEnd()))) {
151-
// Such cases are rare and difficult to handle.
152-
continue;
153-
}
165+
auto *F = SM.getFileEntryForID(SM.getFileID(R.getBegin()));
166+
if (F != SM.getFileEntryForID(SM.getFileID(R.getEnd()))) {
167+
// Such cases are rare and difficult to handle.
168+
continue;
169+
}
154170

155-
auto &Data = FileToRanges[F];
156-
if (!Data.Ref)
157-
Data.Ref = SM.getFileEntryRefForID(SM.getFileID(R.getBegin()));
158-
Data.FromTo.push_back(
159-
{Position::GetBeginSpelling(SM, R),
160-
Position::GetEndSpelling(SM, R, D->getLangOpts())});
171+
auto &Data = FileToRanges[F];
172+
if (!Data.Ref)
173+
Data.Ref = SM.getFileEntryRefForID(SM.getFileID(R.getBegin()));
174+
Data.FromTo.push_back(
175+
{Position::GetBeginSpelling(SM, R),
176+
Position::GetEndSpelling(SM, R, D->getLangOpts())});
177+
}
161178
}
162179

163180
// To simplify output, merge consecutive and intersecting ranges.
@@ -188,10 +205,49 @@ class DeserializedDeclsSourceRangePrinter : public ASTConsumer,
188205

189206
private:
190207
std::vector<const Decl *> PendingDecls;
208+
llvm::SmallPtrSet<const NamespaceDecl *, 0> ProcessedNamespaces;
191209
bool IsCollectingDecls = true;
192210
const SourceManager &SM;
193211
std::unique_ptr<llvm::raw_ostream> OS;
194212

213+
llvm::SmallVector<CharSourceRange, 2> getRangesToMark(const Decl *D) {
214+
auto *NS = dyn_cast<NamespaceDecl>(D);
215+
if (!NS)
216+
return {SM.getExpansionRange(D->getSourceRange())};
217+
218+
SourceLocation LBraceLoc;
219+
if (NS->isAnonymousNamespace()) {
220+
LBraceLoc = NS->getLocation();
221+
} else {
222+
// Start with the location of the identifier.
223+
SourceLocation TokenBeforeLBrace = NS->getLocation();
224+
if (NS->hasAttrs()) {
225+
for (auto *A : NS->getAttrs()) {
226+
// But attributes may go after it.
227+
if (SM.isBeforeInTranslationUnit(TokenBeforeLBrace,
228+
A->getRange().getEnd())) {
229+
// Give up, the attributes are often coming from macros and we
230+
// cannot skip them reliably.
231+
return {};
232+
}
233+
}
234+
}
235+
auto &LangOpts = D->getLangOpts();
236+
// Now skip one token, the next should be the lbrace.
237+
Token Tok;
238+
if (Lexer::getRawToken(TokenBeforeLBrace, Tok, SM, LangOpts, true) ||
239+
Lexer::getRawToken(Tok.getEndLoc(), Tok, SM, LangOpts, true) ||
240+
Tok.getKind() != tok::l_brace) {
241+
// On error or if we did not find the token we expected, avoid marking
242+
// everything inside the namespace as used.
243+
return {};
244+
}
245+
LBraceLoc = Tok.getLocation();
246+
}
247+
return {SM.getExpansionRange(SourceRange(NS->getBeginLoc(), LBraceLoc)),
248+
SM.getExpansionRange(NS->getRBraceLoc())};
249+
}
250+
195251
void printJson(llvm::ArrayRef<RequiredRanges> Result) {
196252
*OS << "{\n";
197253
*OS << R"( "required_ranges": [)" << "\n";

clang/test/Frontend/dump-minimization-hints.cpp

Lines changed: 47 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,36 @@
5959
// RANGE-NEXT: "line": 23,
6060
// RANGE-NEXT: "column": 2
6161
// RANGE-NEXT: }
62+
// RANGE-NEXT: },
63+
// RANGE-NEXT: {
64+
// RANGE-NEXT: "from": {
65+
// RANGE-NEXT: "line": 31,
66+
// RANGE-NEXT: "column": 1
67+
// RANGE-NEXT: },
68+
// RANGE-NEXT: "to": {
69+
// RANGE-NEXT: "line": 31,
70+
// RANGE-NEXT: "column": 27
71+
// RANGE-NEXT: }
72+
// RANGE-NEXT: },
73+
// RANGE-NEXT: {
74+
// RANGE-NEXT: "from": {
75+
// RANGE-NEXT: "line": 32,
76+
// RANGE-NEXT: "column": 3
77+
// RANGE-NEXT: },
78+
// RANGE-NEXT: "to": {
79+
// RANGE-NEXT: "line": 32,
80+
// RANGE-NEXT: "column": 12
81+
// RANGE-NEXT: }
82+
// RANGE-NEXT: },
83+
// RANGE-NEXT: {
84+
// RANGE-NEXT: "from": {
85+
// RANGE-NEXT: "line": 34,
86+
// RANGE-NEXT: "column": 1
87+
// RANGE-NEXT: },
88+
// RANGE-NEXT: "to": {
89+
// RANGE-NEXT: "line": 34,
90+
// RANGE-NEXT: "column": 2
91+
// RANGE-NEXT: }
6292
// RANGE-NEXT: }
6393
// RANGE-NEXT: ]
6494
// RANGE-NEXT: }
@@ -88,7 +118,7 @@ int multiply(int a, int b) {
88118
return a * b;
89119
}
90120

91-
inline int unused_by_foo() {} // line 17
121+
inline void unused_by_foo() {} // line 17
92122

93123
inline void recursively_used_by_foo() {} // line 19
94124
inline int used_by_foo() { // line 20
@@ -98,6 +128,20 @@ inline int used_by_foo() { // line 20
98128

99129
struct UnusedByFoo {};
100130

131+
namespace ns_unused_by_foo {
132+
void x();
133+
}
134+
135+
namespace ns_used_by_foo { // line 31
136+
void x(); // line 32
137+
void unused_y();
138+
} // line 34
139+
140+
// Does not have any declarations that are used, so
141+
// will not be marked as used.
142+
namespace ns_used_by_foo {
143+
void unused_z();
144+
}
101145
//--- foo.cpp
102146
#include "foo.h"
103147
int global_value = 5;
@@ -107,5 +151,6 @@ int main() {
107151
int doubled_value = multiply(current_value, 2);
108152
int final_result = doubled_value + global_value;
109153

110-
return used_by_foo();
154+
used_by_foo();
155+
ns_used_by_foo::x();
111156
}

0 commit comments

Comments
 (0)