xref: /llvm-project/clang-tools-extra/include-cleaner/lib/Types.cpp (revision 64d9713637ab98e2b65c9c4317a50ddba0ba0dbc)
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