| //===--- Types.cpp --------------------------------------------------------===// |
| // |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| // See https://llvm.org/LICENSE.txt for license information. |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include "clang-include-cleaner/Types.h" |
| #include "TypesInternal.h" |
| #include "clang/AST/Decl.h" |
| #include "clang/Basic/FileEntry.h" |
| #include "llvm/ADT/ArrayRef.h" |
| #include "llvm/ADT/STLExtras.h" |
| #include "llvm/ADT/SmallString.h" |
| #include "llvm/ADT/SmallVector.h" |
| #include "llvm/ADT/StringExtras.h" |
| #include "llvm/ADT/StringRef.h" |
| #include "llvm/Support/Path.h" |
| #include "llvm/Support/raw_ostream.h" |
| #include <vector> |
| |
| namespace clang::include_cleaner { |
| |
| std::string Symbol::name() const { |
| switch (kind()) { |
| case include_cleaner::Symbol::Macro: |
| return macro().Name->getName().str(); |
| case include_cleaner::Symbol::Declaration: |
| return llvm::dyn_cast<NamedDecl>(&declaration()) |
| ->getQualifiedNameAsString(); |
| } |
| llvm_unreachable("Unknown symbol kind"); |
| } |
| |
| llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const Symbol &S) { |
| switch (S.kind()) { |
| case Symbol::Declaration: |
| if (const auto *ND = llvm::dyn_cast<NamedDecl>(&S.declaration())) |
| return OS << ND->getQualifiedNameAsString(); |
| return OS << S.declaration().getDeclKindName(); |
| case Symbol::Macro: |
| return OS << S.macro().Name->getName(); |
| } |
| llvm_unreachable("Unhandled Symbol kind"); |
| } |
| |
| llvm::StringRef Header::resolvedPath() const { |
| switch (kind()) { |
| case include_cleaner::Header::Physical: |
| return physical().getFileEntry().tryGetRealPathName(); |
| case include_cleaner::Header::Standard: |
| return standard().name().trim("<>\""); |
| case include_cleaner::Header::Verbatim: |
| return verbatim().trim("<>\""); |
| } |
| llvm_unreachable("Unknown header kind"); |
| } |
| |
| llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const Header &H) { |
| switch (H.kind()) { |
| case Header::Physical: |
| return OS << H.physical().getName(); |
| case Header::Standard: |
| return OS << H.standard().name(); |
| case Header::Verbatim: |
| return OS << H.verbatim(); |
| } |
| llvm_unreachable("Unhandled Header kind"); |
| } |
| |
| llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const Include &I) { |
| return OS << I.Line << ": " << I.quote() << " => " |
| << (I.Resolved ? I.Resolved->getName() : "<missing>"); |
| } |
| |
| llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const SymbolReference &R) { |
| // We can't decode the Location without SourceManager. Its raw representation |
| // isn't completely useless (and distinguishes SymbolReference from Symbol). |
| return OS << R.RT << " reference to " << R.Target << "@0x" |
| << llvm::utohexstr( |
| R.RefLocation.getRawEncoding(), /*LowerCase=*/false, |
| /*Width=*/CHAR_BIT * sizeof(SourceLocation::UIntTy)); |
| } |
| |
| llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, RefType T) { |
| switch (T) { |
| case RefType::Explicit: |
| return OS << "explicit"; |
| case RefType::Implicit: |
| return OS << "implicit"; |
| case RefType::Ambiguous: |
| return OS << "ambiguous"; |
| } |
| llvm_unreachable("Unexpected RefType"); |
| } |
| |
| std::string Include::quote() const { |
| return (llvm::StringRef(Angled ? "<" : "\"") + Spelled + |
| (Angled ? ">" : "\"")) |
| .str(); |
| } |
| |
| static llvm::SmallString<128> normalizePath(llvm::StringRef Path) { |
| namespace path = llvm::sys::path; |
| |
| llvm::SmallString<128> P = Path; |
| path::remove_dots(P, /*remove_dot_dot=*/true); |
| path::native(P, path::Style::posix); |
| while (!P.empty() && P.back() == '/') |
| P.pop_back(); |
| return P; |
| } |
| |
| void Includes::addSearchDirectory(llvm::StringRef Path) { |
| SearchPath.try_emplace(normalizePath(Path)); |
| } |
| |
| void Includes::add(const Include &I) { |
| namespace path = llvm::sys::path; |
| |
| unsigned Index = All.size(); |
| All.push_back(I); |
| auto BySpellingIt = BySpelling.try_emplace(I.Spelled).first; |
| All.back().Spelled = BySpellingIt->first(); // Now we own the backing string. |
| |
| BySpellingIt->second.push_back(Index); |
| ByLine[I.Line] = Index; |
| |
| if (!I.Resolved) |
| return; |
| ByFile[&I.Resolved->getFileEntry()].push_back(Index); |
| |
| // While verbatim headers ideally should match #include spelling exactly, |
| // we want to be tolerant of different spellings of the same file. |
| // |
| // If the search path includes "/a/b" and "/a/b/c/d", |
| // verbatim "e/f" should match (spelled=c/d/e/f, resolved=/a/b/c/d/e/f). |
| // We assume entry's (normalized) name will match the search dirs. |
| auto Path = normalizePath(I.Resolved->getName()); |
| for (llvm::StringRef Parent = path::parent_path(Path); !Parent.empty(); |
| Parent = path::parent_path(Parent)) { |
| if (!SearchPath.contains(Parent)) |
| continue; |
| llvm::StringRef Rel = |
| llvm::StringRef(Path).drop_front(Parent.size()).ltrim('/'); |
| BySpellingAlternate[Rel].push_back(Index); |
| } |
| } |
| |
| const Include *Includes::atLine(unsigned OneBasedIndex) const { |
| auto It = ByLine.find(OneBasedIndex); |
| return (It == ByLine.end()) ? nullptr : &All[It->second]; |
| } |
| |
| llvm::SmallVector<const Include *> Includes::match(Header H) const { |
| llvm::SmallVector<const Include *> Result; |
| switch (H.kind()) { |
| case Header::Physical: |
| for (unsigned I : ByFile.lookup(H.physical())) |
| Result.push_back(&All[I]); |
| break; |
| case Header::Standard: |
| for (unsigned I : BySpelling.lookup(H.standard().name().trim("<>"))) |
| Result.push_back(&All[I]); |
| break; |
| case Header::Verbatim: { |
| llvm::StringRef Spelling = H.verbatim().trim("\"<>"); |
| for (unsigned I : BySpelling.lookup(Spelling)) |
| Result.push_back(&All[I]); |
| for (unsigned I : BySpellingAlternate.lookup(Spelling)) |
| if (!llvm::is_contained(Result, &All[I])) |
| Result.push_back(&All[I]); |
| break; |
| } |
| } |
| return Result; |
| } |
| |
| llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const SymbolLocation &S) { |
| switch (S.kind()) { |
| case SymbolLocation::Physical: |
| // We can't decode the Location without SourceManager. Its raw |
| // representation isn't completely useless (and distinguishes |
| // SymbolReference from Symbol). |
| return OS << "@0x" |
| << llvm::utohexstr( |
| S.physical().getRawEncoding(), /*LowerCase=*/false, |
| /*Width=*/CHAR_BIT * sizeof(SourceLocation::UIntTy)); |
| case SymbolLocation::Standard: |
| return OS << S.standard().scope() << S.standard().name(); |
| } |
| llvm_unreachable("Unhandled Symbol kind"); |
| } |
| |
| bool Header::operator<(const Header &RHS) const { |
| if (kind() != RHS.kind()) |
| return kind() < RHS.kind(); |
| switch (kind()) { |
| case Header::Physical: |
| return physical().getName() < RHS.physical().getName(); |
| case Header::Standard: |
| return standard().name() < RHS.standard().name(); |
| case Header::Verbatim: |
| return verbatim() < RHS.verbatim(); |
| } |
| llvm_unreachable("unhandled Header kind"); |
| } |
| } // namespace clang::include_cleaner |