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
22namespace clang::include_cleaner {
23
24std::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
35llvm::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
47llvm::StringRef Header::resolvedPath() 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
59llvm::raw_ostream &operator<<(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
71llvm::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
76llvm::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
85llvm::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
97std::string Include::quote() const {
98 return (llvm::StringRef(Angled ? "<" : "\"") + Spelled +
99 (Angled ? ">" : "\""))
100 .str();
101}
102
103static 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
114void Includes::addSearchDirectory(llvm::StringRef Path) {
115 SearchPath.try_emplace(Key: normalizePath(Path));
116}
117
118void 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
150const Include *Includes::atLine(unsigned OneBasedIndex) const {
151 auto It = ByLine.find(Val: OneBasedIndex);
152 return (It == ByLine.end()) ? nullptr : &All[It->second];
153}
154
155llvm::SmallVector<const Include *> Includes::match(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
179llvm::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
195bool Header::operator<(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

Provided by KDAB

Privacy Policy
Learn to use CMake with our Intro Training
Find out more

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