[clang-format] Add link to source code in file definitions

Two command line options have been added to clang-doc.
  --repository=<string>       - URL of repository that hosts code; used for links to definition locations.
  --source-root=<string>      - Directory where processed files are stored. Links to definition locations will only be generated if the file is in this dir.

If the file is in the source-root and a repository options is passed;
a link to the source code will be rendered by the HTML generator.

Differential Revision: https://reviews.llvm.org/D65483

llvm-svn: 368460
This commit is contained in:
Diego Astiazaran 2019-08-09 17:49:41 +00:00
parent 2bf522aea6
commit 665e9676c2
13 changed files with 237 additions and 100 deletions

View File

@ -83,7 +83,7 @@ llvm::Error decodeRecord(Record R, llvm::Optional<Location> &Field,
if (R[0] > INT_MAX)
return llvm::make_error<llvm::StringError>("Integer too large to parse.\n",
llvm::inconvertibleErrorCode());
Field.emplace((int)R[0], Blob);
Field.emplace((int)R[0], Blob, (bool)R[1]);
return llvm::Error::success();
}
@ -129,7 +129,7 @@ llvm::Error decodeRecord(Record R, llvm::SmallVectorImpl<Location> &Field,
if (R[0] > INT_MAX)
return llvm::make_error<llvm::StringError>("Integer too large to parse.\n",
llvm::inconvertibleErrorCode());
Field.emplace_back((int)R[0], Blob);
Field.emplace_back((int)R[0], Blob, (bool)R[1]);
return llvm::Error::success();
}

View File

@ -77,10 +77,13 @@ static void LocationAbbrev(std::shared_ptr<llvm::BitCodeAbbrev> &Abbrev) {
{// 0. Fixed-size integer (line number)
llvm::BitCodeAbbrevOp(llvm::BitCodeAbbrevOp::Fixed,
BitCodeConstants::LineNumberSize),
// 1. Fixed-size integer (length of the following string (filename))
// 1. Boolean (IsFileInRootDir)
llvm::BitCodeAbbrevOp(llvm::BitCodeAbbrevOp::Fixed,
BitCodeConstants::BoolSize),
// 2. Fixed-size integer (length of the following string (filename))
llvm::BitCodeAbbrevOp(llvm::BitCodeAbbrevOp::Fixed,
BitCodeConstants::StringLengthSize),
// 2. The string blob
// 3. The string blob
llvm::BitCodeAbbrevOp(llvm::BitCodeAbbrevOp::Blob)});
}
@ -316,6 +319,7 @@ void ClangDocBitcodeWriter::emitRecord(const Location &Loc, RecordId ID) {
// FIXME: Assert that the line number is of the appropriate size.
Record.push_back(Loc.LineNumber);
assert(Loc.Filename.size() < (1U << BitCodeConstants::StringLengthSize));
Record.push_back(Loc.IsFileInRootDir);
Record.push_back(Loc.Filename.size());
Stream.EmitRecordWithBlob(Abbrevs.get(ID), Record, Loc.Filename);
}

View File

@ -301,12 +301,15 @@ genReferenceList(const llvm::SmallVectorImpl<Reference> &Refs,
return Out;
}
static std::vector<std::unique_ptr<TagNode>> genHTML(const EnumInfo &I);
static std::vector<std::unique_ptr<TagNode>> genHTML(const FunctionInfo &I,
StringRef ParentInfoDir);
static std::vector<std::unique_ptr<TagNode>>
genHTML(const EnumInfo &I, const ClangDocContext &CDCtx);
static std::vector<std::unique_ptr<TagNode>>
genHTML(const FunctionInfo &I, const ClangDocContext &CDCtx,
StringRef ParentInfoDir);
static std::vector<std::unique_ptr<TagNode>>
genEnumsBlock(const std::vector<EnumInfo> &Enums) {
genEnumsBlock(const std::vector<EnumInfo> &Enums,
const ClangDocContext &CDCtx) {
if (Enums.empty())
return {};
@ -316,7 +319,7 @@ genEnumsBlock(const std::vector<EnumInfo> &Enums) {
Out.emplace_back(llvm::make_unique<TagNode>(HTMLTag::TAG_DIV));
auto &DivBody = Out.back();
for (const auto &E : Enums) {
std::vector<std::unique_ptr<TagNode>> Nodes = genHTML(E);
std::vector<std::unique_ptr<TagNode>> Nodes = genHTML(E, CDCtx);
AppendVector(std::move(Nodes), DivBody->Children);
}
return Out;
@ -335,7 +338,7 @@ genEnumMembersBlock(const llvm::SmallVector<SmallString<16>, 4> &Members) {
static std::vector<std::unique_ptr<TagNode>>
genFunctionsBlock(const std::vector<FunctionInfo> &Functions,
StringRef ParentInfoDir) {
const ClangDocContext &CDCtx, StringRef ParentInfoDir) {
if (Functions.empty())
return {};
@ -345,7 +348,8 @@ genFunctionsBlock(const std::vector<FunctionInfo> &Functions,
Out.emplace_back(llvm::make_unique<TagNode>(HTMLTag::TAG_DIV));
auto &DivBody = Out.back();
for (const auto &F : Functions) {
std::vector<std::unique_ptr<TagNode>> Nodes = genHTML(F, ParentInfoDir);
std::vector<std::unique_ptr<TagNode>> Nodes =
genHTML(F, CDCtx, ParentInfoDir);
AppendVector(std::move(Nodes), DivBody->Children);
}
return Out;
@ -392,10 +396,30 @@ genReferencesBlock(const std::vector<Reference> &References,
return Out;
}
static std::unique_ptr<TagNode> writeFileDefinition(const Location &L) {
return llvm::make_unique<TagNode>(
HTMLTag::TAG_P,
"Defined at line " + std::to_string(L.LineNumber) + " of " + L.Filename);
static std::unique_ptr<TagNode>
writeFileDefinition(const Location &L,
llvm::Optional<StringRef> RepositoryUrl = None) {
if (!L.IsFileInRootDir || !RepositoryUrl)
return llvm::make_unique<TagNode>(
HTMLTag::TAG_P, "Defined at line " + std::to_string(L.LineNumber) +
" of file " + L.Filename);
SmallString<128> FileURL(RepositoryUrl.getValue());
llvm::sys::path::append(FileURL, llvm::sys::path::Style::posix, L.Filename);
auto Node = llvm::make_unique<TagNode>(HTMLTag::TAG_P);
Node->Children.emplace_back(llvm::make_unique<TextNode>("Defined at line "));
auto LocNumberNode =
llvm::make_unique<TagNode>(HTMLTag::TAG_A, std::to_string(L.LineNumber));
// The links to a specific line in the source code use the github /
// googlesource notation so it won't work for all hosting pages.
LocNumberNode->Attributes.try_emplace(
"href", (FileURL + "#" + std::to_string(L.LineNumber)).str());
Node->Children.emplace_back(std::move(LocNumberNode));
Node->Children.emplace_back(llvm::make_unique<TextNode>(" of file "));
auto LocFileNode = llvm::make_unique<TagNode>(
HTMLTag::TAG_A, llvm::sys::path::filename(FileURL));
LocFileNode->Attributes.try_emplace("href", FileURL);
Node->Children.emplace_back(std::move(LocFileNode));
return Node;
}
static std::vector<std::unique_ptr<TagNode>>
@ -491,7 +515,8 @@ static std::unique_ptr<TagNode> genHTML(const std::vector<CommentInfo> &C) {
return CommentBlock;
}
static std::vector<std::unique_ptr<TagNode>> genHTML(const EnumInfo &I) {
static std::vector<std::unique_ptr<TagNode>>
genHTML(const EnumInfo &I, const ClangDocContext &CDCtx) {
std::vector<std::unique_ptr<TagNode>> Out;
std::string EnumType;
if (I.Scoped)
@ -508,8 +533,13 @@ static std::vector<std::unique_ptr<TagNode>> genHTML(const EnumInfo &I) {
if (Node)
Out.emplace_back(std::move(Node));
if (I.DefLoc)
Out.emplace_back(writeFileDefinition(I.DefLoc.getValue()));
if (I.DefLoc) {
if (!CDCtx.RepositoryUrl)
Out.emplace_back(writeFileDefinition(I.DefLoc.getValue()));
else
Out.emplace_back(writeFileDefinition(
I.DefLoc.getValue(), StringRef{CDCtx.RepositoryUrl.getValue()}));
}
std::string Description;
if (!I.Description.empty())
@ -518,8 +548,9 @@ static std::vector<std::unique_ptr<TagNode>> genHTML(const EnumInfo &I) {
return Out;
}
static std::vector<std::unique_ptr<TagNode>> genHTML(const FunctionInfo &I,
StringRef ParentInfoDir) {
static std::vector<std::unique_ptr<TagNode>>
genHTML(const FunctionInfo &I, const ClangDocContext &CDCtx,
StringRef ParentInfoDir) {
std::vector<std::unique_ptr<TagNode>> Out;
Out.emplace_back(llvm::make_unique<TagNode>(HTMLTag::TAG_H3, I.Name));
// USR is used as id for functions instead of name to disambiguate function
@ -552,8 +583,13 @@ static std::vector<std::unique_ptr<TagNode>> genHTML(const FunctionInfo &I,
}
FunctionHeader->Children.emplace_back(llvm::make_unique<TextNode>(")"));
if (I.DefLoc)
Out.emplace_back(writeFileDefinition(I.DefLoc.getValue()));
if (I.DefLoc) {
if (!CDCtx.RepositoryUrl)
Out.emplace_back(writeFileDefinition(I.DefLoc.getValue()));
else
Out.emplace_back(writeFileDefinition(
I.DefLoc.getValue(), StringRef{CDCtx.RepositoryUrl.getValue()}));
}
std::string Description;
if (!I.Description.empty())
@ -563,7 +599,8 @@ static std::vector<std::unique_ptr<TagNode>> genHTML(const FunctionInfo &I,
}
static std::vector<std::unique_ptr<TagNode>>
genHTML(const NamespaceInfo &I, Index &InfoIndex, std::string &InfoTitle) {
genHTML(const NamespaceInfo &I, Index &InfoIndex, const ClangDocContext &CDCtx,
std::string &InfoTitle) {
std::vector<std::unique_ptr<TagNode>> Out;
if (I.Name.str() == "")
InfoTitle = "Global Namespace";
@ -584,10 +621,10 @@ genHTML(const NamespaceInfo &I, Index &InfoIndex, std::string &InfoTitle) {
AppendVector(std::move(ChildRecords), Out);
std::vector<std::unique_ptr<TagNode>> ChildFunctions =
genFunctionsBlock(I.ChildFunctions, I.Path);
genFunctionsBlock(I.ChildFunctions, CDCtx, I.Path);
AppendVector(std::move(ChildFunctions), Out);
std::vector<std::unique_ptr<TagNode>> ChildEnums =
genEnumsBlock(I.ChildEnums);
genEnumsBlock(I.ChildEnums, CDCtx);
AppendVector(std::move(ChildEnums), Out);
if (!I.ChildNamespaces.empty())
@ -604,13 +641,19 @@ genHTML(const NamespaceInfo &I, Index &InfoIndex, std::string &InfoTitle) {
}
static std::vector<std::unique_ptr<TagNode>>
genHTML(const RecordInfo &I, Index &InfoIndex, std::string &InfoTitle) {
genHTML(const RecordInfo &I, Index &InfoIndex, const ClangDocContext &CDCtx,
std::string &InfoTitle) {
std::vector<std::unique_ptr<TagNode>> Out;
InfoTitle = (getTagType(I.TagType) + " " + I.Name).str();
Out.emplace_back(llvm::make_unique<TagNode>(HTMLTag::TAG_H1, InfoTitle));
if (I.DefLoc)
Out.emplace_back(writeFileDefinition(I.DefLoc.getValue()));
if (I.DefLoc) {
if (!CDCtx.RepositoryUrl)
Out.emplace_back(writeFileDefinition(I.DefLoc.getValue()));
else
Out.emplace_back(writeFileDefinition(
I.DefLoc.getValue(), StringRef{CDCtx.RepositoryUrl.getValue()}));
}
std::string Description;
if (!I.Description.empty())
@ -643,10 +686,10 @@ genHTML(const RecordInfo &I, Index &InfoIndex, std::string &InfoTitle) {
AppendVector(std::move(ChildRecords), Out);
std::vector<std::unique_ptr<TagNode>> ChildFunctions =
genFunctionsBlock(I.ChildFunctions, I.Path);
genFunctionsBlock(I.ChildFunctions, CDCtx, I.Path);
AppendVector(std::move(ChildFunctions), Out);
std::vector<std::unique_ptr<TagNode>> ChildEnums =
genEnumsBlock(I.ChildEnums);
genEnumsBlock(I.ChildEnums, CDCtx);
AppendVector(std::move(ChildEnums), Out);
if (!I.Members.empty())
@ -682,26 +725,27 @@ llvm::Error HTMLGenerator::generateDocForInfo(Info *I, llvm::raw_ostream &OS,
Index InfoIndex;
switch (I->IT) {
case InfoType::IT_namespace: {
std::vector<std::unique_ptr<TagNode>> Nodes = genHTML(
*static_cast<clang::doc::NamespaceInfo *>(I), InfoIndex, InfoTitle);
std::vector<std::unique_ptr<TagNode>> Nodes =
genHTML(*static_cast<clang::doc::NamespaceInfo *>(I), InfoIndex, CDCtx,
InfoTitle);
AppendVector(std::move(Nodes), MainContentNode->Children);
break;
}
case InfoType::IT_record: {
std::vector<std::unique_ptr<TagNode>> Nodes = genHTML(
*static_cast<clang::doc::RecordInfo *>(I), InfoIndex, InfoTitle);
*static_cast<clang::doc::RecordInfo *>(I), InfoIndex, CDCtx, InfoTitle);
AppendVector(std::move(Nodes), MainContentNode->Children);
break;
}
case InfoType::IT_enum: {
std::vector<std::unique_ptr<TagNode>> Nodes =
genHTML(*static_cast<clang::doc::EnumInfo *>(I));
genHTML(*static_cast<clang::doc::EnumInfo *>(I), CDCtx);
AppendVector(std::move(Nodes), MainContentNode->Children);
break;
}
case InfoType::IT_function: {
std::vector<std::unique_ptr<TagNode>> Nodes =
genHTML(*static_cast<clang::doc::FunctionInfo *>(I), "");
genHTML(*static_cast<clang::doc::FunctionInfo *>(I), CDCtx, "");
AppendVector(std::move(Nodes), MainContentNode->Children);
break;
}

View File

@ -36,10 +36,12 @@ template <typename T> bool MapASTVisitor::mapDecl(const T *D) {
// If there is an error generating a USR for the decl, skip this decl.
if (index::generateUSRForDecl(D, USR))
return true;
auto I = serialize::emitInfo(
D, getComment(D, D->getASTContext()), getLine(D, D->getASTContext()),
getFile(D, D->getASTContext()), CDCtx.PublicOnly);
bool IsFileInRootDir;
llvm::SmallString<128> File =
getFile(D, D->getASTContext(), CDCtx.SourceRoot, IsFileInRootDir);
auto I = serialize::emitInfo(D, getComment(D, D->getASTContext()),
getLine(D, D->getASTContext()), File,
IsFileInRootDir, CDCtx.PublicOnly);
// A null in place of I indicates that the serializer is skipping this decl
// for some reason (e.g. we're only reporting public decls).
@ -87,11 +89,26 @@ int MapASTVisitor::getLine(const NamedDecl *D,
return Context.getSourceManager().getPresumedLoc(D->getBeginLoc()).getLine();
}
llvm::StringRef MapASTVisitor::getFile(const NamedDecl *D,
const ASTContext &Context) const {
return Context.getSourceManager()
.getPresumedLoc(D->getBeginLoc())
.getFilename();
llvm::SmallString<128> MapASTVisitor::getFile(const NamedDecl *D,
const ASTContext &Context,
llvm::StringRef RootDir,
bool &IsFileInRootDir) const {
llvm::SmallString<128> File(Context.getSourceManager()
.getPresumedLoc(D->getBeginLoc())
.getFilename());
IsFileInRootDir = false;
if (RootDir.empty() || !File.startswith(RootDir))
return File;
IsFileInRootDir = true;
llvm::SmallString<128> Prefix(RootDir);
// replace_path_prefix removes the exact prefix provided. The result of
// calling that function on ("A/B/C.c", "A/B", "") would be "/C.c", which
// starts with a / that is not needed. This is why we fix Prefix so it always
// ends with a / and the result has the desired format.
if (!llvm::sys::path::is_separator(Prefix.back()))
Prefix += llvm::sys::path::get_separator();
llvm::sys::path::replace_path_prefix(File, Prefix, "");
return File;
}
} // namespace doc

View File

@ -44,7 +44,9 @@ private:
template <typename T> bool mapDecl(const T *D);
int getLine(const NamedDecl *D, const ASTContext &Context) const;
StringRef getFile(const NamedDecl *D, const ASTContext &Context) const;
llvm::SmallString<128> getFile(const NamedDecl *D, const ASTContext &Context,
StringRef RootDir,
bool &IsFileInRootDir) const;
comments::FullComment *getComment(const NamedDecl *D,
const ASTContext &Context) const;

View File

@ -235,5 +235,21 @@ void Index::sort() {
C.sort();
}
ClangDocContext::ClangDocContext(tooling::ExecutionContext *ECtx,
bool PublicOnly, StringRef OutDirectory,
StringRef SourceRoot, StringRef RepositoryUrl,
std::vector<std::string> UserStylesheets,
std::vector<std::string> JsScripts)
: ECtx(ECtx), PublicOnly(PublicOnly), OutDirectory(OutDirectory),
SourceRoot(SourceRoot), UserStylesheets(UserStylesheets),
JsScripts(JsScripts) {
if (!RepositoryUrl.empty()) {
this->RepositoryUrl = RepositoryUrl;
if (!RepositoryUrl.empty() && RepositoryUrl.find("http://") != 0 &&
RepositoryUrl.find("https://") != 0)
this->RepositoryUrl->insert(0, "https://");
}
}
} // namespace doc
} // namespace clang

View File

@ -205,6 +205,9 @@ struct Location {
Location() = default;
Location(int LineNumber, SmallString<16> Filename)
: LineNumber(LineNumber), Filename(std::move(Filename)) {}
Location(int LineNumber, SmallString<16> Filename, bool IsFileInRootDir)
: LineNumber(LineNumber), Filename(std::move(Filename)),
IsFileInRootDir(IsFileInRootDir) {}
bool operator==(const Location &Other) const {
return std::tie(LineNumber, Filename) ==
@ -220,8 +223,9 @@ struct Location {
std::tie(Other.LineNumber, Other.Filename);
}
int LineNumber; // Line number of this Location.
SmallString<32> Filename; // File for this Location.
int LineNumber; // Line number of this Location.
SmallString<32> Filename; // File for this Location.
bool IsFileInRootDir = false; // Indicates if file is inside root directory
};
/// A base struct for Infos.
@ -375,14 +379,18 @@ mergeInfos(std::vector<std::unique_ptr<Info>> &Values);
struct ClangDocContext {
ClangDocContext() = default;
ClangDocContext(tooling::ExecutionContext *ECtx, bool PublicOnly,
StringRef OutDirectory,
StringRef OutDirectory, StringRef SourceRoot,
StringRef RepositoryUrl,
std::vector<std::string> UserStylesheets,
std::vector<std::string> JsScripts)
: ECtx(ECtx), PublicOnly(PublicOnly), OutDirectory(OutDirectory),
UserStylesheets(UserStylesheets), JsScripts(JsScripts) {}
std::vector<std::string> JsScripts);
tooling::ExecutionContext *ECtx;
bool PublicOnly;
std::string OutDirectory;
bool PublicOnly; // Indicates if only public declarations are documented.
std::string OutDirectory; // Directory for outputting generated files.
std::string SourceRoot; // Directory where processed files are stored. Links
// to definition locations will only be generated if
// the file is in this dir.
// URL of repository that hosts code used for links to definition locations.
llvm::Optional<std::string> RepositoryUrl;
// Path of CSS stylesheets that will be copied to OutDirectory and used to
// style all HTML files.
std::vector<std::string> UserStylesheets;

View File

@ -348,19 +348,21 @@ static void populateInfo(Info &I, const T *D, const FullComment *C,
template <typename T>
static void populateSymbolInfo(SymbolInfo &I, const T *D, const FullComment *C,
int LineNumber, StringRef Filename,
bool IsFileInRootDir,
bool &IsInAnonymousNamespace) {
populateInfo(I, D, C, IsInAnonymousNamespace);
if (D->isThisDeclarationADefinition())
I.DefLoc.emplace(LineNumber, Filename);
I.DefLoc.emplace(LineNumber, Filename, IsFileInRootDir);
else
I.Loc.emplace_back(LineNumber, Filename);
I.Loc.emplace_back(LineNumber, Filename, IsFileInRootDir);
}
static void populateFunctionInfo(FunctionInfo &I, const FunctionDecl *D,
const FullComment *FC, int LineNumber,
StringRef Filename,
StringRef Filename, bool IsFileInRootDir,
bool &IsInAnonymousNamespace) {
populateSymbolInfo(I, D, FC, LineNumber, Filename, IsInAnonymousNamespace);
populateSymbolInfo(I, D, FC, LineNumber, Filename, IsFileInRootDir,
IsInAnonymousNamespace);
if (const auto *T = getDeclForType(D->getReturnType())) {
if (dyn_cast<EnumDecl>(T))
I.ReturnType = TypeInfo(getUSRForDecl(T), T->getNameAsString(),
@ -376,7 +378,7 @@ static void populateFunctionInfo(FunctionInfo &I, const FunctionDecl *D,
std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>>
emitInfo(const NamespaceDecl *D, const FullComment *FC, int LineNumber,
llvm::StringRef File, bool PublicOnly) {
llvm::StringRef File, bool IsFileInRootDir, bool PublicOnly) {
auto I = llvm::make_unique<NamespaceInfo>();
bool IsInAnonymousNamespace = false;
populateInfo(*I, D, FC, IsInAnonymousNamespace);
@ -402,10 +404,11 @@ emitInfo(const NamespaceDecl *D, const FullComment *FC, int LineNumber,
std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>>
emitInfo(const RecordDecl *D, const FullComment *FC, int LineNumber,
llvm::StringRef File, bool PublicOnly) {
llvm::StringRef File, bool IsFileInRootDir, bool PublicOnly) {
auto I = llvm::make_unique<RecordInfo>();
bool IsInAnonymousNamespace = false;
populateSymbolInfo(*I, D, FC, LineNumber, File, IsInAnonymousNamespace);
populateSymbolInfo(*I, D, FC, LineNumber, File, IsFileInRootDir,
IsInAnonymousNamespace);
if (PublicOnly && ((IsInAnonymousNamespace ||
!isPublic(D->getAccess(), D->getLinkageInternal()))))
return {};
@ -452,10 +455,11 @@ emitInfo(const RecordDecl *D, const FullComment *FC, int LineNumber,
std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>>
emitInfo(const FunctionDecl *D, const FullComment *FC, int LineNumber,
llvm::StringRef File, bool PublicOnly) {
llvm::StringRef File, bool IsFileInRootDir, bool PublicOnly) {
FunctionInfo Func;
bool IsInAnonymousNamespace = false;
populateFunctionInfo(Func, D, FC, LineNumber, File, IsInAnonymousNamespace);
populateFunctionInfo(Func, D, FC, LineNumber, File, IsFileInRootDir,
IsInAnonymousNamespace);
if (PublicOnly && ((IsInAnonymousNamespace ||
!isPublic(D->getAccess(), D->getLinkageInternal()))))
return {};
@ -477,10 +481,11 @@ emitInfo(const FunctionDecl *D, const FullComment *FC, int LineNumber,
std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>>
emitInfo(const CXXMethodDecl *D, const FullComment *FC, int LineNumber,
llvm::StringRef File, bool PublicOnly) {
llvm::StringRef File, bool IsFileInRootDir, bool PublicOnly) {
FunctionInfo Func;
bool IsInAnonymousNamespace = false;
populateFunctionInfo(Func, D, FC, LineNumber, File, IsInAnonymousNamespace);
populateFunctionInfo(Func, D, FC, LineNumber, File, IsFileInRootDir,
IsInAnonymousNamespace);
if (PublicOnly && ((IsInAnonymousNamespace ||
!isPublic(D->getAccess(), D->getLinkageInternal()))))
return {};
@ -511,10 +516,11 @@ emitInfo(const CXXMethodDecl *D, const FullComment *FC, int LineNumber,
std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>>
emitInfo(const EnumDecl *D, const FullComment *FC, int LineNumber,
llvm::StringRef File, bool PublicOnly) {
llvm::StringRef File, bool IsFileInRootDir, bool PublicOnly) {
EnumInfo Enum;
bool IsInAnonymousNamespace = false;
populateSymbolInfo(Enum, D, FC, LineNumber, File, IsInAnonymousNamespace);
populateSymbolInfo(Enum, D, FC, LineNumber, File, IsFileInRootDir,
IsInAnonymousNamespace);
if (PublicOnly && ((IsInAnonymousNamespace ||
!isPublic(D->getAccess(), D->getLinkageInternal()))))
return {};

View File

@ -38,19 +38,19 @@ namespace serialize {
// nullptr.
std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>>
emitInfo(const NamespaceDecl *D, const FullComment *FC, int LineNumber,
StringRef File, bool PublicOnly);
StringRef File, bool IsFileInRootDir, bool PublicOnly);
std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>>
emitInfo(const RecordDecl *D, const FullComment *FC, int LineNumber,
StringRef File, bool PublicOnly);
StringRef File, bool IsFileInRootDir, bool PublicOnly);
std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>>
emitInfo(const EnumDecl *D, const FullComment *FC, int LineNumber,
StringRef File, bool PublicOnly);
StringRef File, bool IsFileInRootDir, bool PublicOnly);
std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>>
emitInfo(const FunctionDecl *D, const FullComment *FC, int LineNumber,
StringRef File, bool PublicOnly);
StringRef File, bool IsFileInRootDir, bool PublicOnly);
std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>>
emitInfo(const CXXMethodDecl *D, const FullComment *FC, int LineNumber,
StringRef File, bool PublicOnly);
StringRef File, bool IsFileInRootDir, bool PublicOnly);
// Function to hash a given USR value for storage.
// As USRs (Unified Symbol Resolution) could be large, especially for functions

View File

@ -76,6 +76,18 @@ static llvm::cl::list<std::string> UserStylesheets(
llvm::cl::desc("CSS stylesheets to extend the default styles."),
llvm::cl::cat(ClangDocCategory));
static llvm::cl::opt<std::string> SourceRoot("source-root", llvm::cl::desc(R"(
Directory where processed files are stored.
Links to definition locations will only be
generated if the file is in this dir.)"),
llvm::cl::cat(ClangDocCategory));
static llvm::cl::opt<std::string>
RepositoryUrl("repository", llvm::cl::desc(R"(
URL of repository that hosts code.
Used for links to definition locations.)"),
llvm::cl::cat(ClangDocCategory));
enum OutputFormatTy {
md,
yaml,
@ -142,7 +154,7 @@ bool CreateDirectory(const Twine &DirName, bool ClearDirectory = false) {
// <root>/A/B/C.<ext>
//
// namespace A {
// namesapce B {
// namespace B {
//
// class C {};
//
@ -191,10 +203,18 @@ int main(int argc, const char **argv) {
tooling::ArgumentInsertPosition::END),
ArgAdjuster);
llvm::SmallString<128> SourceRootDir;
// Check if the --source-root flag has a value
if (SourceRoot.empty())
// If it's empty the current path is used as the default
llvm::sys::fs::current_path(SourceRootDir);
clang::doc::ClangDocContext CDCtx = {
Exec->get()->getExecutionContext(),
PublicOnly,
OutDirectory,
SourceRootDir.str(),
RepositoryUrl,
{UserStylesheets.begin(), UserStylesheets.end()},
{"index.js", "index_json.js"}};

View File

@ -66,7 +66,7 @@ Options
.. code-block:: console
$ clang-doc --help
$ clang-doc --help
USAGE: clang-doc [options] <source0> [... <sourceN>]
OPTIONS:
@ -79,17 +79,27 @@ Options
clang-doc options:
-doxygen - Use only doxygen-style comments to generate docs.
-dump - Dump intermediate results to bitcode file.
-extra-arg=<string> - Additional argument to append to the compiler command line
-extra-arg-before=<string> - Additional argument to prepend to the compiler command line
--format=<value> - Format for outputted docs.
=yaml - Documentation in YAML format.
=md - Documentation in MD format.
=html - Documentation in HTML format.
-output=<string> - Directory for outputting generated files.
-p=<string> - Build path
--public - Document only public declarations.
--stylesheets=<string> - CSS stylesheets to extend the default styles.
--doxygen - Use only doxygen-style comments to generate docs.
--extra-arg=<string> - Additional argument to append to the compiler command line
--extra-arg-before=<string> - Additional argument to prepend to the compiler command line
--format=<value> - Format for outputted docs.
=yaml - Documentation in YAML format.
=md - Documentation in MD format.
=html - Documentation in HTML format.
--ignore-map-errors - Continue if files are not mapped correctly.
--output=<string> - Directory for outputting generated files.
-p=<string> - Build path
--public - Document only public declarations.
--repository=<string> -
URL of repository that hosts code.
Used for links to definition locations.
--source-root=<string> -
Directory where processed files are stored.
Links to definition locations will only be
generated if the file is in this dir.
--stylesheets=<string> - CSS stylesheets to extend the default styles.
``stylesheets`` should only be used if ``format`` is set to ``html``.
The following flags shoud only be used if ``format`` is set to ``html``:
- ``repository``
- ``source-root``
- ``stylesheets``

View File

@ -23,9 +23,9 @@ std::unique_ptr<Generator> getHTMLGenerator() {
}
ClangDocContext
getClangDocContext(std::vector<std::string> UserStylesheets = {}) {
ClangDocContext CDCtx;
CDCtx.UserStylesheets = {UserStylesheets.begin(), UserStylesheets.end()};
getClangDocContext(std::vector<std::string> UserStylesheets = {},
StringRef RepositoryUrl = "") {
ClangDocContext CDCtx{{}, {}, {}, {}, RepositoryUrl, UserStylesheets, {}};
CDCtx.UserStylesheets.insert(
CDCtx.UserStylesheets.begin(),
"../share/clang/clang-doc-default-stylesheet.css");
@ -127,7 +127,7 @@ TEST(HTMLGeneratorTest, emitRecordHTML) {
I.Path = "X/Y/Z";
I.Namespace.emplace_back(EmptySID, "A", InfoType::IT_namespace);
I.DefLoc = Location(10, llvm::SmallString<16>{"test.cpp"});
I.DefLoc = Location(10, llvm::SmallString<16>{"dir/test.cpp"}, true);
I.Loc.emplace_back(12, llvm::SmallString<16>{"test.cpp"});
SmallString<16> PathTo;
@ -147,7 +147,7 @@ TEST(HTMLGeneratorTest, emitRecordHTML) {
assert(G);
std::string Buffer;
llvm::raw_string_ostream Actual(Buffer);
ClangDocContext CDCtx = getClangDocContext();
ClangDocContext CDCtx = getClangDocContext({}, "http://www.repository.com");
auto Err = G->generateDocForInfo(&I, Actual, CDCtx);
assert(!Err);
std::string Expected = R"raw(<!DOCTYPE html>
@ -194,7 +194,12 @@ TEST(HTMLGeneratorTest, emitRecordHTML) {
</ul>
<div>
<h1>class r</h1>
<p>Defined at line 10 of test.cpp</p>
<p>
Defined at line
<a href="http://www.repository.com/dir/test.cpp#10">10</a>
of file
<a href="http://www.repository.com/dir/test.cpp">test.cpp</a>
</p>
<p>
Inherits from
<a href="../../../path/to/F.html">F</a>
@ -232,7 +237,7 @@ TEST(HTMLGeneratorTest, emitFunctionHTML) {
I.Name = "f";
I.Namespace.emplace_back(EmptySID, "A", InfoType::IT_namespace);
I.DefLoc = Location(10, llvm::SmallString<16>{"test.cpp"});
I.DefLoc = Location(10, llvm::SmallString<16>{"dir/test.cpp"}, false);
I.Loc.emplace_back(12, llvm::SmallString<16>{"test.cpp"});
SmallString<16> PathTo;
@ -246,7 +251,7 @@ TEST(HTMLGeneratorTest, emitFunctionHTML) {
assert(G);
std::string Buffer;
llvm::raw_string_ostream Actual(Buffer);
ClangDocContext CDCtx = getClangDocContext();
ClangDocContext CDCtx = getClangDocContext({}, "https://www.repository.com");
auto Err = G->generateDocForInfo(&I, Actual, CDCtx);
assert(!Err);
std::string Expected = R"raw(<!DOCTYPE html>
@ -263,7 +268,7 @@ TEST(HTMLGeneratorTest, emitFunctionHTML) {
<a href="path/to/int.html">int</a>
P)
</p>
<p>Defined at line 10 of test.cpp</p>
<p>Defined at line 10 of file dir/test.cpp</p>
</div>
)raw";
@ -275,7 +280,7 @@ TEST(HTMLGeneratorTest, emitEnumHTML) {
I.Name = "e";
I.Namespace.emplace_back(EmptySID, "A", InfoType::IT_namespace);
I.DefLoc = Location(10, llvm::SmallString<16>{"test.cpp"});
I.DefLoc = Location(10, llvm::SmallString<16>{"test.cpp"}, true);
I.Loc.emplace_back(12, llvm::SmallString<16>{"test.cpp"});
I.Members.emplace_back("X");
@ -285,7 +290,7 @@ TEST(HTMLGeneratorTest, emitEnumHTML) {
assert(G);
std::string Buffer;
llvm::raw_string_ostream Actual(Buffer);
ClangDocContext CDCtx = getClangDocContext();
ClangDocContext CDCtx = getClangDocContext({}, "www.repository.com");
auto Err = G->generateDocForInfo(&I, Actual, CDCtx);
assert(!Err);
std::string Expected = R"raw(<!DOCTYPE html>
@ -299,7 +304,12 @@ TEST(HTMLGeneratorTest, emitEnumHTML) {
<ul>
<li>X</li>
</ul>
<p>Defined at line 10 of test.cpp</p>
<p>
Defined at line
<a href="https://www.repository.com/test.cpp#10">10</a>
of file
<a href="https://www.repository.com/test.cpp">test.cpp</a>
</p>
</div>
)raw";
@ -368,7 +378,7 @@ TEST(HTMLGeneratorTest, emitCommentHTML) {
<div>
<h3 id="0000000000000000000000000000000000000000">f</h3>
<p>void f(int I, int J)</p>
<p>Defined at line 10 of test.cpp</p>
<p>Defined at line 10 of file test.cpp</p>
<div>
<div>
<p> Brief description.</p>

View File

@ -37,7 +37,7 @@ public:
template <typename T> bool mapDecl(const T *D) {
auto I = serialize::emitInfo(D, getComment(D), /*Line=*/0,
/*File=*/"test.cpp", Public);
/*File=*/"test.cpp", true, Public);
if (I.first)
EmittedInfos.emplace_back(std::move(I.first));
if (I.second)