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>(&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>(&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::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("<>\""); 53 case include_cleaner::Header::Verbatim: 54 return verbatim().trim("<>\""); 55 } 56 llvm_unreachable("Unknown header kind"); 57 } 58 59 llvm::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 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 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(P, /*remove_dot_dot=*/true); 108 path::native(P, 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(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(I); 123 auto BySpellingIt = BySpelling.try_emplace(I.Spelled).first; 124 All.back().Spelled = BySpellingIt->first(); // Now we own the backing string. 125 126 BySpellingIt->second.push_back(Index); 127 ByLine[I.Line] = Index; 128 129 if (!I.Resolved) 130 return; 131 ByFile[&I.Resolved->getFileEntry()].push_back(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(I.Resolved->getName()); 140 for (llvm::StringRef Parent = path::parent_path(Path); !Parent.empty(); 141 Parent = path::parent_path(Parent)) { 142 if (!SearchPath.contains(Parent)) 143 continue; 144 llvm::StringRef Rel = 145 llvm::StringRef(Path).drop_front(Parent.size()).ltrim('/'); 146 BySpellingAlternate[Rel].push_back(Index); 147 } 148 } 149 150 const Include *Includes::atLine(unsigned OneBasedIndex) const { 151 auto It = ByLine.find(OneBasedIndex); 152 return (It == ByLine.end()) ? nullptr : &All[It->second]; 153 } 154 155 llvm::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(H.physical())) 160 Result.push_back(&All[I]); 161 break; 162 case Header::Standard: 163 for (unsigned I : BySpelling.lookup(H.standard().name().trim("<>"))) 164 Result.push_back(&All[I]); 165 break; 166 case Header::Verbatim: { 167 llvm::StringRef Spelling = H.verbatim().trim("\"<>"); 168 for (unsigned I : BySpelling.lookup(Spelling)) 169 Result.push_back(&All[I]); 170 for (unsigned I : BySpellingAlternate.lookup(Spelling)) 171 if (!llvm::is_contained(Result, &All[I])) 172 Result.push_back(&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 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::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