[clang-doc] Handle anonymous namespaces

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
This commit is contained in:
Julie Hockett 2019-06-28 19:07:56 +00:00
parent 70a8027c60
commit d900ef0a5b
5 changed files with 88 additions and 30 deletions

View File

@ -21,6 +21,7 @@
//===----------------------------------------------------------------------===//
#include "Representation.h"
#include "llvm/Support/Error.h"
#include "llvm/Support/Path.h"
namespace clang {
namespace doc {
@ -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

View File

@ -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();

View File

@ -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))
@ -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());
@ -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
@ -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 =
@ -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)) {
@ -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
@ -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;
@ -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);

View File

@ -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;
}
@ -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;

View File

@ -131,6 +131,7 @@ TEST(SerializeTest, emitAnonymousNamespaceInfo) {
NamespaceInfo *A = InfoAsNamespace(Infos[0].get());
NamespaceInfo ExpectedA(EmptySID);
ExpectedA.Name = "@nonymous_namespace";
CheckNamespaceInfo(&ExpectedA, A);
}