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