diff --git a/clang-tools-extra/include-cleaner/lib/AnalysisInternal.h b/clang-tools-extra/include-cleaner/lib/AnalysisInternal.h
--- a/clang-tools-extra/include-cleaner/lib/AnalysisInternal.h
+++ b/clang-tools-extra/include-cleaner/lib/AnalysisInternal.h
@@ -48,7 +48,8 @@
/// Write an HTML summary of the analysis to the given stream.
/// FIXME: Once analysis has a public API, this should be public too.
-void writeHTMLReport(FileID File, llvm::ArrayRef Roots, ASTContext &Ctx,
+void writeHTMLReport(FileID File, llvm::ArrayRef Roots,
+ llvm::ArrayRef MacroRefs, ASTContext &Ctx,
llvm::raw_ostream &OS);
} // namespace include_cleaner
diff --git a/clang-tools-extra/include-cleaner/lib/HTMLReport.cpp b/clang-tools-extra/include-cleaner/lib/HTMLReport.cpp
--- a/clang-tools-extra/include-cleaner/lib/HTMLReport.cpp
+++ b/clang-tools-extra/include-cleaner/lib/HTMLReport.cpp
@@ -15,11 +15,12 @@
#include "AnalysisInternal.h"
#include "clang-include-cleaner/Analysis.h"
+#include "clang-include-cleaner/Types.h"
#include "clang/AST/ASTContext.h"
-#include "clang/AST/Decl.h"
#include "clang/AST/PrettyPrinter.h"
#include "clang/Basic/SourceManager.h"
#include "clang/Lex/Lexer.h"
+#include "llvm/Support/ScopedPrinter.h"
#include "llvm/Support/raw_ostream.h"
namespace clang::include_cleaner {
@@ -38,6 +39,7 @@
}
.ref { text-decoration: underline; color: #008; }
.sel { position: relative; cursor: pointer; }
+ .ref.implicit { background-color: #ff8; }
#hover {
background-color: #aaccff; border: 1px solid black;
z-index: 1;
@@ -47,6 +49,8 @@
}
#hover p, #hover pre { margin: 0; }
#hover section header { font-weight: bold; }
+ #hover section.implicit { background-color: #bbb; }
+ #hover section.ambiguous { background-color: #caf; }
#hover section:not(:first-child) { margin-top: 1em; }
)css";
@@ -75,18 +79,67 @@
)js";
// Print the declaration tersely, but enough to identify e.g. which overload.
-std::string printDecl(const NamedDecl &ND) {
+std::string printSymbol(const Symbol &Sym) {
std::string S;
llvm::raw_string_ostream OS(S);
- PrintingPolicy PP = ND.getASTContext().getPrintingPolicy();
- PP.FullyQualifiedName = true;
- PP.TerseOutput = true;
- PP.SuppressInitializers = true;
- ND.print(OS, PP);
+ switch (Sym.kind()) {
+ case Symbol::Declaration: {
+ const auto &D = Sym.declaration();
+ PrintingPolicy PP = D.getASTContext().getPrintingPolicy();
+ PP.FullyQualifiedName = true;
+ PP.TerseOutput = true;
+ PP.SuppressInitializers = true;
+ D.print(OS, PP);
+ break;
+ }
+ case Symbol::Macro:
+ OS << "#define " << Sym.macro().Name->getName() << " ...";
+ break;
+ case Symbol::Standard:
+ OS << "(defined in stdlib)";
+ break;
+ }
llvm::erase_value(S, '\n');
return S;
}
+// Categorize the symbol, like FunctionDecl or Macro
+llvm::StringRef describeSymbol(const Symbol &Sym) {
+ switch (Sym.kind()) {
+ case Symbol::Declaration:
+ return Sym.declaration().getDeclKindName();
+ case Symbol::Macro:
+ return "Macro";
+ case Symbol::Standard:
+ return "Standard symbol";
+ }
+ llvm_unreachable("unhandled symbol kind");
+}
+
+// Categorize the symbol, like FunctionDecl or Macro
+std::string locateSymbol(const Symbol &Sym, const SourceManager &SM) {
+ switch (Sym.kind()) {
+ case Symbol::Declaration:
+ return Sym.declaration().getLocation().printToString(SM);
+ case Symbol::Macro:
+ return Sym.macro().Definition.printToString(SM);
+ case Symbol::Standard:
+ return ("stdlib header " + Sym.standard().header().name()).str();
+ }
+ llvm_unreachable("unhandled symbol kind");
+}
+
+llvm::StringRef refType(RefType T) {
+ switch (T) {
+ case RefType::Explicit:
+ return "";
+ case RefType::Implicit:
+ return "implicit";
+ case RefType::Ambiguous:
+ return "ambiguous";
+ }
+}
+
class Reporter {
llvm::raw_ostream &OS;
const ASTContext &Ctx;
@@ -94,29 +147,41 @@
FileID File;
// Symbols that are referenced from the main file.
+ // FIXME: should we deduplicate these?
struct Target {
- const NamedDecl *D;
+ Symbol Sym;
+ RefType Type;
+ SmallVector Providers;
};
std::vector Targets;
// Points within the main file that reference a Target.
- std::vector> Refs;
+ // Implicit refs will be marked with a symbol just before the token.
+ struct Ref {
+ unsigned Offset;
+ bool Implicit;
+ size_t TargetIndex;
+ bool operator<(const Ref &Other) const {
+ return std::forward_as_tuple(Offset, !Implicit, TargetIndex) <
+ std::forward_as_tuple(Other.Offset, !Other.Implicit, TargetIndex);
+ }
+ };
+ std::vector[ Refs;
public:
Reporter(llvm::raw_ostream &OS, ASTContext &Ctx, FileID File)
: OS(OS), Ctx(Ctx), SM(Ctx.getSourceManager()), File(File) {}
- void addRef(SourceLocation Loc, const NamedDecl &D) {
- auto [File, Offset] = SM.getDecomposedLoc(SM.getFileLoc(Loc));
+ void addRef(const SymbolReference &SR, llvm::ArrayRef Headers) {
+ auto [File, Offset] = SM.getDecomposedLoc(SM.getFileLoc(SR.RefLocation));
if (File != this->File) {
// Can get here e.g. if there's an #include inside a root Decl.
// FIXME: do something more useful than this.
- llvm::errs() << "Ref location outside file! "
- << D.getQualifiedNameAsString() << " at "
- << Loc.printToString(SM) << "\n";
+ llvm::errs() << "Ref location outside file! " << SR.Target << " at "
+ << SR.RefLocation.printToString(SM) << "\n";
return;
}
- Targets.push_back({&D});
- Refs.push_back({Offset, Targets.size() - 1});
+ Targets.push_back({SR.Target, SR.RT, {Headers.begin(), Headers.end()}});
+ Refs.push_back({Offset, SR.RT == RefType::Implicit, Targets.size() - 1});
}
void write() {
@@ -126,7 +191,8 @@
OS << "\n";
OS << "\n";
for (unsigned I = 0; I < Targets.size(); ++I) {
- OS << "";
+ OS << "";
writeTarget(Targets[I]);
OS << "\n";
}
@@ -157,15 +223,27 @@
}
void writeTarget(const Target &T) {
- OS << "" << T.D->getDeclKindName() << " ";
- escapeString(T.D->getQualifiedNameAsString());
- OS << "";
+ OS << "";
+ if (T.Type != RefType::Explicit)
+ OS << refType(T.Type) << " ";
+ OS << describeSymbol(T.Sym) << " ";
+ escapeString(llvm::to_string(T.Sym));
+ OS << "\n";
OS << "]declared at ";
- escapeString(SM.getFileLoc(T.D->getLocation()).printToString(SM));
- OS << "
";
- escapeString(printDecl(*T.D));
- OS << "
";
+ escapeString(locateSymbol(T.Sym, SM));
+ OS << "
\n";
+
+ OS << "";
+ escapeString(printSymbol(T.Sym));
+ OS << "
\n";
+
+ OS << "";
+ for (Header H : T.Providers) {
+ OS << "Candidate: ";
+ escapeString(llvm::to_string(H));
+ }
+ OS << "
\n";
}
void writeCode() {
@@ -182,14 +260,23 @@
OS << "";
End = 0;
}
+ // Handle implicit refs, which are rendered *before* the token.
+ while (!Rest.empty() && Rest.front().Offset == I &&
+ Rest.front().Implicit) {
+ const Ref &R = Rest.front();
+ OS << "◊";
+ Rest = Rest.drop_front();
+ };
+ // Accumulate all explicit refs that appear on the same token.
std::string TargetList;
- Rest = Rest.drop_while([&](auto &R) {
- if (R.first != I)
+ Rest = Rest.drop_while([&](const Ref &R) {
+ if (R.Offset != I)
return false;
if (!TargetList.empty())
TargetList.push_back(',');
TargetList.push_back('t');
- TargetList.append(std::to_string(R.second));
+ TargetList.append(std::to_string(R.TargetIndex));
return true;
});
if (!TargetList.empty()) {
@@ -209,13 +296,14 @@
} // namespace
-void writeHTMLReport(FileID File, llvm::ArrayRef Roots, ASTContext &Ctx,
+void writeHTMLReport(FileID File, llvm::ArrayRef Roots,
+ llvm::ArrayRef MacroRefs, ASTContext &Ctx,
llvm::raw_ostream &OS) {
Reporter R(OS, Ctx, File);
- for (Decl *Root : Roots)
- walkAST(*Root, [&](SourceLocation Loc, const NamedDecl &D, RefType) {
- R.addRef(Loc, D);
- });
+ walkUsed(Roots, MacroRefs, Ctx.getSourceManager(),
+ [&](const SymbolReference &Ref, llvm::ArrayRef Headers) {
+ R.addRef(Ref, Headers);
+ });
R.write();
}
diff --git a/clang-tools-extra/include-cleaner/lib/Types.cpp b/clang-tools-extra/include-cleaner/lib/Types.cpp
--- a/clang-tools-extra/include-cleaner/lib/Types.cpp
+++ b/clang-tools-extra/include-cleaner/lib/Types.cpp
@@ -21,7 +21,7 @@
return OS << ND->getNameAsString();
return OS << S.declaration().getDeclKindName();
case Symbol::Macro:
- return OS << S.macro().Name;
+ return OS << S.macro().Name->getName();
case Symbol::Standard:
return OS << S.standard().scope() << S.standard().name();
}
diff --git a/clang-tools-extra/include-cleaner/tool/IncludeCleaner.cpp b/clang-tools-extra/include-cleaner/tool/IncludeCleaner.cpp
--- a/clang-tools-extra/include-cleaner/tool/IncludeCleaner.cpp
+++ b/clang-tools-extra/include-cleaner/tool/IncludeCleaner.cpp
@@ -47,6 +47,13 @@
class HTMLReportAction : public clang::ASTFrontendAction {
RecordedAST AST;
+ RecordedPP PP;
+
+ void ExecuteAction() override {
+ auto &P = getCompilerInstance().getPreprocessor();
+ P.addPPCallbacks(PP.record(P));
+ ASTFrontendAction::ExecuteAction();
+ }
std::unique_ptr CreateASTConsumer(CompilerInstance &CI,
StringRef File) override {
@@ -62,7 +69,7 @@
exit(1);
}
writeHTMLReport(AST.Ctx->getSourceManager().getMainFileID(), AST.Roots,
- *AST.Ctx, OS);
+ PP.MacroReferences, *AST.Ctx, OS);
}
};