1 //===--- FindHeaders.cpp --------------------------------------------------===//
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
7 //===----------------------------------------------------------------------===//
9 #include "AnalysisInternal.h"
10 #include "TypesInternal.h"
11 #include "clang-include-cleaner/Record.h"
12 #include "clang-include-cleaner/Types.h"
13 #include "clang/AST/ASTContext.h"
14 #include "clang/AST/Decl.h"
15 #include "clang/AST/DeclBase.h"
16 #include "clang/AST/DeclCXX.h"
17 #include "clang/Basic/Builtins.h"
18 #include "clang/Basic/FileEntry.h"
19 #include "clang/Basic/SourceLocation.h"
20 #include "clang/Basic/SourceManager.h"
21 #include "clang/Tooling/Inclusions/StandardLibrary.h"
22 #include "llvm/ADT/ArrayRef.h"
23 #include "llvm/ADT/STLExtras.h"
24 #include "llvm/ADT/SmallVector.h"
25 #include "llvm/ADT/StringRef.h"
26 #include "llvm/Support/Casting.h"
27 #include "llvm/Support/ErrorHandling.h"
33 namespace clang::include_cleaner
{
35 llvm::SmallVector
<Hinted
<Header
>>
36 applyHints(llvm::SmallVector
<Hinted
<Header
>> Headers
, Hints H
) {
37 for (auto &Header
: Headers
)
42 llvm::SmallVector
<Header
> ranked(llvm::SmallVector
<Hinted
<Header
>> Headers
) {
43 llvm::stable_sort(llvm::reverse(Headers
),
44 [](const Hinted
<Header
> &LHS
, const Hinted
<Header
> &RHS
) {
47 return llvm::SmallVector
<Header
>(Headers
.begin(), Headers
.end());
50 // Return the basename from a verbatim header spelling, leaves only the file
52 llvm::StringRef
basename(llvm::StringRef Header
) {
53 Header
= Header
.trim("<>\"");
54 Header
= llvm::sys::path::filename(Header
);
55 // Drop everything after first `.` (dot).
58 Header
= Header
.substr(0, Header
.find('.'));
62 // Check if spelling of \p H matches \p DeclName.
63 bool nameMatch(llvm::StringRef DeclName
, Header H
) {
65 case Header::Physical
:
66 return basename(H
.physical().getName()).equals_insensitive(DeclName
);
67 case Header::Standard
:
68 return basename(H
.standard().name()).equals_insensitive(DeclName
);
69 case Header::Verbatim
:
70 return basename(H
.verbatim()).equals_insensitive(DeclName
);
72 llvm_unreachable("unhandled Header kind!");
75 llvm::StringRef
symbolName(const Symbol
&S
) {
77 case Symbol::Declaration
:
78 // Unnamed decls like operators and anonymous structs won't get any name
80 if (const auto *ND
= llvm::dyn_cast
<NamedDecl
>(&S
.declaration()))
81 if (auto *II
= ND
->getIdentifier())
85 return S
.macro().Name
->getName();
87 llvm_unreachable("unhandled Symbol kind!");
90 Hints
isPublicHeader(const FileEntry
*FE
, const PragmaIncludes
&PI
) {
91 if (PI
.isPrivate(FE
) || !PI
.isSelfContained(FE
))
93 return Hints::PublicHeader
;
96 llvm::SmallVector
<Hinted
<Header
>>
97 hintedHeadersForStdHeaders(llvm::ArrayRef
<tooling::stdlib::Header
> Headers
,
98 const SourceManager
&SM
, const PragmaIncludes
*PI
) {
99 llvm::SmallVector
<Hinted
<Header
>> Results
;
100 for (const auto &H
: Headers
) {
101 Results
.emplace_back(H
, Hints::PublicHeader
| Hints::OriginHeader
);
104 for (FileEntryRef Export
: PI
->getExporters(H
, SM
.getFileManager()))
105 Results
.emplace_back(Header(Export
), isPublicHeader(Export
, *PI
));
107 // StandardLibrary returns headers in preference order, so only mark the
109 if (!Results
.empty())
110 Results
.front().Hint
|= Hints::PreferredHeader
;
114 // Symbol to header mapping for std::move and std::remove, based on number of
116 std::optional
<tooling::stdlib::Header
>
117 headerForAmbiguousStdSymbol(const NamedDecl
*ND
) {
118 if (!ND
->isInStdNamespace())
120 if (auto* USD
= llvm::dyn_cast
<UsingShadowDecl
>(ND
))
121 ND
= USD
->getTargetDecl();
122 const auto *FD
= ND
->getAsFunction();
125 llvm::StringRef FName
= symbolName(*ND
);
126 if (FName
== "move") {
127 if (FD
->getNumParams() == 1)
129 return tooling::stdlib::Header::named("<utility>");
130 if (FD
->getNumParams() == 3 || FD
->getNumParams() == 4)
131 // move(InputIt first, InputIt last, OutputIt dest);
132 // move(ExecutionPolicy&& policy, ForwardIt1 first,
133 // ForwardIt1 last, ForwardIt2 d_first);
134 return tooling::stdlib::Header::named("<algorithm>");
135 } else if (FName
== "remove") {
136 if (FD
->getNumParams() == 1)
137 // remove(const char*);
138 return tooling::stdlib::Header::named("<cstdio>");
139 if (FD
->getNumParams() == 3)
140 // remove(ForwardIt first, ForwardIt last, const T& value);
141 return tooling::stdlib::Header::named("<algorithm>");
146 // Special-case symbols without proper locations, like the ambiguous standard
147 // library symbols (e.g. std::move) or builtin declarations.
148 std::optional
<llvm::SmallVector
<Hinted
<Header
>>>
149 headersForSpecialSymbol(const Symbol
&S
, const SourceManager
&SM
,
150 const PragmaIncludes
*PI
) {
151 // Our special casing logic only deals with decls, so bail out early for
153 if (S
.kind() != Symbol::Declaration
)
155 const auto *ND
= llvm::cast
<NamedDecl
>(&S
.declaration());
156 // We map based on names, so again bail out early if there are no names.
159 auto *II
= ND
->getIdentifier();
163 // Check first for symbols that are part of our stdlib mapping. As we have
164 // header names for those.
165 if (auto Header
= headerForAmbiguousStdSymbol(ND
)) {
166 return applyHints(hintedHeadersForStdHeaders({*Header
}, SM
, PI
),
167 Hints::CompleteSymbol
);
170 // Now check for builtin symbols, we shouldn't suggest any headers for ones
171 // without any headers.
172 if (auto ID
= II
->getBuiltinID()) {
173 const char *BuiltinHeader
=
174 ND
->getASTContext().BuiltinInfo
.getHeaderName(ID
);
176 return llvm::SmallVector
<Hinted
<Header
>>{};
177 // FIXME: Use the header mapping for builtins with a known header.
184 llvm::SmallVector
<Hinted
<Header
>> findHeaders(const SymbolLocation
&Loc
,
185 const SourceManager
&SM
,
186 const PragmaIncludes
*PI
) {
187 llvm::SmallVector
<Hinted
<Header
>> Results
;
188 switch (Loc
.kind()) {
189 case SymbolLocation::Physical
: {
190 FileID FID
= SM
.getFileID(SM
.getExpansionLoc(Loc
.physical()));
191 OptionalFileEntryRef FE
= SM
.getFileEntryRefForID(FID
);
195 return {{*FE
, Hints::PublicHeader
| Hints::OriginHeader
}};
196 bool IsOrigin
= true;
197 std::queue
<FileEntryRef
> Exporters
;
199 Results
.emplace_back(*FE
,
200 isPublicHeader(*FE
, *PI
) |
201 (IsOrigin
? Hints::OriginHeader
: Hints::None
));
202 for (FileEntryRef Export
: PI
->getExporters(*FE
, SM
.getFileManager()))
203 Exporters
.push(Export
);
205 if (auto Verbatim
= PI
->getPublic(*FE
); !Verbatim
.empty()) {
206 Results
.emplace_back(Verbatim
,
207 Hints::PublicHeader
| Hints::PreferredHeader
);
210 if (PI
->isSelfContained(*FE
) || FID
== SM
.getMainFileID())
213 // Walkup the include stack for non self-contained headers.
214 FID
= SM
.getDecomposedIncludedLoc(FID
).first
;
215 FE
= SM
.getFileEntryRefForID(FID
);
218 // Now traverse provider trees rooted at exporters.
219 // Note that we only traverse export edges, and ignore private -> public
220 // mappings, as those pragmas apply to exporter, and not the main provider
221 // being exported in this header.
222 std::set
<const FileEntry
*> SeenExports
;
223 while (!Exporters
.empty()) {
224 FileEntryRef Export
= Exporters
.front();
226 if (!SeenExports
.insert(Export
).second
) // In case of cyclic exports
228 Results
.emplace_back(Export
, isPublicHeader(Export
, *PI
));
229 for (FileEntryRef Export
: PI
->getExporters(Export
, SM
.getFileManager()))
230 Exporters
.push(Export
);
234 case SymbolLocation::Standard
: {
235 return hintedHeadersForStdHeaders(Loc
.standard().headers(), SM
, PI
);
238 llvm_unreachable("unhandled SymbolLocation kind!");
241 llvm::SmallVector
<Header
> headersForSymbol(const Symbol
&S
,
242 const SourceManager
&SM
,
243 const PragmaIncludes
*PI
) {
244 // Get headers for all the locations providing Symbol. Same header can be
245 // reached through different traversals, deduplicate those into a single
246 // Header by merging their hints.
247 llvm::SmallVector
<Hinted
<Header
>> Headers
;
248 if (auto SpecialHeaders
= headersForSpecialSymbol(S
, SM
, PI
)) {
249 Headers
= std::move(*SpecialHeaders
);
251 for (auto &Loc
: locateSymbol(S
))
252 Headers
.append(applyHints(findHeaders(Loc
, SM
, PI
), Loc
.Hint
));
254 // If two Headers probably refer to the same file (e.g. Verbatim(foo.h) and
255 // Physical(/path/to/foo.h), we won't deduplicate them or merge their hints
257 Headers
, [](const Hinted
<Header
> &LHS
, const Hinted
<Header
> &RHS
) {
258 return static_cast<Header
>(LHS
) < static_cast<Header
>(RHS
);
260 auto *Write
= Headers
.begin();
261 for (auto *Read
= Headers
.begin(); Read
!= Headers
.end(); ++Write
) {
263 while (Read
!= Headers
.end() &&
264 static_cast<Header
>(*Write
) == static_cast<Header
>(*Read
)) {
265 Write
->Hint
|= Read
->Hint
;
269 Headers
.erase(Write
, Headers
.end());
271 // Add name match hints to deduplicated providers.
272 llvm::StringRef SymbolName
= symbolName(S
);
273 for (auto &H
: Headers
) {
274 // Don't apply name match hints to standard headers as the standard headers
275 // are already ranked in the stdlib mapping.
276 if (H
.kind() == Header::Standard
)
278 // Don't apply name match hints to exporting headers. As they usually have
279 // names similar to the original header, e.g. foo_wrapper/foo.h vs
280 // foo/foo.h, but shouldn't be preferred (unless marked as the public
282 if ((H
.Hint
& Hints::OriginHeader
) == Hints::None
)
284 if (nameMatch(SymbolName
, H
))
285 H
.Hint
|= Hints::PreferredHeader
;
288 // FIXME: Introduce a MainFile header kind or signal and boost it.
289 return ranked(std::move(Headers
));
291 } // namespace clang::include_cleaner