Refs) {
OS << "";
writeProvides(Refs);
OS << "
";
}
void writeTarget(const Ref &R) {
OS << "";
OS << "| Symbol | ";
OS << describeSymbol(R.Sym) << " ";
escapeString(llvm::to_string(R.Sym));
OS << " |
\n";
std::string Details = printDetails(R.Sym);
if (!Details.empty()) {
OS << " | ";
escapeString(Details);
OS << " |
\n";
}
for (const auto &Loc : R.Locations) {
OS << "| Location | ";
if (Loc.kind() == SymbolLocation::Physical) // needs SM to print properly.
printSourceLocation(Loc.physical());
else
escapeString(llvm::to_string(Loc));
OS << " |
\n";
}
for (const auto &H : R.Headers) {
OS << "| Header | ";
switch (H.kind()) {
case Header::Physical:
printFilename(H.physical().getName());
break;
case Header::Standard:
OS << "stdlib " << H.standard().name();
break;
case Header::Verbatim:
OS << "verbatim ";
escapeString(H.verbatim());
break;
}
OS << " |
\n";
}
for (const auto *I : R.Includes) {
OS << "| Included | ";
escapeString(I->quote());
OS << ", line " << I->Line << "";
OS << " |
";
}
if (!R.Insert.empty()) {
OS << "| Insert | ";
escapeString(R.Insert);
OS << " |
";
}
OS << "
";
}
void writeCode() {
llvm::StringRef Code = SM.getBufferData(MainFile);
OS << "";
std::vector Insertions{Insertion.keys().begin(),
Insertion.keys().end()};
llvm::sort(Insertions);
for (llvm::StringRef Insertion : Insertions) {
OS << ""
<< "#include ";
escapeString(Insertion);
OS << "\n";
}
const Include *Inc = nullptr;
unsigned LineNum = 0;
// Lines are , include lines have an inner .
auto StartLine = [&] {
++LineNum;
OS << "";
if ((Inc = Includes.atLine(LineNum)))
OS << "";
};
auto EndLine = [&] {
if (Inc)
OS << "";
OS << "\n";
};
std::vector RefOrder(Refs.size());
std::iota(RefOrder.begin(), RefOrder.end(), 0);
llvm::stable_sort(RefOrder, [&](unsigned A, unsigned B) {
return std::make_pair(Refs[A].Offset, Refs[A].Type != RefType::Implicit) <
std::make_pair(Refs[B].Offset, Refs[B].Type != RefType::Implicit);
});
auto Rest = llvm::ArrayRef(RefOrder);
unsigned End = 0;
StartLine();
for (unsigned I = 0; I < Code.size(); ++I) {
// Finish refs early at EOL to avoid dealing with splitting the span.
if (End && (End == I || Code[I] == '\n')) {
OS << "";
End = 0;
}
// Handle implicit refs, which are rendered *before* the token.
while (!Rest.empty() && Refs[Rest.front()].Offset == I &&
Refs[Rest.front()].Type == RefType::Implicit) {
const Ref &R = Refs[Rest.front()];
OS << "◊";
Rest = Rest.drop_front();
};
// Accumulate all explicit refs that appear on the same token.
std::string TargetList;
bool Unsatisfied = false;
Rest = Rest.drop_while([&](unsigned RefIndex) {
const Ref &R = Refs[RefIndex];
if (R.Offset != I)
return false;
if (!TargetList.empty())
TargetList.push_back(',');
TargetList.push_back('t');
TargetList.append(std::to_string(RefIndex));
Unsatisfied = Unsatisfied || !R.Satisfied;
return true;
});
if (!TargetList.empty()) {
assert(End == 0 && "Overlapping tokens!");
OS << "";
End = I + Lexer::MeasureTokenLength(SM.getComposedLoc(MainFile, I), SM,
Ctx.getLangOpts());
}
if (Code[I] == '\n') {
EndLine();
StartLine();
} else
escapeChar(Code[I]);
}
EndLine();
OS << "\n";
}
};
} // namespace
void writeHTMLReport(FileID File, const include_cleaner::Includes &Includes,
llvm::ArrayRef Roots,
llvm::ArrayRef MacroRefs, ASTContext &Ctx,
const HeaderSearch &HS, PragmaIncludes *PI,
llvm::raw_ostream &OS) {
Reporter R(OS, Ctx, HS, Includes, PI, File);
const auto& SM = Ctx.getSourceManager();
for (Decl *Root : Roots)
walkAST(*Root, [&](SourceLocation Loc, const NamedDecl &D, RefType T) {
if(!SM.isWrittenInMainFile(SM.getSpellingLoc(Loc)))
return;
R.addRef(SymbolReference{D, Loc, T});
});
for (const SymbolReference &Ref : MacroRefs) {
if (!SM.isWrittenInMainFile(SM.getSpellingLoc(Ref.RefLocation)))
continue;
R.addRef(Ref);
}
R.write();
}
} // namespace clang::include_cleaner