Skip to content

Commit

Permalink
[clang-doc] Handle anonymous namespaces
Browse files Browse the repository at this point in the history
Improves output for anonymous decls, and updates the '--public' flag to exclude everything under an anonymous namespace.

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

llvm-svn: 364674
  • Loading branch information
Julie Hockett committed Jun 28, 2019
1 parent 70a8027 commit d900ef0
Show file tree
Hide file tree
Showing 5 changed files with 88 additions and 30 deletions.
33 changes: 33 additions & 0 deletions clang-tools-extra/clang-doc/Representation.cpp
Expand Up @@ -21,6 +21,7 @@
//===----------------------------------------------------------------------===//
#include "Representation.h"
#include "llvm/Support/Error.h"
#include "llvm/Support/Path.h"

namespace clang {
namespace doc {
Expand Down Expand Up @@ -194,5 +195,37 @@ void FunctionInfo::merge(FunctionInfo &&Other) {
SymbolInfo::merge(std::move(Other));
}

llvm::SmallString<16> Info::extractName() {
if (!Name.empty())
return Name;

switch (IT) {
case InfoType::IT_namespace:
// Cover the case where the project contains a base namespace called
// 'GlobalNamespace' (i.e. a namespace at the same level as the global
// namespace, which would conflict with the hard-coded global namespace name
// below.)
if (Name == "GlobalNamespace" && Namespace.empty())
return llvm::SmallString<16>("@GlobalNamespace");
// The case of anonymous namespaces is taken care of in serialization,
// so here we can safely assume an unnamed namespace is the global
// one.
return llvm::SmallString<16>("GlobalNamespace");
case InfoType::IT_record:
return llvm::SmallString<16>("@nonymous_record_" +
toHex(llvm::toStringRef(USR)));
case InfoType::IT_enum:
return llvm::SmallString<16>("@nonymous_enum_" +
toHex(llvm::toStringRef(USR)));
case InfoType::IT_function:
return llvm::SmallString<16>("@nonymous_function_" +
toHex(llvm::toStringRef(USR)));
case InfoType::IT_default:
return llvm::SmallString<16>("@nonymous_" + toHex(llvm::toStringRef(USR)));
}
llvm_unreachable("Invalid InfoType.");
return llvm::SmallString<16>("");
}

} // namespace doc
} // namespace clang
2 changes: 2 additions & 0 deletions clang-tools-extra/clang-doc/Representation.h
Expand Up @@ -223,6 +223,8 @@ struct Info {
void mergeBase(Info &&I);
bool mergeable(const Info &Other);

llvm::SmallString<16> extractName();

// Returns a reference to the parent scope (that is, the immediate parent
// namespace or class in which this decl resides).
llvm::Expected<Reference> getEnclosingScope();
Expand Down
75 changes: 50 additions & 25 deletions clang-tools-extra/clang-doc/Serialize.cpp
Expand Up @@ -270,13 +270,19 @@ static void parseBases(RecordInfo &I, const CXXRecordDecl *D) {
template <typename T>
static void
populateParentNamespaces(llvm::SmallVector<Reference, 4> &Namespaces,
const T *D) {
const T *D, bool &IsAnonymousNamespace) {
const auto *DC = dyn_cast<DeclContext>(D);
while ((DC = DC->getParent())) {
if (const auto *N = dyn_cast<NamespaceDecl>(DC))
Namespaces.emplace_back(getUSRForDecl(N), N->getNameAsString(),
if (const auto *N = dyn_cast<NamespaceDecl>(DC)) {
std::string Namespace;
if (N->isAnonymousNamespace()) {
Namespace = "@nonymous_namespace";
IsAnonymousNamespace = true;
} else
Namespace = N->getNameAsString();
Namespaces.emplace_back(getUSRForDecl(N), Namespace,
InfoType::IT_namespace);
else if (const auto *N = dyn_cast<RecordDecl>(DC))
} else if (const auto *N = dyn_cast<RecordDecl>(DC))
Namespaces.emplace_back(getUSRForDecl(N), N->getNameAsString(),
InfoType::IT_record);
else if (const auto *N = dyn_cast<FunctionDecl>(DC))
Expand All @@ -289,10 +295,11 @@ populateParentNamespaces(llvm::SmallVector<Reference, 4> &Namespaces,
}

template <typename T>
static void populateInfo(Info &I, const T *D, const FullComment *C) {
static void populateInfo(Info &I, const T *D, const FullComment *C,
bool &IsInAnonymousNamespace) {
I.USR = getUSRForDecl(D);
I.Name = D->getNameAsString();
populateParentNamespaces(I.Namespace, D);
populateParentNamespaces(I.Namespace, D, IsInAnonymousNamespace);
if (C) {
I.Description.emplace_back();
parseFullComment(C, I.Description.back());
Expand All @@ -301,8 +308,9 @@ 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) {
populateInfo(I, D, C);
int LineNumber, StringRef Filename,
bool &IsInAnonymousNamespace) {
populateInfo(I, D, C, IsInAnonymousNamespace);
if (D->isThisDeclarationADefinition())
I.DefLoc.emplace(LineNumber, Filename);
else
Expand All @@ -311,8 +319,9 @@ static void populateSymbolInfo(SymbolInfo &I, const T *D, const FullComment *C,

static void populateFunctionInfo(FunctionInfo &I, const FunctionDecl *D,
const FullComment *FC, int LineNumber,
StringRef Filename) {
populateSymbolInfo(I, D, FC, LineNumber, Filename);
StringRef Filename,
bool &IsInAnonymousNamespace) {
populateSymbolInfo(I, D, FC, LineNumber, Filename, IsInAnonymousNamespace);
if (const auto *T = getDeclForType(D->getReturnType())) {
if (dyn_cast<EnumDecl>(T))
I.ReturnType =
Expand All @@ -329,21 +338,28 @@ static void populateFunctionInfo(FunctionInfo &I, const FunctionDecl *D,
std::unique_ptr<Info> emitInfo(const NamespaceDecl *D, const FullComment *FC,
int LineNumber, llvm::StringRef File,
bool PublicOnly) {
if (PublicOnly && ((D->isAnonymousNamespace()) ||
auto I = llvm::make_unique<NamespaceInfo>();
bool IsInAnonymousNamespace = false;
populateInfo(*I, D, FC, IsInAnonymousNamespace);
if (PublicOnly && ((IsInAnonymousNamespace || D->isAnonymousNamespace()) ||
!isPublic(D->getAccess(), D->getLinkageInternal())))
return nullptr;
auto I = llvm::make_unique<NamespaceInfo>();
populateInfo(*I, D, FC);
I->Name = D->isAnonymousNamespace()
? llvm::SmallString<16>("@nonymous_namespace")
: I->Name;
return std::unique_ptr<Info>{std::move(I)};
}

std::unique_ptr<Info> emitInfo(const RecordDecl *D, const FullComment *FC,
int LineNumber, llvm::StringRef File,
bool PublicOnly) {
if (PublicOnly && !isPublic(D->getAccess(), D->getLinkageInternal()))
return nullptr;
auto I = llvm::make_unique<RecordInfo>();
populateSymbolInfo(*I, D, FC, LineNumber, File);
bool IsInAnonymousNamespace = false;
populateSymbolInfo(*I, D, FC, LineNumber, File, IsInAnonymousNamespace);
if (PublicOnly && ((IsInAnonymousNamespace ||
!isPublic(D->getAccess(), D->getLinkageInternal()))))
return nullptr;

I->TagType = D->getTagKind();
parseFields(*I, D, PublicOnly);
if (const auto *C = dyn_cast<CXXRecordDecl>(D)) {
Expand All @@ -359,10 +375,13 @@ std::unique_ptr<Info> emitInfo(const RecordDecl *D, const FullComment *FC,
std::unique_ptr<Info> emitInfo(const FunctionDecl *D, const FullComment *FC,
int LineNumber, llvm::StringRef File,
bool PublicOnly) {
if (PublicOnly && !isPublic(D->getAccess(), D->getLinkageInternal()))
return nullptr;
FunctionInfo Func;
populateFunctionInfo(Func, D, FC, LineNumber, File);
bool IsInAnonymousNamespace = false;
populateFunctionInfo(Func, D, FC, LineNumber, File, IsInAnonymousNamespace);
if (PublicOnly && ((IsInAnonymousNamespace ||
!isPublic(D->getAccess(), D->getLinkageInternal()))))
return nullptr;

Func.Access = clang::AccessSpecifier::AS_none;

// Wrap in enclosing scope
Expand All @@ -378,10 +397,13 @@ std::unique_ptr<Info> emitInfo(const FunctionDecl *D, const FullComment *FC,
std::unique_ptr<Info> emitInfo(const CXXMethodDecl *D, const FullComment *FC,
int LineNumber, llvm::StringRef File,
bool PublicOnly) {
if (PublicOnly && !isPublic(D->getAccess(), D->getLinkageInternal()))
return nullptr;
FunctionInfo Func;
populateFunctionInfo(Func, D, FC, LineNumber, File);
bool IsInAnonymousNamespace = false;
populateFunctionInfo(Func, D, FC, LineNumber, File, IsInAnonymousNamespace);
if (PublicOnly && ((IsInAnonymousNamespace ||
!isPublic(D->getAccess(), D->getLinkageInternal()))))
return nullptr;

Func.IsMethod = true;

const NamedDecl *Parent = nullptr;
Expand All @@ -406,10 +428,13 @@ std::unique_ptr<Info> emitInfo(const CXXMethodDecl *D, const FullComment *FC,
std::unique_ptr<Info> emitInfo(const EnumDecl *D, const FullComment *FC,
int LineNumber, llvm::StringRef File,
bool PublicOnly) {
if (PublicOnly && !isPublic(D->getAccess(), D->getLinkageInternal()))
return nullptr;
EnumInfo Enum;
populateSymbolInfo(Enum, D, FC, LineNumber, File);
bool IsInAnonymousNamespace = false;
populateSymbolInfo(Enum, D, FC, LineNumber, File, IsInAnonymousNamespace);
if (PublicOnly && ((IsInAnonymousNamespace ||
!isPublic(D->getAccess(), D->getLinkageInternal()))))
return nullptr;

Enum.Scoped = D->isScoped();
parseEnumerators(Enum, D);

Expand Down
7 changes: 2 additions & 5 deletions clang-tools-extra/clang-doc/tool/ClangDocMain.cpp
Expand Up @@ -132,9 +132,6 @@ getInfoOutputFile(StringRef Root,
if (CreateDirectory(Path))
return llvm::make_error<llvm::StringError>("Unable to create directory.\n",
llvm::inconvertibleErrorCode());

if (Name.empty())
Name = "GlobalNamespace";
llvm::sys::path::append(Path, Name + Ext);
return Path;
}
Expand Down Expand Up @@ -222,8 +219,8 @@ int main(int argc, const char **argv) {

doc::Info *I = Reduced.get().get();

auto InfoPath =
getInfoOutputFile(OutDirectory, I->Namespace, I->Name, "." + Format);
auto InfoPath = getInfoOutputFile(OutDirectory, I->Namespace,
I->extractName(), "." + Format);
if (!InfoPath) {
llvm::errs() << toString(InfoPath.takeError()) << "\n";
continue;
Expand Down
1 change: 1 addition & 0 deletions clang-tools-extra/unittests/clang-doc/SerializeTest.cpp
Expand Up @@ -131,6 +131,7 @@ TEST(SerializeTest, emitAnonymousNamespaceInfo) {

NamespaceInfo *A = InfoAsNamespace(Infos[0].get());
NamespaceInfo ExpectedA(EmptySID);
ExpectedA.Name = "@nonymous_namespace";
CheckNamespaceInfo(&ExpectedA, A);
}

Expand Down

0 comments on commit d900ef0

Please sign in to comment.