1 | //===--- Types.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-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" |
20 | #include <vector> |
21 | |
22 | namespace clang::include_cleaner { |
23 | |
24 | std::string Symbol::name() const { |
25 | switch (kind()) { |
26 | case include_cleaner::Symbol::Macro: |
27 | return macro().Name->getName().str(); |
28 | case include_cleaner::Symbol::Declaration: |
29 | return llvm::dyn_cast<NamedDecl>(Val: &declaration()) |
30 | ->getQualifiedNameAsString(); |
31 | } |
32 | llvm_unreachable("Unknown symbol kind" ); |
33 | } |
34 | |
35 | llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const Symbol &S) { |
36 | switch (S.kind()) { |
37 | case Symbol::Declaration: |
38 | if (const auto *ND = llvm::dyn_cast<NamedDecl>(Val: &S.declaration())) |
39 | return OS << ND->getQualifiedNameAsString(); |
40 | return OS << S.declaration().getDeclKindName(); |
41 | case Symbol::Macro: |
42 | return OS << S.macro().Name->getName(); |
43 | } |
44 | llvm_unreachable("Unhandled Symbol kind" ); |
45 | } |
46 | |
47 | llvm::StringRef Header::() const { |
48 | switch (kind()) { |
49 | case include_cleaner::Header::Physical: |
50 | return physical().getName(); |
51 | case include_cleaner::Header::Standard: |
52 | return standard().name().trim(Chars: "<>\"" ); |
53 | case include_cleaner::Header::Verbatim: |
54 | return verbatim().trim(Chars: "<>\"" ); |
55 | } |
56 | llvm_unreachable("Unknown header kind" ); |
57 | } |
58 | |
59 | llvm::raw_ostream &(llvm::raw_ostream &OS, const Header &H) { |
60 | switch (H.kind()) { |
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(); |
67 | } |
68 | llvm_unreachable("Unhandled Header kind" ); |
69 | } |
70 | |
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>" ); |
74 | } |
75 | |
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" |
80 | << llvm::utohexstr( |
81 | X: R.RefLocation.getRawEncoding(), /*LowerCase=*/false, |
82 | /*Width=*/CHAR_BIT * sizeof(SourceLocation::UIntTy)); |
83 | } |
84 | |
85 | llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, RefType T) { |
86 | switch (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" ; |
93 | } |
94 | llvm_unreachable("Unexpected RefType" ); |
95 | } |
96 | |
97 | std::string Include::quote() const { |
98 | return (llvm::StringRef(Angled ? "<" : "\"" ) + Spelled + |
99 | (Angled ? ">" : "\"" )) |
100 | .str(); |
101 | } |
102 | |
103 | static llvm::SmallString<128> normalizePath(llvm::StringRef Path) { |
104 | namespace path = llvm::sys::path; |
105 | |
106 | llvm::SmallString<128> P = Path; |
107 | path::remove_dots(path&: P, /*remove_dot_dot=*/true); |
108 | path::native(path&: P, style: path::Style::posix); |
109 | while (!P.empty() && P.back() == '/') |
110 | P.pop_back(); |
111 | return P; |
112 | } |
113 | |
114 | void Includes::addSearchDirectory(llvm::StringRef Path) { |
115 | SearchPath.try_emplace(Key: normalizePath(Path)); |
116 | } |
117 | |
118 | void Includes::add(const Include &I) { |
119 | namespace path = llvm::sys::path; |
120 | |
121 | unsigned Index = All.size(); |
122 | All.push_back(x: I); |
123 | auto BySpellingIt = BySpelling.try_emplace(Key: I.Spelled).first; |
124 | All.back().Spelled = BySpellingIt->first(); // Now we own the backing string. |
125 | |
126 | BySpellingIt->second.push_back(Elt: Index); |
127 | ByLine[I.Line] = Index; |
128 | |
129 | if (!I.Resolved) |
130 | return; |
131 | ByFile[&I.Resolved->getFileEntry()].push_back(Elt: Index); |
132 | |
133 | // While verbatim headers ideally should match #include spelling exactly, |
134 | // we want to be tolerant of different spellings of the same file. |
135 | // |
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(Path: I.Resolved->getName()); |
140 | for (llvm::StringRef Parent = path::parent_path(path: Path); !Parent.empty(); |
141 | Parent = path::parent_path(path: Parent)) { |
142 | if (!SearchPath.contains(key: Parent)) |
143 | continue; |
144 | llvm::StringRef Rel = |
145 | llvm::StringRef(Path).drop_front(N: Parent.size()).ltrim(Char: '/'); |
146 | BySpellingAlternate[Rel].push_back(Elt: Index); |
147 | } |
148 | } |
149 | |
150 | const Include *Includes::atLine(unsigned OneBasedIndex) const { |
151 | auto It = ByLine.find(Val: OneBasedIndex); |
152 | return (It == ByLine.end()) ? nullptr : &All[It->second]; |
153 | } |
154 | |
155 | llvm::SmallVector<const Include *> Includes::(Header H) const { |
156 | llvm::SmallVector<const Include *> Result; |
157 | switch (H.kind()) { |
158 | case Header::Physical: |
159 | for (unsigned I : ByFile.lookup(Val: H.physical())) |
160 | Result.push_back(Elt: &All[I]); |
161 | break; |
162 | case Header::Standard: |
163 | for (unsigned I : BySpelling.lookup(Key: H.standard().name().trim(Chars: "<>" ))) |
164 | Result.push_back(Elt: &All[I]); |
165 | break; |
166 | case Header::Verbatim: { |
167 | llvm::StringRef Spelling = H.verbatim().trim(Chars: "\"<>" ); |
168 | for (unsigned I : BySpelling.lookup(Key: Spelling)) |
169 | Result.push_back(Elt: &All[I]); |
170 | for (unsigned I : BySpellingAlternate.lookup(Key: Spelling)) |
171 | if (!llvm::is_contained(Range&: Result, Element: &All[I])) |
172 | Result.push_back(Elt: &All[I]); |
173 | break; |
174 | } |
175 | } |
176 | return Result; |
177 | } |
178 | |
179 | llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const SymbolLocation &S) { |
180 | switch (S.kind()) { |
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). |
185 | return OS << "@0x" |
186 | << llvm::utohexstr( |
187 | X: 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(); |
191 | } |
192 | llvm_unreachable("Unhandled Symbol kind" ); |
193 | } |
194 | |
195 | bool Header::(const Header &RHS) const { |
196 | if (kind() != RHS.kind()) |
197 | return kind() < RHS.kind(); |
198 | switch (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(); |
205 | } |
206 | llvm_unreachable("unhandled Header kind" ); |
207 | } |
208 | } // namespace clang::include_cleaner |
209 | |