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
23namespace clang::include_cleaner {
24
25std::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
36llvm::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
48llvm::StringRef Header::resolvedPath() 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
60llvm::raw_ostream &operator<<(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
72llvm::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
77llvm::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
86llvm::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
98std::string Include::quote() const {
99 return (llvm::StringRef(Angled ? "<" : "\"") + Spelled +
100 (Angled ? ">" : "\""))
101 .str();
102}
103
104static 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
115void Includes::addSearchDirectory(llvm::StringRef Path) {
116 SearchPath.try_emplace(Key: normalizePath(Path));
117}
118
119void 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
151const Include *Includes::atLine(unsigned OneBasedIndex) const {
152 auto It = ByLine.find(Val: OneBasedIndex);
153 return (It == ByLine.end()) ? nullptr : &All[It->second];
154}
155
156llvm::SmallVector<const Include *> Includes::match(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
180llvm::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
196bool Header::operator<(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

source code of clang-tools-extra/include-cleaner/lib/Types.cpp