[clang][modules] Don't prevent translation of FW_Private includes when explicitly...
[llvm-project.git] / clang-tools-extra / include-cleaner / lib / Types.cpp
blobcb8a55ed13e5d00a0ecd989bbe665d1202e2ad81
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 //===----------------------------------------------------------------------===//
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>
23 namespace clang::include_cleaner {
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>(&declaration())
31 ->getQualifiedNameAsString();
33 llvm_unreachable("Unknown symbol kind");
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>(&S.declaration()))
40 return OS << ND->getQualifiedNameAsString();
41 return OS << S.declaration().getDeclKindName();
42 case Symbol::Macro:
43 return OS << S.macro().Name->getName();
45 llvm_unreachable("Unhandled Symbol kind");
48 llvm::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("<>\"");
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) {
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();
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"
81 << llvm::utohexstr(
82 R.RefLocation.getRawEncoding(), /*LowerCase=*/false,
83 /*Width=*/CHAR_BIT * sizeof(SourceLocation::UIntTy));
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";
95 llvm_unreachable("Unexpected RefType");
98 std::string Include::quote() const {
99 return (llvm::StringRef(Angled ? "<" : "\"") + Spelled +
100 (Angled ? ">" : "\""))
101 .str();
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() == '/')
111 P.pop_back();
112 return P;
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();
123 All.push_back(I);
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;
130 if (!I.Resolved)
131 return;
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))
144 continue;
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;
158 switch (H.kind()) {
159 case Header::Physical:
160 for (unsigned I : ByFile.lookup(H.physical()))
161 Result.push_back(&All[I]);
162 break;
163 case Header::Standard:
164 for (unsigned I : BySpelling.lookup(H.standard().name().trim("<>")))
165 Result.push_back(&All[I]);
166 break;
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]);
174 break;
177 return Result;
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 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();
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();
207 llvm_unreachable("unhandled Header kind");
209 } // namespace clang::include_cleaner