Skip to content

Commit

Permalink
[clang-doc] Adding PublicOnly flag
Browse files Browse the repository at this point in the history
Submitted on behalf of Annie Cherkaev (@anniecherk)

Added a flag which, when enabled, documents only those methods and
fields which have a Public attribute.

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

llvm-svn: 337602
  • Loading branch information
Julie Hockett committed Jul 20, 2018
1 parent 1434b81 commit eb50a2e
Show file tree
Hide file tree
Showing 12 changed files with 528 additions and 32 deletions.
17 changes: 9 additions & 8 deletions clang-tools-extra/clang-doc/ClangDoc.cpp
Expand Up @@ -15,6 +15,7 @@

#include "ClangDoc.h"
#include "Mapper.h"
#include "Representation.h"
#include "clang/AST/AST.h"
#include "clang/AST/ASTConsumer.h"
#include "clang/AST/ASTContext.h"
Expand All @@ -28,33 +29,33 @@ namespace doc {

class MapperActionFactory : public tooling::FrontendActionFactory {
public:
MapperActionFactory(tooling::ExecutionContext *ECtx) : ECtx(ECtx) {}
MapperActionFactory(ClangDocContext CDCtx) : CDCtx(CDCtx) {}
clang::FrontendAction *create() override;

private:
tooling::ExecutionContext *ECtx;
ClangDocContext CDCtx;
};

clang::FrontendAction *MapperActionFactory::create() {
class ClangDocAction : public clang::ASTFrontendAction {
public:
ClangDocAction(ExecutionContext *ECtx) : ECtx(ECtx) {}
ClangDocAction(ClangDocContext CDCtx) : CDCtx(CDCtx) {}

std::unique_ptr<clang::ASTConsumer>
CreateASTConsumer(clang::CompilerInstance &Compiler,
llvm::StringRef InFile) override {
return llvm::make_unique<MapASTVisitor>(&Compiler.getASTContext(), ECtx);
return llvm::make_unique<MapASTVisitor>(&Compiler.getASTContext(), CDCtx);
}

private:
ExecutionContext *ECtx;
ClangDocContext CDCtx;
};
return new ClangDocAction(ECtx);
return new ClangDocAction(CDCtx);
}

std::unique_ptr<tooling::FrontendActionFactory>
newMapperActionFactory(tooling::ExecutionContext *ECtx) {
return llvm::make_unique<MapperActionFactory>(ECtx);
newMapperActionFactory(ClangDocContext CDCtx) {
return llvm::make_unique<MapperActionFactory>(CDCtx);
}

} // namespace doc
Expand Down
3 changes: 2 additions & 1 deletion clang-tools-extra/clang-doc/ClangDoc.h
Expand Up @@ -17,6 +17,7 @@
#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_DOC_CLANGDOC_H
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_DOC_CLANGDOC_H

#include "Representation.h"
#include "clang/Tooling/Execution.h"
#include "clang/Tooling/StandaloneExecution.h"
#include "clang/Tooling/Tooling.h"
Expand All @@ -25,7 +26,7 @@ namespace clang {
namespace doc {

std::unique_ptr<tooling::FrontendActionFactory>
newMapperActionFactory(tooling::ExecutionContext *ECtx);
newMapperActionFactory(ClangDocContext CDCtx);

} // namespace doc
} // namespace clang
Expand Down
12 changes: 8 additions & 4 deletions clang-tools-extra/clang-doc/Mapper.cpp
Expand Up @@ -33,10 +33,14 @@ template <typename T> bool MapASTVisitor::mapDecl(const T *D) {
if (index::generateUSRForDecl(D, USR))
return true;

ECtx->reportResult(llvm::toHex(llvm::toStringRef(serialize::hashUSR(USR))),
serialize::emitInfo(D, getComment(D, D->getASTContext()),
getLine(D, D->getASTContext()),
getFile(D, D->getASTContext())));
std::string info = serialize::emitInfo(
D, getComment(D, D->getASTContext()), getLine(D, D->getASTContext()),
getFile(D, D->getASTContext()), CDCtx.PublicOnly);

if (info != "")
CDCtx.ECtx->reportResult(
llvm::toHex(llvm::toStringRef(serialize::hashUSR(USR))), info);

return true;
}

Expand Down
7 changes: 4 additions & 3 deletions clang-tools-extra/clang-doc/Mapper.h
Expand Up @@ -18,6 +18,7 @@
#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_DOC_MAPPER_H
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_DOC_MAPPER_H

#include "Representation.h"
#include "clang/AST/RecursiveASTVisitor.h"
#include "clang/Tooling/Execution.h"

Expand All @@ -30,8 +31,8 @@ namespace doc {
class MapASTVisitor : public clang::RecursiveASTVisitor<MapASTVisitor>,
public ASTConsumer {
public:
explicit MapASTVisitor(ASTContext *Ctx, ExecutionContext *ECtx)
: ECtx(ECtx) {}
explicit MapASTVisitor(ASTContext *Ctx, ClangDocContext CDCtx)
: CDCtx(CDCtx) {}

void HandleTranslationUnit(ASTContext &Context) override;
bool VisitNamespaceDecl(const NamespaceDecl *D);
Expand All @@ -48,7 +49,7 @@ class MapASTVisitor : public clang::RecursiveASTVisitor<MapASTVisitor>,
comments::FullComment *getComment(const NamedDecl *D,
const ASTContext &Context) const;

ExecutionContext *ECtx;
ClangDocContext CDCtx;
};

} // namespace doc
Expand Down
2 changes: 1 addition & 1 deletion clang-tools-extra/clang-doc/Representation.cpp
Expand Up @@ -42,7 +42,7 @@ llvm::Expected<std::unique_ptr<Info>>
mergeInfos(std::vector<std::unique_ptr<Info>> &Values) {
if (Values.empty())
return llvm::make_error<llvm::StringError>("No info values to merge.\n",
llvm::inconvertibleErrorCode());
llvm::inconvertibleErrorCode());

switch (Values[0]->IT) {
case InfoType::IT_namespace:
Expand Down
6 changes: 6 additions & 0 deletions clang-tools-extra/clang-doc/Representation.h
Expand Up @@ -17,6 +17,7 @@

#include "clang/AST/Type.h"
#include "clang/Basic/Specifiers.h"
#include "clang/Tooling/StandaloneExecution.h"
#include "llvm/ADT/Optional.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/StringExtras.h"
Expand Down Expand Up @@ -239,6 +240,11 @@ struct EnumInfo : public SymbolInfo {
llvm::Expected<std::unique_ptr<Info>>
mergeInfos(std::vector<std::unique_ptr<Info>> &Values);

struct ClangDocContext {
tooling::ExecutionContext *ECtx;
bool PublicOnly;
};

} // namespace doc
} // namespace clang

Expand Down
37 changes: 30 additions & 7 deletions clang-tools-extra/clang-doc/Serialize.cpp
Expand Up @@ -171,8 +171,20 @@ static RecordDecl *getDeclForType(const QualType &T) {
return Ty->getDecl()->getDefinition();
}

static void parseFields(RecordInfo &I, const RecordDecl *D) {
static bool isPublic(const clang::AccessSpecifier AS,
const clang::Linkage Link) {
if (AS == clang::AccessSpecifier::AS_private)
return false;
else if ((Link == clang::Linkage::ModuleLinkage) ||
(Link == clang::Linkage::ExternalLinkage))
return true;
return false; // otherwise, linkage is some form of internal linkage
}

static void parseFields(RecordInfo &I, const RecordDecl *D, bool PublicOnly) {
for (const FieldDecl *F : D->fields()) {
if (PublicOnly && !isPublic(F->getAccessUnsafe(), F->getLinkageInternal()))
continue;
if (const auto *T = getDeclForType(F->getTypeSourceInfo()->getType())) {
// Use getAccessUnsafe so that we just get the default AS_none if it's not
// valid, as opposed to an assert.
Expand Down Expand Up @@ -295,33 +307,42 @@ static void populateFunctionInfo(FunctionInfo &I, const FunctionDecl *D,
}

std::string emitInfo(const NamespaceDecl *D, const FullComment *FC,
int LineNumber, llvm::StringRef File) {
int LineNumber, llvm::StringRef File, bool PublicOnly) {
if (PublicOnly && ((D->isAnonymousNamespace()) ||
!isPublic(D->getAccess(), D->getLinkageInternal())))
return "";
NamespaceInfo I;
populateInfo(I, D, FC);
return serialize(I);
}

std::string emitInfo(const RecordDecl *D, const FullComment *FC, int LineNumber,
llvm::StringRef File) {
llvm::StringRef File, bool PublicOnly) {
if (PublicOnly && !isPublic(D->getAccess(), D->getLinkageInternal()))
return "";
RecordInfo I;
populateSymbolInfo(I, D, FC, LineNumber, File);
I.TagType = D->getTagKind();
parseFields(I, D);
parseFields(I, D, PublicOnly);
if (const auto *C = dyn_cast<CXXRecordDecl>(D))
parseBases(I, C);
return serialize(I);
}

std::string emitInfo(const FunctionDecl *D, const FullComment *FC,
int LineNumber, llvm::StringRef File) {
int LineNumber, llvm::StringRef File, bool PublicOnly) {
if (PublicOnly && !isPublic(D->getAccess(), D->getLinkageInternal()))
return "";
FunctionInfo I;
populateFunctionInfo(I, D, FC, LineNumber, File);
I.Access = clang::AccessSpecifier::AS_none;
return serialize(I);
}

std::string emitInfo(const CXXMethodDecl *D, const FullComment *FC,
int LineNumber, llvm::StringRef File) {
int LineNumber, llvm::StringRef File, bool PublicOnly) {
if (PublicOnly && !isPublic(D->getAccess(), D->getLinkageInternal()))
return "";
FunctionInfo I;
populateFunctionInfo(I, D, FC, LineNumber, File);
I.IsMethod = true;
Expand All @@ -332,7 +353,9 @@ std::string emitInfo(const CXXMethodDecl *D, const FullComment *FC,
}

std::string emitInfo(const EnumDecl *D, const FullComment *FC, int LineNumber,
llvm::StringRef File) {
llvm::StringRef File, bool PublicOnly) {
if (PublicOnly && !isPublic(D->getAccess(), D->getLinkageInternal()))
return "";
EnumInfo I;
populateSymbolInfo(I, D, FC, LineNumber, File);
I.Scoped = D->isScoped();
Expand Down
10 changes: 5 additions & 5 deletions clang-tools-extra/clang-doc/Serialize.h
Expand Up @@ -29,15 +29,15 @@ namespace doc {
namespace serialize {

std::string emitInfo(const NamespaceDecl *D, const FullComment *FC,
int LineNumber, StringRef File);
int LineNumber, StringRef File, bool PublicOnly);
std::string emitInfo(const RecordDecl *D, const FullComment *FC, int LineNumber,
StringRef File);
StringRef File, bool PublicOnly);
std::string emitInfo(const EnumDecl *D, const FullComment *FC, int LineNumber,
StringRef File);
StringRef File, bool PublicOnly);
std::string emitInfo(const FunctionDecl *D, const FullComment *FC,
int LineNumber, StringRef File);
int LineNumber, StringRef File, bool PublicOnly);
std::string emitInfo(const CXXMethodDecl *D, const FullComment *FC,
int LineNumber, StringRef File);
int LineNumber, StringRef File, bool PublicOnly);

// Function to hash a given USR value for storage.
// As USRs (Unified Symbol Resolution) could be large, especially for functions
Expand Down
11 changes: 8 additions & 3 deletions clang-tools-extra/clang-doc/tool/ClangDocMain.cpp
Expand Up @@ -64,6 +64,10 @@ static llvm::cl::opt<bool> DumpIntermediateResult(
llvm::cl::desc("Dump intermediate results to bitcode file."),
llvm::cl::init(false), llvm::cl::cat(ClangDocCategory));

static llvm::cl::opt<bool>
PublicOnly("public", llvm::cl::desc("Document only public declarations."),
llvm::cl::init(false), llvm::cl::cat(ClangDocCategory));

enum OutputFormatTy {
yaml,
};
Expand Down Expand Up @@ -171,9 +175,10 @@ int main(int argc, const char **argv) {

// Mapping phase
llvm::outs() << "Mapping decls...\n";
auto Err = Exec->get()->execute(
doc::newMapperActionFactory(Exec->get()->getExecutionContext()),
ArgAdjuster);
clang::doc::ClangDocContext CDCtx = {Exec->get()->getExecutionContext(),
PublicOnly};
auto Err =
Exec->get()->execute(doc::newMapperActionFactory(CDCtx), ArgAdjuster);
if (Err) {
llvm::errs() << toString(std::move(Err)) << "\n";
return 1;
Expand Down
61 changes: 61 additions & 0 deletions clang-tools-extra/test/clang-doc/module.cpp
@@ -0,0 +1,61 @@
// RUN: rm -rf %t
// RUN: mkdir %t
// RUN: echo "" > %t/compile_flags.txt
// RUN: cp "%s" "%t/test.cpp"
// RUN: clang-doc --extra-arg=-fmodules-ts --doxygen -p %t %t/test.cpp -output=%t/docs
// RUN: cat %t/docs/moduleFunction.yaml | FileCheck %s --check-prefix=CHECK-A
// RUN: cat %t/docs/staticModuleFunction.yaml | FileCheck %s --check-prefix=CHECK-B
// RUN: cat %t/docs/exportedModuleFunction.yaml | FileCheck %s --check-prefix=CHECK-C

export module M;

int moduleFunction(int x); //ModuleLinkage
// CHECK-A: ---
// CHECK-A-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}'
// CHECK-A-NEXT: Name: 'moduleFunction'
// CHECK-A-NEXT: Location:
// CHECK-A-NEXT: - LineNumber: 12
// CHECK-A-NEXT: Filename: {{.*}}
// CHECK-A-NEXT: Params:
// CHECK-A-NEXT: - Type:
// CHECK-A-NEXT: Name: 'int'
// CHECK-A-NEXT: Name: 'x'
// CHECK-A-NEXT: ReturnType:
// CHECK-A-NEXT: Type:
// CHECK-A-NEXT: Name: 'int'
// CHECK-A-NEXT: ...

static int staticModuleFunction(int x); //ModuleInternalLinkage
// CHECK-B: ---
// CHECK-B-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}'
// CHECK-B-NEXT: Name: 'staticModuleFunction'
// CHECK-B-NEXT: Location:
// CHECK-B-NEXT: - LineNumber: 28
// CHECK-B-NEXT: Filename: {{.*}}
// CHECK-B-NEXT: Params:
// CHECK-B-NEXT: - Type:
// CHECK-B-NEXT: Name: 'int'
// CHECK-B-NEXT: Name: 'x'
// CHECK-B-NEXT: ReturnType:
// CHECK-B-NEXT: Type:
// CHECK-B-NEXT: Name: 'int'
// CHECK-B-NEXT: ...

export double exportedModuleFunction(double y, int z); //ExternalLinkage
// CHECK-C: ---
// CHECK-C-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}'
// CHECK-C-NEXT: Name: 'exportedModuleFunction'
// CHECK-C-NEXT: Location:
// CHECK-C-NEXT: - LineNumber: 44
// CHECK-C-NEXT: Filename: {{.*}}
// CHECK-C-NEXT: Params:
// CHECK-C-NEXT: - Type:
// CHECK-C-NEXT: Name: 'double'
// CHECK-C-NEXT: Name: 'y'
// CHECK-C-NEXT: - Type:
// CHECK-C-NEXT: Name: 'int'
// CHECK-C-NEXT: Name: 'z'
// CHECK-C-NEXT: ReturnType:
// CHECK-C-NEXT: Type:
// CHECK-C-NEXT: Name: 'double'
// CHECK-C-NEXT: ...
53 changes: 53 additions & 0 deletions clang-tools-extra/test/clang-doc/public-module.cpp
@@ -0,0 +1,53 @@
// This test requires linux because it uses `diff` and compares filepaths
// REQUIRES: system-linux
// RUN: rm -rf %t
// RUN: mkdir %t
// RUN: echo "" > %t/compile_flags.txt
// RUN: cp "%s" "%t/test.cpp"
// RUN: clang-doc --public --extra-arg=-fmodules-ts --doxygen -p %t %t/test.cpp -output=%t/docs-with-public-flag
// RUN: clang-doc --extra-arg=-fmodules-ts --doxygen -p %t %t/test.cpp -output=%t/docs-without
// RUN: cat %t/docs-with-public-flag/moduleFunction.yaml | FileCheck %s --check-prefix=CHECK-A
// RUN: cat %t/docs-with-public-flag/exportedModuleFunction.yaml | FileCheck %s --check-prefix=CHECK-B
// RUN: (diff -qry %t/docs-with-public-flag %t/docs-without | sed 's:.*/::' > %t/public.diff) || true
// RUN: cat %t/public.diff | FileCheck %s --check-prefix=CHECK-C

export module M;

int moduleFunction(int x); //ModuleLinkage
// CHECK-A: ---
// CHECK-A-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}'
// CHECK-A-NEXT: Name: 'moduleFunction'
// CHECK-A-NEXT: Location:
// CHECK-A-NEXT: - LineNumber: 16
// CHECK-A-NEXT: Filename: {{.*}}
// CHECK-A-NEXT: Params:
// CHECK-A-NEXT: - Type:
// CHECK-A-NEXT: Name: 'int'
// CHECK-A-NEXT: Name: 'x'
// CHECK-A-NEXT: ReturnType:
// CHECK-A-NEXT: Type:
// CHECK-A-NEXT: Name: 'int'
// CHECK-A-NEXT: ...

static int staticModuleFunction(int x); //ModuleInternalLinkage

export double exportedModuleFunction(double y, int z); //ExternalLinkage
// CHECK-B: ---
// CHECK-B-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}'
// CHECK-B-NEXT: Name: 'exportedModuleFunction'
// CHECK-B-NEXT: Location:
// CHECK-B-NEXT: - LineNumber: 34
// CHECK-B-NEXT: Filename: {{.*}}
// CHECK-B-NEXT: Params:
// CHECK-B-NEXT: - Type:
// CHECK-B-NEXT: Name: 'double'
// CHECK-B-NEXT: Name: 'y'
// CHECK-B-NEXT: - Type:
// CHECK-B-NEXT: Name: 'int'
// CHECK-B-NEXT: Name: 'z'
// CHECK-B-NEXT: ReturnType:
// CHECK-B-NEXT: Type:
// CHECK-B-NEXT: Name: 'double'
// CHECK-B-NEXT: ...

// CHECK-C: docs-without: staticModuleFunction.yaml

0 comments on commit eb50a2e

Please sign in to comment.