From 30d33335f9cb81a928f254eb331fc02b2b5b9e26 Mon Sep 17 00:00:00 2001 From: Fernando Pelliccioni Date: Thu, 12 Sep 2024 12:49:52 +0200 Subject: [PATCH] feat: tagfiles generation for cross-referencing in doxygen format --- include/mrdocs/Corpus.hpp | 33 ++ include/mrdocs/Generator.hpp | 1 + src/lib/Gen/tagfile/TagfileGenerator.cpp | 45 +++ src/lib/Gen/tagfile/TagfileGenerator.hpp | 57 +++ src/lib/Gen/tagfile/TagfileWriter.cpp | 451 +++++++++++++++++++++++ src/lib/Gen/tagfile/TagfileWriter.hpp | 76 ++++ src/lib/Lib/ConfigOptions.json | 1 + src/lib/Support/GeneratorsImpl.cpp | 5 + util/generate-config-info.py | 2 +- 9 files changed, 670 insertions(+), 1 deletion(-) create mode 100644 src/lib/Gen/tagfile/TagfileGenerator.cpp create mode 100644 src/lib/Gen/tagfile/TagfileGenerator.hpp create mode 100644 src/lib/Gen/tagfile/TagfileWriter.cpp create mode 100644 src/lib/Gen/tagfile/TagfileWriter.hpp diff --git a/include/mrdocs/Corpus.hpp b/include/mrdocs/Corpus.hpp index aaa10d293..33c95bfda 100644 --- a/include/mrdocs/Corpus.hpp +++ b/include/mrdocs/Corpus.hpp @@ -160,6 +160,39 @@ class MRDOCS_VISIBLE } } + /** Visit the members of specified Info. + + This function iterates the members of the specified + Info `I`. For each member associated with a + function with the same name as the member, the + function object `f` is invoked with the member + as the first argument, followed by `args...`. + + When there are more than one member function + with the same name, the function object `f` is + invoked with an @ref OverloadSet as the first + argument, followed by `args...`. + + @param I The Info to traverse. + @param pred The predicate to use to determine if the member should be visited. + @param f The function to invoke with the member as the first argument, followed by `args...`. + @param args The arguments to pass to the function. + */ + template + void + traverseIf( + T const& I, Pred&& pred, F&& f, Args&&... args) const + { + for (auto const& id : I.Members) + { + if (pred(get(id))) + { + visit(get(id), std::forward(f), + std::forward(args)...); + } + } + } + /** Visit the function members of specified Info. This function iterates the members of the specified diff --git a/include/mrdocs/Generator.hpp b/include/mrdocs/Generator.hpp index dcc4f7c41..83fc89d72 100644 --- a/include/mrdocs/Generator.hpp +++ b/include/mrdocs/Generator.hpp @@ -64,6 +64,7 @@ class MRDOCS_VISIBLE @li "adoc" Asciidoctor @li "xml" XML @li "html" HTML + @li "tagfile" Tagfile The returned string should not include a leading period. diff --git a/src/lib/Gen/tagfile/TagfileGenerator.cpp b/src/lib/Gen/tagfile/TagfileGenerator.cpp new file mode 100644 index 000000000..2d844c44e --- /dev/null +++ b/src/lib/Gen/tagfile/TagfileGenerator.cpp @@ -0,0 +1,45 @@ +// +// 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) 2024 Fernando Pelliccioni (fpelliccioni@gmail.com) +// +// Official repository: https://github.com/cppalliance/mrdocs +// + +#include "TagfileGenerator.hpp" +#include "TagfileWriter.hpp" +#include "lib/Support/Radix.hpp" +#include "lib/Support/RawOstream.hpp" +#include +#include + +namespace clang { +namespace mrdocs { +namespace tagfile { + +Error +TagfileGenerator:: +buildOne( + std::ostream& os, + Corpus const& corpus) const +{ + namespace fs = llvm::sys::fs; + RawOstream raw_os(os); + return TagfileWriter(raw_os, corpus).build(); +} + +} // tagfile + +//------------------------------------------------ + +std::unique_ptr +makeTagfileGenerator() +{ + return std::make_unique(); +} + +} // mrdocs +} // clang diff --git a/src/lib/Gen/tagfile/TagfileGenerator.hpp b/src/lib/Gen/tagfile/TagfileGenerator.hpp new file mode 100644 index 000000000..4e626c135 --- /dev/null +++ b/src/lib/Gen/tagfile/TagfileGenerator.hpp @@ -0,0 +1,57 @@ +// +// 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) 2024 Fernando Pelliccioni (fpelliccioni@gmail.com) +// +// Official repository: https://github.com/cppalliance/mrdocs +// + +#ifndef MRDOCS_LIB_GEN_TAGFILE_TAGFILEGENERATOR_HPP +#define MRDOCS_LIB_GEN_TAGFILE_TAGFILEGENERATOR_HPP + +#include +#include +#include +#include +#include + +namespace clang { +namespace mrdocs { +namespace tagfile { + +//------------------------------------------------ + +struct TagfileGenerator : Generator +{ + std::string_view + id() const noexcept override + { + return "tagfile"; + } + + std::string_view + displayName() const noexcept override + { + return "Doxygen Tagfile"; + } + + std::string_view + fileExtension() const noexcept override + { + return "tag.xml"; + } + + Error + buildOne( + std::ostream& os, + Corpus const& corpus) const override; +}; + +} // tagfile +} // mrdocs +} // clang + +#endif diff --git a/src/lib/Gen/tagfile/TagfileWriter.cpp b/src/lib/Gen/tagfile/TagfileWriter.cpp new file mode 100644 index 000000000..06929e904 --- /dev/null +++ b/src/lib/Gen/tagfile/TagfileWriter.cpp @@ -0,0 +1,451 @@ +// +// 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) 2024 Fernando Pelliccioni (fpelliccioni@gmail.com) +// +// Official repository: https://github.com/cppalliance/mrdocs +// + +#include "../xml/CXXTags.hpp" +#include "TagfileWriter.hpp" +#include "lib/Lib/ConfigImpl.hpp" + +namespace clang { +namespace mrdocs { +namespace tagfile { + +//------------------------------------------------ +// +// XMLWriter +// +//------------------------------------------------ + +TagfileWriter:: +TagfileWriter( + llvm::raw_ostream& os, + Corpus const& corpus) noexcept + : tags_(os) + , os_(os) + , corpus_(corpus) +{ +} + +Error +TagfileWriter:: +build() +{ + os_ << "\n"; + os_ << "\n"; + + visit(corpus_.globalNamespace(), *this); + + os_ << "\n"; + + return {}; +} + +//------------------------------------------------ + +void +TagfileWriter:: +writeIndex() +{ + // Do nothing +} + +//------------------------------------------------ + +template +void +TagfileWriter:: +operator()( + T const& I) +{ + #define INFO_PASCAL(Type) if constexpr(T::is##Type()) write##Type(I); + #include +} + +template +void +TagfileWriter:: +operator()( + T const& I, + SimpleWriterTag) +{ + #define INFO_PASCAL(Type) if constexpr(T::is##Type()) write##Type(I, SimpleWriterTag{}); + #include +} + +//------------------------------------------------ + +void +TagfileWriter:: +writeSourceInfo( + SourceInfo const& I) +{ + if(I.DefLoc) + writeLocation(*I.DefLoc, true); + for(auto const& loc : I.Loc) + writeLocation(loc, false); +} + +void +TagfileWriter:: +writeLocation( + Location const& loc, + bool def) +{ + tags_.write("filename", loc.Filename); +} + +void +TagfileWriter:: +writeNamespace( + NamespaceInfo const& I, + SimpleWriterTag) +{ + // Do nothing +} + +bool +TagfileWriter:: +containsOnlyNamespaces( + NamespaceInfo const& I) const +{ + bool onlyNamespaces = true; + corpus_.traverse(I, [&](Info const& I) + { + if (I.Kind != InfoKind::Namespace) + { + onlyNamespaces = false; + return false; + } + return true; + }); + return onlyNamespaces; +} + +void +TagfileWriter:: +writeNamespace( + NamespaceInfo const& I) +{ + if (!containsOnlyNamespaces(I)) + { + tags_.open("compound", { + { "kind", "namespace" } + }); + + std::string temp; + temp.reserve(256); + tags_.write("name", corpus_.getFullyQualifiedName(I, temp)); + tags_.write("name", I.Name); + + corpus_.traverseIf(I, [](Info const& I) + { + return I.Kind != InfoKind::Function; + }, *this, SimpleWriterTag{}); + + corpus_.traverseIf(I, [](Info const& I) + { + return I.Kind == InfoKind::Function; + }, *this, SimpleWriterTag{}); + + tags_.close("compound"); + } + corpus_.traverse(I, *this); +} + +template +void +TagfileWriter:: +writeClassLike(T const& I, SimpleWriterTag) +{ + std::string temp; + temp.reserve(256); + tags_.write("class", corpus_.getFullyQualifiedName(I, temp), {{"kind", "class"}}); +} + +template +void +TagfileWriter:: +writeClassLike(T const& I) +{ + tags_.open("compound", { + { "kind", "class" } + }); + + std::string temp; + temp.reserve(256); + tags_.write("name", corpus_.getFullyQualifiedName(I, temp)); + + writeSourceInfo(I); + + tags_.close("compound"); +} + +void +TagfileWriter:: +writeEnum( + EnumInfo const& I, + SimpleWriterTag) +{ + writeClassLike(I, SimpleWriterTag{}); +} + +void +TagfileWriter:: +writeEnum( + EnumInfo const& I) +{ + writeClassLike(I); +} + +void +TagfileWriter:: +writeEnumerator( + EnumeratorInfo const& I, + SimpleWriterTag) +{ + writeClassLike(I, SimpleWriterTag{}); +} + +void +TagfileWriter:: +writeEnumerator( + EnumeratorInfo const& I) +{ + writeClassLike(I); +} + +void +TagfileWriter:: +writeFriend( + FriendInfo const& I, + SimpleWriterTag) +{ + writeClassLike(I, SimpleWriterTag{}); +} + +void +TagfileWriter:: +writeFriend( + FriendInfo const& I) +{ + writeClassLike(I); +} + +void +TagfileWriter:: +writeFunction( + FunctionInfo const& I, + SimpleWriterTag) +{ + // Do nothing +} + +void +TagfileWriter:: +writeFunction( + FunctionInfo const& I) +{ + using xml::toString; + + tags_.open("member", {{"kind", "function"}}); + tags_.write("type", toString(*I.ReturnType)); + tags_.write("name", I.Name); + + std::string arglist = "("; + for(auto const& J : I.Params) + { + arglist += toString(*J.Type); + arglist += " "; + arglist += J.Name; + arglist += ", "; + } + if (arglist.size() > 2) { + arglist.resize(arglist.size() - 2); + } + arglist += ")"; + tags_.write("arglist", arglist); + + for(auto const& loc : I.Loc) + tags_.write("anchorfile", loc.Filename); + + tags_.write("anchor", ""); + tags_.close("member"); +} + +void +TagfileWriter:: +writeGuide( + GuideInfo const& I, + SimpleWriterTag) +{ + // Do nothing +} + +void +TagfileWriter:: +writeGuide( + GuideInfo const& I) +{ +} + +void +TagfileWriter:: +writeConcept( + ConceptInfo const& I, + SimpleWriterTag) +{ + writeClassLike(I, SimpleWriterTag{}); +} + +void +TagfileWriter:: +writeConcept( + ConceptInfo const& I) +{ + writeClassLike(I); +} + + +void +TagfileWriter:: +writeAlias( + AliasInfo const& I, + SimpleWriterTag) +{ + writeClassLike(I, SimpleWriterTag{}); +} + +void +TagfileWriter:: +writeAlias( + AliasInfo const& I) +{ + writeClassLike(I); +} + +void +TagfileWriter:: +writeUsing(UsingInfo const& I, + SimpleWriterTag) +{ + writeClassLike(I, SimpleWriterTag{}); +} + +void +TagfileWriter:: +writeUsing(UsingInfo const& I) +{ + writeClassLike(I); +} + +void +TagfileWriter:: +writeRecord( + RecordInfo const& I, + SimpleWriterTag) +{ + writeClassLike(I, SimpleWriterTag{}); +} + +void +TagfileWriter:: +writeRecord( + RecordInfo const& I) +{ + writeClassLike(I); +} + +void +TagfileWriter:: +writeTypedef( + TypedefInfo const& I, + SimpleWriterTag) +{ + writeClassLike(I, SimpleWriterTag{}); +} + +void +TagfileWriter:: +writeTypedef( + TypedefInfo const& I) +{ + writeClassLike(I); +} + +void +TagfileWriter:: +writeField( + const FieldInfo& I, + SimpleWriterTag) +{ + // Do nothing +} + +void +TagfileWriter:: +writeField( + const FieldInfo& I) +{ + // Do nothing +} + +void +TagfileWriter:: +writeVariable( + VariableInfo const& I, + SimpleWriterTag) +{ + // Do nothing +} + +void +TagfileWriter:: +writeVariable( + VariableInfo const& I) +{ + // Do nothing +} + +void +TagfileWriter:: +openTemplate( + const std::unique_ptr& I) +{ + // Do nothing +} + +void +TagfileWriter:: +closeTemplate( + const std::unique_ptr& I) +{ + // Do nothing +} + +void +TagfileWriter:: +writeSpecialization( + const SpecializationInfo& I, + SimpleWriterTag) +{ + // Do nothing +} + +void +TagfileWriter:: +writeSpecialization( + const SpecializationInfo& I) +{ + // Do nothing +} + +} // tagfile +} // mrdocs +} // clang diff --git a/src/lib/Gen/tagfile/TagfileWriter.hpp b/src/lib/Gen/tagfile/TagfileWriter.hpp new file mode 100644 index 000000000..b1907c986 --- /dev/null +++ b/src/lib/Gen/tagfile/TagfileWriter.hpp @@ -0,0 +1,76 @@ +// +// 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) 2024 Fernando Pelliccioni (fpelliccioni@gmail.com) +// +// Official repository: https://github.com/cppalliance/mrdocs +// + +#ifndef MRDOCS_LIB_GEN_TAGFILE_TAGFILEWRITER_HPP +#define MRDOCS_LIB_GEN_TAGFILE_TAGFILEWRITER_HPP + +#include "../xml/XMLTags.hpp" +#include +#include +#include +#include + +namespace clang { +namespace mrdocs { +namespace tagfile { + +struct SimpleWriterTag {}; //Tag dispatch for simple writers + +class jit_indenter; + +/** A writer which outputs Tagfiles. +*/ +class TagfileWriter +{ +protected: + xml::XMLTags tags_; + llvm::raw_ostream& os_; + Corpus const& corpus_; + +public: + TagfileWriter( + llvm::raw_ostream& os, + Corpus const& corpus) noexcept; + + Error build(); + + void writeIndex(); + + template + void operator()(T const&); + + template + void operator()(T const&, SimpleWriterTag); + + bool containsOnlyNamespaces(NamespaceInfo const& I) const; + +#define INFO_PASCAL(Type) void write##Type(Type##Info const&); +#include + +#define INFO_PASCAL(Type) void write##Type(Type##Info const&, SimpleWriterTag); +#include + + template + void writeClassLike(T const& I); + template + void writeClassLike(T const& I, SimpleWriterTag); + + void writeSourceInfo(SourceInfo const& I); + void writeLocation(Location const& loc, bool def = false); + void openTemplate(const std::unique_ptr& I); + void closeTemplate(const std::unique_ptr& I); +}; + +} // tagfile +} // mrdocs +} // clang + +#endif diff --git a/src/lib/Lib/ConfigOptions.json b/src/lib/Lib/ConfigOptions.json index 01ff74027..c27c63191 100644 --- a/src/lib/Lib/ConfigOptions.json +++ b/src/lib/Lib/ConfigOptions.json @@ -122,6 +122,7 @@ "values": [ "adoc", "html", + "tagfile", "xml" ], "default": "adoc" diff --git a/src/lib/Support/GeneratorsImpl.cpp b/src/lib/Support/GeneratorsImpl.cpp index c19ec9dfb..0f3a0f374 100644 --- a/src/lib/Support/GeneratorsImpl.cpp +++ b/src/lib/Support/GeneratorsImpl.cpp @@ -30,6 +30,10 @@ extern std::unique_ptr makeHTMLGenerator(); +extern +std::unique_ptr +makeTagfileGenerator(); + Generators:: ~Generators() noexcept = default; @@ -51,6 +55,7 @@ GeneratorsImpl() err = insert(makeBitcodeGenerator()); err = insert(makeXMLGenerator()); err = insert(makeHTMLGenerator()); + err = insert(makeTagfileGenerator()); } Generator const* diff --git a/util/generate-config-info.py b/util/generate-config-info.py index f57605387..e3165b893 100644 --- a/util/generate-config-info.py +++ b/util/generate-config-info.py @@ -61,7 +61,7 @@ def get_flat_suboptions(option_name, options): def get_valid_enum_categories(): valid_enum_cats = { - 'generator': ["adoc", "html", "xml"], + 'generator': ["adoc", "html", "tagfile", "xml"], "extract-policy": ["always", "dependency", "never"] } return valid_enum_cats