1 //===--- Types.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 "clang-include-cleaner/Types.h"
10 #include "TypesInternal.h"
11 #include "clang/AST/Decl.h"
12 #include "clang/Basic/FileEntry.h"
13 #include "llvm/ADT/ArrayRef.h"
14 #include "llvm/ADT/STLExtras.h"
15 #include "llvm/ADT/SmallString.h"
16 #include "llvm/ADT/SmallVector.h"
17 #include "llvm/ADT/StringExtras.h"
18 #include "llvm/ADT/StringRef.h"
19 #include "llvm/Support/Path.h"
20 #include "llvm/Support/raw_ostream.h"
23 namespace clang::include_cleaner
{
25 std::string
Symbol::name() const {
27 case include_cleaner::Symbol::Macro
:
28 return macro().Name
->getName().str();
29 case include_cleaner::Symbol::Declaration
:
30 return llvm::dyn_cast
<NamedDecl
>(&declaration())
31 ->getQualifiedNameAsString();
33 llvm_unreachable("Unknown symbol kind");
36 llvm::raw_ostream
&operator<<(llvm::raw_ostream
&OS
, const Symbol
&S
) {
38 case Symbol::Declaration
:
39 if (const auto *ND
= llvm::dyn_cast
<NamedDecl
>(&S
.declaration()))
40 return OS
<< ND
->getQualifiedNameAsString();
41 return OS
<< S
.declaration().getDeclKindName();
43 return OS
<< S
.macro().Name
->getName();
45 llvm_unreachable("Unhandled Symbol kind");
48 llvm::StringRef
Header::resolvedPath() const {
50 case include_cleaner::Header::Physical
:
51 return physical().getFileEntry().tryGetRealPathName();
52 case include_cleaner::Header::Standard
:
53 return standard().name().trim("<>\"");
54 case include_cleaner::Header::Verbatim
:
55 return verbatim().trim("<>\"");
57 llvm_unreachable("Unknown header kind");
60 llvm::raw_ostream
&operator<<(llvm::raw_ostream
&OS
, const Header
&H
) {
62 case Header::Physical
:
63 return OS
<< H
.physical().getName();
64 case Header::Standard
:
65 return OS
<< H
.standard().name();
66 case Header::Verbatim
:
67 return OS
<< H
.verbatim();
69 llvm_unreachable("Unhandled Header kind");
72 llvm::raw_ostream
&operator<<(llvm::raw_ostream
&OS
, const Include
&I
) {
73 return OS
<< I
.Line
<< ": " << I
.quote() << " => "
74 << (I
.Resolved
? I
.Resolved
->getName() : "<missing>");
77 llvm::raw_ostream
&operator<<(llvm::raw_ostream
&OS
, const SymbolReference
&R
) {
78 // We can't decode the Location without SourceManager. Its raw representation
79 // isn't completely useless (and distinguishes SymbolReference from Symbol).
80 return OS
<< R
.RT
<< " reference to " << R
.Target
<< "@0x"
82 R
.RefLocation
.getRawEncoding(), /*LowerCase=*/false,
83 /*Width=*/CHAR_BIT
* sizeof(SourceLocation::UIntTy
));
86 llvm::raw_ostream
&operator<<(llvm::raw_ostream
&OS
, RefType T
) {
88 case RefType::Explicit
:
89 return OS
<< "explicit";
90 case RefType::Implicit
:
91 return OS
<< "implicit";
92 case RefType::Ambiguous
:
93 return OS
<< "ambiguous";
95 llvm_unreachable("Unexpected RefType");
98 std::string
Include::quote() const {
99 return (llvm::StringRef(Angled
? "<" : "\"") + Spelled
+
100 (Angled
? ">" : "\""))
104 static llvm::SmallString
<128> normalizePath(llvm::StringRef Path
) {
105 namespace path
= llvm::sys::path
;
107 llvm::SmallString
<128> P
= Path
;
108 path::remove_dots(P
, /*remove_dot_dot=*/true);
109 path::native(P
, path::Style::posix
);
110 while (!P
.empty() && P
.back() == '/')
115 void Includes::addSearchDirectory(llvm::StringRef Path
) {
116 SearchPath
.try_emplace(normalizePath(Path
));
119 void Includes::add(const Include
&I
) {
120 namespace path
= llvm::sys::path
;
122 unsigned Index
= All
.size();
124 auto BySpellingIt
= BySpelling
.try_emplace(I
.Spelled
).first
;
125 All
.back().Spelled
= BySpellingIt
->first(); // Now we own the backing string.
127 BySpellingIt
->second
.push_back(Index
);
128 ByLine
[I
.Line
] = Index
;
132 ByFile
[&I
.Resolved
->getFileEntry()].push_back(Index
);
134 // While verbatim headers ideally should match #include spelling exactly,
135 // we want to be tolerant of different spellings of the same file.
137 // If the search path includes "/a/b" and "/a/b/c/d",
138 // verbatim "e/f" should match (spelled=c/d/e/f, resolved=/a/b/c/d/e/f).
139 // We assume entry's (normalized) name will match the search dirs.
140 auto Path
= normalizePath(I
.Resolved
->getName());
141 for (llvm::StringRef Parent
= path::parent_path(Path
); !Parent
.empty();
142 Parent
= path::parent_path(Parent
)) {
143 if (!SearchPath
.contains(Parent
))
145 llvm::StringRef Rel
=
146 llvm::StringRef(Path
).drop_front(Parent
.size()).ltrim('/');
147 BySpellingAlternate
[Rel
].push_back(Index
);
151 const Include
*Includes::atLine(unsigned OneBasedIndex
) const {
152 auto It
= ByLine
.find(OneBasedIndex
);
153 return (It
== ByLine
.end()) ? nullptr : &All
[It
->second
];
156 llvm::SmallVector
<const Include
*> Includes::match(Header H
) const {
157 llvm::SmallVector
<const Include
*> Result
;
159 case Header::Physical
:
160 for (unsigned I
: ByFile
.lookup(H
.physical()))
161 Result
.push_back(&All
[I
]);
163 case Header::Standard
:
164 for (unsigned I
: BySpelling
.lookup(H
.standard().name().trim("<>")))
165 Result
.push_back(&All
[I
]);
167 case Header::Verbatim
: {
168 llvm::StringRef Spelling
= H
.verbatim().trim("\"<>");
169 for (unsigned I
: BySpelling
.lookup(Spelling
))
170 Result
.push_back(&All
[I
]);
171 for (unsigned I
: BySpellingAlternate
.lookup(Spelling
))
172 if (!llvm::is_contained(Result
, &All
[I
]))
173 Result
.push_back(&All
[I
]);
180 llvm::raw_ostream
&operator<<(llvm::raw_ostream
&OS
, const SymbolLocation
&S
) {
182 case SymbolLocation::Physical
:
183 // We can't decode the Location without SourceManager. Its raw
184 // representation isn't completely useless (and distinguishes
185 // SymbolReference from Symbol).
188 S
.physical().getRawEncoding(), /*LowerCase=*/false,
189 /*Width=*/CHAR_BIT
* sizeof(SourceLocation::UIntTy
));
190 case SymbolLocation::Standard
:
191 return OS
<< S
.standard().scope() << S
.standard().name();
193 llvm_unreachable("Unhandled Symbol kind");
196 bool Header::operator<(const Header
&RHS
) const {
197 if (kind() != RHS
.kind())
198 return kind() < RHS
.kind();
200 case Header::Physical
:
201 return physical().getName() < RHS
.physical().getName();
202 case Header::Standard
:
203 return standard().name() < RHS
.standard().name();
204 case Header::Verbatim
:
205 return verbatim() < RHS
.verbatim();
207 llvm_unreachable("unhandled Header kind");
209 } // namespace clang::include_cleaner