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/STLExtras.h"
14 #include "llvm/ADT/SmallString.h"
15 #include "llvm/ADT/SmallVector.h"
16 #include "llvm/ADT/StringExtras.h"
17 #include "llvm/ADT/StringRef.h"
18 #include "llvm/Support/Path.h"
19 #include "llvm/Support/raw_ostream.h"
22 namespace clang::include_cleaner
{
24 std::string
Symbol::name() const {
26 case include_cleaner::Symbol::Macro
:
27 return macro().Name
->getName().str();
28 case include_cleaner::Symbol::Declaration
:
29 return llvm::dyn_cast
<NamedDecl
>(&declaration())
30 ->getQualifiedNameAsString();
32 llvm_unreachable("Unknown symbol kind");
35 llvm::raw_ostream
&operator<<(llvm::raw_ostream
&OS
, const Symbol
&S
) {
37 case Symbol::Declaration
:
38 if (const auto *ND
= llvm::dyn_cast
<NamedDecl
>(&S
.declaration()))
39 return OS
<< ND
->getQualifiedNameAsString();
40 return OS
<< S
.declaration().getDeclKindName();
42 return OS
<< S
.macro().Name
->getName();
44 llvm_unreachable("Unhandled Symbol kind");
47 llvm::StringRef
Header::resolvedPath() const {
49 case include_cleaner::Header::Physical
:
50 return physical().getName();
51 case include_cleaner::Header::Standard
:
52 return standard().name().trim("<>\"");
53 case include_cleaner::Header::Verbatim
:
54 return verbatim().trim("<>\"");
56 llvm_unreachable("Unknown header kind");
59 llvm::raw_ostream
&operator<<(llvm::raw_ostream
&OS
, const Header
&H
) {
61 case Header::Physical
:
62 return OS
<< H
.physical().getName();
63 case Header::Standard
:
64 return OS
<< H
.standard().name();
65 case Header::Verbatim
:
66 return OS
<< H
.verbatim();
68 llvm_unreachable("Unhandled Header kind");
71 llvm::raw_ostream
&operator<<(llvm::raw_ostream
&OS
, const Include
&I
) {
72 return OS
<< I
.Line
<< ": " << I
.quote() << " => "
73 << (I
.Resolved
? I
.Resolved
->getName() : "<missing>");
76 llvm::raw_ostream
&operator<<(llvm::raw_ostream
&OS
, const SymbolReference
&R
) {
77 // We can't decode the Location without SourceManager. Its raw representation
78 // isn't completely useless (and distinguishes SymbolReference from Symbol).
79 return OS
<< R
.RT
<< " reference to " << R
.Target
<< "@0x"
81 R
.RefLocation
.getRawEncoding(), /*LowerCase=*/false,
82 /*Width=*/CHAR_BIT
* sizeof(SourceLocation::UIntTy
));
85 llvm::raw_ostream
&operator<<(llvm::raw_ostream
&OS
, RefType T
) {
87 case RefType::Explicit
:
88 return OS
<< "explicit";
89 case RefType::Implicit
:
90 return OS
<< "implicit";
91 case RefType::Ambiguous
:
92 return OS
<< "ambiguous";
94 llvm_unreachable("Unexpected RefType");
97 std::string
Include::quote() const {
98 return (llvm::StringRef(Angled
? "<" : "\"") + Spelled
+
99 (Angled
? ">" : "\""))
103 static llvm::SmallString
<128> normalizePath(llvm::StringRef Path
) {
104 namespace path
= llvm::sys::path
;
106 llvm::SmallString
<128> P
= Path
;
107 path::remove_dots(P
, /*remove_dot_dot=*/true);
108 path::native(P
, path::Style::posix
);
109 while (!P
.empty() && P
.back() == '/')
114 void Includes::addSearchDirectory(llvm::StringRef Path
) {
115 SearchPath
.try_emplace(normalizePath(Path
));
118 void Includes::add(const Include
&I
) {
119 namespace path
= llvm::sys::path
;
121 unsigned Index
= All
.size();
123 auto BySpellingIt
= BySpelling
.try_emplace(I
.Spelled
).first
;
124 All
.back().Spelled
= BySpellingIt
->first(); // Now we own the backing string.
126 BySpellingIt
->second
.push_back(Index
);
127 ByLine
[I
.Line
] = Index
;
131 ByFile
[&I
.Resolved
->getFileEntry()].push_back(Index
);
133 // While verbatim headers ideally should match #include spelling exactly,
134 // we want to be tolerant of different spellings of the same file.
136 // If the search path includes "/a/b" and "/a/b/c/d",
137 // verbatim "e/f" should match (spelled=c/d/e/f, resolved=/a/b/c/d/e/f).
138 // We assume entry's (normalized) name will match the search dirs.
139 auto Path
= normalizePath(I
.Resolved
->getName());
140 for (llvm::StringRef Parent
= path::parent_path(Path
); !Parent
.empty();
141 Parent
= path::parent_path(Parent
)) {
142 if (!SearchPath
.contains(Parent
))
144 llvm::StringRef Rel
=
145 llvm::StringRef(Path
).drop_front(Parent
.size()).ltrim('/');
146 BySpellingAlternate
[Rel
].push_back(Index
);
150 const Include
*Includes::atLine(unsigned OneBasedIndex
) const {
151 auto It
= ByLine
.find(OneBasedIndex
);
152 return (It
== ByLine
.end()) ? nullptr : &All
[It
->second
];
155 llvm::SmallVector
<const Include
*> Includes::match(Header H
) const {
156 llvm::SmallVector
<const Include
*> Result
;
158 case Header::Physical
:
159 for (unsigned I
: ByFile
.lookup(H
.physical()))
160 Result
.push_back(&All
[I
]);
162 case Header::Standard
:
163 for (unsigned I
: BySpelling
.lookup(H
.standard().name().trim("<>")))
164 Result
.push_back(&All
[I
]);
166 case Header::Verbatim
: {
167 llvm::StringRef Spelling
= H
.verbatim().trim("\"<>");
168 for (unsigned I
: BySpelling
.lookup(Spelling
))
169 Result
.push_back(&All
[I
]);
170 for (unsigned I
: BySpellingAlternate
.lookup(Spelling
))
171 if (!llvm::is_contained(Result
, &All
[I
]))
172 Result
.push_back(&All
[I
]);
179 llvm::raw_ostream
&operator<<(llvm::raw_ostream
&OS
, const SymbolLocation
&S
) {
181 case SymbolLocation::Physical
:
182 // We can't decode the Location without SourceManager. Its raw
183 // representation isn't completely useless (and distinguishes
184 // SymbolReference from Symbol).
187 S
.physical().getRawEncoding(), /*LowerCase=*/false,
188 /*Width=*/CHAR_BIT
* sizeof(SourceLocation::UIntTy
));
189 case SymbolLocation::Standard
:
190 return OS
<< S
.standard().scope() << S
.standard().name();
192 llvm_unreachable("Unhandled Symbol kind");
195 bool Header::operator<(const Header
&RHS
) const {
196 if (kind() != RHS
.kind())
197 return kind() < RHS
.kind();
199 case Header::Physical
:
200 return physical().getName() < RHS
.physical().getName();
201 case Header::Standard
:
202 return standard().name() < RHS
.standard().name();
203 case Header::Verbatim
:
204 return verbatim() < RHS
.verbatim();
206 llvm_unreachable("unhandled Header kind");
208 } // namespace clang::include_cleaner