| 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 | |