diff --git a/CMakeLists.txt b/CMakeLists.txt index 9f078147a..8ba1aa7b2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -93,7 +93,7 @@ file(GLOB_RECURSE LIB_SOURCES CONFIGURE_DEPENDS source/api/*.h source/api/*.hpp source/api/*.cpp - #source/api/*.natvis + source/api/*.natvis ) add_library(mrdox-api ${MRDOX_LINK_MODE} ${LIB_INCLUDES} ${LIB_SOURCES}) diff --git a/include/mrdox/Corpus.hpp b/include/mrdox/Corpus.hpp index bcf07a789..4d9017197 100644 --- a/include/mrdox/Corpus.hpp +++ b/include/mrdox/Corpus.hpp @@ -106,7 +106,7 @@ class MRDOX_VISIBLE MRDOX_DECL virtual bool visit(FunctionInfo const&); MRDOX_DECL virtual bool visit(TypedefInfo const&); MRDOX_DECL virtual bool visit(EnumInfo const&); - MRDOX_DECL virtual bool visit(VariableInfo const&); + MRDOX_DECL virtual bool visit(VarInfo const&); }; /** Visit the specified symbol ID or node. @@ -160,7 +160,7 @@ get( Assert(t->IT == InfoType::IT_typedef); else if constexpr(std::is_same_v) Assert(t->IT == InfoType::IT_enum); - else if constexpr(std::is_same_v) + else if constexpr(std::is_same_v) Assert(t->IT == InfoType::IT_variable); return *t; } diff --git a/include/mrdox/Metadata.hpp b/include/mrdox/Metadata.hpp index 24218bd33..2f7423408 100644 --- a/include/mrdox/Metadata.hpp +++ b/include/mrdox/Metadata.hpp @@ -36,6 +36,6 @@ #include #include #include -#include +#include #endif diff --git a/include/mrdox/Metadata/Function.hpp b/include/mrdox/Metadata/Function.hpp index 27bae6d9c..de00902af 100644 --- a/include/mrdox/Metadata/Function.hpp +++ b/include/mrdox/Metadata/Function.hpp @@ -83,7 +83,9 @@ enum class FnFlags1 : std::uint32_t 0x00000008 + 0x00000010, - isExplicit = 0x00000020 + isExplicit = 0x00000020, + + functionKind = 0b00000000'00000000'00011111'11000000 }; // TODO: Expand to allow for documenting templating and default args. diff --git a/include/mrdox/Metadata/Scope.hpp b/include/mrdox/Metadata/Scope.hpp index 522c3d09f..b5f0feedc 100644 --- a/include/mrdox/Metadata/Scope.hpp +++ b/include/mrdox/Metadata/Scope.hpp @@ -39,7 +39,7 @@ struct Scope std::vector Functions; std::vector Typedefs; std::vector Enums; - std::vector Variables; + std::vector Vars; explicit Scope( diff --git a/include/mrdox/Metadata/Variable.hpp b/include/mrdox/Metadata/Var.hpp similarity index 97% rename from include/mrdox/Metadata/Variable.hpp rename to include/mrdox/Metadata/Var.hpp index 55b8d2318..733fcec3a 100644 --- a/include/mrdox/Metadata/Variable.hpp +++ b/include/mrdox/Metadata/Var.hpp @@ -31,7 +31,7 @@ enum class VarFlags0 : std::uint32_t This includes variables at namespace scope, and static variables at class scope. */ -struct VariableInfo +struct VarInfo : SymbolInfo , TypeInfo // holds the type of this variable { @@ -42,7 +42,7 @@ struct VariableInfo static constexpr InfoType type_id = InfoType::IT_variable; explicit - VariableInfo( + VarInfo( SymbolID ID = SymbolID(), llvm::StringRef Name = llvm::StringRef()) : SymbolInfo(InfoType::IT_variable, ID, Name) diff --git a/include/mrdox/MetadataFwd.hpp b/include/mrdox/MetadataFwd.hpp index 03664e2d2..c0054b34e 100644 --- a/include/mrdox/MetadataFwd.hpp +++ b/include/mrdox/MetadataFwd.hpp @@ -44,7 +44,7 @@ struct TemplateParamInfo; struct TemplateSpecializationInfo; struct TypeInfo; struct TypedefInfo; -struct VariableInfo; +struct VarInfo; struct VerbatimBlock; } // mrdox diff --git a/mrdox.rnc b/mrdox.rnc index 940f679dd..927ff5a5d 100644 --- a/mrdox.rnc +++ b/mrdox.rnc @@ -100,7 +100,7 @@ grammar { Javadoc? } - VariableInfo = element variable { + VarInfo = element var { Name, Id, (Location & diff --git a/mrdox.rng b/mrdox.rng index 1c2970b50..2c1a74c72 100644 --- a/mrdox.rng +++ b/mrdox.rng @@ -205,8 +205,8 @@ - - + + @@ -302,7 +302,7 @@ - + diff --git a/source/api/AST/ASTVisitor.cpp b/source/api/AST/ASTVisitor.cpp index 1204eb557..d14cece47 100644 --- a/source/api/AST/ASTVisitor.cpp +++ b/source/api/AST/ASTVisitor.cpp @@ -10,61 +10,622 @@ // #include "ASTVisitor.hpp" +#include "Bitcode.hpp" #include "Commands.hpp" #include "ConfigImpl.hpp" -#include "Serializer.hpp" +#include "ParseJavadoc.hpp" #include "Support/Path.hpp" #include -#include +#include +#include +#include +#include +#include #include +#include +#include +#include #include #include #include #include +#include +#include namespace clang { namespace mrdox { +//------------------------------------------------ +// +// ASTVisitor +// //------------------------------------------------ -// An instance of Visitor runs on one translation unit. -void ASTVisitor:: -HandleTranslationUnit( - ASTContext& Context) +ASTVisitor( + tooling::ExecutionContext& ex, + ConfigImpl const& config, + Reporter& R) noexcept + : ex_(ex) + , config_(config) + , R_(R) + , PublicOnly(! config_.includePrivate_) + , IsFileInRootDir(true) { - initCustomCommentCommands(Context); +} - llvm::Optional filePath = - Context.getSourceManager().getNonBuiltinFilenameForID( - Context.getSourceManager().getMainFileID()); - if(! filePath) - return; +//------------------------------------------------ + +// Function to hash a given USR value for storage. +// As USRs (Unified Symbol Resolution) could be +// large, especially for functions with long type +// arguments, we use 160-bits SHA1(USR) values to +// guarantee the uniqueness of symbols while using +// a relatively small amount of memory (vs storing +// USRs directly). +// +static +SymbolID +getUSRForDecl( + Decl const* D) +{ + llvm::SmallString<128> USR; + if(index::generateUSRForDecl(D, USR)) + return SymbolID(); + return llvm::SHA1::hash(arrayRefFromStringRef(USR)); +} + +//------------------------------------------------ + +static +bool +shouldSerializeInfo( + bool PublicOnly, + bool IsInAnonymousNamespace, + NamedDecl const* D) noexcept +{ + if(! PublicOnly) + return true; + if(IsInAnonymousNamespace) + return false; + if(auto const* N = dyn_cast(D)) + if(N->isAnonymousNamespace()) + return false; + // bool isPublic() + AccessSpecifier access = D->getAccessUnsafe(); + if(access == AccessSpecifier::AS_private) + return false; + Linkage linkage = D->getLinkageInternal(); + if( linkage == Linkage::ModuleLinkage || + linkage == Linkage::ExternalLinkage) + return true; + // some form of internal linkage + return false; +} + +//------------------------------------------------ + +static +void +getParent( + SymbolID& parent, + Decl const* D) +{ + bool isParentAnonymous = false; + DeclContext const* DC = D->getDeclContext(); + Assert(DC != nullptr); + if(auto const* N = dyn_cast(DC)) + { + if(N->isAnonymousNamespace()) + { + isParentAnonymous = true; + } + parent = getUSRForDecl(N); + } + else if(auto const* N = dyn_cast(DC)) + { + parent = getUSRForDecl(N); + } + else if(auto const* N = dyn_cast(DC)) + { + parent = getUSRForDecl(N); + } + else if(auto const* N = dyn_cast(DC)) + { + parent = getUSRForDecl(N); + } + else + { + Assert(false); + } + (void)isParentAnonymous; +} + +static +void +getParentNamespaces( + llvm::SmallVector& Namespaces, + Decl const* D, + bool& IsInAnonymousNamespace) +{ + IsInAnonymousNamespace = false; + DeclContext const* DC = D->getDeclContext(); + do + { + if(auto const* N = dyn_cast(DC)) + { + std::string Namespace; + if(N->isAnonymousNamespace()) + { + Namespace = "@nonymous_namespace"; + IsInAnonymousNamespace = true; + } + else + { + Namespace = N->getNameAsString(); + } + Namespaces.emplace_back( + getUSRForDecl(N), + Namespace, + InfoType::IT_namespace); + } + else if(auto const* N = dyn_cast(DC)) + { + Namespaces.emplace_back( + getUSRForDecl(N), + N->getNameAsString(), + InfoType::IT_record); + } + else if(auto const* N = dyn_cast(DC)) + { + Namespaces.emplace_back( + getUSRForDecl(N), + N->getNameAsString(), + InfoType::IT_function); + } + else if(auto const* N = dyn_cast(DC)) + { + Namespaces.emplace_back( + getUSRForDecl(N), + N->getNameAsString(), + InfoType::IT_enum); + } + } + while((DC = DC->getParent())); + + // The global namespace should be added to the + // list of namespaces if the decl corresponds to + // a Record and if it doesn't have any namespace + // (because this means it's in the global namespace). + // Also if its outermost namespace is a record because + // that record matches the previous condition mentioned. + if((Namespaces.empty() && isa(D)) || + (!Namespaces.empty() && Namespaces.back().RefType == InfoType::IT_record)) + { + Namespaces.emplace_back( + globalNamespaceID, + "", //"GlobalNamespace", + InfoType::IT_namespace); + } +} + +//------------------------------------------------ + +static +std::string +getSourceCode( + Decl const* D, + SourceRange const& R) +{ + return Lexer::getSourceText(CharSourceRange::getTokenRange(R), + D->getASTContext().getSourceManager(), + D->getASTContext().getLangOpts()) + .str(); +} + +//------------------------------------------------ + +static +TagDecl* +getTagDeclForType( + QualType const& T) +{ + if(TagDecl const* D = T->getAsTagDecl()) + return D->getDefinition(); + return nullptr; +} + +static +RecordDecl* +getRecordDeclForType( + QualType const& T) +{ + if(RecordDecl const* D = T->getAsRecordDecl()) + return D->getDefinition(); + return nullptr; +} + +static +TypeInfo +getTypeInfoForType( + QualType const& T) +{ + TagDecl const* TD = getTagDeclForType(T); + if(!TD) + return TypeInfo(Reference(EmptySID, T.getAsString())); + InfoType IT; + if(dyn_cast(TD)) + IT = InfoType::IT_enum; + else if(dyn_cast(TD)) + IT = InfoType::IT_record; + else + IT = InfoType::IT_default; + return TypeInfo(Reference( + getUSRForDecl(TD), TD->getNameAsString(), IT)); +} + +static +void +parseParameters( + FunctionInfo& I, + FunctionDecl const* D) +{ + for(ParmVarDecl const* P : D->parameters()) + { + FieldTypeInfo& FieldInfo = I.Params.emplace_back( + getTypeInfoForType(P->getOriginalType()), + P->getNameAsString()); + FieldInfo.DefaultValue = getSourceCode( + D, P->getDefaultArgRange()); + } +} + +void +getTemplateParams( + llvm::Optional& TemplateInfo, + const Decl* D) +{ + if(TemplateParameterList const* ParamList = + D->getDescribedTemplateParams()) + { + if(!TemplateInfo) + { + TemplateInfo.emplace(); + } + for(const NamedDecl* ND : *ParamList) + { + TemplateInfo->Params.emplace_back(*ND); + } + } +} + +static +void +parseJavadoc( + llvm::Optional& javadoc, + Decl const* D) +{ + // VFALCO investigate whether we can use + // ASTContext::getCommentForDecl instead + RawComment* RC = + D->getASTContext().getRawCommentForDeclNoCache(D); + if(RC) + { + RC->setAttached(); + javadoc.emplace(parseJavadoc(RC, D->getASTContext(), D)); + } + else + { + javadoc.reset(); + } +} + +//------------------------------------------------ + +static +void +getMemberTypeInfo( + MemberTypeInfo& I, + FieldDecl const* D, + Reporter& R) +{ + Assert(D && "Expect non-null FieldDecl in getMemberTypeInfo"); + parseJavadoc(I.javadoc, D); +} + +//------------------------------------------------ + +template +requires + std::derived_from && + std::is_same_v +static +void +insertChild(Parent& parent, Child&& I) +{ + if constexpr(std::is_same_v) + { + // namespace requires parent namespace + Assert(Parent::type_id == InfoType::IT_namespace); + parent.Children.Namespaces.emplace_back(I.id, I.Name, Child::type_id); + } + else if constexpr(std::is_same_v) + { + parent.Children.Records.emplace_back(I.id, I.Name, Child::type_id); + } + else if constexpr(std::is_same_v) + { + parent.Children.Functions.emplace_back(I.id, I.Name, Child::type_id); + } + else if constexpr(std::is_same_v) + { + parent.Children.Typedefs.emplace_back(I.id, I.Name, Child::type_id); + } + else if constexpr(std::is_same_v) + { + parent.Children.Enums.emplace_back(I.id, I.Name, Child::type_id); + } + else if constexpr(std::is_same_v) + { + parent.Children.Vars.emplace_back(I.id, I.Name, Child::type_id); + } + else + { + static_error("unknown Info type", I); + } +} + +// Create an empty parent for the child with the +// child inserted either as a reference or by moving +// the entire record. Then return the parent as a +// serialized bitcode. +template +requires std::derived_from +static +Bitcode +writeParent(Child&& I) +{ + if(I.Namespace.empty()) + { + if(I.id == globalNamespaceID) + { + // Global namespace has no parent. + return {}; + } + + // In global namespace + NamespaceInfo P; + Assert(P.id == globalNamespaceID); + insertChild(P, std::move(I)); + return writeBitcode(P); + } + if(I.Namespace[0].RefType == InfoType::IT_namespace) + { + NamespaceInfo P(I.Namespace[0].id); + insertChild(P, std::move(I)); + return writeBitcode(P); + } + Assert(I.Namespace[0].RefType == InfoType::IT_record); + Assert(Child::type_id != InfoType::IT_namespace); + RecordInfo P(I.Namespace[0].id); + insertChild(P, std::move(I)); + return writeBitcode(P); +} + +// There are two uses for this function. +// 1) Getting the resulting mode of inheritance of a record. +// Example: class A {}; class B : private A {}; class C : public B {}; +// It's explicit that C is publicly inherited from C and B is privately +// inherited from A. It's not explicit but C is also privately inherited from +// A. This is the AS that this function calculates. FirstAS is the +// inheritance mode of `class C : B` and SecondAS is the inheritance mode of +// `class B : A`. +// 2) Getting the inheritance mode of an inherited attribute / method. +// Example : class A { public: int M; }; class B : private A {}; +// Class B is inherited from class A, which has a public attribute. This +// attribute is now part of the derived class B but it's not public. This +// will be private because the inheritance is private. This is the AS that +// this function calculates. FirstAS is the inheritance mode and SecondAS is +// the AS of the attribute / method. +static +AccessSpecifier +getFinalAccessSpecifier( + AccessSpecifier FirstAS, + AccessSpecifier SecondAS) +{ + if(FirstAS == AccessSpecifier::AS_none || + SecondAS == AccessSpecifier::AS_none) + return AccessSpecifier::AS_none; + if(FirstAS == AccessSpecifier::AS_private || + SecondAS == AccessSpecifier::AS_private) + return AccessSpecifier::AS_private; + if(FirstAS == AccessSpecifier::AS_protected || + SecondAS == AccessSpecifier::AS_protected) + return AccessSpecifier::AS_protected; + return AccessSpecifier::AS_public; +} + +// The Access parameter is only provided when parsing the field of an inherited +// record, the access specification of the field depends on the inheritance mode +static +void +parseFields( + RecordInfo& I, + const RecordDecl* D, + bool PublicOnly, + AccessSpecifier Access, + Reporter& R) +{ + for(const FieldDecl* F : D->fields()) + { + if(!shouldSerializeInfo(PublicOnly, /*IsInAnonymousNamespace=*/false, F)) + continue; - llvm::SmallString<0> s(*filePath); - convert_to_slash(s); - if(! config_.shouldVisitTU(s)) + // Use getAccessUnsafe so that we just get the default AS_none if it's not + // valid, as opposed to an assert. + MemberTypeInfo& NewMember = I.Members.emplace_back( + getTypeInfoForType(F->getTypeSourceInfo()->getType()), + F->getNameAsString(), + getFinalAccessSpecifier(Access, F->getAccessUnsafe())); + getMemberTypeInfo(NewMember, F, R); + } +} + +static +void +parseEnumerators( + EnumInfo& I, + const EnumDecl* D) +{ + for(const EnumConstantDecl* E : D->enumerators()) + { + std::string ValueExpr; + if(const Expr* InitExpr = E->getInitExpr()) + ValueExpr = getSourceCode(D, InitExpr->getSourceRange()); + + SmallString<16> ValueStr; + E->getInitVal().toString(ValueStr); + I.Members.emplace_back(E->getNameAsString(), ValueStr, ValueExpr); + } +} + +// TODO: Remove the serialization of Parents and VirtualParents, this +// information is also extracted in the other definition of parseBases. +static +void +parseBases( + ASTVisitor& sr, + RecordInfo& I, + CXXRecordDecl const* D) +{ + // Don't parse bases if this isn't a definition. + if(!D->isThisDeclarationADefinition()) return; + for(const CXXBaseSpecifier& B : D->bases()) + { + if(B.isVirtual()) + continue; + if(auto const* Ty = B.getType()->getAs()) + { + TemplateDecl const* D = Ty->getTemplateName().getAsTemplateDecl(); + I.Parents.emplace_back( + getUSRForDecl(D), + B.getType().getAsString(), + InfoType::IT_record); + } + else if(RecordDecl const* P = getRecordDeclForType(B.getType())) + { + I.Parents.emplace_back( + getUSRForDecl(P), + P->getNameAsString(), + InfoType::IT_record); + } + else + { + I.Parents.emplace_back( + globalNamespaceID, + B.getType().getAsString()); + } + } + for(CXXBaseSpecifier const& B : D->vbases()) + { + if(RecordDecl const* P = getRecordDeclForType(B.getType())) + { + I.VirtualParents.emplace_back( + getUSRForDecl(P), + P->getNameAsString(), + InfoType::IT_record); + } + else + { + I.VirtualParents.emplace_back( + globalNamespaceID, + B.getType().getAsString()); + } + } +} - //mc_.reset(MicrosoftMangleContext::create( - //mc_.reset(ItaniumMangleContext::create( - //Context, Context.getDiagnostics())); +//------------------------------------------------ - TraverseDecl(Context.getTranslationUnitDecl()); +static +void +parseBases( + ASTVisitor& sr, + RecordInfo& I, + CXXRecordDecl const* D, + bool IsFileInRootDir, + bool PublicOnly, + bool IsParent, + AccessSpecifier ParentAccess, + Reporter& R) +{ + // Don't parse bases if this isn't a definition. + if(!D->isThisDeclarationADefinition()) + return; + for(const CXXBaseSpecifier& B : D->bases()) + { + if(const RecordType* Ty = B.getType()->getAs()) + { + if(const CXXRecordDecl* Base = + cast_or_null(Ty->getDecl()->getDefinition())) + { + // Initialized without USR and name, this will be set in the following + // if-else stmt. + BaseRecordInfo BI( + {}, "", B.isVirtual(), + getFinalAccessSpecifier(ParentAccess, B.getAccessSpecifier()), + IsParent); + if(auto const* Ty = B.getType()->getAs()) + { + const TemplateDecl* D = Ty->getTemplateName().getAsTemplateDecl(); + BI.id = getUSRForDecl(D); + BI.Name = B.getType().getAsString(); + } + else + { + BI.id = getUSRForDecl(Base); + BI.Name = Base->getNameAsString(); + } + parseFields(BI, Base, PublicOnly, BI.Access, R); + for(auto const& Decl : Base->decls()) + { + if(auto const* MD = dyn_cast(Decl)) + { + // Don't serialize private methods + if(MD->getAccessUnsafe() == AccessSpecifier::AS_private || + !MD->isUserProvided()) + continue; + BI.Children.Functions.emplace_back(sr.getFunctionReference(MD)); + } + } + I.Bases.emplace_back(std::move(BI)); + // Call this function recursively to get the inherited classes of + // this base; these new bases will also get stored in the original + // RecordInfo: I. + // + // VFALCO Commented this out because we only want to show immediate + // bases. Alternatively, the generator could check IsParent + // + #if 0 + parseBases(sr, I, Base, IsFileInRootDir, PublicOnly, false, + I.Bases.back().Access, R); + #endif + } + } + } } -// Returns `true` if something got mapped -template +//------------------------------------------------ + +// This also sets IsFileInRootDir bool ASTVisitor:: -mapDecl(T const* D) +shouldExtract( + Decl const* D) { namespace path = llvm::sys::path; - clang::SourceManager const& sm = - D->getASTContext().getSourceManager(); - - if(sm.isInSystemHeader(D->getLocation())) + if(sourceManager_->isInSystemHeader(D->getLocation())) { // skip system header return false; @@ -72,13 +633,13 @@ mapDecl(T const* D) if(D->getParentFunctionOrMethod()) { - // skip function-local declarations + // skip function-local declaration, + // and skip function ParmVarDecls. return false; } - llvm::SmallString<512> filePath; clang::PresumedLoc const loc = - sm.getPresumedLoc(D->getBeginLoc()); + sourceManager_->getPresumedLoc(D->getBeginLoc()); auto result = fileFilter_.emplace( loc.getIncludeLoc().getRawEncoding(), FileFilter()); @@ -88,73 +649,533 @@ mapDecl(T const* D) FileFilter const& ff = result.first->second; if(! ff.include) return false; - filePath = loc.getFilename(); // native - convert_to_slash(filePath); + File = loc.getFilename(); // native + convert_to_slash(File); // VFALCO we could assert that the prefix // matches and just lop off the // first ff.prefix.size() characters. - path::replace_path_prefix(filePath, ff.prefix, ""); + path::replace_path_prefix(File, ff.prefix, ""); } else { // new element - filePath = loc.getFilename(); - convert_to_slash(filePath); + File = loc.getFilename(); + convert_to_slash(File); FileFilter& ff = result.first->second; - ff.include = config_.shouldVisitFile(filePath, ff.prefix); + ff.include = config_.shouldVisitFile(File, ff.prefix); if(! ff.include) return false; // VFALCO we could assert that the prefix // matches and just lop off the // first ff.prefix.size() characters. - path::replace_path_prefix(filePath, ff.prefix, ""); + path::replace_path_prefix(File, ff.prefix, ""); } - // If there is an error generating a USR for the decl, skip this decl. + IsFileInRootDir = true; + + return true; +} + +bool +ASTVisitor:: +extractSymbolID( + SymbolID& id, NamedDecl const* D) +{ + usr_.clear(); + auto const shouldIgnore = index::generateUSRForDecl(D, usr_); + if(shouldIgnore) + return false; + id = llvm::SHA1::hash(arrayRefFromStringRef(usr_)); + return true; +} + +bool +ASTVisitor:: +extractInfo( + Info& I, + NamedDecl const* D) +{ + bool IsInAnonymousNamespace; + getParentNamespaces( + I.Namespace, D, IsInAnonymousNamespace); + if(! shouldSerializeInfo(PublicOnly, IsInAnonymousNamespace, D)) + return false; + if(! extractSymbolID(I.id, D)) + return false; + I.Name = D->getNameAsString(); + parseJavadoc(I.javadoc, D); + return true; +} + +// Return a Reference for the function. +Reference +ASTVisitor:: +getFunctionReference( + FunctionDecl const* D) +{ + Reference ref; + /*shouldExtract=*/ extractSymbolID(ref.id, D); + ref.Name = D->getNameAsString(); + ref.RefType = InfoType::IT_function; + return ref; +} + +int +ASTVisitor:: +getLine( + NamedDecl const* D) const +{ + return sourceManager_->getPresumedLoc( + D->getBeginLoc()).getLine(); +} + +//------------------------------------------------ + +// Decl types which have isThisDeclarationADefinition: +// +// VarTemplateDecl +// FunctionTemplateDecl +// FunctionDecl +// TagDecl +// ClassTemplateDecl +// CXXDeductionGuideDecl + +void +ASTVisitor:: +buildNamespace( + NamespaceDecl * D) +{ + if(! shouldExtract(D)) + return; + NamespaceInfo I; + if(! extractInfo(I, D)) + return; + if(D->isAnonymousNamespace()) + I.Name = "@nonymous_namespace"; // VFALCO BAD! + insertBitcode(ex_, writeBitcode(I)); + insertBitcode(ex_, writeParent(std::move(I))); +} + +void +ASTVisitor:: +buildRecord( + CXXRecordDecl* D) +{ + if(! shouldExtract(D)) + return; + RecordInfo I; + if(! extractInfo(I, D)) + return; + LineNumber = getLine(D); + if(D->isThisDeclarationADefinition()) + I.DefLoc.emplace(LineNumber, File, IsFileInRootDir); + else + I.Loc.emplace_back(LineNumber, File, IsFileInRootDir); + I.TagType = D->getTagKind(); + parseFields(I, D, PublicOnly, AccessSpecifier::AS_public, R_); + + // These are from CXXRecordDecl::isEffectivelyFinal() + I.specs.set(D->template hasAttr()); + if(auto const DT = D->getDestructor()) + I.specs.set(DT->template hasAttr()); + + if(TypedefNameDecl const* TD = D->getTypedefNameForAnonDecl()) { - llvm::SmallString<128> USR; - if (index::generateUSRForDecl(D, USR)) + I.Name = TD->getNameAsString(); + I.IsTypeDef = true; + } + // VFALCO: remove first call to parseBases, + // that function should be deleted + parseBases(*this, I, D); // VFALCO WHY? + + parseBases(*this, + I, + D, + IsFileInRootDir, + PublicOnly, + true, + AccessSpecifier::AS_public, + R_); + + getTemplateParams(I.Template, D); + + // Full and partial specializations. + if(auto* CTSD = dyn_cast(D)) + { + if(!I.Template) + I.Template.emplace(); + I.Template->Specialization.emplace(); + auto& Specialization = *I.Template->Specialization; + + // What this is a specialization of. + auto SpecOf = CTSD->getSpecializedTemplateOrPartial(); + if(SpecOf.is()) { - // VFALCO report this, it seems to never happen - return false; + Specialization.SpecializationOf = + getUSRForDecl(SpecOf.get()); + } + else if(SpecOf.is()) + { + Specialization.SpecializationOf = + getUSRForDecl(SpecOf.get()); + } + + // Parameters to the specilization. For partial specializations, get the + // parameters "as written" from the ClassTemplatePartialSpecializationDecl + // because the non-explicit template parameters will have generated internal + // placeholder names rather than the names the user typed that match the + // template parameters. + if(ClassTemplatePartialSpecializationDecl const* CTPSD = + dyn_cast(D)) + { + if(ASTTemplateArgumentListInfo const* AsWritten = + CTPSD->getTemplateArgsAsWritten()) + { + for(unsigned i = 0; i < AsWritten->getNumTemplateArgs(); i++) + { + Specialization.Params.emplace_back( + getSourceCode(D, (*AsWritten)[i].getSourceRange())); + } + } + } + else + { + for(TemplateArgument const& Arg : CTSD->getTemplateArgs().asArray()) + { + Specialization.Params.emplace_back(*D, Arg); + } + } + } + + insertBitcode(ex_, writeBitcode(I)); + insertBitcode(ex_, writeParent(std::move(I))); +} + +template +bool +ASTVisitor:: +buildFunction( + FunctionInfo& I, DeclTy* D) +{ + if(! extractInfo(I, D)) + return false; + LineNumber = getLine(D); + if(D->isThisDeclarationADefinition()) + I.DefLoc.emplace(LineNumber, File, IsFileInRootDir); + else + I.Loc.emplace_back(LineNumber, File, IsFileInRootDir); + QualType const qt = D->getReturnType(); + std::string s = qt.getAsString(); + I.ReturnType = getTypeInfoForType(qt); + parseParameters(I, D); + + getTemplateParams(I.Template, D); + + // Handle function template specializations. + if(FunctionTemplateSpecializationInfo const* FTSI = + D->getTemplateSpecializationInfo()) + { + if(!I.Template) + I.Template.emplace(); + I.Template->Specialization.emplace(); + auto& Specialization = *I.Template->Specialization; + + Specialization.SpecializationOf = getUSRForDecl(FTSI->getTemplate()); + + // Template parameters to the specialization. + if(FTSI->TemplateArguments) + { + for(TemplateArgument const& Arg : + FTSI->TemplateArguments->asArray()) + { + Specialization.Params.emplace_back(*D, Arg); + } } } - // VFALCO is this right? - bool const IsFileInRootDir = true; + // + // FunctionDecl + // + I.specs0.set(D->isVariadic()); + I.specs0.set(D->isVirtualAsWritten()); + I.specs0.set(D->isPure()); + I.specs0.set(D->isDefaulted()); + I.specs0.set(D->isExplicitlyDefaulted()); + I.specs0.set(D->isDeleted()); + I.specs0.set(D->isDeletedAsWritten()); + I.specs0.set(D->isNoReturn()); + // subsumes D->hasAttr() + // subsumes D->hasAttr() + // subsumes D->hasAttr() + // subsumes D->getType()->getAs()->getNoReturnAttr() + I.specs0.set(D->template hasAttr()); + if(auto const* FP = D->getType()->template getAs()) + I.specs0.set(FP->hasTrailingReturn()); + I.specs0.set(D->getConstexprKind()); + // subsumes D->isConstexpr(); + // subsumes D->isConstexprSpecified(); + // subsumes D->isConsteval(); + I.specs0.set(D->getExceptionSpecType()); + I.specs0.set(D->getOverloadedOperator()); + I.specs0.set(D->getStorageClass()); + if(auto attr = D->template getAttr()) + { + I.specs1.set(true); + I.specs1.set(attr->getSemanticSpelling()); + } - SerializeResult res; - if constexpr(std::is_same_v) + if constexpr(! std::derived_from) { - res = Serializer( - *mc_, - 0,//getLine(D, D->getASTContext()), - filePath, - IsFileInRootDir, - config_, - R_).build(D); + I.IsMethod = false; + I.Access = AccessSpecifier::AS_none; } - else + + // + // CXXMethodDecl + // + if constexpr(std::derived_from) { - res = Serializer( - *mc_, - getLine(D, D->getASTContext()), - filePath, - IsFileInRootDir, - config_, - R_).build(D); + I.IsMethod = true; + NamedDecl const* PD = nullptr; + if(auto const* SD = dyn_cast(D->getParent())) + PD = SD->getSpecializedTemplate(); + else + PD = D->getParent(); + SymbolID ParentID = getUSRForDecl(PD); + I.Parent = Reference( + ParentID, + PD->getNameAsString(), + InfoType::IT_record); + I.Access = D->getAccess(); + + I.specs0.set(D->isConst()); + I.specs0.set(D->isVolatile()); + I.specs0.set(D->getRefQualifier()); + //D->isCopyAssignmentOperator() + //D->isMoveAssignmentOperator() + //D->isOverloadedOperator(); + //D->isStaticOverloadedOperator(); } - if(res.bitcodes.empty()) - return false; + // + // CXXDestructorDecl + // + if constexpr(std::derived_from) + { + //I.Name.append("-dtor"); + } + + // + // CXXConstructorDecl + // + if constexpr(std::derived_from) + { + //I.Name.append("-ctor"); + I.specs1.set(D->getExplicitSpecifier().isSpecified()); + } + + // + // CXXConversionDecl + // + if constexpr(std::derived_from) + { + //I.Name.append("-conv"); + I.specs1.set(D->getExplicitSpecifier().isSpecified()); + } + + // + // CXXDeductionGuideDecl + // + if constexpr(std::derived_from) + { + I.specs1.set(D->getExplicitSpecifier().isSpecified()); + } - for(auto&& bitcode : res.bitcodes) - insertBitcode(ex_, std::move(bitcode)); return true; } +template +void +ASTVisitor:: +buildFunction( + DeclTy* D) +{ + if(! shouldExtract(D)) + return; + FunctionInfo I; + if(! buildFunction(I, D)) + return; + insertBitcode(ex_, writeBitcode(I)); + insertBitcode(ex_, writeParent(std::move(I))); +} + +template +void +ASTVisitor:: +buildFriend( + DeclTy* D) +{ + if(NamedDecl* ND = D->getFriendDecl()) + { + // D does not name a type + if(FunctionDecl* FD = dyn_cast(ND)) + { + if(! shouldExtract(FD)) + return; + FunctionInfo I; + if(! buildFunction(I, FD)) + return; + // VFALCO This is unfortunate, but the + // default of 0 would be AS_public. see #84 + I.Access = AccessSpecifier::AS_none; + SymbolID id; + getParent(id, D); + RecordInfo P(id); + P.Friends.emplace_back(I.id); + bool isInAnonymous; + getParentNamespaces(P.Namespace, ND, isInAnonymous); + insertBitcode(ex_, writeBitcode(I)); + insertBitcode(ex_, writeParent(std::move(I))); + insertBitcode(ex_, writeBitcode(P)); + insertBitcode(ex_, writeParent(std::move(P))); + return; + } + if(FunctionTemplateDecl* FT = dyn_cast(ND)) + { + // VFALCO TODO + (void)FT; + return; + } + if(ClassTemplateDecl* CT = dyn_cast(ND)) + { + // VFALCO TODO + (void)CT; + return; + } + + Assert(false); + return; + } + else if(TypeSourceInfo* TS = D->getFriendType()) + { + (void)TS; + return; + } + else + { + Assert(false); + } + return; +} + +template +void +ASTVisitor:: +buildTypedef( + DeclTy* D) +{ + if(! shouldExtract(D)) + return; + TypedefInfo I; + if(! extractInfo(I, D)) + return; + I.Underlying = getTypeInfoForType(D->getUnderlyingType()); + if(I.Underlying.Type.Name.empty()) + { + // Typedef for an unnamed type. This is like + // "typedef struct { } Foo;". The record serializer + // explicitly checks for this syntax and constructs + // a record with that name, so we don't want to emit + // a duplicate here. + return; + } + + LineNumber = getLine(D); + // D->isThisDeclarationADefinition(); // not available + I.DefLoc.emplace(LineNumber, File, IsFileInRootDir); + I.IsUsing = std::is_same_v; + insertBitcode(ex_, writeBitcode(I)); + insertBitcode(ex_, writeParent(std::move(I))); +} + +void +ASTVisitor:: +buildEnum( + EnumDecl* D) +{ + if(! shouldExtract(D)) + return; + EnumInfo I; + if(! extractInfo(I, D)) + return; + LineNumber = getLine(D); + if(D->isThisDeclarationADefinition()) + I.DefLoc.emplace(LineNumber, File, IsFileInRootDir); + else + I.Loc.emplace_back(LineNumber, File, IsFileInRootDir); + I.Scoped = D->isScoped(); + if(D->isFixed()) + { + auto Name = D->getIntegerType().getAsString(); + I.BaseType = TypeInfo(Name); + } + parseEnumerators(I, D); + insertBitcode(ex_, writeBitcode(I)); + insertBitcode(ex_, writeParent(std::move(I))); +} + +void +ASTVisitor:: +buildVar( + VarDecl* D) +{ + if(! shouldExtract(D)) + return; + VarInfo I; + if(! extractInfo(I, D)) + return; + LineNumber = getLine(D); + if(D->isThisDeclarationADefinition()) + I.DefLoc.emplace(LineNumber, File, IsFileInRootDir); + else + I.Loc.emplace_back(LineNumber, File, IsFileInRootDir); + static_cast(I) = + getTypeInfoForType(D->getTypeSourceInfo()->getType()); + I.specs.set(D->getStorageClass()); + insertBitcode(ex_, writeBitcode(I)); + insertBitcode(ex_, writeParent(std::move(I))); +} + //------------------------------------------------ +// An instance of Visitor runs on one translation unit. +void +ASTVisitor:: +HandleTranslationUnit( + ASTContext& Context) +{ + // cache contextual variables + astContext_ = &Context; + sourceManager_ = &astContext_->getSourceManager(); + + // Install handlers for our custom commands + initCustomCommentCommands(Context); + + llvm::Optional filePath = + Context.getSourceManager().getNonBuiltinFilenameForID( + Context.getSourceManager().getMainFileID()); + if(! filePath) + return; + + // Filter out TUs we don't care about + File = *filePath; + convert_to_slash(File); + if(! config_.shouldVisitTU(File)) + return; + + TraverseDecl(Context.getTranslationUnitDecl()); +} + // Returning false from any of these // functions will abort the _entire_ traversal @@ -163,7 +1184,7 @@ ASTVisitor:: WalkUpFromNamespaceDecl( NamespaceDecl* D) { - mapDecl(D); + buildNamespace(D); return true; } @@ -172,7 +1193,7 @@ ASTVisitor:: WalkUpFromCXXRecordDecl( CXXRecordDecl* D) { - mapDecl(D); + buildRecord(D); return true; } @@ -181,36 +1202,34 @@ ASTVisitor:: WalkUpFromCXXMethodDecl( CXXMethodDecl* D) { - mapDecl(D); + buildFunction(D); return true; } bool ASTVisitor:: -WalkUpFromFriendDecl( - FriendDecl* D) +WalkUpFromCXXDestructorDecl( + CXXDestructorDecl* D) { - mapDecl(D); + buildFunction(D); return true; } -#if 0 bool ASTVisitor:: -WalkUpFromUsingDecl( - UsingDecl* D) +WalkUpFromCXXConstructorDecl( + CXXConstructorDecl* D) { - mapDecl(D); + buildFunction(D); return true; } -#endif bool ASTVisitor:: -WalkUpFromUsingShadowDecl( - UsingShadowDecl* D) +WalkUpFromCXXConversionDecl( + CXXConversionDecl* D) { - mapDecl(D); + buildFunction(D); return true; } @@ -219,8 +1238,16 @@ ASTVisitor:: WalkUpFromFunctionDecl( FunctionDecl* D) { - Assert(! dyn_cast(D)); - mapDecl(D); + buildFunction(D); + return true; +} + +bool +ASTVisitor:: +WalkUpFromFriendDecl( + FriendDecl* D) +{ + buildFriend(D); return true; } @@ -229,7 +1256,7 @@ ASTVisitor:: WalkUpFromTypeAliasDecl( TypeAliasDecl* D) { - mapDecl(D); + buildTypedef(D); return true; } @@ -238,7 +1265,7 @@ ASTVisitor:: WalkUpFromTypedefDecl( TypedefDecl* D) { - mapDecl(D); + buildTypedef(D); return true; } @@ -247,7 +1274,7 @@ ASTVisitor:: WalkUpFromEnumDecl( EnumDecl* D) { - mapDecl(D); + buildEnum(D); return true; } @@ -256,84 +1283,20 @@ ASTVisitor:: WalkUpFromVarDecl( VarDecl* D) { - mapDecl(D); + buildVar(D); return true; } -int +bool ASTVisitor:: -getLine( - NamedDecl const* D, - ASTContext const& Context) const +WalkUpFromParmVarDecl( + ParmVarDecl* D) { - return Context.getSourceManager().getPresumedLoc( - D->getBeginLoc()).getLine(); -} - -//------------------------------------------------ - -namespace { - -struct Action - : public clang::ASTFrontendAction -{ - Action( - tooling::ExecutionContext& exc, - ConfigImpl const& config, - Reporter& R) noexcept - : ex_(exc) - , config_(config) - , R_(R) - { - } - - std::unique_ptr - CreateASTConsumer( - clang::CompilerInstance& Compiler, - llvm::StringRef InFile) override - { - return std::make_unique(ex_, config_, R_); - } - -private: - tooling::ExecutionContext& ex_; - ConfigImpl const& config_; - Reporter& R_; -}; - -struct Factory : tooling::FrontendActionFactory -{ - Factory( - tooling::ExecutionContext& exc, - ConfigImpl const& config, - Reporter& R) noexcept - : ex_(exc) - , config_(config) - , R_(R) - { - } - - std::unique_ptr - create() override - { - return std::make_unique(ex_, config_, R_); - } - -private: - tooling::ExecutionContext& ex_; - ConfigImpl const& config_; - Reporter& R_; -}; - -} // (anon) - -std::unique_ptr -makeFrontendActionFactory( - tooling::ExecutionContext& exc, - ConfigImpl const& config, - Reporter& R) -{ - return std::make_unique(exc, config, R); + // We do nothing here, to prevent ParmVarDecl + // from appearing as VarDecl. We pick up the + // function parameters as a group from the + // FunctionDecl instead of visiting ParmVarDecl. + return true; } } // mrdox diff --git a/source/api/AST/ASTVisitor.hpp b/source/api/AST/ASTVisitor.hpp index b4df6bc99..4d65ae912 100644 --- a/source/api/AST/ASTVisitor.hpp +++ b/source/api/AST/ASTVisitor.hpp @@ -12,10 +12,9 @@ #ifndef MRDOX_API_AST_ASTVISITOR_HPP #define MRDOX_API_AST_ASTVISITOR_HPP -#include #include "ConfigImpl.hpp" #include -#include +#include #include #include #include @@ -23,14 +22,16 @@ namespace clang { namespace mrdox { +struct SerializeResult; + /** Convert AST to our metadata and serialize to bitcode. An instance of this object visits the AST for exactly one translation unit. The AST is - converted into our metadata, and this metadata - is then serialized into bitcode. The resulting - bitcode is inserted into the tool results, - keyed by ID. Each ID can have multiple + extracted and converted into our metadata, and + this metadata is then serialized into bitcode. + The resulting bitcode is inserted into the tool + results, keyed by ID. Each ID can have multiple serialized bitcodes, as the same declaration in a particular include file can be seen by more than one translation unit. @@ -39,6 +40,7 @@ class ASTVisitor : public RecursiveASTVisitor , public ASTConsumer { +public: struct FileFilter { llvm::SmallString<0> prefix; @@ -48,52 +50,69 @@ class ASTVisitor tooling::ExecutionContext& ex_; ConfigImpl const& config_; Reporter& R_; + + llvm::SmallString<512> File; + int LineNumber; + bool PublicOnly; + bool IsFileInRootDir; + + llvm::SmallString<128> usr_; + + ASTContext* astContext_; + clang::SourceManager const* sourceManager_; std::unordered_map< clang::SourceLocation::UIntTy, FileFilter> fileFilter_; - std::unique_ptr mc_; public: ASTVisitor( tooling::ExecutionContext& ex, ConfigImpl const& config, - Reporter& R) noexcept - : ex_(ex) - , config_(config) - , R_(R) - { - } + Reporter& R) noexcept; -//private: - void HandleTranslationUnit(ASTContext& Context) override; +public://private: + bool shouldExtract(Decl const* D); + bool extractSymbolID(SymbolID& id, NamedDecl const* D); + bool extractInfo(Info& I, NamedDecl const* D); + Reference getFunctionReference(FunctionDecl const* D); + int getLine(NamedDecl const* D) const; + +private: + void buildNamespace(NamespaceDecl* D); + + void buildRecord(CXXRecordDecl* D); + + template + bool buildFunction(FunctionInfo& I, DeclTy* D); + template + void buildFunction(DeclTy* D); + + template + void buildFriend(DeclTy* D); + + template + void buildTypedef(DeclTy* D); + + void buildEnum(EnumDecl* D); + + void buildVar(VarDecl* D); + +public: + void HandleTranslationUnit(ASTContext& Context) override; bool WalkUpFromNamespaceDecl(NamespaceDecl* D); bool WalkUpFromCXXRecordDecl(CXXRecordDecl* D); bool WalkUpFromCXXMethodDecl(CXXMethodDecl* D); - bool WalkUpFromFriendDecl(FriendDecl* D); - //bool WalkUpFromUsingDecl(UsingDecl* D); - bool WalkUpFromUsingShadowDecl(UsingShadowDecl* D); + bool WalkUpFromCXXDestructorDecl(CXXDestructorDecl* D); + bool WalkUpFromCXXConstructorDecl(CXXConstructorDecl* D); + bool WalkUpFromCXXConversionDecl(CXXConversionDecl* D); bool WalkUpFromFunctionDecl(FunctionDecl* D); + bool WalkUpFromFriendDecl(FriendDecl* D); bool WalkUpFromTypeAliasDecl(TypeAliasDecl* D); bool WalkUpFromTypedefDecl(TypedefDecl* D); bool WalkUpFromEnumDecl(EnumDecl* D); bool WalkUpFromVarDecl(VarDecl* D); - -private: - template - bool mapDecl(T const* D); - - int - getLine( - NamedDecl const* D, - ASTContext const& Context) const; - - llvm::SmallString<128> - getFile( - NamedDecl const* D, - ASTContext const& Context, - StringRef RootDir, - bool& IsFileInRootDir) const; + bool WalkUpFromParmVarDecl(ParmVarDecl* D); }; } // mrdox diff --git a/source/api/AST/AnyBlock.hpp b/source/api/AST/AnyBlock.hpp index 5aaf599b5..e06350da8 100644 --- a/source/api/AST/AnyBlock.hpp +++ b/source/api/AST/AnyBlock.hpp @@ -807,7 +807,7 @@ class TopLevelBlock std::derived_from || std::derived_from) { - I->Children.Variables.emplace_back(std::move(R)); + I->Children.Vars.emplace_back(std::move(R)); return llvm::Error::success(); } break; @@ -1149,13 +1149,13 @@ class EnumBlock //------------------------------------------------ -class VariableBlock - : public TopLevelBlock +class VarBlock + : public TopLevelBlock { public: explicit - VariableBlock( + VarBlock( BitcodeReader& br) : TopLevelBlock(br) { @@ -1222,7 +1222,7 @@ readChild( I.Enums.emplace_back(B.I); break; case FieldId::F_child_variable: - I.Variables.emplace_back(B.I); + I.Vars.emplace_back(B.I); break; default: return makeWrongFieldError(B.F); diff --git a/source/api/AST/BitcodeReader.cpp b/source/api/AST/BitcodeReader.cpp index 024c4a8f2..6bea07793 100644 --- a/source/api/AST/BitcodeReader.cpp +++ b/source/api/AST/BitcodeReader.cpp @@ -93,7 +93,7 @@ getInfos() } case BI_VARIABLE_BLOCK_ID: { - auto I = readInfo(ID); + auto I = readInfo(ID); if(! I) return I.takeError(); Infos.emplace_back(std::move(I.get())); diff --git a/source/api/AST/BitcodeWriter.cpp b/source/api/AST/BitcodeWriter.cpp index 231d9a592..ecf933bd5 100644 --- a/source/api/AST/BitcodeWriter.cpp +++ b/source/api/AST/BitcodeWriter.cpp @@ -212,7 +212,7 @@ BlockIdNameMap = []() {BI_TEMPLATE_BLOCK_ID, "TemplateBlock"}, {BI_TEMPLATE_SPECIALIZATION_BLOCK_ID, "TemplateSpecializationBlock"}, {BI_TEMPLATE_PARAM_BLOCK_ID, "TemplateParamBlock"}, - {BI_VARIABLE_BLOCK_ID, "VariableBlock"} + {BI_VARIABLE_BLOCK_ID, "VarBlock"} }; Assert(Inits.size() == BlockIdCount); for (const auto& Init : Inits) @@ -337,7 +337,7 @@ RecordsByBlock{ // TypedefInfo {BI_TYPEDEF_BLOCK_ID, {TYPEDEF_IS_USING}}, - // VariableInfo + // VarInfo {BI_VARIABLE_BLOCK_ID, {VARIABLE_BITS}} }; @@ -375,7 +375,7 @@ dispatchInfoForWrite(Info const* I) emitBlock(*static_cast(I)); break; case InfoType::IT_variable: - emitBlock(*static_cast(I)); + emitBlock(*static_cast(I)); break; default: llvm::errs() << "Unexpected info, unable to write.\n"; @@ -903,7 +903,7 @@ emitBlock( emitBlock(ref, FieldId::F_child_typedef); for (const auto& ref : I.Children.Enums) emitBlock(ref, FieldId::F_child_enum); - for (const auto& ref : I.Children.Variables) + for (const auto& ref : I.Children.Vars) emitBlock(ref, FieldId::F_child_variable); } @@ -934,7 +934,7 @@ emitBlock( emitBlock(ref, FieldId::F_child_typedef); for (const auto& ref : I.Children.Enums) emitBlock(ref, FieldId::F_child_enum); - for (const auto& ref : I.Children.Variables) + for (const auto& ref : I.Children.Vars) emitBlock(ref, FieldId::F_child_variable); if (I.Template) emitBlock(*I.Template); @@ -1011,7 +1011,7 @@ emitBlock( void BitcodeWriter:: emitBlock( - VariableInfo const& I) + VarInfo const& I) { StreamSubBlockGuard Block(Stream, BI_VARIABLE_BLOCK_ID); emitInfoPart(I); diff --git a/source/api/AST/BitcodeWriter.hpp b/source/api/AST/BitcodeWriter.hpp index df390ab81..14a9ade5b 100644 --- a/source/api/AST/BitcodeWriter.hpp +++ b/source/api/AST/BitcodeWriter.hpp @@ -137,7 +137,7 @@ class BitcodeWriter void emitBlock(TemplateSpecializationInfo const& T); void emitBlock(TypedefInfo const& I); void emitBlock(TypeInfo const& I); - void emitBlock(VariableInfo const& I); + void emitBlock(VarInfo const& I); //-------------------------------------------- diff --git a/source/api/AST/FrontendAction.cpp b/source/api/AST/FrontendAction.cpp new file mode 100644 index 000000000..9b2b5e3bf --- /dev/null +++ b/source/api/AST/FrontendAction.cpp @@ -0,0 +1,88 @@ +// +// This is a derivative work. originally part of the LLVM Project. +// Licensed under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +// Copyright (c) 2023 Vinnie Falco (vinnie.falco@gmail.com) +// +// Official repository: https://github.com/cppalliance/mrdox +// + +#include "ASTVisitor.hpp" +#include "FrontendAction.hpp" +#include + +namespace clang { +namespace mrdox { + +namespace { + +struct Action + : public clang::ASTFrontendAction +{ + Action( + tooling::ExecutionContext& exc, + ConfigImpl const& config, + Reporter& R) noexcept + : ex_(exc) + , config_(config) + , R_(R) + { + } + + std::unique_ptr + CreateASTConsumer( + clang::CompilerInstance& Compiler, + llvm::StringRef InFile) override + { + return std::make_unique(ex_, config_, R_); + } + +private: + tooling::ExecutionContext& ex_; + ConfigImpl const& config_; + Reporter& R_; +}; + +//------------------------------------------------ + +struct Factory : tooling::FrontendActionFactory +{ + Factory( + tooling::ExecutionContext& exc, + ConfigImpl const& config, + Reporter& R) noexcept + : ex_(exc) + , config_(config) + , R_(R) + { + } + + std::unique_ptr + create() override + { + return std::make_unique(ex_, config_, R_); + } + +private: + tooling::ExecutionContext& ex_; + ConfigImpl const& config_; + Reporter& R_; +}; + +} // (anon) + +//------------------------------------------------ + +std::unique_ptr +makeFrontendActionFactory( + tooling::ExecutionContext& exc, + ConfigImpl const& config, + Reporter& R) +{ + return std::make_unique(exc, config, R); +} + +} // mrdox +} // clang diff --git a/source/api/AST/FrontendAction.hpp b/source/api/AST/FrontendAction.hpp index e0ab4dd3c..0645b27fb 100644 --- a/source/api/AST/FrontendAction.hpp +++ b/source/api/AST/FrontendAction.hpp @@ -33,4 +33,4 @@ makeFrontendActionFactory( } // mrdox } // clang -#endif +#endif \ No newline at end of file diff --git a/source/api/AST/Serializer.cpp b/source/api/AST/Serializer.cpp deleted file mode 100644 index 837ee9ac0..000000000 --- a/source/api/AST/Serializer.cpp +++ /dev/null @@ -1,1269 +0,0 @@ -// -// This is a derivative work. originally part of the LLVM Project. -// Licensed under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -// Copyright (c) 2023 Vinnie Falco (vinnie.falco@gmail.com) -// -// Official repository: https://github.com/cppalliance/mrdox -// - -#include "Serializer.hpp" -#include "Bitcode.hpp" -#include "ParseJavadoc.hpp" -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -namespace clang { -namespace mrdox { - -//------------------------------------------------ - -// Function to hash a given USR value for storage. -// As USRs (Unified Symbol Resolution) could be -// large, especially for functions with long type -// arguments, we use 160-bits SHA1(USR) values to -// guarantee the uniqueness of symbols while using -// a relatively small amount of memory (vs storing -// USRs directly). -// -static -SymbolID -getUSRForDecl( - Decl const* D) -{ - llvm::SmallString<128> USR; - if(index::generateUSRForDecl(D, USR)) - return SymbolID(); - return llvm::SHA1::hash(arrayRefFromStringRef(USR)); -} - -//------------------------------------------------ - -static -bool -shouldSerializeInfo( - bool PublicOnly, - bool IsInAnonymousNamespace, - NamedDecl const* D) noexcept -{ - if(! PublicOnly) - return true; - if(IsInAnonymousNamespace) - return false; - if(auto const* N = dyn_cast(D)) - if(N->isAnonymousNamespace()) - return false; - // bool isPublic() - AccessSpecifier access = D->getAccessUnsafe(); - if(access == AccessSpecifier::AS_private) - return false; - Linkage linkage = D->getLinkageInternal(); - if(linkage == Linkage::ModuleLinkage || - linkage == Linkage::ExternalLinkage) - return true; - // some form of internal linkage - return false; -} - -// handles TypedefDecl and TypeAliasDecl -static -bool -shouldSerializeInfo( - bool PublicOnly, - bool IsInAnonymousNamespace, - TypedefNameDecl const* D) -{ - if(! PublicOnly) - return true; - if(IsInAnonymousNamespace) - return false; - if(auto const* N = dyn_cast(D)) - if(N->isAnonymousNamespace()) - return false; - if(D->getAccessUnsafe() == AccessSpecifier::AS_private) - return false; - return true; -} - -//------------------------------------------------ - -static -void -getParent( - SymbolID& parent, - Decl const* D) -{ - bool isParentAnonymous = false; - DeclContext const* DC = D->getDeclContext(); - Assert(DC != nullptr); - if(auto const* N = dyn_cast(DC)) - { - if(N->isAnonymousNamespace()) - { - isParentAnonymous = true; - } - parent = getUSRForDecl(N); - } - else if(auto const* N = dyn_cast(DC)) - { - parent = getUSRForDecl(N); - } - else if(auto const* N = dyn_cast(DC)) - { - parent = getUSRForDecl(N); - } - else if(auto const* N = dyn_cast(DC)) - { - parent = getUSRForDecl(N); - } - else - { - Assert(false); - } - (void)isParentAnonymous; -} - -static -void -getParentNamespaces( - llvm::SmallVector& Namespaces, - Decl const* D, - bool& IsInAnonymousNamespace) -{ - IsInAnonymousNamespace = false; - DeclContext const* DC = D->getDeclContext(); - do - { - if(auto const* N = dyn_cast(DC)) - { - std::string Namespace; - if(N->isAnonymousNamespace()) - { - Namespace = "@nonymous_namespace"; - IsInAnonymousNamespace = true; - } - else - { - Namespace = N->getNameAsString(); - } - Namespaces.emplace_back( - getUSRForDecl(N), - Namespace, - InfoType::IT_namespace); - } - else if(auto const* N = dyn_cast(DC)) - { - Namespaces.emplace_back( - getUSRForDecl(N), - N->getNameAsString(), - InfoType::IT_record); - } - else if(auto const* N = dyn_cast(DC)) - { - Namespaces.emplace_back( - getUSRForDecl(N), - N->getNameAsString(), - InfoType::IT_function); - } - else if(auto const* N = dyn_cast(DC)) - { - Namespaces.emplace_back( - getUSRForDecl(N), - N->getNameAsString(), - InfoType::IT_enum); - } - } - while((DC = DC->getParent())); - - // The global namespace should be added to the - // list of namespaces if the decl corresponds to - // a Record and if it doesn't have any namespace - // (because this means it's in the global namespace). - // Also if its outermost namespace is a record because - // that record matches the previous condition mentioned. - if((Namespaces.empty() && isa(D)) || - (!Namespaces.empty() && Namespaces.back().RefType == InfoType::IT_record)) - { - Namespaces.emplace_back( - globalNamespaceID, - "", //"GlobalNamespace", - InfoType::IT_namespace); - } -} - -//------------------------------------------------ - -static -std::string -getSourceCode( - Decl const* D, - SourceRange const& R) -{ - return Lexer::getSourceText(CharSourceRange::getTokenRange(R), - D->getASTContext().getSourceManager(), - D->getASTContext().getLangOpts()) - .str(); -} - -//------------------------------------------------ - -static -TagDecl* -getTagDeclForType( - QualType const& T) -{ - if(TagDecl const* D = T->getAsTagDecl()) - return D->getDefinition(); - return nullptr; -} - -static -RecordDecl* -getRecordDeclForType( - QualType const& T) -{ - if(RecordDecl const* D = T->getAsRecordDecl()) - return D->getDefinition(); - return nullptr; -} - -static -TypeInfo -getTypeInfoForType( - QualType const& T) -{ - TagDecl const* TD = getTagDeclForType(T); - if(!TD) - return TypeInfo(Reference(EmptySID, T.getAsString())); - InfoType IT; - if(dyn_cast(TD)) - IT = InfoType::IT_enum; - else if(dyn_cast(TD)) - IT = InfoType::IT_record; - else - IT = InfoType::IT_default; - return TypeInfo(Reference( - getUSRForDecl(TD), TD->getNameAsString(), IT)); -} - -static -void -parseParameters( - FunctionInfo& I, - FunctionDecl const* D) -{ - for(ParmVarDecl const* P : D->parameters()) - { - FieldTypeInfo& FieldInfo = I.Params.emplace_back( - getTypeInfoForType(P->getOriginalType()), - P->getNameAsString()); - FieldInfo.DefaultValue = getSourceCode( - D, P->getDefaultArgRange()); - } -} - -void -getTemplateParams( - llvm::Optional& TemplateInfo, - const Decl* D) -{ - if(TemplateParameterList const* ParamList = - D->getDescribedTemplateParams()) - { - if(!TemplateInfo) - { - TemplateInfo.emplace(); - } - for(const NamedDecl* ND : *ParamList) - { - TemplateInfo->Params.emplace_back(*ND); - } - } -} - -static -void -parseJavadoc( - llvm::Optional& javadoc, - Decl const* D) -{ - // VFALCO investigate whether we can use - // ASTContext::getCommentForDecl instead - RawComment* RC = - D->getASTContext().getRawCommentForDeclNoCache(D); - if(RC) - { - RC->setAttached(); - javadoc.emplace(parseJavadoc(RC, D->getASTContext(), D)); - } - else - { - javadoc.reset(); - } -} - -//------------------------------------------------ -// -// Info -// -//------------------------------------------------ - -static -bool -getInfo( - Serializer& sr, - Info& I, - NamedDecl const* D) -{ - bool IsInAnonymousNamespace; - getParentNamespaces( - I.Namespace, D, IsInAnonymousNamespace); - if(! shouldSerializeInfo( - sr.PublicOnly, IsInAnonymousNamespace, D)) - return false; - I.id = getUSRForDecl(D); - //I.Name = D->getName(); - //if(I.Name.empty()) - I.Name = D->getNameAsString(); -//if(D->getName() != I.Name) -//debug_outs() << D->getName() << ", " << I.Name << "\n"; - parseJavadoc(I.javadoc, D); - return true; -} - -static -bool -getInfo( - Serializer& sr, - Info& I, - TypedefNameDecl const* D) -{ - bool IsInAnonymousNamespace; - getParentNamespaces( - I.Namespace, D, IsInAnonymousNamespace); - if(! shouldSerializeInfo( - sr.PublicOnly, IsInAnonymousNamespace, D)) - return false; - I.id = getUSRForDecl(D); - I.Name = D->getNameAsString(); - parseJavadoc(I.javadoc, D); - return true; -} - -//------------------------------------------------ -// -// SymbolInfo -// -//------------------------------------------------ - -// The member function isThisDeclarationADefinition -// is non-virtual and only exists for certain concrete -// AST types, so we make this a function template. -template -static -bool -getSymbolInfo( - Serializer& sr, - SymbolInfo& I, - Decl const* D) -{ - if(! getInfo(sr, I, D)) - return false; - if(D->isThisDeclarationADefinition()) - I.DefLoc.emplace(sr.LineNumber, sr.File, sr.IsFileInRootDir); - else - I.Loc.emplace_back(sr.LineNumber, sr.File, sr.IsFileInRootDir); - return true; -} - -//------------------------------------------------ -// -// FunctionInfo -// -//------------------------------------------------ - -static -bool -getFunctionInfo( - Serializer& sr, - FunctionInfo& I, - FunctionDecl const* D) -{ - if(! getSymbolInfo(sr, I, D)) - return false; - QualType const qt = D->getReturnType(); - std::string s = qt.getAsString(); - I.ReturnType = getTypeInfoForType(qt); - parseParameters(I, D); - - getTemplateParams(I.Template, D); - - // Handle function template specializations. - if(FunctionTemplateSpecializationInfo const* FTSI = - D->getTemplateSpecializationInfo()) - { - if(!I.Template) - I.Template.emplace(); - I.Template->Specialization.emplace(); - auto& Specialization = *I.Template->Specialization; - - Specialization.SpecializationOf = getUSRForDecl(FTSI->getTemplate()); - - // Template parameters to the specialization. - if(FTSI->TemplateArguments) - { - for(TemplateArgument const& Arg : - FTSI->TemplateArguments->asArray()) - { - Specialization.Params.emplace_back(*D, Arg); - } - } - } - return true; -} - -//------------------------------------------------ - -static -void -getMemberTypeInfo( - MemberTypeInfo& I, - FieldDecl const* D, - Reporter& R) -{ - Assert(D && "Expect non-null FieldDecl in getMemberTypeInfo"); - parseJavadoc(I.javadoc, D); -} - -//------------------------------------------------ - -template -requires - std::derived_from && - std::is_same_v -static -void -insertChild(Parent& parent, Child&& I) -{ - if constexpr(std::is_same_v) - { - // namespace requires parent namespace - Assert(Parent::type_id == InfoType::IT_namespace); - parent.Children.Namespaces.emplace_back(I.id, I.Name, Child::type_id); - } - else if constexpr(std::is_same_v) - { - parent.Children.Records.emplace_back(I.id, I.Name, Child::type_id); - } - else if constexpr(std::is_same_v) - { - parent.Children.Functions.emplace_back(I.id, I.Name, Child::type_id); - } - else if constexpr(std::is_same_v) - { - parent.Children.Typedefs.emplace_back(I.id, I.Name, Child::type_id); - } - else if constexpr(std::is_same_v) - { - parent.Children.Enums.emplace_back(I.id, I.Name, Child::type_id); - } - else if constexpr(std::is_same_v) - { - parent.Children.Variables.emplace_back(I.id, I.Name, Child::type_id); - } - else - { - static_error("unknown Info type", I); - } -} - -// Create an empty parent for the child with the -// child inserted either as a reference or by moving -// the entire record. Then return the parent as a -// serialized bitcode. -template -requires std::derived_from -static -Bitcode -writeParent(Child&& I) -{ - if(I.Namespace.empty()) - { - if(I.id == globalNamespaceID) - { - // Global namespace has no parent. - return {}; - } - - // In global namespace - NamespaceInfo P; - Assert(P.id == globalNamespaceID); - insertChild(P, std::move(I)); - return writeBitcode(P); - } - if(I.Namespace[0].RefType == InfoType::IT_namespace) - { - NamespaceInfo P(I.Namespace[0].id); - insertChild(P, std::move(I)); - return writeBitcode(P); - } - Assert(I.Namespace[0].RefType == InfoType::IT_record); - Assert(Child::type_id != InfoType::IT_namespace); - RecordInfo P(I.Namespace[0].id); - insertChild(P, std::move(I)); - return writeBitcode(P); -} - -// There are two uses for this function. -// 1) Getting the resulting mode of inheritance of a record. -// Example: class A {}; class B : private A {}; class C : public B {}; -// It's explicit that C is publicly inherited from C and B is privately -// inherited from A. It's not explicit but C is also privately inherited from -// A. This is the AS that this function calculates. FirstAS is the -// inheritance mode of `class C : B` and SecondAS is the inheritance mode of -// `class B : A`. -// 2) Getting the inheritance mode of an inherited attribute / method. -// Example : class A { public: int M; }; class B : private A {}; -// Class B is inherited from class A, which has a public attribute. This -// attribute is now part of the derived class B but it's not public. This -// will be private because the inheritance is private. This is the AS that -// this function calculates. FirstAS is the inheritance mode and SecondAS is -// the AS of the attribute / method. -static -AccessSpecifier -getFinalAccessSpecifier( - AccessSpecifier FirstAS, - AccessSpecifier SecondAS) -{ - if(FirstAS == AccessSpecifier::AS_none || - SecondAS == AccessSpecifier::AS_none) - return AccessSpecifier::AS_none; - if(FirstAS == AccessSpecifier::AS_private || - SecondAS == AccessSpecifier::AS_private) - return AccessSpecifier::AS_private; - if(FirstAS == AccessSpecifier::AS_protected || - SecondAS == AccessSpecifier::AS_protected) - return AccessSpecifier::AS_protected; - return AccessSpecifier::AS_public; -} - -// The Access parameter is only provided when parsing the field of an inherited -// record, the access specification of the field depends on the inheritance mode -static -void -parseFields( - RecordInfo& I, - const RecordDecl* D, - bool PublicOnly, - AccessSpecifier Access, - Reporter& R) -{ - for(const FieldDecl* F : D->fields()) - { - if(!shouldSerializeInfo(PublicOnly, /*IsInAnonymousNamespace=*/false, F)) - continue; - - // Use getAccessUnsafe so that we just get the default AS_none if it's not - // valid, as opposed to an assert. - MemberTypeInfo& NewMember = I.Members.emplace_back( - getTypeInfoForType(F->getTypeSourceInfo()->getType()), - F->getNameAsString(), - getFinalAccessSpecifier(Access, F->getAccessUnsafe())); - getMemberTypeInfo(NewMember, F, R); - } -} - -static -void -parseEnumerators( - EnumInfo& I, - const EnumDecl* D) -{ - for(const EnumConstantDecl* E : D->enumerators()) - { - std::string ValueExpr; - if(const Expr* InitExpr = E->getInitExpr()) - ValueExpr = getSourceCode(D, InitExpr->getSourceRange()); - - SmallString<16> ValueStr; - E->getInitVal().toString(ValueStr); - I.Members.emplace_back(E->getNameAsString(), ValueStr, ValueExpr); - } -} - -// TODO: Remove the serialization of Parents and VirtualParents, this -// information is also extracted in the other definition of parseBases. -static -void -parseBases( - RecordInfo& I, - CXXRecordDecl const* D) -{ - // Don't parse bases if this isn't a definition. - if(!D->isThisDeclarationADefinition()) - return; - for(const CXXBaseSpecifier& B : D->bases()) - { - if(B.isVirtual()) - continue; - if(auto const* Ty = B.getType()->getAs()) - { - TemplateDecl const* D = Ty->getTemplateName().getAsTemplateDecl(); - I.Parents.emplace_back( - getUSRForDecl(D), - B.getType().getAsString(), - InfoType::IT_record); - } - else if(RecordDecl const* P = getRecordDeclForType(B.getType())) - { - I.Parents.emplace_back( - getUSRForDecl(P), - P->getNameAsString(), - InfoType::IT_record); - } - else - { - I.Parents.emplace_back( - globalNamespaceID, - B.getType().getAsString()); - } - } - for(CXXBaseSpecifier const& B : D->vbases()) - { - if(RecordDecl const* P = getRecordDeclForType(B.getType())) - { - I.VirtualParents.emplace_back( - getUSRForDecl(P), - P->getNameAsString(), - InfoType::IT_record); - } - else - { - I.VirtualParents.emplace_back( - globalNamespaceID, - B.getType().getAsString()); - } - } -} - -//------------------------------------------------ - -template -static -void -getInfo( - Info& I, - T const* D, - Javadoc jd, - bool& IsInAnonymousNamespace, - Reporter& R) -{ - I.id = getUSRForDecl(D); - I.Name = D->getNameAsString(); - getParentNamespaces( - I.Namespace, - D, - IsInAnonymousNamespace); - I.javadoc = std::move(jd); -} - -//------------------------------------------------ - -template -static -void -getSymbolInfo( - SymbolInfo& I, - T const* D, - Javadoc jd, - int LineNumber, - StringRef Filename, - bool IsFileInRootDir, - bool& IsInAnonymousNamespace, - Reporter& R) -{ - getInfo(I, D, std::move(jd), IsInAnonymousNamespace, R); - if(D->isThisDeclarationADefinition()) - I.DefLoc.emplace(LineNumber, Filename, IsFileInRootDir); - else - I.Loc.emplace_back(LineNumber, Filename, IsFileInRootDir); -} - -//------------------------------------------------ - -static -void -getFunctionInfo( - FunctionInfo& I, - FunctionDecl const* D, - Javadoc jd, - int LineNumber, - StringRef Filename, - bool IsFileInRootDir, - bool& IsInAnonymousNamespace, - Reporter& R) -{ - getSymbolInfo( - I, D, std::move(jd), - LineNumber, Filename, - IsFileInRootDir, - IsInAnonymousNamespace, - R); - QualType const qt = D->getReturnType(); - std::string s = qt.getAsString(); - I.ReturnType = getTypeInfoForType(qt); - parseParameters(I, D); - - getTemplateParams(I.Template, D); - - // Handle function template specializations. - if(const FunctionTemplateSpecializationInfo* FTSI = - D->getTemplateSpecializationInfo()) - { - if(!I.Template) - I.Template.emplace(); - I.Template->Specialization.emplace(); - auto& Specialization = *I.Template->Specialization; - - Specialization.SpecializationOf = getUSRForDecl(FTSI->getTemplate()); - - // Template parameters to the specialization. - if(FTSI->TemplateArguments) - { - for(const TemplateArgument& Arg : - FTSI->TemplateArguments->asArray()) - { - Specialization.Params.emplace_back(*D, Arg); - } - } - } -} - -static -void -parseBases( - RecordInfo& I, - CXXRecordDecl const* D, - bool IsFileInRootDir, - bool PublicOnly, - bool IsParent, - AccessSpecifier ParentAccess, - Reporter& R) -{ - // Don't parse bases if this isn't a definition. - if(!D->isThisDeclarationADefinition()) - return; - for(const CXXBaseSpecifier& B : D->bases()) - { - if(const RecordType* Ty = B.getType()->getAs()) - { - if(const CXXRecordDecl* Base = - cast_or_null(Ty->getDecl()->getDefinition())) - { - // Initialized without USR and name, this will be set in the following - // if-else stmt. - BaseRecordInfo BI( - {}, "", B.isVirtual(), - getFinalAccessSpecifier(ParentAccess, B.getAccessSpecifier()), - IsParent); - if(auto const* Ty = B.getType()->getAs()) - { - const TemplateDecl* D = Ty->getTemplateName().getAsTemplateDecl(); - BI.id = getUSRForDecl(D); - BI.Name = B.getType().getAsString(); - } - else - { - BI.id = getUSRForDecl(Base); - BI.Name = Base->getNameAsString(); - } - parseFields(BI, Base, PublicOnly, BI.Access, R); - for(auto const& Decl : Base->decls()) - { - if(auto const* MD = dyn_cast(Decl)) - { - // Don't serialize private methods - if(MD->getAccessUnsafe() == AccessSpecifier::AS_private || - !MD->isUserProvided()) - continue; - FunctionInfo FI; - FI.IsMethod = true; - // The seventh arg in getFunctionInfo is a boolean passed by - // reference, its value is not relevant in here so it's not used - // anywhere besides the function call. - bool IsInAnonymousNamespace; - getFunctionInfo(FI, MD, Javadoc(), /*LineNumber=*/{}, - /*FileName=*/{}, IsFileInRootDir, - IsInAnonymousNamespace, R); - FI.Access = - getFinalAccessSpecifier(BI.Access, MD->getAccessUnsafe()); - BI.Children.Functions.emplace_back( - FI.id, - FI.Name, - InfoType::IT_function); - } - } - I.Bases.emplace_back(std::move(BI)); - // Call this function recursively to get the inherited classes of - // this base; these new bases will also get stored in the original - // RecordInfo: I. - // - // VFALCO Commented this out because we only want to show immediate - // bases. Alternatively, the generator could check IsParent - // - #if 0 - parseBases(I, Base, IsFileInRootDir, PublicOnly, false, - I.Bases.back().Access, R); - #endif - } - } - } -} - -//------------------------------------------------ - -static -int -getLineNumber( - NamedDecl const* D) -{ - return D->getASTContext() - .getSourceManager() - .getPresumedLoc( - D->getBeginLoc()).getLine(); -} - -//------------------------------------------------ - -SerializeResult -Serializer:: -build( - NamespaceDecl const* D) -{ - NamespaceInfo I; - if(! getInfo(*this, I, D)) - return {}; - - if(D->isAnonymousNamespace()) - I.Name = "@nonymous_namespace"; - - return { - writeBitcode(I), - writeParent(std::move(I)) }; -} - -static -void -getCXXRecordSpecs( - RecordInfo& I, - CXXRecordDecl const* D) -{ - // These are from CXXRecordDecl::isEffectivelyFinal() - I.specs.set(D->hasAttr()); - if(auto const DT = D->getDestructor()) - { - I.specs.set(DT->hasAttr()); - } -} - -SerializeResult -Serializer:: -build( - CXXRecordDecl const* D) -{ - RecordInfo I; - if(! getSymbolInfo(*this, I, D)) - return {}; - I.TagType = D->getTagKind(); - getCXXRecordSpecs(I, D); - parseFields(I, D, PublicOnly, AccessSpecifier::AS_public, R_); - if(auto const* C = dyn_cast(D)) - { - if(TypedefNameDecl const* TD = C->getTypedefNameForAnonDecl()) - { - I.Name = TD->getNameAsString(); - I.IsTypeDef = true; - } - // VFALCO: remove first call to parseBases, - // that function should be deleted - parseBases(I, C); // VFALCO WHY? - - parseBases( - I, - C, - IsFileInRootDir, - PublicOnly, - true, - AccessSpecifier::AS_public, - R_); - } - - getTemplateParams(I.Template, D); - - // Full and partial specializations. - if(auto* CTSD = dyn_cast(D)) - { - if(!I.Template) - I.Template.emplace(); - I.Template->Specialization.emplace(); - auto& Specialization = *I.Template->Specialization; - - // What this is a specialization of. - auto SpecOf = CTSD->getSpecializedTemplateOrPartial(); - if(SpecOf.is()) - { - Specialization.SpecializationOf = - getUSRForDecl(SpecOf.get()); - } - else if(SpecOf.is()) - { - Specialization.SpecializationOf = - getUSRForDecl(SpecOf.get()); - } - - // Parameters to the specilization. For partial specializations, get the - // parameters "as written" from the ClassTemplatePartialSpecializationDecl - // because the non-explicit template parameters will have generated internal - // placeholder names rather than the names the user typed that match the - // template parameters. - if(ClassTemplatePartialSpecializationDecl const* CTPSD = - dyn_cast(D)) - { - if(ASTTemplateArgumentListInfo const* AsWritten = - CTPSD->getTemplateArgsAsWritten()) - { - for(unsigned i = 0; i < AsWritten->getNumTemplateArgs(); i++) - { - Specialization.Params.emplace_back( - getSourceCode(D, (*AsWritten)[i].getSourceRange())); - } - } - } - else - { - for(TemplateArgument const& Arg : CTSD->getTemplateArgs().asArray()) - { - Specialization.Params.emplace_back(*D, Arg); - } - } - } - - return { writeBitcode(I), writeParent(std::move(I)) }; -} - -//------------------------------------------------ -// -// Functions, Member Functions -// -//------------------------------------------------ -/* - Types of member functions: - - destructor - constructor - conversion operator -*/ -// VFALCO could this be done in getFunctionInfo? -// but getFunctionInfo is called parseBases() -static -void -getFunctionSpecs( - FunctionInfo& I, - FunctionDecl const* D) -{ - I.specs0.set(D->isVariadic()); - I.specs0.set(D->isVirtualAsWritten()); - I.specs0.set(D->isPure()); - I.specs0.set(D->isDefaulted()); - I.specs0.set(D->isExplicitlyDefaulted()); - I.specs0.set(D->isDeleted()); - I.specs0.set(D->isDeletedAsWritten()); - I.specs0.set(D->isNoReturn()); - // subsumes D->hasAttr() - // subsumes D->hasAttr() - // subsumes D->hasAttr() - // subsumes D->getType()->getAs()->getNoReturnAttr() - I.specs0.set(D->hasAttr()); - if(auto const* FP = D->getType()->getAs()) - I.specs0.set(FP->hasTrailingReturn()); - - I.specs0.set(D->getConstexprKind()); - // subsumes D->isConstexpr(); - // subsumes D->isConstexprSpecified(); - // subsumes D->isConsteval(); - I.specs0.set(D->getExceptionSpecType()); - I.specs0.set(D->getOverloadedOperator()); - I.specs0.set(D->getStorageClass()); - - if(auto const MF = dyn_cast(D)) - { - I.specs0.set(MF->isConst()); - I.specs0.set(MF->isVolatile()); - I.specs0.set(MF->getRefQualifier()); - //MF->isCopyAssignmentOperator() - //MF->isMoveAssignmentOperator() - //MF->isOverloadedOperator(); - //MF->isStaticOverloadedOperator(); - - if(auto const Dtor = dyn_cast(MF)) - { -//I.Name.append("-dtor"); - } - else if(auto const Ctor = dyn_cast(MF)) - { -//I.Name.append("-ctor"); - I.specs1.set(Ctor->getExplicitSpecifier().isSpecified()); - } - else if(auto const Conv = dyn_cast(MF)) - { -//I.Name.append("-conv"); - I.specs1.set(Conv->getExplicitSpecifier().isSpecified()); - } - } - else if(auto const DG = dyn_cast(D)) - { - I.specs1.set(DG->getExplicitSpecifier().isSpecified()); - } - - if(auto attr = D->getAttr()) - { - I.specs1.set(true); - I.specs1.set(attr->getSemanticSpelling()); - } -} - -static -bool -buildFunctionInfo( - Serializer& sr, - FunctionInfo& I, - FunctionDecl const* D) -{ - if(! getFunctionInfo(sr, I, D)) - return false; - I.Access = AccessSpecifier::AS_none; - getFunctionSpecs(I, D); - return true; -} - -SerializeResult -Serializer:: -build( - CXXMethodDecl const* D) -{ - FunctionInfo I; - if(! getFunctionInfo(*this, I, D)) - return {}; - getFunctionSpecs(I, D); - - I.IsMethod = true; - NamedDecl const* PD = nullptr; - if(auto const* SD = dyn_cast(D->getParent())) - PD = SD->getSpecializedTemplate(); - else - PD = D->getParent(); - SymbolID ParentID = getUSRForDecl(PD); - I.Parent = Reference( - ParentID, - PD->getNameAsString(), - InfoType::IT_record); - I.Access = D->getAccess(); - - return { writeBitcode(I), writeParent(std::move(I)) }; -} - -SerializeResult -Serializer:: -build( - FriendDecl const* D) -{ - if(NamedDecl const* ND = D->getFriendDecl()) - { - // D does not name a type - if(FunctionDecl const* FD = dyn_cast(ND)) - { - // VFALCO HACK, slam LineNumber before it is - // inserted into Loc or DefLoc - LineNumber = getLineNumber(ND); - - FunctionInfo I; - if(! buildFunctionInfo(*this, I, FD)) - return {}; - // VFALCO This is unfortunate, but the - // default of 0 would be AS_public. see #84 - I.Access = AccessSpecifier::AS_none; - SymbolID id; - getParent(id, D); - RecordInfo P(id); - P.Friends.emplace_back(I.id); - bool isInAnonymous; - getParentNamespaces(P.Namespace, ND, isInAnonymous); - return { - writeBitcode(I), - writeParent(std::move(I)), - writeBitcode(P), - writeParent(std::move(P)) }; - } - if(FunctionTemplateDecl const* FT = dyn_cast(ND)) - { - // VFALCO TODO - (void)FT; - return {}; - } - if(ClassTemplateDecl const* CT = dyn_cast(ND)) - { - // VFALCO TODO - (void)CT; - return {}; - } - - Assert(false); - return {}; - } - else if(TypeSourceInfo const* TS = D->getFriendType()) - { - (void)TS; - return {}; - } - else - { - Assert(false); - } - return {}; -} - -SerializeResult -Serializer:: -build( - UsingDecl const* D) -{ -#if 0 - DeclarationNameInfo DNI = D->getNameInfo(); - NestedNameSpecifier const* NNS = D->getQualifier(); - //auto const& specs = NNS->getSpecifiers(); - bool hasTypename = D->hasTypename(); - bool isAccess = D->isAccessDeclaration(); - auto DN = DNI.getName(); - auto s = DNI.getAsString(); -#endif - return {}; -} - -SerializeResult -Serializer:: -build( - UsingShadowDecl const* D) -{ - return {}; -} - -SerializeResult -Serializer:: -build( - FunctionDecl const* D) -{ - FunctionInfo I; - if(! buildFunctionInfo(*this, I, D)) - return {}; - return { writeBitcode(I), writeParent(std::move(I)) }; -} - -SerializeResult -Serializer:: -build( - TypedefDecl const* D) -{ - TypedefInfo I; - if(! getInfo(*this, I, D)) - return {}; - I.Underlying = getTypeInfoForType(D->getUnderlyingType()); - if(I.Underlying.Type.Name.empty()) - { - // Typedef for an unnamed type. This is like - // "typedef struct { } Foo;". The record serializer - // explicitly checks for this syntax and constructs - // a record with that name, so we don't want to emit - // a duplicate here. - return {}; - } - I.DefLoc.emplace(LineNumber, File, IsFileInRootDir); - I.IsUsing = false; - - return { writeBitcode(I), writeParent(std::move(I)) }; -} - -// VFALCO This is basically a copy of the typedef -// builder except IsUsing=true, we need to refactor -// these. -// -SerializeResult -Serializer:: -build( - TypeAliasDecl const* D) -{ - TypedefInfo I; - if(! getInfo(*this, I, D)) - return {}; - I.Underlying = getTypeInfoForType(D->getUnderlyingType()); - if(I.Underlying.Type.Name.empty()) - { - // Typedef for an unnamed type. This is like - // "typedef struct { } Foo;". The record serializer - // explicitly checks for this syntax and constructs - // a record with that name, so we don't want to emit - // a duplicate here. - return {}; - } - I.DefLoc.emplace(LineNumber, File, IsFileInRootDir); - I.IsUsing = true; - - return { writeBitcode(I), writeParent(std::move(I)) }; -} - -SerializeResult -Serializer:: -build( - EnumDecl const* D) -{ - EnumInfo I; - if(! getSymbolInfo(*this, I, D)) - return {}; - I.Scoped = D->isScoped(); - if(D->isFixed()) - { - auto Name = D->getIntegerType().getAsString(); - I.BaseType = TypeInfo(Name); - } - parseEnumerators(I, D); - - return { writeBitcode(I), writeParent(std::move(I)) }; -} - -SerializeResult -Serializer:: -build( - VarDecl const* D) -{ - VariableInfo I; - if(! getSymbolInfo(*this, I, D)) - return {}; - static_cast(I) = - getTypeInfoForType(D->getTypeSourceInfo()->getType()); - I.specs.set(D->getStorageClass()); - return { writeBitcode(I), writeParent(std::move(I)) }; -} - -} // mrdox -} // clang diff --git a/source/api/AST/Serializer.hpp b/source/api/AST/Serializer.hpp deleted file mode 100644 index 2187a9116..000000000 --- a/source/api/AST/Serializer.hpp +++ /dev/null @@ -1,104 +0,0 @@ -// -// This is a derivative work. originally part of the LLVM Project. -// Licensed under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -// Copyright (c) 2023 Vinnie Falco (vinnie.falco@gmail.com) -// -// Official repository: https://github.com/cppalliance/mrdox -// - -#ifndef MRDOX_API_AST_SERIALIZER_HPP -#define MRDOX_API_AST_SERIALIZER_HPP - -#include -#include "clangASTComment.hpp" -#include "Bitcode.hpp" -#include "ConfigImpl.hpp" -#include -#include -#include -#include -#include -#include -#include -#include - -/* The serialization algorithm takes as input a - typed AST node, and produces as output a pair - of one or two Bitcode objects. A bitcode - contains the metadata for a given symbol ID, - serialized as bitcode. -*/ - -namespace clang { -namespace mrdox { - -/** Holds the result of serializing a Decl. - - This can result in two bitcodes. One for - the declaration itself, and possibly one - for the parent which is referenced by the - decl. -*/ -struct SerializeResult -{ - llvm::SmallVector bitcodes; - - template... Args> - SerializeResult( - Args&&... args) - { - (bitcodes.emplace_back(std::forward(args)), ...); - } -}; - -/** State information used during serialization to bitcode. -*/ -class Serializer -{ -public: - MangleContext& mc; - ConfigImpl const& config_; - Reporter& R_; - StringRef File; - int LineNumber; - bool PublicOnly; - bool IsFileInRootDir; - - Serializer( - MangleContext& mc_, - int LineNumber_, - StringRef File_, - bool IsFileInRootDir_, - ConfigImpl const& config, - Reporter& R) - : mc(mc_) - , config_(config) - , R_(R) - , File(File_) - , LineNumber(LineNumber_) - , PublicOnly(! config_.includePrivate_) - , IsFileInRootDir(IsFileInRootDir_) - { - } - - SerializeResult build(NamespaceDecl const* D); - SerializeResult build(CXXRecordDecl const* D); - SerializeResult build(CXXMethodDecl const* D); - SerializeResult build(FriendDecl const* D); - SerializeResult build(UsingDecl const* D); - SerializeResult build(UsingShadowDecl const* D); - SerializeResult build(FunctionDecl const* D); - SerializeResult build(TypedefDecl const* D); - SerializeResult build(TypeAliasDecl const* D); - SerializeResult build(EnumDecl const* D); - SerializeResult build(VarDecl const* D); -}; - -} // mrdox -} // clang - -#endif - diff --git a/source/api/Corpus.cpp b/source/api/Corpus.cpp index d05e5f98b..95697ed8a 100644 --- a/source/api/Corpus.cpp +++ b/source/api/Corpus.cpp @@ -48,7 +48,7 @@ mergeInfos(std::vector>& Values) case InfoType::IT_typedef: return reduce(Values); case InfoType::IT_variable: - return reduce(Values); + return reduce(Values); default: return llvm::createStringError(llvm::inconvertibleErrorCode(), "unexpected info type"); @@ -138,7 +138,7 @@ bool Corpus:: Visitor:: visit( - VariableInfo const&) + VarInfo const&) { return true; } @@ -193,8 +193,8 @@ visit(Scope const& I, Visitor& f) const for(auto const& ref : I.Enums) if(! visit(get(ref.id), f)) return false; - for(auto const& ref : I.Variables) - if(! visit(get(ref.id), f)) + for(auto const& ref : I.Vars) + if(! visit(get(ref.id), f)) return false; return true; } @@ -249,8 +249,8 @@ visitWithOverloads( for(auto const& ref : I.Enums) if(! visit(get(ref.id), f)) return false; - for(auto const& ref : I.Variables) - if(! visit(get(ref.id), f)) + for(auto const& ref : I.Vars) + if(! visit(get(ref.id), f)) return false; return true; } @@ -272,7 +272,7 @@ visit(Info const& I, Visitor& f) const case InfoType::IT_enum: return f.visit(static_cast(I)); case InfoType::IT_variable: - return f.visit(static_cast(I)); + return f.visit(static_cast(I)); default: llvm_unreachable("wrong InfoType for viist"); } diff --git a/source/api/CorpusImpl.cpp b/source/api/CorpusImpl.cpp index d519d7e6f..9ebd10c36 100644 --- a/source/api/CorpusImpl.cpp +++ b/source/api/CorpusImpl.cpp @@ -82,8 +82,8 @@ visit(Scope& I, MutableVisitor& f) visit(get(ref.id), f); for(auto& ref : I.Enums) visit(get(ref.id), f); - for(auto& ref : I.Variables) - visit(get(ref.id), f); + for(auto& ref : I.Vars) + visit(get(ref.id), f); } void @@ -103,7 +103,7 @@ visit(Info& I, MutableVisitor& f) case InfoType::IT_enum: return f.visit(static_cast(I)); case InfoType::IT_variable: - return f.visit(static_cast(I)); + return f.visit(static_cast(I)); default: llvm_unreachable("wrong InfoType for viist"); } @@ -161,7 +161,7 @@ class CorpusImpl:: postProcess(I); } - void visit(VariableInfo& I) override + void visit(VarInfo& I) override { postProcess(I); } @@ -183,7 +183,7 @@ class CorpusImpl:: canonicalize(scope.Functions); canonicalize(scope.Typedefs); canonicalize(scope.Enums); - canonicalize(scope.Variables); + canonicalize(scope.Vars); } void canonicalize(std::vector& list) noexcept diff --git a/source/api/CorpusImpl.hpp b/source/api/CorpusImpl.hpp index 6e730f433..f71635174 100644 --- a/source/api/CorpusImpl.hpp +++ b/source/api/CorpusImpl.hpp @@ -78,7 +78,7 @@ class CorpusImpl : public Corpus virtual void visit(FunctionInfo&) {} virtual void visit(TypedefInfo&) {} virtual void visit(EnumInfo&) {} - virtual void visit(VariableInfo&) {} + virtual void visit(VarInfo&) {} }; /** Visit the specified symbol ID or node. diff --git a/source/api/Metadata/FunctionKind.cpp b/source/api/Metadata/FunctionKind.cpp index f1a9872b1..8d412aaef 100644 --- a/source/api/Metadata/FunctionKind.cpp +++ b/source/api/Metadata/FunctionKind.cpp @@ -33,51 +33,54 @@ getFunctionKindString( }; using FK = FunctionKind; static constinit Item const tab[] = { - { "new", "new", FK::Plain }, - { "delete", "del", FK::OpDelete }, - { "new[]", "arr_new", FK::OpArray_New }, - { "delete[]", "arr_del", FK::OpArray_Delete }, - { "+", "plus", FK::OpPlus }, - { "-", "minus", FK::OpMinus }, - { "*", "star", FK::OpStar }, - { "/", "slash", FK::OpSlash }, - { "%", "mod", FK::OpPercent }, - { "^", "xor", FK::OpCaret }, - { "&", "bitand", FK::OpAmp }, - { "|", "bitor", FK::OpPipe }, - { "~", "bitnot", FK::OpTilde }, - { "!", "not", FK::OpExclaim }, - { "=", "assign", FK::OpEqual }, - { "<", "lt", FK::OpLess }, - { ">", "gt", FK::OpGreater }, - { "+=", "plus_eq", FK::OpPlusEqual }, - { "-=", "minus_eq", FK::OpMinusEqual }, - { "*=", "star_eq", FK::OpStarEqual }, - { "/=", "slash_eq", FK::OpSlashEqual }, - { "%=", "mod_eq", FK::OpPercentEqual }, - { "^=", "xor_eq", FK::OpCaretEqual }, - { "&=", "and_eq", FK::OpAmpEqual }, - { "|=", "or_eq", FK::OpPipeEqual }, - { "<<", "lt_lt", FK::OpLessLess }, - { ">>", "gt_gt", FK::OpGreaterGreater }, - { "<<=", "lt_lt_eq", FK::OpLessLessEqual }, - { ">>=", "gt_gt_eq", FK::OpGreaterGreaterEqual }, - { "==", "eq", FK::OpEqualEqual }, - { "!=", "not_eq", FK::OpExclaimEqual }, - { "<=", "le", FK::OpLessEqual }, - { ">=", "ge", FK::OpGreaterEqual }, - { "<=>", "3way", FK::OpSpaceship }, - { "&&", "and", FK::OpAmpAmp }, - { "||", "or", FK::OpPipePipe }, - { "++", "inc", FK::OpPlusPlus }, - { "--", "dec", FK::OpMinusMinus }, - { ",", "comma", FK::OpComma }, - { "->*", "ptrmem", FK::OpArrowStar }, - { "->", "ptr", FK::OpArrow }, - { "()", "call", FK::OpCall }, - { "[]", "subs", FK::OpSubscript }, - { "?", "ternary", FK::OpConditional }, - { "co_await", "coawait", FK::OpCoawait } + { "new", "new", FK::Plain }, + { "delete", "del", FK::OpDelete }, + { "new[]", "arr_new", FK::OpArray_New }, + { "delete[]", "arr_del", FK::OpArray_Delete }, + { "+", "plus", FK::OpPlus }, + { "-", "minus", FK::OpMinus }, + { "*", "star", FK::OpStar }, + { "/", "slash", FK::OpSlash }, + { "%", "mod", FK::OpPercent }, + { "^", "xor", FK::OpCaret }, + { "&", "bitand", FK::OpAmp }, + { "|", "bitor", FK::OpPipe }, + { "~", "bitnot", FK::OpTilde }, + { "!", "not", FK::OpExclaim }, + { "=", "assign", FK::OpEqual }, + { "<", "lt", FK::OpLess }, + { ">", "gt", FK::OpGreater }, + { "+=", "plus_eq", FK::OpPlusEqual }, + { "-=", "minus_eq", FK::OpMinusEqual }, + { "*=", "star_eq", FK::OpStarEqual }, + { "/=", "slash_eq", FK::OpSlashEqual }, + { "%=", "mod_eq", FK::OpPercentEqual }, + { "^=", "xor_eq", FK::OpCaretEqual }, + { "&=", "and_eq", FK::OpAmpEqual }, + { "|=", "or_eq", FK::OpPipeEqual }, + { "<<", "lt_lt", FK::OpLessLess }, + { ">>", "gt_gt", FK::OpGreaterGreater }, + { "<<=", "lt_lt_eq", FK::OpLessLessEqual }, + { ">>=", "gt_gt_eq", FK::OpGreaterGreaterEqual }, + { "==", "eq", FK::OpEqualEqual }, + { "!=", "not_eq", FK::OpExclaimEqual }, + { "<=", "le", FK::OpLessEqual }, + { ">=", "ge", FK::OpGreaterEqual }, + { "<=>", "3way", FK::OpSpaceship }, + { "&&", "and", FK::OpAmpAmp }, + { "||", "or", FK::OpPipePipe }, + { "++", "inc", FK::OpPlusPlus }, + { "--", "dec", FK::OpMinusMinus }, + { ",", "comma", FK::OpComma }, + { "->*", "ptrmem", FK::OpArrowStar }, + { "->", "ptr", FK::OpArrow }, + { "()", "call", FK::OpCall }, + { "[]", "subs", FK::OpSubscript }, + { "?", "ternary", FK::OpConditional }, + { "co_await", "coawait", FK::OpCoawait }, + { "~", "dtor", FK::Destructor }, + { "", "ctor", FK::Constructor }, + { "", "conv", FK::Conversion } }; Assert(tab[to_underlying(kind)].kind == kind); return tab[to_underlying(kind)].name; diff --git a/source/api/Metadata/Reduce.cpp b/source/api/Metadata/Reduce.cpp index 81473825f..22b0c6b54 100644 --- a/source/api/Metadata/Reduce.cpp +++ b/source/api/Metadata/Reduce.cpp @@ -113,7 +113,7 @@ void merge(NamespaceInfo& I, NamespaceInfo&& Other) reduceChildren(I.Children.Functions, std::move(Other.Children.Functions)); reduceChildren(I.Children.Typedefs, std::move(Other.Children.Typedefs)); reduceChildren(I.Children.Enums, std::move(Other.Children.Enums)); - reduceChildren(I.Children.Variables, std::move(Other.Children.Variables)); + reduceChildren(I.Children.Vars, std::move(Other.Children.Vars)); mergeInfo(I, std::move(Other)); } @@ -137,7 +137,7 @@ void merge(RecordInfo& I, RecordInfo&& Other) reduceChildren(I.Children.Functions, std::move(Other.Children.Functions)); reduceChildren(I.Children.Typedefs, std::move(Other.Children.Typedefs)); reduceChildren(I.Children.Enums, std::move(Other.Children.Enums)); - reduceChildren(I.Children.Variables, std::move(Other.Children.Variables)); + reduceChildren(I.Children.Vars, std::move(Other.Children.Vars)); mergeSymbolInfo(I, std::move(Other)); if (!I.Template) I.Template = Other.Template; @@ -195,7 +195,7 @@ void merge(EnumInfo& I, EnumInfo&& Other) mergeSymbolInfo(I, std::move(Other)); } -void merge(VariableInfo& I, VariableInfo&& Other) +void merge(VarInfo& I, VarInfo&& Other) { Assert(canMerge(I, Other)); if(I.Type.id == EmptySID && I.Type.Name.empty()) diff --git a/source/api/Metadata/Reduce.hpp b/source/api/Metadata/Reduce.hpp index f19e9d25e..2a3ccaade 100644 --- a/source/api/Metadata/Reduce.hpp +++ b/source/api/Metadata/Reduce.hpp @@ -26,7 +26,7 @@ void merge(RecordInfo& I, RecordInfo&& Other); void merge(FunctionInfo& I, FunctionInfo&& Other); void merge(TypedefInfo& I, TypedefInfo&& Other); void merge(EnumInfo& I, EnumInfo&& Other); -void merge(VariableInfo& I, VariableInfo&& Other); +void merge(VarInfo& I, VarInfo&& Other); void merge(Reference& I, Reference&& Other); // diff --git a/source/api/Support/SafeNames.cpp b/source/api/Support/SafeNames.cpp index 0a144cf81..8b0576da7 100644 --- a/source/api/Support/SafeNames.cpp +++ b/source/api/Support/SafeNames.cpp @@ -81,7 +81,7 @@ class SafeNames:: scope.Functions.size() + scope.Typedefs.size() + scope.Enums.size() + - scope.Variables.size()); + scope.Vars.size()); for(auto const& ref : scope.Namespaces) infos.emplace_back(corpus_.find(ref.id)); for(auto const& ref : scope.Records) @@ -92,7 +92,7 @@ class SafeNames:: infos.emplace_back(corpus_.find(ref.id)); for(auto const& ref : scope.Enums) infos.emplace_back(corpus_.find(ref.id)); - for(auto const& ref : scope.Variables) + for(auto const& ref : scope.Vars) infos.emplace_back(corpus_.find(ref.id)); if(infos.size() < 2) return infos; diff --git a/source/api/_XML/CXXTags.cpp b/source/api/_XML/CXXTags.cpp index 5f102ad74..f08a83e7a 100644 --- a/source/api/_XML/CXXTags.cpp +++ b/source/api/_XML/CXXTags.cpp @@ -44,7 +44,7 @@ getTagName(Info const& I) noexcept case InfoType::IT_enum: return enumTagName; case InfoType::IT_variable: - return variableTagName; + return varTagName; default: break; } diff --git a/source/api/_XML/CXXTags.hpp b/source/api/_XML/CXXTags.hpp index a945a4fdf..99bb16656 100644 --- a/source/api/_XML/CXXTags.hpp +++ b/source/api/_XML/CXXTags.hpp @@ -18,7 +18,7 @@ #include #include #include -#include +#include /* This file holds the business logic for transforming @@ -38,7 +38,7 @@ constexpr llvm::StringRef functionTagName = "function"; constexpr llvm::StringRef typedefTagName = "typedef"; constexpr llvm::StringRef aliasTagName = "alias"; constexpr llvm::StringRef enumTagName = "enum"; -constexpr llvm::StringRef variableTagName = "variable"; +constexpr llvm::StringRef varTagName = "var"; constexpr llvm::StringRef attributeTagName = "attr"; constexpr llvm::StringRef returnTagName = "return"; constexpr llvm::StringRef paramTagName = "param"; diff --git a/source/api/_XML/XMLWriter.cpp b/source/api/_XML/XMLWriter.cpp index c9de12462..3ac3da6b1 100644 --- a/source/api/_XML/XMLWriter.cpp +++ b/source/api/_XML/XMLWriter.cpp @@ -13,6 +13,7 @@ #include "ConfigImpl.hpp" #include "CXXTags.hpp" #include "Support/Radix.hpp" +#include "Support/SafeNames.hpp" #include "Support/Operator.hpp" #include #include @@ -65,6 +66,7 @@ struct llvm::yaml::MappingTraits< auto& opt= opt_.opt; io.mapOptional("index", opt.index); io.mapOptional("prolog", opt.prolog); + io.mapOptional("safe-names", opt.safe_names); } }; @@ -144,7 +146,7 @@ build() "\n"; - if(options_.index) + if(options_.index || options_.safe_names) writeIndex(); if(! corpus_.visit(globalNamespaceID, *this)) @@ -165,11 +167,27 @@ writeIndex() std::string temp; temp.reserve(256); tags_.open("symbols"); - for(auto I : corpus_.index()) - tags_.write("symbol", {}, { - { "name", I->getFullyQualifiedName(temp) }, - { "tag", getTagName(*I) }, - { I->id } }); + if(options_.safe_names) + { + SafeNames names(corpus_); + for(auto I : corpus_.index()) + { + auto safe_name = names.get(I->id); + tags_.write("symbol", {}, { + { "safe", safe_name }, + { "name", I->getFullyQualifiedName(temp) }, + { "tag", getTagName(*I) }, + { I->id } }); + } + } + else + { + for(auto I : corpus_.index()) + tags_.write("symbol", {}, { + { "name", I->getFullyQualifiedName(temp) }, + { "tag", getTagName(*I) }, + { I->id } }); + } tags_.close("symbols"); } @@ -262,6 +280,7 @@ visit( writeSymbol(I); write(I.specs0, tags_); + //write(I.specs1, tags_); writeReturnType(I.ReturnType, tags_); @@ -339,9 +358,9 @@ visit( bool XMLWriter:: visit( - VariableInfo const& I) + VarInfo const& I) { - tags_.open(variableTagName, { + tags_.open(varTagName, { { "name", I.Name }, { I.id } }); @@ -357,7 +376,7 @@ visit( writeJavadoc(I.javadoc); - tags_.close(variableTagName); + tags_.close(varTagName); return true; } diff --git a/source/api/_XML/XMLWriter.hpp b/source/api/_XML/XMLWriter.hpp index cd90bbe28..09280170f 100644 --- a/source/api/_XML/XMLWriter.hpp +++ b/source/api/_XML/XMLWriter.hpp @@ -45,6 +45,7 @@ class XMLWriter { bool index = false; bool prolog = true; + bool safe_names = false; }; Options options_; @@ -64,7 +65,7 @@ class XMLWriter bool visit(FunctionInfo const&) override; bool visit(TypedefInfo const&) override; bool visit(EnumInfo const&) override; - bool visit(VariableInfo const&) override; + bool visit(VarInfo const&) override; void writeInfo(Info const&); void writeSymbol(SymbolInfo const& I); diff --git a/source/api/_adoc/AdocMultiPageWriter.cpp b/source/api/_adoc/AdocMultiPageWriter.cpp index 1a6b0fc44..f210c0c7a 100644 --- a/source/api/_adoc/AdocMultiPageWriter.cpp +++ b/source/api/_adoc/AdocMultiPageWriter.cpp @@ -69,7 +69,7 @@ build( void AdocMultiPageWriter:: build( - VariableInfo const& I) + VarInfo const& I) { } diff --git a/source/api/_adoc/AdocMultiPageWriter.hpp b/source/api/_adoc/AdocMultiPageWriter.hpp index 3278969ef..7d714a582 100644 --- a/source/api/_adoc/AdocMultiPageWriter.hpp +++ b/source/api/_adoc/AdocMultiPageWriter.hpp @@ -36,7 +36,7 @@ class AdocMultiPageWriter void build(FunctionInfo const&); void build(TypedefInfo const&); void build(EnumInfo const&); - void build(VariableInfo const&); + void build(VarInfo const&); void build(OverloadInfo const&); diff --git a/source/api/_adoc/AdocSinglePageWriter.cpp b/source/api/_adoc/AdocSinglePageWriter.cpp index 23003b8cc..3df8e825c 100644 --- a/source/api/_adoc/AdocSinglePageWriter.cpp +++ b/source/api/_adoc/AdocSinglePageWriter.cpp @@ -81,7 +81,7 @@ visit( auto functionOverloads = makeNamespaceOverloads(I, corpus_); auto typedefList = buildSortedList(I.Children.Typedefs); auto enumList = buildSortedList(I.Children.Enums); - auto variableList = buildSortedList(I.Children.Variables); + auto variableList = buildSortedList(I.Children.Vars); // don't emit empty namespaces, // but still visit child namespaces. @@ -89,7 +89,7 @@ visit( ! functionOverloads.list.empty() || ! I.Children.Typedefs.empty() || ! I.Children.Enums.empty() || - ! I.Children.Variables.empty()) + ! I.Children.Vars.empty()) { std::string s; if(I.id == EmptySID) @@ -169,7 +169,7 @@ visit( if(! variableList.empty()) { - beginSection("Variables"); + beginSection("Vars"); os_ << "\n" "[cols=1]\n" "|===\n"; @@ -262,7 +262,7 @@ visit( bool AdocSinglePageWriter:: visit( - VariableInfo const&) + VarInfo const&) { return true; } diff --git a/source/api/_adoc/AdocSinglePageWriter.hpp b/source/api/_adoc/AdocSinglePageWriter.hpp index a3fd44021..d2102a156 100644 --- a/source/api/_adoc/AdocSinglePageWriter.hpp +++ b/source/api/_adoc/AdocSinglePageWriter.hpp @@ -47,7 +47,7 @@ class AdocSinglePageWriter bool visit(FunctionInfo const&) override; bool visit(TypedefInfo const&) override; bool visit(EnumInfo const&) override; - bool visit(VariableInfo const&) override; + bool visit(VarInfo const&) override; //bool visitOverloads(Info const& P, OverloadInfo const&); }; diff --git a/source/api/_adoc/AdocWriter.cpp b/source/api/_adoc/AdocWriter.cpp index 1546c6edc..137625818 100644 --- a/source/api/_adoc/AdocWriter.cpp +++ b/source/api/_adoc/AdocWriter.cpp @@ -402,7 +402,7 @@ write( void AdocWriter:: write( - VariableInfo const& I) + VarInfo const& I) { } diff --git a/source/api/_adoc/AdocWriter.hpp b/source/api/_adoc/AdocWriter.hpp index fec05927d..501d3df6f 100644 --- a/source/api/_adoc/AdocWriter.hpp +++ b/source/api/_adoc/AdocWriter.hpp @@ -73,7 +73,7 @@ class AdocWriter void write(FunctionInfo const& I); void write(TypedefInfo const& I); void write(EnumInfo const& I); - void write(VariableInfo const& I); + void write(VarInfo const& I); virtual llvm::StringRef linkFor(Info const&); virtual llvm::StringRef linkFor(Info const&, OverloadInfo const&); diff --git a/source/api/llvm.natvis b/source/api/llvm.natvis index 5ef405c58..de47568b5 100644 --- a/source/api/llvm.natvis +++ b/source/api/llvm.natvis @@ -399,4 +399,4 @@ For later versions of Visual Studio, no setup is required. $(Type) {PassID} {Kind} - + \ No newline at end of file diff --git a/test-files/old-tests/ns-variables.xml b/test-files/old-tests/ns-variables.xml index fa4650da3..079e8ddac 100644 --- a/test-files/old-tests/ns-variables.xml +++ b/test-files/old-tests/ns-variables.xml @@ -5,18 +5,18 @@ - + - - + + - - + + - +