summaryrefslogtreecommitdiffstats
path: root/src/qdoc
diff options
context:
space:
mode:
Diffstat (limited to 'src/qdoc')
-rw-r--r--src/qdoc/CMakeLists.txt134
-rw-r--r--src/qdoc/access.h40
-rw-r--r--src/qdoc/catch/CATCH_LICENSE.txt23
-rw-r--r--src/qdoc/catch/CMakeLists.txt9
-rw-r--r--src/qdoc/catch/include/catch/catch.hpp17976
-rw-r--r--src/qdoc/catch/qt_attribution.json20
-rw-r--r--src/qdoc/catch_conversions/CMakeLists.txt12
-rw-r--r--src/qdoc/catch_conversions/src/catch_conversions/qdoc_catch_conversions.h24
-rw-r--r--src/qdoc/catch_conversions/src/catch_conversions/qt_catch_conversions.h19
-rw-r--r--src/qdoc/catch_conversions/src/catch_conversions/std_catch_conversions.h16
-rw-r--r--src/qdoc/catch_generators/CMakeLists.txt16
-rw-r--r--src/qdoc/catch_generators/src/catch_generators/generators/combinators/cycle_generator.h80
-rw-r--r--src/qdoc/catch_generators/src/catch_generators/generators/combinators/oneof_generator.h185
-rw-r--r--src/qdoc/catch_generators/src/catch_generators/generators/k_partition_of_r_generator.h113
-rw-r--r--src/qdoc/catch_generators/src/catch_generators/generators/path_generator.h853
-rw-r--r--src/qdoc/catch_generators/src/catch_generators/generators/qchar_generator.h110
-rw-r--r--src/qdoc/catch_generators/src/catch_generators/generators/qstring_generator.h92
-rw-r--r--src/qdoc/catch_generators/src/catch_generators/namespaces.h14
-rw-r--r--src/qdoc/catch_generators/src/catch_generators/utilities/semantics/copy_value.h26
-rw-r--r--src/qdoc/catch_generators/src/catch_generators/utilities/semantics/generator_handler.h97
-rw-r--r--src/qdoc/catch_generators/src/catch_generators/utilities/semantics/move_into_vector.h62
-rw-r--r--src/qdoc/catch_generators/src/catch_generators/utilities/statistics/distribution.h158
-rw-r--r--src/qdoc/catch_generators/src/catch_generators/utilities/statistics/percentages.h49
-rw-r--r--src/qdoc/catch_generators/tests/CMakeLists.txt20
-rw-r--r--src/qdoc/catch_generators/tests/generators/catch_k_partition_of_r_generator.cpp41
-rw-r--r--src/qdoc/catch_generators/tests/generators/catch_path_generator.cpp755
-rw-r--r--src/qdoc/catch_generators/tests/generators/catch_qchar_generator.cpp102
-rw-r--r--src/qdoc/catch_generators/tests/generators/catch_qstring_generator.cpp89
-rw-r--r--src/qdoc/catch_generators/tests/generators/combinators/catch_cycle_generator.cpp70
-rw-r--r--src/qdoc/catch_generators/tests/generators/combinators/catch_oneof_generator.cpp362
-rw-r--r--src/qdoc/catch_generators/tests/main.cpp13
-rw-r--r--src/qdoc/catch_generators/tests/utilities/semantics/catch_generator_handler.cpp28
-rw-r--r--src/qdoc/clangcodeparser.h89
-rw-r--r--src/qdoc/codechunk.h99
-rw-r--r--src/qdoc/codeparser.cpp246
-rw-r--r--src/qdoc/codeparser.h164
-rw-r--r--src/qdoc/collectionnode.cpp153
-rw-r--r--src/qdoc/collectionnode.h86
-rw-r--r--src/qdoc/cppcodemarker.h62
-rw-r--r--src/qdoc/cppcodeparser.h100
-rw-r--r--src/qdoc/doc/corefeatures.qdoc35
-rw-r--r--src/qdoc/doc/examples/main.cpp41
-rw-r--r--src/qdoc/doc/examples/minimum.qdocconf36
-rw-r--r--src/qdoc/doc/images/happy.gifbin11526 -> 0 bytes
-rw-r--r--src/qdoc/doc/images/qa-table.pngbin7057 -> 0 bytes
-rw-r--r--src/qdoc/doc/images/qt-logo.pngbin1214 -> 0 bytes
-rw-r--r--src/qdoc/doc/qdoc-manual-topiccmds.qdoc1627
-rw-r--r--src/qdoc/doc/qdoc-minimum-qdocconf.qdoc89
-rw-r--r--src/qdoc/docprivate.cpp55
-rw-r--r--src/qdoc/docutilities.h55
-rw-r--r--src/qdoc/editdistance.cpp93
-rw-r--r--src/qdoc/editdistance.h42
-rw-r--r--src/qdoc/enumitem.h55
-rw-r--r--src/qdoc/enumnode.cpp94
-rw-r--r--src/qdoc/enumnode.h73
-rw-r--r--src/qdoc/examplenode.h67
-rw-r--r--src/qdoc/externalpagenode.cpp53
-rw-r--r--src/qdoc/externalpagenode.h51
-rw-r--r--src/qdoc/headernode.cpp68
-rw-r--r--src/qdoc/headernode.h72
-rw-r--r--src/qdoc/importrec.h58
-rw-r--r--src/qdoc/jscodemarker.cpp140
-rw-r--r--src/qdoc/jscodemarker.h56
-rw-r--r--src/qdoc/macro.h52
-rw-r--r--src/qdoc/manifestwriter.h76
-rw-r--r--src/qdoc/openedlist.h72
-rw-r--r--src/qdoc/pagenode.h93
-rw-r--r--src/qdoc/proxynode.h48
-rw-r--r--src/qdoc/puredocparser.cpp126
-rw-r--r--src/qdoc/puredocparser.h55
-rw-r--r--src/qdoc/qdoc/CMakeLists.txt117
-rw-r--r--src/qdoc/qdoc/doc/config/qdoc.qdocconf (renamed from src/qdoc/doc/config/qdoc.qdocconf)4
-rw-r--r--src/qdoc/qdoc/doc/corefeatures.qdoc11
-rw-r--r--src/qdoc/qdoc/doc/examples/cpp.qdoc.sample (renamed from src/qdoc/doc/examples/cpp.qdoc.sample)28
-rw-r--r--src/qdoc/qdoc/doc/examples/layoutmanagement.qdocinc (renamed from src/qdoc/doc/examples/layoutmanagement.qdocinc)0
-rw-r--r--src/qdoc/qdoc/doc/examples/main.cpp16
-rw-r--r--src/qdoc/qdoc/doc/examples/mainwindow.cpp (renamed from src/qdoc/doc/examples/mainwindow.cpp)35
-rw-r--r--src/qdoc/qdoc/doc/examples/minimum.qdocconf46
-rw-r--r--src/qdoc/qdoc/doc/examples/objectmodel.qdocinc (renamed from src/qdoc/doc/examples/objectmodel.qdocinc)0
-rw-r--r--src/qdoc/qdoc/doc/examples/qml.qdoc.sample (renamed from src/qdoc/doc/examples/qml.qdoc.sample)30
-rw-r--r--src/qdoc/qdoc/doc/examples/samples.qdocinc (renamed from src/qdoc/doc/examples/samples.qdocinc)32
-rw-r--r--src/qdoc/qdoc/doc/examples/signalandslots.qdocinc (renamed from src/qdoc/doc/examples/signalandslots.qdocinc)0
-rw-r--r--src/qdoc/qdoc/doc/files/basicqt.qdoc.sample (renamed from src/qdoc/doc/files/basicqt.qdoc.sample)0
-rw-r--r--src/qdoc/qdoc/doc/files/compat.qdocconf (renamed from src/qdoc/doc/files/compat.qdocconf)2
-rw-r--r--src/qdoc/qdoc/doc/files/qtgui.qdocconf (renamed from src/qdoc/doc/files/qtgui.qdocconf)0
-rw-r--r--src/qdoc/qdoc/doc/images/happyguy.jpg (renamed from src/qdoc/doc/images/happyguy.jpg)bin53442 -> 53442 bytes
-rw-r--r--src/qdoc/qdoc/doc/images/link-to-qquickitem.png (renamed from src/qdoc/doc/images/link-to-qquickitem.png)bin46571 -> 46571 bytes
-rw-r--r--src/qdoc/qdoc/doc/images/links-to-broken-links.png (renamed from src/qdoc/doc/images/links-to-broken-links.png)bin16569 -> 16569 bytes
-rw-r--r--src/qdoc/qdoc/doc/images/links-to-links.png (renamed from src/qdoc/doc/images/links-to-links.png)bin10042 -> 10042 bytes
-rw-r--r--src/qdoc/qdoc/doc/images/qt-logo.pngbin0 -> 1008 bytes
-rw-r--r--src/qdoc/qdoc/doc/images/training.jpg (renamed from src/qdoc/doc/images/training.jpg)bin8368 -> 8368 bytes
-rw-r--r--src/qdoc/qdoc/doc/images/windows-pushbutton.png (renamed from src/qdoc/doc/images/windows-pushbutton.png)bin722 -> 722 bytes
-rw-r--r--src/qdoc/qdoc/doc/images/windows-toolbutton.png (renamed from src/qdoc/doc/images/windows-toolbutton.png)bin771 -> 771 bytes
-rw-r--r--src/qdoc/qdoc/doc/qdoc-guide/qdoc-guide.qdoc (renamed from src/qdoc/doc/qdoc-guide/qdoc-guide.qdoc)75
-rw-r--r--src/qdoc/qdoc/doc/qdoc-guide/qtwritingstyle-cpp.qdoc (renamed from src/qdoc/doc/qdoc-guide/qtwritingstyle-cpp.qdoc)28
-rw-r--r--src/qdoc/qdoc/doc/qdoc-guide/qtwritingstyle-qml.qdoc (renamed from src/qdoc/doc/qdoc-guide/qtwritingstyle-qml.qdoc)38
-rw-r--r--src/qdoc/qdoc/doc/qdoc-manual-cmdindex.qdoc (renamed from src/qdoc/doc/qdoc-manual-cmdindex.qdoc)46
-rw-r--r--src/qdoc/qdoc/doc/qdoc-manual-contextcmds.qdoc (renamed from src/qdoc/doc/qdoc-manual-contextcmds.qdoc)542
-rw-r--r--src/qdoc/qdoc/doc/qdoc-manual-intro.qdoc (renamed from src/qdoc/doc/qdoc-manual-intro.qdoc)40
-rw-r--r--src/qdoc/qdoc/doc/qdoc-manual-macros.qdoc497
-rw-r--r--src/qdoc/qdoc/doc/qdoc-manual-markupcmds.qdoc (renamed from src/qdoc/doc/qdoc-manual-markupcmds.qdoc)2857
-rw-r--r--src/qdoc/qdoc/doc/qdoc-manual-qdocconf.qdoc (renamed from src/qdoc/doc/qdoc-manual-qdocconf.qdoc)459
-rw-r--r--src/qdoc/qdoc/doc/qdoc-manual-topiccmds.qdoc1049
-rw-r--r--src/qdoc/qdoc/doc/qdoc-manual.qdoc (renamed from src/qdoc/doc/qdoc-manual.qdoc)39
-rw-r--r--src/qdoc/qdoc/doc/qdoc-warnings.qdoc (renamed from src/qdoc/doc/qdoc-warnings.qdoc)211
-rw-r--r--src/qdoc/qdoc/doc/qtgui-qdocconf.qdoc (renamed from src/qdoc/doc/qtgui-qdocconf.qdoc)45
-rw-r--r--src/qdoc/qdoc/src/qdoc/access.h15
-rw-r--r--src/qdoc/qdoc/src/qdoc/aggregate.cpp (renamed from src/qdoc/aggregate.cpp)596
-rw-r--r--src/qdoc/qdoc/src/qdoc/aggregate.h (renamed from src/qdoc/aggregate.h)72
-rw-r--r--src/qdoc/qdoc/src/qdoc/atom.cpp (renamed from src/qdoc/atom.cpp)112
-rw-r--r--src/qdoc/qdoc/src/qdoc/atom.h (renamed from src/qdoc/atom.h)56
-rw-r--r--src/qdoc/qdoc/src/qdoc/boundaries/filesystem/directorypath.cpp126
-rw-r--r--src/qdoc/qdoc/src/qdoc/boundaries/filesystem/directorypath.h17
-rw-r--r--src/qdoc/qdoc/src/qdoc/boundaries/filesystem/filepath.cpp125
-rw-r--r--src/qdoc/qdoc/src/qdoc/boundaries/filesystem/filepath.h17
-rw-r--r--src/qdoc/qdoc/src/qdoc/boundaries/filesystem/resolvedfile.cpp96
-rw-r--r--src/qdoc/qdoc/src/qdoc/boundaries/filesystem/resolvedfile.h20
-rw-r--r--src/qdoc/qdoc/src/qdoc/boundaries/refined_typedef.h207
-rw-r--r--src/qdoc/qdoc/src/qdoc/boundaries/refined_typedef_members.qdocinc162
-rw-r--r--src/qdoc/qdoc/src/qdoc/clang/AST/LLVM_LICENSE.txt279
-rw-r--r--src/qdoc/qdoc/src/qdoc/clang/AST/QualTypeNames.h491
-rw-r--r--src/qdoc/qdoc/src/qdoc/clang/AST/qt_attribution.json20
-rw-r--r--src/qdoc/qdoc/src/qdoc/clangcodeparser.cpp (renamed from src/qdoc/clangcodeparser.cpp)1407
-rw-r--r--src/qdoc/qdoc/src/qdoc/clangcodeparser.h94
-rw-r--r--src/qdoc/qdoc/src/qdoc/classnode.cpp (renamed from src/qdoc/classnode.cpp)62
-rw-r--r--src/qdoc/qdoc/src/qdoc/classnode.h (renamed from src/qdoc/classnode.h)44
-rw-r--r--src/qdoc/qdoc/src/qdoc/codechunk.cpp (renamed from src/qdoc/codechunk.cpp)29
-rw-r--r--src/qdoc/qdoc/src/qdoc/codechunk.h74
-rw-r--r--src/qdoc/qdoc/src/qdoc/codemarker.cpp (renamed from src/qdoc/codemarker.cpp)135
-rw-r--r--src/qdoc/qdoc/src/qdoc/codemarker.h (renamed from src/qdoc/codemarker.h)33
-rw-r--r--src/qdoc/qdoc/src/qdoc/codeparser.cpp139
-rw-r--r--src/qdoc/qdoc/src/qdoc/codeparser.h142
-rw-r--r--src/qdoc/qdoc/src/qdoc/collectionnode.cpp104
-rw-r--r--src/qdoc/qdoc/src/qdoc/collectionnode.h126
-rw-r--r--src/qdoc/qdoc/src/qdoc/comparisoncategory.cpp28
-rw-r--r--src/qdoc/qdoc/src/qdoc/comparisoncategory.h54
-rw-r--r--src/qdoc/qdoc/src/qdoc/config.cpp (renamed from src/qdoc/config.cpp)507
-rw-r--r--src/qdoc/qdoc/src/qdoc/config.h (renamed from src/qdoc/config.h)154
-rw-r--r--src/qdoc/qdoc/src/qdoc/cppcodemarker.cpp (renamed from src/qdoc/cppcodemarker.cpp)225
-rw-r--r--src/qdoc/qdoc/src/qdoc/cppcodemarker.h35
-rw-r--r--src/qdoc/qdoc/src/qdoc/cppcodeparser.cpp (renamed from src/qdoc/cppcodeparser.cpp)830
-rw-r--r--src/qdoc/qdoc/src/qdoc/cppcodeparser.h111
-rw-r--r--src/qdoc/qdoc/src/qdoc/doc.cpp (renamed from src/qdoc/doc.cpp)213
-rw-r--r--src/qdoc/qdoc/src/qdoc/doc.h (renamed from src/qdoc/doc.h)49
-rw-r--r--src/qdoc/qdoc/src/qdoc/docbookgenerator.cpp (renamed from src/qdoc/docbookgenerator.cpp)2320
-rw-r--r--src/qdoc/qdoc/src/qdoc/docbookgenerator.h (renamed from src/qdoc/docbookgenerator.h)84
-rw-r--r--src/qdoc/qdoc/src/qdoc/docparser.cpp (renamed from src/qdoc/docparser.cpp)1665
-rw-r--r--src/qdoc/qdoc/src/qdoc/docparser.h (renamed from src/qdoc/docparser.h)110
-rw-r--r--src/qdoc/qdoc/src/qdoc/docprivate.cpp30
-rw-r--r--src/qdoc/qdoc/src/qdoc/docprivate.h (renamed from src/qdoc/docprivate.h)30
-rw-r--r--src/qdoc/qdoc/src/qdoc/docutilities.h28
-rw-r--r--src/qdoc/qdoc/src/qdoc/editdistance.cpp68
-rw-r--r--src/qdoc/qdoc/src/qdoc/editdistance.h17
-rw-r--r--src/qdoc/qdoc/src/qdoc/enumitem.h36
-rw-r--r--src/qdoc/qdoc/src/qdoc/enumnode.cpp82
-rw-r--r--src/qdoc/qdoc/src/qdoc/enumnode.h49
-rw-r--r--src/qdoc/qdoc/src/qdoc/examplenode.h42
-rw-r--r--src/qdoc/qdoc/src/qdoc/externalpagenode.cpp28
-rw-r--r--src/qdoc/qdoc/src/qdoc/externalpagenode.h25
-rw-r--r--src/qdoc/qdoc/src/qdoc/filesystem/fileresolver.cpp161
-rw-r--r--src/qdoc/qdoc/src/qdoc/filesystem/fileresolver.h24
-rw-r--r--src/qdoc/qdoc/src/qdoc/functionnode.cpp (renamed from src/qdoc/functionnode.cpp)331
-rw-r--r--src/qdoc/qdoc/src/qdoc/functionnode.h (renamed from src/qdoc/functionnode.h)84
-rw-r--r--src/qdoc/qdoc/src/qdoc/generator.cpp (renamed from src/qdoc/generator.cpp)1047
-rw-r--r--src/qdoc/qdoc/src/qdoc/generator.h (renamed from src/qdoc/generator.h)92
-rw-r--r--src/qdoc/qdoc/src/qdoc/headernode.cpp43
-rw-r--r--src/qdoc/qdoc/src/qdoc/headernode.h46
-rw-r--r--src/qdoc/qdoc/src/qdoc/helpprojectwriter.cpp (renamed from src/qdoc/helpprojectwriter.cpp)222
-rw-r--r--src/qdoc/qdoc/src/qdoc/helpprojectwriter.h (renamed from src/qdoc/helpprojectwriter.h)30
-rw-r--r--src/qdoc/qdoc/src/qdoc/htmlgenerator.cpp (renamed from src/qdoc/htmlgenerator.cpp)1298
-rw-r--r--src/qdoc/qdoc/src/qdoc/htmlgenerator.h (renamed from src/qdoc/htmlgenerator.h)58
-rw-r--r--src/qdoc/qdoc/src/qdoc/importrec.h33
-rw-r--r--src/qdoc/qdoc/src/qdoc/location.cpp (renamed from src/qdoc/location.cpp)47
-rw-r--r--src/qdoc/qdoc/src/qdoc/location.h (renamed from src/qdoc/location.h)29
-rw-r--r--src/qdoc/qdoc/src/qdoc/macro.h27
-rw-r--r--src/qdoc/qdoc/src/qdoc/main.cpp (renamed from src/qdoc/main.cpp)577
-rw-r--r--src/qdoc/qdoc/src/qdoc/manifestwriter.cpp (renamed from src/qdoc/manifestwriter.cpp)303
-rw-r--r--src/qdoc/qdoc/src/qdoc/manifestwriter.h46
-rw-r--r--src/qdoc/qdoc/src/qdoc/namespacenode.cpp (renamed from src/qdoc/namespacenode.cpp)49
-rw-r--r--src/qdoc/qdoc/src/qdoc/namespacenode.h (renamed from src/qdoc/namespacenode.h)31
-rw-r--r--src/qdoc/qdoc/src/qdoc/node.cpp (renamed from src/qdoc/node.cpp)569
-rw-r--r--src/qdoc/qdoc/src/qdoc/node.h (renamed from src/qdoc/node.h)130
-rw-r--r--src/qdoc/qdoc/src/qdoc/openedlist.cpp (renamed from src/qdoc/openedlist.cpp)31
-rw-r--r--src/qdoc/qdoc/src/qdoc/openedlist.h47
-rw-r--r--src/qdoc/qdoc/src/qdoc/pagenode.cpp (renamed from src/qdoc/pagenode.cpp)51
-rw-r--r--src/qdoc/qdoc/src/qdoc/pagenode.h82
-rw-r--r--src/qdoc/qdoc/src/qdoc/parameters.cpp (renamed from src/qdoc/parameters.cpp)33
-rw-r--r--src/qdoc/qdoc/src/qdoc/parameters.h (renamed from src/qdoc/parameters.h)29
-rw-r--r--src/qdoc/qdoc/src/qdoc/parsererror.cpp90
-rw-r--r--src/qdoc/qdoc/src/qdoc/parsererror.h26
-rw-r--r--src/qdoc/qdoc/src/qdoc/propertynode.cpp (renamed from src/qdoc/propertynode.cpp)67
-rw-r--r--src/qdoc/qdoc/src/qdoc/propertynode.h (renamed from src/qdoc/propertynode.h)54
-rw-r--r--src/qdoc/qdoc/src/qdoc/proxynode.cpp (renamed from src/qdoc/proxynode.cpp)29
-rw-r--r--src/qdoc/qdoc/src/qdoc/proxynode.h23
-rw-r--r--src/qdoc/qdoc/src/qdoc/puredocparser.cpp74
-rw-r--r--src/qdoc/qdoc/src/qdoc/puredocparser.h31
-rw-r--r--src/qdoc/qdoc/src/qdoc/qdoccommandlineparser.cpp (renamed from src/qdoc/qdoccommandlineparser.cpp)104
-rw-r--r--src/qdoc/qdoc/src/qdoc/qdoccommandlineparser.h28
-rw-r--r--src/qdoc/qdoc/src/qdoc/qdocdatabase.cpp (renamed from src/qdoc/qdocdatabase.cpp)699
-rw-r--r--src/qdoc/qdoc/src/qdoc/qdocdatabase.h (renamed from src/qdoc/qdocdatabase.h)74
-rw-r--r--src/qdoc/qdoc/src/qdoc/qdocindexfiles.cpp (renamed from src/qdoc/qdocindexfiles.cpp)566
-rw-r--r--src/qdoc/qdoc/src/qdoc/qdocindexfiles.h (renamed from src/qdoc/qdocindexfiles.h)33
-rw-r--r--src/qdoc/qdoc/src/qdoc/qmlcodemarker.cpp (renamed from src/qdoc/qmlcodemarker.cpp)89
-rw-r--r--src/qdoc/qdoc/src/qdoc/qmlcodemarker.h38
-rw-r--r--src/qdoc/qdoc/src/qdoc/qmlcodeparser.cpp143
-rw-r--r--src/qdoc/qdoc/src/qdoc/qmlcodeparser.h38
-rw-r--r--src/qdoc/qdoc/src/qdoc/qmlmarkupvisitor.cpp (renamed from src/qdoc/qmlmarkupvisitor.cpp)64
-rw-r--r--src/qdoc/qdoc/src/qdoc/qmlmarkupvisitor.h (renamed from src/qdoc/qmlmarkupvisitor.h)36
-rw-r--r--src/qdoc/qdoc/src/qdoc/qmlpropertynode.cpp175
-rw-r--r--src/qdoc/qdoc/src/qdoc/qmlpropertynode.h (renamed from src/qdoc/qmlpropertynode.h)45
-rw-r--r--src/qdoc/qdoc/src/qdoc/qmltypenode.cpp (renamed from src/qdoc/qmltypenode.cpp)56
-rw-r--r--src/qdoc/qdoc/src/qdoc/qmltypenode.h (renamed from src/qdoc/qmltypenode.h)47
-rw-r--r--src/qdoc/qdoc/src/qdoc/qmlvisitor.cpp (renamed from src/qdoc/qmlvisitor.cpp)273
-rw-r--r--src/qdoc/qdoc/src/qdoc/qmlvisitor.h (renamed from src/qdoc/qmlvisitor.h)39
-rw-r--r--src/qdoc/qdoc/src/qdoc/quoter.cpp (renamed from src/qdoc/quoter.cpp)43
-rw-r--r--src/qdoc/qdoc/src/qdoc/quoter.h (renamed from src/qdoc/quoter.h)29
-rw-r--r--src/qdoc/qdoc/src/qdoc/relatedclass.cpp (renamed from src/qdoc/relatedclass.cpp)29
-rw-r--r--src/qdoc/qdoc/src/qdoc/relatedclass.h34
-rw-r--r--src/qdoc/qdoc/src/qdoc/sections.cpp (renamed from src/qdoc/sections.cpp)511
-rw-r--r--src/qdoc/qdoc/src/qdoc/sections.h (renamed from src/qdoc/sections.h)125
-rw-r--r--src/qdoc/qdoc/src/qdoc/sharedcommentnode.cpp56
-rw-r--r--src/qdoc/qdoc/src/qdoc/sharedcommentnode.h51
-rw-r--r--src/qdoc/qdoc/src/qdoc/singleton.h32
-rw-r--r--src/qdoc/qdoc/src/qdoc/sourcefileparser.h82
-rw-r--r--src/qdoc/qdoc/src/qdoc/tagfilewriter.cpp (renamed from src/qdoc/tagfilewriter.cpp)47
-rw-r--r--src/qdoc/qdoc/src/qdoc/tagfilewriter.h33
-rw-r--r--src/qdoc/qdoc/src/qdoc/template_declaration.h502
-rw-r--r--src/qdoc/qdoc/src/qdoc/text.cpp (renamed from src/qdoc/text.cpp)115
-rw-r--r--src/qdoc/qdoc/src/qdoc/text.h (renamed from src/qdoc/text.h)39
-rw-r--r--src/qdoc/qdoc/src/qdoc/tokenizer.cpp (renamed from src/qdoc/tokenizer.cpp)69
-rw-r--r--src/qdoc/qdoc/src/qdoc/tokenizer.h (renamed from src/qdoc/tokenizer.h)35
-rw-r--r--src/qdoc/qdoc/src/qdoc/topic.h29
-rw-r--r--src/qdoc/qdoc/src/qdoc/tree.cpp (renamed from src/qdoc/tree.cpp)598
-rw-r--r--src/qdoc/qdoc/src/qdoc/tree.h (renamed from src/qdoc/tree.h)76
-rw-r--r--src/qdoc/qdoc/src/qdoc/typedefnode.cpp55
-rw-r--r--src/qdoc/qdoc/src/qdoc/typedefnode.h54
-rw-r--r--src/qdoc/qdoc/src/qdoc/utilities.cpp (renamed from src/qdoc/utilities.cpp)102
-rw-r--r--src/qdoc/qdoc/src/qdoc/utilities.h28
-rw-r--r--src/qdoc/qdoc/src/qdoc/variablenode.cpp23
-rw-r--r--src/qdoc/qdoc/src/qdoc/variablenode.h44
-rw-r--r--src/qdoc/qdoc/src/qdoc/webxmlgenerator.cpp (renamed from src/qdoc/webxmlgenerator.cpp)211
-rw-r--r--src/qdoc/qdoc/src/qdoc/webxmlgenerator.h (renamed from src/qdoc/webxmlgenerator.h)35
-rw-r--r--src/qdoc/qdoc/src/qdoc/xmlgenerator.cpp (renamed from src/qdoc/xmlgenerator.cpp)166
-rw-r--r--src/qdoc/qdoc/src/qdoc/xmlgenerator.h54
-rw-r--r--src/qdoc/qdoc/tests/CMakeLists.txt6
-rw-r--r--src/qdoc/qdoc/tests/config/CMakeLists.txt24
-rw-r--r--src/qdoc/qdoc/tests/config/testdata/configs/exampletest.qdocconf2
-rw-r--r--src/qdoc/qdoc/tests/config/testdata/configs/expandvars.qdocconf13
-rw-r--r--src/qdoc/qdoc/tests/config/testdata/configs/includepaths.qdocconf2
-rw-r--r--src/qdoc/qdoc/tests/config/testdata/configs/includes/test.qdoc1
-rw-r--r--src/qdoc/qdoc/tests/config/testdata/configs/paths.qdocconf5
-rw-r--r--src/qdoc/qdoc/tests/config/testdata/configs/vars.qdocconf17
-rw-r--r--src/qdoc/qdoc/tests/config/testdata/exampletest/examples/test/empty/test.pro1
-rw-r--r--src/qdoc/qdoc/tests/config/testdata/exampletest/examples/test/example1/example1.pro1
-rw-r--r--src/qdoc/qdoc/tests/config/testdata/exampletest/examples/test/example2/example2.qmlproject1
-rw-r--r--src/qdoc/qdoc/tests/config/testdata/exampletest/examples/test/example3/example3.pyproject1
-rw-r--r--src/qdoc/qdoc/tests/config/testdata/exampletest/examples/test/example4/CMakeLists.txt1
-rw-r--r--src/qdoc/qdoc/tests/config/testdata/exampletest/examples/test/example4/example4.pro1
-rw-r--r--src/qdoc/qdoc/tests/config/testdata/includepaths/include/framework/ignore.h1
-rw-r--r--src/qdoc/qdoc/tests/config/testdata/includepaths/include/more/ignore.h1
-rw-r--r--src/qdoc/qdoc/tests/config/testdata/includepaths/include/purpose.h1
-rw-r--r--src/qdoc/qdoc/tests/config/testdata/includepaths/include/system/ignore.h1
-rw-r--r--src/qdoc/qdoc/tests/config/testdata/includepaths/includepaths.qdocconf16
-rw-r--r--src/qdoc/qdoc/tests/config/testdata/paths/includes/test.qdoc1
-rw-r--r--src/qdoc/qdoc/tests/config/testdata/paths/paths.qdocconf2
-rw-r--r--src/qdoc/qdoc/tests/config/tst_config.cpp180
-rw-r--r--src/qdoc/qdoc/tests/generatedoutput/CMakeLists.txt46
-rw-r--r--src/qdoc/qdoc/tests/generatedoutput/expected_output/autolinking.html36
-rw-r--r--src/qdoc/qdoc/tests/generatedoutput/expected_output/cpptypes.html25
-rw-r--r--src/qdoc/qdoc/tests/generatedoutput/expected_output/crossmodule/crossmodule/all-namespaces.html20
-rw-r--r--src/qdoc/qdoc/tests/generatedoutput/expected_output/crossmodule/crossmodule/testtype-members.html37
-rw-r--r--src/qdoc/qdoc/tests/generatedoutput/expected_output/crossmodule/crossmodule/testtype.html67
-rw-r--r--src/qdoc/qdoc/tests/generatedoutput/expected_output/crossmodule/crossmoduleref-sub-crossmodule.html30
-rw-r--r--src/qdoc/qdoc/tests/generatedoutput/expected_output/crossmodule/testtype-members.html37
-rw-r--r--src/qdoc/qdoc/tests/generatedoutput/expected_output/crossmodule/testtype.html67
-rw-r--r--src/qdoc/qdoc/tests/generatedoutput/expected_output/includefromexampledirs/index.html27
-rw-r--r--src/qdoc/qdoc/tests/generatedoutput/expected_output/includefromexampledirs/qml-qdoc-test-abstractparent-members.html20
-rw-r--r--src/qdoc/qdoc/tests/generatedoutput/expected_output/includefromexampledirs/qml-qdoc-test-abstractparent.html68
-rw-r--r--src/qdoc/qdoc/tests/generatedoutput/expected_output/index-linking.html37
-rw-r--r--src/qdoc/qdoc/tests/generatedoutput/expected_output/noautolist-docbook/qdoc-test-qmlmodule.xml16
-rw-r--r--src/qdoc/qdoc/tests/generatedoutput/expected_output/noautolist-docbook/test-componentset-example.xml37
-rw-r--r--src/qdoc/qdoc/tests/generatedoutput/expected_output/noautolist-docbook/testcpp-module.xml49
-rw-r--r--src/qdoc/qdoc/tests/generatedoutput/expected_output/noautolist/qdoc-test-qmlmodule.html19
-rw-r--r--src/qdoc/qdoc/tests/generatedoutput/expected_output/noautolist/test-componentset-example.html47
-rw-r--r--src/qdoc/qdoc/tests/generatedoutput/expected_output/noautolist/testcpp-module.html45
-rw-r--r--src/qdoc/qdoc/tests/generatedoutput/expected_output/obsolete-classes.html32
-rw-r--r--src/qdoc/qdoc/tests/generatedoutput/expected_output/qml-linkmodule-grandchild-members.html26
-rw-r--r--src/qdoc/qdoc/tests/generatedoutput/expected_output/qmlpropertygroups-docbook/qml-qdoc-test-parent.xml45
-rw-r--r--src/qdoc/qdoc/tests/generatedoutput/expected_output/qmlpropertygroups/qml-qdoc-test-anotherchild-members.html28
-rw-r--r--src/qdoc/qdoc/tests/generatedoutput/expected_output/qmlpropertygroups/qml-qdoc-test-parent.html61
-rw-r--r--src/qdoc/qdoc/tests/generatedoutput/expected_output/testcpp-module.html57
-rw-r--r--src/qdoc/qdoc/tests/generatedoutput/expected_output/testcpp.index79
-rw-r--r--src/qdoc/qdoc/tests/generatedoutput/expected_output/testcpp/crossmoduleref.html48
-rw-r--r--src/qdoc/qdoc/tests/generatedoutput/expected_output/testcpp/testcpp-module.html57
-rw-r--r--src/qdoc/qdoc/tests/generatedoutput/expected_output/testcpp/testqdoc-test-members.html30
-rw-r--r--src/qdoc/qdoc/tests/generatedoutput/expected_output/testcpp/testqdoc-test.html162
-rw-r--r--src/qdoc/qdoc/tests/generatedoutput/expected_output/testcpp/testqdoc.html64
-rw-r--r--src/qdoc/qdoc/tests/generatedoutput/expected_output/testqdoc-test-members.html30
-rw-r--r--src/qdoc/qdoc/tests/generatedoutput/expected_output/testqdoc-test-obsolete.html45
-rw-r--r--src/qdoc/qdoc/tests/generatedoutput/expected_output/testqdoc-test.html162
-rw-r--r--src/qdoc/qdoc/tests/generatedoutput/expected_output/testqdoc-testderived-members.html33
-rw-r--r--src/qdoc/qdoc/tests/generatedoutput/expected_output/testqdoc-testderived-obsolete.html26
-rw-r--r--src/qdoc/qdoc/tests/generatedoutput/expected_output/testqdoc-testderived.html81
-rw-r--r--src/qdoc/qdoc/tests/generatedoutput/expected_output/testqdoc.html64
-rw-r--r--src/qdoc/qdoc/tests/generatedoutput/testdata/configs/noautolist.qdocconf64
-rw-r--r--src/qdoc/qdoc/tests/generatedoutput/testdata/configs/testcpp.qdocconf31
-rw-r--r--src/qdoc/qdoc/tests/generatedoutput/testdata/configs/testcpp_singleexec.qdocconf32
-rw-r--r--src/qdoc/qdoc/tests/generatedoutput/testdata/crossmodule/CrossModule2
-rw-r--r--src/qdoc/qdoc/tests/generatedoutput/testdata/crossmodule/crossmodule.qdocconf26
-rw-r--r--src/qdoc/qdoc/tests/generatedoutput/testdata/crossmodule/crossmodule_singleexec.qdocconf6
-rw-r--r--src/qdoc/qdoc/tests/generatedoutput/testdata/crossmodule/namespaces.qdoc9
-rw-r--r--src/qdoc/qdoc/tests/generatedoutput/testdata/crossmodule/testtype.cpp45
-rw-r--r--src/qdoc/qdoc/tests/generatedoutput/testdata/crossmodule/testtype.h16
-rw-r--r--src/qdoc/qdoc/tests/generatedoutput/testdata/dontdocument/TestCPP2
-rw-r--r--src/qdoc/qdoc/tests/generatedoutput/testdata/dontdocument/dontdocument.qdocconf46
-rw-r--r--src/qdoc/qdoc/tests/generatedoutput/testdata/examples/demos/demo/demo.cpp11
-rw-r--r--src/qdoc/qdoc/tests/generatedoutput/testdata/examples/demos/demo/demo.pro2
-rw-r--r--src/qdoc/qdoc/tests/generatedoutput/testdata/examples/demos/demo/doc/src/demo.qdoc11
-rw-r--r--src/qdoc/qdoc/tests/generatedoutput/testdata/examples/demos/demo/dontxclude/CMakeLists.txt2
-rw-r--r--src/qdoc/qdoc/tests/generatedoutput/testdata/examples/demos/demo/excludes/CMakeLists.txt2
-rw-r--r--src/qdoc/qdoc/tests/generatedoutput/testdata/examples/demos/hidden/doc/src/hidden.qdoc11
-rw-r--r--src/qdoc/qdoc/tests/generatedoutput/testdata/examples/demos/hidden/hidden.pro2
-rw-r--r--src/qdoc/qdoc/tests/generatedoutput/testdata/images/01.pngbin0 -> 1142 bytes
-rw-r--r--src/qdoc/qdoc/tests/generatedoutput/testdata/images/leonardo-da-vinci.pngbin0 -> 15076 bytes
-rw-r--r--src/qdoc/qdoc/tests/generatedoutput/testdata/includefromexampledirs/excludes/anotherindex.qdoc14
-rw-r--r--src/qdoc/qdoc/tests/generatedoutput/testdata/includefromexampledirs/excludes/parentinclude.qdoc28
-rw-r--r--src/qdoc/qdoc/tests/generatedoutput/testdata/includefromexampledirs/includefromexampledirs.qdocconf64
-rw-r--r--src/qdoc/qdoc/tests/generatedoutput/testdata/includefromexampledirs/src/includefromparent.qdoc41
-rw-r--r--src/qdoc/qdoc/tests/generatedoutput/testdata/includefromexampledirs/src/parent.qdocinc1
-rw-r--r--src/qdoc/qdoc/tests/generatedoutput/testdata/indexlinking/indexlinking.qdocconf29
-rw-r--r--src/qdoc/qdoc/tests/generatedoutput/testdata/indexlinking/linking.qdoc41
-rw-r--r--src/qdoc/qdoc/tests/generatedoutput/testdata/qml/DocTest.qml86
-rw-r--r--src/qdoc/qdoc/tests/generatedoutput/testdata/qml/cmaketest/CMakeLists.txt2
-rw-r--r--src/qdoc/qdoc/tests/generatedoutput/testdata/qml/cmaketest/doc/src/cmaketest.qdoc9
-rw-r--r--src/qdoc/qdoc/tests/generatedoutput/testdata/qml/cmaketest/main.cpp1
-rw-r--r--src/qdoc/qdoc/tests/generatedoutput/testdata/qml/componentset/ProgressBar.qml98
-rw-r--r--src/qdoc/qdoc/tests/generatedoutput/testdata/qml/componentset/Switch.qml105
-rw-r--r--src/qdoc/qdoc/tests/generatedoutput/testdata/qml/componentset/TabWidget.qml146
-rw-r--r--src/qdoc/qdoc/tests/generatedoutput/testdata/qml/componentset/componentset.pro5
-rw-r--r--src/qdoc/qdoc/tests/generatedoutput/testdata/qml/componentset/componentset.qml7
-rw-r--r--src/qdoc/qdoc/tests/generatedoutput/testdata/qml/componentset/examples.qdoc82
-rw-r--r--src/qdoc/qdoc/tests/generatedoutput/testdata/qml/componentset/uicomponents.qdoc.sample14
-rw-r--r--src/qdoc/qdoc/tests/generatedoutput/testdata/qml/doctest/DocTest.qml11
-rw-r--r--src/qdoc/qdoc/tests/generatedoutput/testdata/qml/modules.qdoc19
-rw-r--r--src/qdoc/qdoc/tests/generatedoutput/testdata/qml/parent.qdoc87
-rw-r--r--src/qdoc/qdoc/tests/generatedoutput/testdata/qml/type.cpp127
-rw-r--r--src/qdoc/qdoc/tests/generatedoutput/testdata/qmlpropertygroups/parent.qdoc37
-rw-r--r--src/qdoc/qdoc/tests/generatedoutput/testdata/qmlpropertygroups/qmlpropertygroups.qdocconf63
-rw-r--r--src/qdoc/qdoc/tests/generatedoutput/testdata/singleexec/singleexec.qdocconf4
-rw-r--r--src/qdoc/qdoc/tests/generatedoutput/testdata/testcpp/TestCPP5
-rw-r--r--src/qdoc/qdoc/tests/generatedoutput/testdata/testcpp/classlists.qdoc51
-rw-r--r--src/qdoc/qdoc/tests/generatedoutput/testdata/testcpp/properties.qdoc55
-rw-r--r--src/qdoc/qdoc/tests/generatedoutput/testdata/testcpp/snippets/snippet_testcpp.cpp3
-rw-r--r--src/qdoc/qdoc/tests/generatedoutput/testdata/testcpp/testcpp.cpp418
-rw-r--r--src/qdoc/qdoc/tests/generatedoutput/testdata/testcpp/testcpp.h137
-rw-r--r--src/qdoc/qdoc/tests/generatedoutput/tst_generatedoutput.cpp283
-rw-r--r--src/qdoc/qdoc/tests/qdoc/CMakeLists.txt28
-rw-r--r--src/qdoc/qdoc/tests/qdoc/boundaries/filesystem/catch_directorypath.cpp195
-rw-r--r--src/qdoc/qdoc/tests/qdoc/boundaries/filesystem/catch_filepath.cpp136
-rw-r--r--src/qdoc/qdoc/tests/qdoc/filesystem/catch_fileresolver.cpp307
-rw-r--r--src/qdoc/qdoc/tests/qdoc/main.cpp12
-rw-r--r--src/qdoc/qdoc/tests/qdoccommandlineparser/CMakeLists.txt41
-rw-r--r--src/qdoc/qdoc/tests/qdoccommandlineparser/tst_arguments.txt22
-rw-r--r--src/qdoc/qdoc/tests/qdoccommandlineparser/tst_qdoccommandlineparser.cpp161
-rw-r--r--src/qdoc/qdoc/tests/utilities/CMakeLists.txt21
-rw-r--r--src/qdoc/qdoc/tests/utilities/tst_utilities.cpp134
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/CMakeLists.txt44
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/README.md82
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/bug80259/bug80259.qdocconf31
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/bug80259/expected/html/first-members.html17
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/bug80259/expected/html/first-nested.html27
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/bug80259/expected/html/first.html37
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/bug80259/expected/html/index.html21
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/bug80259/expected/html/second.html29
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/bug80259/expected/html/testmodule.index13
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/bug80259/expected/html/third.html29
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/bug80259/expected/webxml/first-nested.webxml10
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/bug80259/expected/webxml/first.webxml15
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/bug80259/expected/webxml/index.webxml10
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/bug80259/expected/webxml/second.webxml10
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/bug80259/expected/webxml/testmodule.index13
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/bug80259/expected/webxml/third.webxml10
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/bug80259/src/inc/testmodule/TestModule.h6
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/bug80259/src/inc/testmodule/aaa.h7
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/bug80259/src/inc/testmodule/bbb.h8
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/bug80259/src/inc/testmodule/ccc.h7
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/bug80259/src/main.cpp29
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/bug80259/src/qdoc/index.qdoc10
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/cmakedocumentation/cmakedocumentation.qdocconf29
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/cmakedocumentation/expected/docbook/car.xml42
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/cmakedocumentation/expected/docbook/engine.xml42
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/cmakedocumentation/expected/docbook/testcar-module.xml23
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/cmakedocumentation/expected/docbook/testcarprivate-module.xml23
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/cmakedocumentation/expected/html/car.html36
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/cmakedocumentation/expected/html/cmakedocumentation.index15
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/cmakedocumentation/expected/html/engine.html36
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/cmakedocumentation/expected/html/testcar-module.html29
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/cmakedocumentation/expected/html/testcarprivate-module.html29
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/cmakedocumentation/expected/webxml/car.webxml15
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/cmakedocumentation/expected/webxml/cmakedocumentation.index15
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/cmakedocumentation/expected/webxml/engine.webxml20
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/cmakedocumentation/expected/webxml/testcar-module.webxml4
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/cmakedocumentation/expected/webxml/testcarprivate-module.webxml4
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/cmakedocumentation/src/car.cpp46
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/cmakedocumentation/src/car.h17
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/compiler_generated_member_functions/compiler_generated_member_functions.qdocconf23
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/compiler_generated_member_functions/expected/docbook/qdoctests-compilergeneratedmemberfunctions.xml39
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/compiler_generated_member_functions/expected/docbook/qdoctests-module.xml38
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/compiler_generated_member_functions/expected/docbook/qdoctests.xml30
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/compiler_generated_member_functions/expected/html/compiler-generated-member-functions.index18
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/compiler_generated_member_functions/expected/html/qdoctests-compilergeneratedmemberfunctions-members.html20
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/compiler_generated_member_functions/expected/html/qdoctests-compilergeneratedmemberfunctions.html59
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/compiler_generated_member_functions/expected/html/qdoctests-module.html38
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/compiler_generated_member_functions/expected/html/qdoctests.html43
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/compiler_generated_member_functions/expected/webxml/compiler-generated-member-functions.index18
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/compiler_generated_member_functions/expected/webxml/qdoctests-compilergeneratedmemberfunctions.webxml28
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/compiler_generated_member_functions/expected/webxml/qdoctests-module.webxml4
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/compiler_generated_member_functions/expected/webxml/qdoctests.webxml34
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/compiler_generated_member_functions/src/compilergeneratedmemberfunctions.cpp45
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/compiler_generated_member_functions/src/compilergeneratedmemberfunctions.h20
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/comprehensiveproject.qdocconf105
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/docbook/autolinking.xml32
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/docbook/classes.xml30
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/docbook/cpptypes.xml33
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/docbook/crossmoduleref.xml59
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/docbook/images/leonardo-da-vinci.pngbin0 -> 15076 bytes
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/docbook/obsolete-classes.xml31
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/docbook/qdoc-test-qmlmodule.xml54
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/docbook/qml-int.xml36
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/docbook/qml-qdoc-test-abstractparent.xml79
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/docbook/qml-qdoc-test-child.xml79
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/docbook/qml-qdoc-test-doctest.xml117
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/docbook/qml-qdoc-test-oldtype.xml37
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/docbook/qml-qdoc-test-type.xml237
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/docbook/qml-qdoc-test-yetanotherchild.xml44
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/docbook/qml-test-nover-doctest.xml35
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/docbook/qml-test-nover-typenoversion.xml35
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/docbook/qml-themodule-thetype.xml43
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/docbook/qml-uicomponents-progressbar.xml104
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/docbook/qml-uicomponents-switch.xml47
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/docbook/qml-uicomponents-tabwidget.xml77
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/docbook/qmlmodules.xml103
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/docbook/seenclass.xml42
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/docbook/test-cmaketest-cmakelists-txt.xml14
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/docbook/test-cmaketest-example.xml27
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/docbook/test-cmaketest-main-cpp.xml13
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/docbook/test-componentset-componentset-pro.xml18
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/docbook/test-componentset-componentset-qml.xml20
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/docbook/test-componentset-example.xml55
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/docbook/test-componentset-progressbar-qml.xml111
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/docbook/test-componentset-switch-qml.xml116
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/docbook/test-componentset-tabwidget-qml.xml158
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/docbook/test-demos-demo-demo-cpp.xml21
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/docbook/test-demos-demo-demo-pro.xml14
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/docbook/test-demos-demo-dontxclude-cmakelists-txt.xml14
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/docbook/test-demos-demo-example.xml32
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/docbook/test-demos-hidden-example.xml12
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/docbook/test-empty-qmlmodule.xml12
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/docbook/test-nover-qmlmodule.xml30
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/docbook/testcpp-module.xml91
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/docbook/testqdoc-test.xml376
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/docbook/testqdoc-testderived.xml308
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/docbook/testqdoc.xml73
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/docbook/themodule-qmlmodule.xml16
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/docbook/uicomponents-qmlmodule.xml34
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/autolinking.html36
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/classes.html21
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/cpptypes.html25
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/crossmoduleref.html47
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/images/leonardo-da-vinci.pngbin0 -> 15076 bytes
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/obsolete-classes.html32
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/qdoc-test-qmlmodule.html27
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/qml-int.html45
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/qml-qdoc-test-abstractparent-members.html23
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/qml-qdoc-test-abstractparent.html98
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/qml-qdoc-test-child-members.html23
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/qml-qdoc-test-child.html98
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/qml-qdoc-test-doctest-members.html25
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/qml-qdoc-test-doctest.html139
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/qml-qdoc-test-oldtype-members.html16
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/qml-qdoc-test-oldtype.html33
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/qml-qdoc-test-type-members.html37
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/qml-qdoc-test-type-obsolete.html33
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/qml-qdoc-test-type.html209
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/qml-qdoc-test-yetanotherchild-members.html19
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/qml-qdoc-test-yetanotherchild.html48
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/qml-test-nover-doctest-members.html17
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/qml-test-nover-doctest.html33
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/qml-test-nover-typenoversion-members.html17
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/qml-test-nover-typenoversion.html33
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/qml-themodule-thetype-members.html19
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/qml-themodule-thetype.html45
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/qml-uicomponents-progressbar-members.html24
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/qml-uicomponents-progressbar.html98
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/qml-uicomponents-switch-members.html21
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/qml-uicomponents-switch.html68
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/qml-uicomponents-tabwidget-members.html21
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/qml-uicomponents-tabwidget.html84
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/qmlmodules.html49
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/seenclass.html37
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/test-cmaketest-cmakelists-txt.html11
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/test-cmaketest-example.html21
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/test-cmaketest-main-cpp.html10
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/test-componentset-componentset-pro.html15
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/test-componentset-componentset-qml.html17
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/test-componentset-example.html55
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/test-componentset-progressbar-qml.html108
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/test-componentset-switch-qml.html113
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/test-componentset-tabwidget-qml.html155
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/test-demos-demo-demo-cpp.html18
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/test-demos-demo-demo-pro.html11
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/test-demos-demo-dontxclude-cmakelists-txt.html11
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/test-demos-demo-example.html24
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/test-demos-hidden-example.html21
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/test-empty-qmlmodule.html17
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/test-nover-qmlmodule.html23
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/test.index238
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/test.qhp260
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/testcpp-module.html60
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/testqdoc-test-members.html30
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/testqdoc-test-obsolete.html45
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/testqdoc-test.html165
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/testqdoc-testderived-members.html50
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/testqdoc-testderived-obsolete.html26
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/testqdoc-testderived.html173
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/testqdoc.html65
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/testtagfile.tags500
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/themodule-qmlmodule.html18
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/uicomponents-qmlmodule.html24
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/webxml/autolinking.webxml36
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/webxml/classes.webxml10
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/webxml/cpptypes.webxml22
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/webxml/crossmoduleref.webxml15
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/webxml/images/leonardo-da-vinci.pngbin0 -> 15076 bytes
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/webxml/obsolete-classes.webxml18
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/webxml/qdoc-test-qmlmodule.webxml4
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/webxml/qmlmodules.webxml21
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/webxml/seenclass.webxml10
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/webxml/test-cmaketest-cmakelists-txt.webxml11
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/webxml/test-cmaketest-example.webxml23
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/webxml/test-cmaketest-main-cpp.webxml10
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/webxml/test-componentset-componentset-pro.webxml14
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/webxml/test-componentset-componentset-qml.webxml16
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/webxml/test-componentset-example.webxml67
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/webxml/test-componentset-progressbar-qml.webxml107
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/webxml/test-componentset-switch-qml.webxml112
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/webxml/test-componentset-tabwidget-qml.webxml154
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/webxml/test-demos-demo-demo-cpp.webxml18
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/webxml/test-demos-demo-demo-pro.webxml11
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/webxml/test-demos-demo-dontxclude-cmakelists-txt.webxml11
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/webxml/test-demos-demo-example.webxml30
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/webxml/test-demos-hidden-example.webxml11
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/webxml/test-empty-qmlmodule.webxml4
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/webxml/test-nover-qmlmodule.webxml4
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/webxml/test.index226
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/webxml/testcpp-module.webxml4
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/webxml/testqdoc-test.webxml121
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/webxml/testqdoc-testderived.webxml124
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/webxml/testqdoc.webxml253
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/webxml/testtagfile.tags500
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/webxml/themodule-qmlmodule.webxml4
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/webxml/uicomponents-qmlmodule.webxml4
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/src/TestCPP5
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/src/classlists.qdoc51
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/src/dont.cpp22
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/src/dont.h16
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/src/examples/demos/demo/demo.cpp11
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/src/examples/demos/demo/demo.pro2
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/src/examples/demos/demo/doc/src/demo.qdoc11
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/src/examples/demos/demo/dontxclude/CMakeLists.txt2
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/src/examples/demos/demo/excludes/CMakeLists.txt2
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/src/examples/demos/hidden/doc/src/hidden.qdoc11
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/src/examples/demos/hidden/hidden.pro2
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/src/images/01.pngbin0 -> 1142 bytes
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/src/images/leonardo-da-vinci.pngbin0 -> 15076 bytes
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/src/properties.qdoc55
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/src/qml/DocTest.qml86
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/src/qml/cmaketest/CMakeLists.txt2
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/src/qml/cmaketest/doc/src/cmaketest.qdoc9
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/src/qml/cmaketest/main.cpp1
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/src/qml/componentset/ProgressBar.qml98
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/src/qml/componentset/Switch.qml105
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/src/qml/componentset/TabWidget.qml146
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/src/qml/componentset/componentset.pro5
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/src/qml/componentset/componentset.qml7
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/src/qml/componentset/examples.qdoc82
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/src/qml/componentset/uicomponents.qdoc.sample14
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/src/qml/doctest/DocTest.qml11
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/src/qml/modules.qdoc19
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/src/qml/parent.qdoc87
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/src/qml/type.cpp133
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/src/snippets/snippet_testcpp.cpp3
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/src/testcpp.cpp418
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/src/testcpp.h140
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/src/unseenclass.qdoc11
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/cxx20/cxx20.qdocconf32
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/cxx20/expected/docbook/bar.xml21
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/cxx20/expected/docbook/baz.xml21
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/cxx20/expected/docbook/comparesstronglywithoneclassandpartiallywithanother.xml27
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/cxx20/expected/docbook/comparesstronglywiththreeclasses.xml22
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/cxx20/expected/docbook/comparesstronglywiththreeclassesacrossmultiplelines.xml22
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/cxx20/expected/docbook/comparesstronglywithtwoclasses.xml26
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/cxx20/expected/docbook/equalitycomparableclass.xml22
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/cxx20/expected/docbook/foo.xml21
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/cxx20/expected/docbook/partiallyorderedclass.xml22
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/cxx20/expected/docbook/stronglyorderedclass.xml22
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/cxx20/expected/docbook/weaklyorderedclass.xml22
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/cxx20/expected/html/bar.html28
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/cxx20/expected/html/baz.html28
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/cxx20/expected/html/comparesstronglywithoneclassandpartiallywithanother.html37
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/cxx20/expected/html/comparesstronglywiththreeclasses.html29
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/cxx20/expected/html/comparesstronglywiththreeclassesacrossmultiplelines.html29
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/cxx20/expected/html/comparesstronglywithtwoclasses.html33
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/cxx20/expected/html/cxx20.index18
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/cxx20/expected/html/equalitycomparableclass.html29
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/cxx20/expected/html/foo.html28
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/cxx20/expected/html/partiallyorderedclass.html29
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/cxx20/expected/html/stronglyorderedclass.html29
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/cxx20/expected/html/weaklyorderedclass.html29
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/cxx20/expected/webxml/bar.webxml8
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/cxx20/expected/webxml/baz.webxml8
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/cxx20/expected/webxml/comparesstronglywithoneclassandpartiallywithanother.webxml11
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/cxx20/expected/webxml/comparesstronglywiththreeclasses.webxml8
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/cxx20/expected/webxml/comparesstronglywiththreeclassesacrossmultiplelines.webxml8
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/cxx20/expected/webxml/comparesstronglywithtwoclasses.webxml8
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/cxx20/expected/webxml/cxx20.index18
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/cxx20/expected/webxml/equalitycomparableclass.webxml8
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/cxx20/expected/webxml/foo.webxml8
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/cxx20/expected/webxml/partiallyorderedclass.webxml8
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/cxx20/expected/webxml/stronglyorderedclass.webxml8
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/cxx20/expected/webxml/weaklyorderedclass.webxml8
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/cxx20/src/classes_with_various_ordering.cpp89
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/cxx20/src/classes_with_various_ordering.h41
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/duplicate_section_titles_have_unique_anchors/duplicate_section_titles_have_unique_anchors.qdocconf25
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/duplicate_section_titles_have_unique_anchors/expected/docbook/page-one.xml27
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/duplicate_section_titles_have_unique_anchors/expected/html/duplicate-section-titles-have-unique-anchors.index12
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/duplicate_section_titles_have_unique_anchors/expected/html/page-one.html35
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/duplicate_section_titles_have_unique_anchors/expected/webxml/duplicate-section-titles-have-unique-anchors.index12
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/duplicate_section_titles_have_unique_anchors/expected/webxml/page-one.webxml30
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/duplicate_section_titles_have_unique_anchors/src/page_one.qdoc21
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/globalfunc/expected/html/globals.html45
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/globalfunc/expected/html/testglobals-module.html29
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/globalfunc/expected/html/testglobals.index23
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/globalfunc/globalfunc.qdocconf23
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/globalfunc/src/TestGlobals1
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/globalfunc/src/global.h8
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/globalfunc/src/global.qdoc25
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/headerfile/expected/docbook/headers.xml19
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/headerfile/expected/docbook/testheader.xml68
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/headerfile/expected/docbook/tests.xml19
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/headerfile/expected/html/headerfile.index23
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/headerfile/expected/html/headers.html19
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/headerfile/expected/html/testheader.html69
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/headerfile/expected/html/tests.html19
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/headerfile/expected/webxml/headerfile.index23
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/headerfile/expected/webxml/headers.webxml21
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/headerfile/expected/webxml/testheader.webxml34
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/headerfile/expected/webxml/tests.webxml21
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/headerfile/headerfile.qdocconf25
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/headerfile/src/testheader.cpp43
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/headerfile/src/testheader.h8
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/illformatted_documentation/expected/html/another-page-with-comments-in-the-brief.html18
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/illformatted_documentation/expected/html/brief-adventures.html27
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/illformatted_documentation/expected/html/illformatted-examples.html31
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/illformatted_documentation/expected/html/illformatteddocumentation-someexample-example.html16
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/illformatted_documentation/expected/html/illformatteddocumentation.index22
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/illformatted_documentation/expected/html/page-with-an-image-at-the-top.html19
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/illformatted_documentation/expected/html/page-with-comment-after-brief.html18
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/illformatted_documentation/expected/html/page-with-comment-in-brief.html18
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/illformatted_documentation/expected/webxml/another-page-with-comments-in-the-brief.webxml11
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/illformatted_documentation/expected/webxml/brief-adventures.webxml17
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/illformatted_documentation/expected/webxml/illformatted-examples.webxml31
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/illformatted_documentation/expected/webxml/illformatteddocumentation-someexample-example.webxml8
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/illformatted_documentation/expected/webxml/illformatteddocumentation.index22
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/illformatted_documentation/expected/webxml/page-with-an-image-at-the-top.webxml12
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/illformatted_documentation/expected/webxml/page-with-comment-after-brief.webxml11
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/illformatted_documentation/expected/webxml/page-with-comment-in-brief.webxml11
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/illformatted_documentation/illformatted_documentation.qdocconf29
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/illformatted_documentation/src/brief_adventures.qdoc75
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/illformatted_documentation/src/illformatted-examples.qdoc17
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/illformatted_documentation/src/some_example.qdoc8
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/line_comments/expected/docbook/a-page-with-a-line-comment-in-the-see-also-command.xml20
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/line_comments/expected/docbook/another-page-with-an-image-at-the-top.xml19
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/line_comments/expected/docbook/images/leonardo-da-vinci.pngbin0 -> 15076 bytes
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/line_comments/expected/docbook/line-comment-adventures.xml16
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/line_comments/expected/html/a-page-with-a-line-comment-in-the-see-also-command.html18
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/line_comments/expected/html/another-page-with-an-image-at-the-top.html19
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/line_comments/expected/html/images/leonardo-da-vinci.pngbin0 -> 15076 bytes
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/line_comments/expected/html/line-comment-adventures.html27
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/line_comments/expected/html/linecomment.index11
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/line_comments/expected/webxml/a-page-with-a-line-comment-in-the-see-also-command.webxml14
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/line_comments/expected/webxml/another-page-with-an-image-at-the-top.webxml13
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/line_comments/expected/webxml/images/leonardo-da-vinci.pngbin0 -> 15076 bytes
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/line_comments/expected/webxml/line-comment-adventures.webxml16
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/line_comments/expected/webxml/linecomment.index11
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/line_comments/line_comments.qdocconf27
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/line_comments/src/images/leonardo-da-vinci.pngbin0 -> 15076 bytes
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/line_comments/src/line_comment_adventures.qdoc39
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/minimal_configuration/expected/a-minimal-qdoc-configuration.index7
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/minimal_configuration/expected/readme.html17
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/minimal_configuration/minimal_configuration.qdocconf12
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/minimal_configuration/src/README.qdoc9
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/modifiedoutputfilenames/expected/docbook/cppmodule-module-suffix.xml26
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/modifiedoutputfilenames/expected/docbook/group.xml11
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/modifiedoutputfilenames/expected/docbook/modifiedoutputfilenames-test-example.xml10
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/modifiedoutputfilenames/expected/docbook/page.xml10
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/modifiedoutputfilenames/expected/docbook/prefix-header-suffix.xml21
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/modifiedoutputfilenames/expected/docbook/prefix-namespace-class-suffix.xml22
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/modifiedoutputfilenames/expected/docbook/prefix-namespace-suffix.xml27
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/modifiedoutputfilenames/expected/docbook/qml-qmlmodule-suffix-type.xml21
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/modifiedoutputfilenames/expected/docbook/qmlmodule-qmlmodule-suffix.xml16
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/modifiedoutputfilenames/expected/html/cppmodule-module-suffix.html32
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/modifiedoutputfilenames/expected/html/group.html15
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/modifiedoutputfilenames/expected/html/modifiedoutputfilenames-test-example.html15
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/modifiedoutputfilenames/expected/html/modifiedoutputfilenames.index16
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/modifiedoutputfilenames/expected/html/page.html15
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/modifiedoutputfilenames/expected/html/prefix-header-suffix.html24
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/modifiedoutputfilenames/expected/html/prefix-namespace-class-suffix.html29
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/modifiedoutputfilenames/expected/html/prefix-namespace-suffix.html35
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/modifiedoutputfilenames/expected/html/qml-qmlmodule-suffix-type-members.html14
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/modifiedoutputfilenames/expected/html/qml-qmlmodule-suffix-type.html27
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/modifiedoutputfilenames/expected/html/qmlmodule-qmlmodule-suffix.html18
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/modifiedoutputfilenames/expected/webxml/cppmodule-module-suffix.webxml4
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/modifiedoutputfilenames/expected/webxml/group.webxml10
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/modifiedoutputfilenames/expected/webxml/modifiedoutputfilenames-test-example.webxml8
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/modifiedoutputfilenames/expected/webxml/modifiedoutputfilenames.index16
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/modifiedoutputfilenames/expected/webxml/page.webxml8
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/modifiedoutputfilenames/expected/webxml/prefix-header-suffix.webxml8
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/modifiedoutputfilenames/expected/webxml/prefix-namespace-class-suffix.webxml8
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/modifiedoutputfilenames/expected/webxml/prefix-namespace-suffix.webxml11
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/modifiedoutputfilenames/expected/webxml/qmlmodule-qmlmodule-suffix.webxml4
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/modifiedoutputfilenames/modifiedoutputfilenames.qdocconf22
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/modifiedoutputfilenames/src/example/test/CMakeLists.txt1
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/modifiedoutputfilenames/src/test.cpp24
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/modifiedoutputfilenames/src/test.h12
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/modulestate/expected/docbook/boringclass.xml28
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/modulestate/expected/docbook/excitingclass.xml32
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/modulestate/expected/docbook/moduleinstate-module.xml23
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/modulestate/expected/html/boringclass.html31
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/modulestate/expected/html/excitingclass.html34
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/modulestate/expected/html/moduleinstate-module.html30
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/modulestate/expected/html/modulestate.index11
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/modulestate/expected/webxml/boringclass.webxml10
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/modulestate/expected/webxml/excitingclass.webxml15
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/modulestate/expected/webxml/moduleinstate-module.webxml4
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/modulestate/expected/webxml/modulestate.index11
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/modulestate/modulestate.qdocconf29
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/modulestate/src/classes_in_stateful_module.h15
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/modulestate/src/module_in_a_state.qdoc26
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/non_ascii_character_input/expected/8b5c72eb.html16
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/non_ascii_character_input/expected/adventures-with-non-ascii-characters.html46
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/non_ascii_character_input/expected/e85685de.html16
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/non_ascii_character_input/expected/html/8b5c72eb.webxml10
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/non_ascii_character_input/expected/html/adventures-with-non-ascii-characters.webxml45
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/non_ascii_character_input/expected/html/e85685de.webxml10
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/non_ascii_character_input/expected/html/mozzarella-7c883eff.webxml10
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/non_ascii_character_input/expected/html/nonasciicharacterinput.index19
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/non_ascii_character_input/expected/html/santa-14209312.webxml10
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/non_ascii_character_input/expected/html/seite-mit-ausschlie-lich-gro-buchstaben-im-titel-berschrift-htm-bfa91582.webxml10
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/non_ascii_character_input/expected/mozzarella-7c883eff.html16
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/non_ascii_character_input/expected/nonasciicharacterinput.index19
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/non_ascii_character_input/expected/santa-14209312.html16
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/non_ascii_character_input/expected/seite-mit-ausschlie-lich-gro-buchstaben-im-titel-berschrift-htm-bfa91582.html16
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/non_ascii_character_input/non_ascii_character_input.qdocconf30
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/non_ascii_character_input/src/adventures_with_non_ascii_characters.qdoc89
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/outputfromqdocfiles/expected/docbook/crash.xml12
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/outputfromqdocfiles/expected/docbook/images/01.pngbin0 -> 1142 bytes
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/outputfromqdocfiles/expected/docbook/images/leonardo-da-vinci.pngbin0 -> 15076 bytes
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/outputfromqdocfiles/expected/docbook/qdoctests-qdocfileoutput-exhaustive.xml129
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/outputfromqdocfiles/expected/docbook/qdoctests-qdocfileoutput-linking.xml19
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/outputfromqdocfiles/expected/docbook/qdoctests-qdocfileoutput.xml80
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/outputfromqdocfiles/expected/docbook/qdoctests-qdocmanuallikefileoutput.xml27
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/outputfromqdocfiles/expected/docbook/toc.xml23
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/outputfromqdocfiles/expected/html/crash.html16
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/outputfromqdocfiles/expected/html/images/01.pngbin0 -> 1142 bytes
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/outputfromqdocfiles/expected/html/images/leonardo-da-vinci.pngbin0 -> 15076 bytes
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/outputfromqdocfiles/expected/html/outputfromqdocfiles.index33
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/outputfromqdocfiles/expected/html/qdoctests-qdocfileoutput-exhaustive.html78
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/outputfromqdocfiles/expected/html/qdoctests-qdocfileoutput-linking.html37
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/outputfromqdocfiles/expected/html/qdoctests-qdocfileoutput.html70
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/outputfromqdocfiles/expected/html/qdoctests-qdocmanuallikefileoutput.html63
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/outputfromqdocfiles/expected/html/toc.html29
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/outputfromqdocfiles/expected/webxml/crash.webxml10
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/outputfromqdocfiles/expected/webxml/images/01.pngbin0 -> 1142 bytes
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/outputfromqdocfiles/expected/webxml/images/leonardo-da-vinci.pngbin0 -> 15076 bytes
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/outputfromqdocfiles/expected/webxml/outputfromqdocfiles.index33
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/outputfromqdocfiles/expected/webxml/qdoctests-qdocfileoutput-exhaustive.webxml90
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/outputfromqdocfiles/expected/webxml/qdoctests-qdocfileoutput-linking.webxml20
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/outputfromqdocfiles/expected/webxml/qdoctests-qdocfileoutput.webxml95
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/outputfromqdocfiles/expected/webxml/qdoctests-qdocmanuallikefileoutput.webxml57
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/outputfromqdocfiles/expected/webxml/toc.webxml24
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/outputfromqdocfiles/outputfromqdocfiles.qdocconf43
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/outputfromqdocfiles/src/images/01.pngbin0 -> 1142 bytes
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/outputfromqdocfiles/src/images/leonardo-da-vinci.pngbin0 -> 15076 bytes
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/outputfromqdocfiles/src/qdoctests-outputfromqdocfiles.qdoc241
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/outputfromqdocfiles/src/qdoctests-outputfromqdocmanuallikefiles.qdoc59
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/outputfromqdocfiles/src/snippets/main.cpp10
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/proxypage/expected/docbook/stdpair-proxypage-proxy.xml16
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/proxypage/expected/html/proxypage.index10
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/proxypage/expected/html/stdpair-proxypage-proxy.html20
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/proxypage/proxypage.qdocconf24
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/proxypage/src/proxy.h11
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/proxypage/src/proxy.qdoc9
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/qml_nativetype_synopsis/expected/docbook/cppcar.xml28
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/qml_nativetype_synopsis/expected/docbook/qml-qmlnativetypesynopsis-car.xml66
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/qml_nativetype_synopsis/expected/docbook/qml-qmlnativetypesynopsis-truck.xml66
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/qml_nativetype_synopsis/expected/docbook/qmlnativetypesynopsis-qmlmodule.xml25
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/qml_nativetype_synopsis/expected/html/cppcar.html33
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/qml_nativetype_synopsis/expected/html/qml-nativetype-synopsis-test.index20
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/qml_nativetype_synopsis/expected/html/qml-qmlnativetypesynopsis-car-members.html15
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/qml_nativetype_synopsis/expected/html/qml-qmlnativetypesynopsis-car.html46
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/qml_nativetype_synopsis/expected/html/qml-qmlnativetypesynopsis-truck-members.html15
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/qml_nativetype_synopsis/expected/html/qml-qmlnativetypesynopsis-truck.html46
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/qml_nativetype_synopsis/expected/html/qmlnativetypesynopsis-qmlmodule.html19
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/qml_nativetype_synopsis/expected/webxml/cppcar.webxml10
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/qml_nativetype_synopsis/expected/webxml/qml-nativetype-synopsis-test.index20
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/qml_nativetype_synopsis/expected/webxml/qmlnativetypesynopsis-qmlmodule.webxml4
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/qml_nativetype_synopsis/qml_nativetype_synopsis.qdocconf23
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/qml_nativetype_synopsis/src/cppcar.cpp68
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/qml_nativetype_synopsis/src/cppcar.h10
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/qmlenumvaluesfromcpp/expected/docbook/class.xml136
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/qmlenumvaluesfromcpp/expected/docbook/module-module.xml18
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/qmlenumvaluesfromcpp/expected/docbook/qml-qmlmodule-type.xml103
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/qmlenumvaluesfromcpp/expected/docbook/qmlmodule-qmlmodule.xml16
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/qmlenumvaluesfromcpp/expected/html/class-members.html18
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/qmlenumvaluesfromcpp/expected/html/class.html59
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/qmlenumvaluesfromcpp/expected/html/module-module.html28
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/qmlenumvaluesfromcpp/expected/html/qml-qmlmodule-type-members.html18
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/qmlenumvaluesfromcpp/expected/html/qml-qmlmodule-type.html62
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/qmlenumvaluesfromcpp/expected/html/qmlenumvaluesfromcpp.index23
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/qmlenumvaluesfromcpp/expected/html/qmlmodule-qmlmodule.html18
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/qmlenumvaluesfromcpp/expected/webxml/class.webxml54
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/qmlenumvaluesfromcpp/expected/webxml/module-module.webxml4
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/qmlenumvaluesfromcpp/expected/webxml/qmlenumvaluesfromcpp.index23
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/qmlenumvaluesfromcpp/expected/webxml/qmlmodule-qmlmodule.webxml4
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/qmlenumvaluesfromcpp/qmlenumvaluesfromcpp.qdocconf22
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/qmlenumvaluesfromcpp/src/class.cpp36
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/qmlenumvaluesfromcpp/src/class.h10
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/qmlenumvaluesfromcpp/src/qmltype.qdoc23
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/relatesordering/a.cpp11
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/relatesordering/a.h5
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/relatesordering/b.cpp12
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/relatesordering/expected/bar.html35
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/relatesordering/expected/module-module.html22
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/relatesordering/expected/relatesordering.index11
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/relatesordering/relatesordering.qdocconf7
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/scopedenum/expected/docbook/autolinking.xml32
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/scopedenum/expected/docbook/cpptypes.xml30
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/scopedenum/expected/docbook/crossmoduleref.xml49
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/scopedenum/expected/docbook/obsolete-classes.xml31
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/scopedenum/expected/docbook/scoped-enum-linking.xml12
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/scopedenum/expected/docbook/testcpp-module.xml77
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/scopedenum/expected/docbook/testqdoc-test.xml253
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/scopedenum/expected/docbook/testqdoc-testderived.xml83
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/scopedenum/expected/docbook/testqdoc.xml63
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/scopedenum/expected/docbook/whatsnew.xml11
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/scopedenum/expected/html/autolinking.html36
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/scopedenum/expected/html/cpptypes.html24
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/scopedenum/expected/html/crossmoduleref.html47
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/scopedenum/expected/html/obsolete-classes.html32
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/scopedenum/expected/html/scoped-enum-linking.html18
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/scopedenum/expected/html/testcpp-module.html55
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/scopedenum/expected/html/testcpp.index91
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/scopedenum/expected/html/testqdoc-test-members.html30
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/scopedenum/expected/html/testqdoc-test-obsolete.html45
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/scopedenum/expected/html/testqdoc-test.html173
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/scopedenum/expected/html/testqdoc-testderived-members.html38
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/scopedenum/expected/html/testqdoc-testderived-obsolete.html26
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/scopedenum/expected/html/testqdoc-testderived.html81
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/scopedenum/expected/html/testqdoc.html64
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/scopedenum/expected/html/whatsnew.html37
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/scopedenum/expected/webxml/autolinking.webxml36
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/scopedenum/expected/webxml/cpptypes.webxml22
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/scopedenum/expected/webxml/crossmoduleref.webxml15
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/scopedenum/expected/webxml/obsolete-classes.webxml18
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/scopedenum/expected/webxml/scoped-enum-linking.webxml11
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/scopedenum/expected/webxml/testcpp-module.webxml4
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/scopedenum/expected/webxml/testcpp.index91
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/scopedenum/expected/webxml/testqdoc-test.webxml160
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/scopedenum/expected/webxml/testqdoc-testderived.webxml33
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/scopedenum/expected/webxml/testqdoc.webxml201
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/scopedenum/expected/webxml/whatsnew.webxml10
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/scopedenum/scopedenum.qdocconf37
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/scopedenum/src/classlists.qdoc51
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/scopedenum/src/scopedenum.qdoc43
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/scopedenum/src/snippets/snippet_testcpp.cpp3
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/scopedenum/src/testcpp.cpp402
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/scopedenum/src/testcpp.h139
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/tableaftervalue/expected/docbok/tableaftervalue.xml60
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/tableaftervalue/expected/html/tableaftervalue-members.html18
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/tableaftervalue/expected/html/tableaftervalue.html56
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/tableaftervalue/expected/html/tableaftervalue.index13
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/tableaftervalue/expected/webxml/tableaftervalue.index13
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/tableaftervalue/expected/webxml/tableaftervalue.webxml35
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/tableaftervalue/src/table-after-value.cpp28
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/tableaftervalue/src/table-after-value.h8
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/tableaftervalue/tableaftervalue.qdocconf29
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/templatedcallables/expected/docbook/templated-callables-h.xml65
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/templatedcallables/expected/docbook/templatedclass.xml66
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/templatedcallables/expected/html/templated-callables-h.html81
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/templatedcallables/expected/html/templatedcallables.index77
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/templatedcallables/expected/html/templatedclass-members.html26
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/templatedcallables/expected/html/templatedclass.html90
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/templatedcallables/expected/webxml/templated-callables-h.webxml66
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/templatedcallables/expected/webxml/templatedcallables.index77
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/templatedcallables/expected/webxml/templatedclass.webxml66
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/templatedcallables/src/templated_callables.cpp149
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/templatedcallables/src/templated_callables.h73
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/templatedcallables/templatedcallables.qdocconf29
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/testtemplate/expected/docbook/autolinking.xml32
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/testtemplate/expected/docbook/bar.xml43
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/testtemplate/expected/docbook/baz.xml42
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/testtemplate/expected/docbook/cpptypes.xml30
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/testtemplate/expected/docbook/crossmoduleref.xml49
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/testtemplate/expected/docbook/foo.xml43
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/testtemplate/expected/docbook/obsolete-classes.xml31
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/testtemplate/expected/docbook/testcpp-module.xml107
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/testtemplate/expected/docbook/testqdoc-test-struct.xml42
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/testtemplate/expected/docbook/testqdoc-test.xml189
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/testtemplate/expected/docbook/testqdoc-testderived.xml83
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/testtemplate/expected/docbook/testqdoc-vec.xml43
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/testtemplate/expected/docbook/testqdoc.xml67
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/testtemplate/expected/html/autolinking.html36
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/testtemplate/expected/html/bar.html38
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/testtemplate/expected/html/baz.html38
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/testtemplate/expected/html/cpptypes.html24
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/testtemplate/expected/html/crossmoduleref.html47
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/testtemplate/expected/html/foo.html38
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/testtemplate/expected/html/obsolete-classes.html32
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/testtemplate/expected/html/testcpp-module.html60
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/testtemplate/expected/html/testqdoc-test-members.html31
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/testtemplate/expected/html/testqdoc-test-obsolete.html45
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/testtemplate/expected/html/testqdoc-test-struct.html32
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/testtemplate/expected/html/testqdoc-test.html167
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/testtemplate/expected/html/testqdoc-testderived-members.html39
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/testtemplate/expected/html/testqdoc-testderived-obsolete.html26
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/testtemplate/expected/html/testqdoc-testderived.html81
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/testtemplate/expected/html/testqdoc-vec.html38
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/testtemplate/expected/html/testqdoc.html68
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/testtemplate/expected/html/testtemplate.index87
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/testtemplate/expected/webxml/autolinking.webxml36
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/testtemplate/expected/webxml/bar.webxml10
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/testtemplate/expected/webxml/baz.webxml10
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/testtemplate/expected/webxml/cpptypes.webxml22
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/testtemplate/expected/webxml/crossmoduleref.webxml15
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/testtemplate/expected/webxml/foo.webxml10
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/testtemplate/expected/webxml/obsolete-classes.webxml18
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/testtemplate/expected/webxml/testcpp-module.webxml4
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/testtemplate/expected/webxml/testqdoc-test-struct.webxml10
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/testtemplate/expected/webxml/testqdoc-test.webxml130
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/testtemplate/expected/webxml/testqdoc-testderived.webxml33
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/testtemplate/expected/webxml/testqdoc-vec.webxml10
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/testtemplate/expected/webxml/testqdoc.webxml176
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/testtemplate/expected/webxml/testtemplate.index87
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/testtemplate/src/classlists.qdoc51
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/testtemplate/src/snippets/snippet_testcpp.cpp3
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/testtemplate/src/testcpp.cpp402
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/testtemplate/src/testcpp.h137
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/testtemplate/src/testtemplate.cpp23
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/testtemplate/src/testtemplate.h28
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/testtemplate/testtemplate.qdocconf38
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/tocnavigation/expected/docbook/crash.xml14
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/tocnavigation/expected/docbook/images/01.pngbin0 -> 1142 bytes
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/tocnavigation/expected/docbook/images/leonardo-da-vinci.pngbin0 -> 15076 bytes
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/tocnavigation/expected/docbook/qdoctests-qdocfileoutput-exhaustive.xml131
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/tocnavigation/expected/docbook/qdoctests-qdocfileoutput-linking.xml19
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/tocnavigation/expected/docbook/qdoctests-qdocfileoutput.xml80
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/tocnavigation/expected/docbook/qdoctests-qdocmanuallikefileoutput.xml27
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/tocnavigation/expected/docbook/toc-test.xml40
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/tocnavigation/expected/docbook/toc.xml23
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/tocnavigation/expected/html/crash.html26
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/tocnavigation/expected/html/images/01.pngbin0 -> 1142 bytes
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/tocnavigation/expected/html/images/leonardo-da-vinci.pngbin0 -> 15076 bytes
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/tocnavigation/expected/html/outputfromqdocfiles.index34
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/tocnavigation/expected/html/qdoctests-qdocfileoutput-exhaustive.html91
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/tocnavigation/expected/html/qdoctests-qdocfileoutput-linking.html39
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/tocnavigation/expected/html/qdoctests-qdocfileoutput.html72
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/tocnavigation/expected/html/qdoctests-qdocmanuallikefileoutput.html63
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/tocnavigation/expected/html/toc-test.html32
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/tocnavigation/expected/html/toc.html31
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/tocnavigation/expected/webxml/crash.webxml12
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/tocnavigation/expected/webxml/images/01.pngbin0 -> 1142 bytes
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/tocnavigation/expected/webxml/images/leonardo-da-vinci.pngbin0 -> 15076 bytes
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/tocnavigation/expected/webxml/outputfromqdocfiles.index34
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/tocnavigation/expected/webxml/qdoctests-qdocfileoutput-exhaustive.webxml92
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/tocnavigation/expected/webxml/qdoctests-qdocfileoutput-linking.webxml20
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/tocnavigation/expected/webxml/qdoctests-qdocfileoutput.webxml95
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/tocnavigation/expected/webxml/qdoctests-qdocmanuallikefileoutput.webxml57
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/tocnavigation/expected/webxml/toc-test.webxml44
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/tocnavigation/expected/webxml/toc.webxml24
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/tocnavigation/src/images/01.pngbin0 -> 1142 bytes
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/tocnavigation/src/images/leonardo-da-vinci.pngbin0 -> 15076 bytes
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/tocnavigation/src/qdoctests-outputfromqdocfiles.qdoc241
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/tocnavigation/src/qdoctests-outputfromqdocmanuallikefiles.qdoc59
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/tocnavigation/src/snippets/main.cpp10
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/tocnavigation/src/toc.qdoc23
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/tocnavigation/tocnavigation.qdocconf48
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/trademark_command/expected/docbook/trademark-test.xml15
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/trademark_command/expected/docbook/trademarks.xml19
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/trademark_command/expected/html/trademark-test.html26
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/trademark_command/expected/html/trademarkcommand.index12
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/trademark_command/expected/html/trademarks.html23
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/trademark_command/expected/webxml/trademark-test.webxml15
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/trademark_command/expected/webxml/trademarkcommand.index12
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/trademark_command/expected/webxml/trademarks.webxml20
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/trademark_command/src/test.qdoc32
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/trademark_command/trademark_command.qdocconf20
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/trailing_backslashes/expected/docbook/struct.xml57
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/trailing_backslashes/expected/html/struct-members.html19
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/trailing_backslashes/expected/html/struct.html53
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/trailing_backslashes/expected/html/trailingbackslashes.index18
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/trailing_backslashes/expected/webxml/struct.webxml32
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/trailing_backslashes/expected/webxml/trailingbackslashes.index18
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/trailing_backslashes/src/trailing_backslashes.cpp32
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/trailing_backslashes/src/trailing_backslashes.h14
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/trailing_backslashes/trailing_backslashes.qdocconf29
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/usingdirective/expected/docbook/space.xml29
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/usingdirective/expected/html/space.html43
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/usingdirective/expected/html/usingdirective.index15
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/usingdirective/expected/webxml/space.webxml16
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/usingdirective/expected/webxml/usingdirective.index15
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/usingdirective/src/UsingDirective2
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/usingdirective/src/alias.h10
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/usingdirective/src/space.cpp21
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/usingdirective/src/space.h10
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/usingdirective/usingdirective.qdocconf27
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/tst_validateqdocoutputfiles.cpp182
-rw-r--r--src/qdoc/qdoccommandlineparser.h53
-rw-r--r--src/qdoc/qmlcodemarker.h69
-rw-r--r--src/qdoc/qmlcodeparser.cpp226
-rw-r--r--src/qdoc/qmlcodeparser.h77
-rw-r--r--src/qdoc/qmlpropertynode.cpp158
-rw-r--r--src/qdoc/relatedclass.h59
-rw-r--r--src/qdoc/sharedcommentnode.cpp81
-rw-r--r--src/qdoc/sharedcommentnode.h77
-rw-r--r--src/qdoc/singleton.h57
-rw-r--r--src/qdoc/tagfilewriter.h58
-rw-r--r--src/qdoc/topic.h54
-rw-r--r--src/qdoc/typedefnode.cpp80
-rw-r--r--src/qdoc/typedefnode.h79
-rw-r--r--src/qdoc/usingclause.cpp69
-rw-r--r--src/qdoc/usingclause.h55
-rw-r--r--src/qdoc/utilities.h52
-rw-r--r--src/qdoc/variablenode.cpp48
-rw-r--r--src/qdoc/variablenode.h69
-rw-r--r--src/qdoc/xmlgenerator.h73
1046 files changed, 71390 insertions, 18486 deletions
diff --git a/src/qdoc/CMakeLists.txt b/src/qdoc/CMakeLists.txt
index 9a9442650..9662b34c6 100644
--- a/src/qdoc/CMakeLists.txt
+++ b/src/qdoc/CMakeLists.txt
@@ -1,130 +1,10 @@
-# Generated from qdoc.pro.
+# Copyright (C) 2023 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
-# special case begin
-if(CMAKE_VERSION VERSION_LESS "3.19" AND MSVC AND CMAKE_GENERATOR STREQUAL "Ninja Multi-Config")
- message(WARNING "qdoc will not be built in this configuration.")
- return()
-endif()
+add_subdirectory(catch)
+add_subdirectory(catch_conversions)
+add_subdirectory(catch_generators)
-if (MINGW)
- set_property(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" PROPERTY _qt_skip_separate_debug_info ON)
+if(QT_FEATURE_qdoc)
+ add_subdirectory(qdoc)
endif()
-
-# special case end
-
-#####################################################################
-## qdoc Tool:
-#####################################################################
-
-qt_get_tool_target_name(target_name qdoc)
-qt_internal_add_tool(${target_name}
- TARGET_DESCRIPTION "Qt Documentation Compiler"
- TOOLS_TARGET Tools # special case
- USER_FACING
- SOURCES
- access.h
- aggregate.cpp aggregate.h
- atom.cpp atom.h
- clangcodeparser.cpp clangcodeparser.h
- classnode.cpp classnode.h
- codechunk.cpp codechunk.h
- codemarker.cpp codemarker.h
- codeparser.cpp codeparser.h
- collectionnode.cpp collectionnode.h
- config.cpp config.h
- cppcodemarker.cpp cppcodemarker.h
- cppcodeparser.cpp cppcodeparser.h
- doc.cpp doc.h
- docbookgenerator.cpp docbookgenerator.h
- docparser.cpp docparser.h
- docprivate.cpp docprivate.h
- docutilities.h
- editdistance.cpp editdistance.h
- enumitem.h
- enumnode.cpp enumnode.h
- examplenode.h
- externalpagenode.cpp externalpagenode.h
- functionnode.cpp functionnode.h
- generator.cpp generator.h
- headernode.cpp headernode.h
- helpprojectwriter.cpp helpprojectwriter.h
- htmlgenerator.cpp htmlgenerator.h
- importrec.h
- jscodemarker.cpp jscodemarker.h
- location.cpp location.h
- macro.h
- main.cpp
- manifestwriter.cpp manifestwriter.h
- namespacenode.cpp namespacenode.h
- node.cpp node.h
- openedlist.cpp openedlist.h
- pagenode.cpp pagenode.h
- parameters.cpp parameters.h
- propertynode.cpp propertynode.h
- proxynode.cpp proxynode.h
- puredocparser.cpp puredocparser.h
- qdoccommandlineparser.cpp qdoccommandlineparser.h
- qdocdatabase.cpp qdocdatabase.h
- qdocindexfiles.cpp qdocindexfiles.h
- qmlcodemarker.cpp qmlcodemarker.h
- qmlcodeparser.cpp qmlcodeparser.h
- qmlmarkupvisitor.cpp qmlmarkupvisitor.h
- qmlpropertynode.cpp qmlpropertynode.h
- qmltypenode.cpp qmltypenode.h
- qmlvisitor.cpp qmlvisitor.h
- quoter.cpp quoter.h
- relatedclass.cpp relatedclass.h
- sections.cpp sections.h
- sharedcommentnode.cpp sharedcommentnode.h
- singleton.h
- tagfilewriter.cpp tagfilewriter.h
- text.cpp text.h
- tokenizer.cpp tokenizer.h
- topic.h
- tree.cpp tree.h
- typedefnode.cpp typedefnode.h
- usingclause.cpp usingclause.h
- utilities.cpp utilities.h
- variablenode.cpp variablenode.h
- webxmlgenerator.cpp webxmlgenerator.h
- xmlgenerator.cpp xmlgenerator.h
- LIBRARIES # special case
- WrapLibClang::WrapLibClang # special case
- DEFINES
- #(CLANG_RESOURCE_DIR=\"/clang//include\") # special case remove
- CLANG_RESOURCE_DIR=${QT_LIBCLANG_RESOURCE_DIR} # special case
- QDOC2_COMPAT
-)
-qt_internal_return_unless_building_tools()
-
-#### Keys ignored in scope 1:.:.:qdoc.pro:<TRUE>:
-# QMAKE_TARGET_DESCRIPTION = "Qt Documentation Compiler"
-# TR_EXCLUDE = "$$PWD/*"
-# _OPTION = "host_build"
-
-## Scopes:
-#####################################################################
-
-#### Keys ignored in scope 2:.:.:qdoc.pro:NOT force_bootstrap:
-# _REQUIREMENTS = "qtConfig(xmlstreamwriter)"
-
-qt_internal_extend_target(${target_name} CONDITION TARGET Qt::QmlPrivate
- LIBRARIES
- Qt::QmlPrivate
-)
-
-qt_internal_extend_target(${target_name} CONDITION NOT TARGET Qt::QmlPrivate
- DEFINES
- QT_NO_DECLARATIVE
-)
-
-#### Keys ignored in scope 6:.:.:qdoc.pro:NOT QMAKE_DEFAULT_LIBDIRS___contains____ss_CLANG_LIBDIR AND NOT disable_external_rpath:
-# QMAKE_RPATHDIR = "$$CLANG_LIBDIR"
-
-qt_internal_extend_target(${target_name} CONDITION (WIN32 AND ICC) OR MSVC
- LINK_OPTIONS
- "/STACK:4194304"
-)
-qt_internal_add_docs(${target_name}
- doc/config/qdoc.qdocconf
-)
diff --git a/src/qdoc/access.h b/src/qdoc/access.h
deleted file mode 100644
index 668b0239e..000000000
--- a/src/qdoc/access.h
+++ /dev/null
@@ -1,40 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2020 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the tools applications of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include <QtCore/qglobal.h>
-
-#ifndef ACCESS_H
-#define ACCESS_H
-
-QT_BEGIN_NAMESPACE
-
-enum class Access : unsigned char { Public, Protected, Private };
-
-QT_END_NAMESPACE
-
-#endif // ACCESS_H
diff --git a/src/qdoc/catch/CATCH_LICENSE.txt b/src/qdoc/catch/CATCH_LICENSE.txt
new file mode 100644
index 000000000..36b7cd93c
--- /dev/null
+++ b/src/qdoc/catch/CATCH_LICENSE.txt
@@ -0,0 +1,23 @@
+Boost Software License - Version 1.0 - August 17th, 2003
+
+Permission is hereby granted, free of charge, to any person or organization
+obtaining a copy of the software and accompanying documentation covered by
+this license (the "Software") to use, reproduce, display, distribute,
+execute, and transmit the Software, and to prepare derivative works of the
+Software, and to permit third-parties to whom the Software is furnished to
+do so, all subject to the following:
+
+The copyright notices in the Software and this entire statement, including
+the above license grant, this restriction and the following disclaimer,
+must be included in all copies of the Software, in whole or in part, and
+all derivative works of the Software, unless such copies or derivative
+works are solely in the form of machine-executable object code generated by
+a source language processor.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
+SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
+FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
diff --git a/src/qdoc/catch/CMakeLists.txt b/src/qdoc/catch/CMakeLists.txt
new file mode 100644
index 000000000..3f933a1dd
--- /dev/null
+++ b/src/qdoc/catch/CMakeLists.txt
@@ -0,0 +1,9 @@
+qt_internal_add_3rdparty_header_module(QDocCatchPrivate
+ EXTERNAL_HEADERS_DIR include
+)
+
+qt_internal_extend_target(QDocCatchPrivate
+ PUBLIC_INCLUDE_DIRECTORIES
+ $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
+ $<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}/QtQDocCatch>
+)
diff --git a/src/qdoc/catch/include/catch/catch.hpp b/src/qdoc/catch/include/catch/catch.hpp
new file mode 100644
index 000000000..9b309bddc
--- /dev/null
+++ b/src/qdoc/catch/include/catch/catch.hpp
@@ -0,0 +1,17976 @@
+/*
+ * Catch v2.13.10
+ * Generated: 2022-10-16 11:01:23.452308
+ * ----------------------------------------------------------
+ * This file has been merged from multiple headers. Please don't edit it directly
+ * Copyright (c) 2022 Two Blue Cubes Ltd. All rights reserved.
+ *
+ * Distributed under the Boost Software License, Version 1.0. (See accompanying
+ * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+ */
+#ifndef TWOBLUECUBES_SINGLE_INCLUDE_CATCH_HPP_INCLUDED
+#define TWOBLUECUBES_SINGLE_INCLUDE_CATCH_HPP_INCLUDED
+// start catch.hpp
+
+
+#define CATCH_VERSION_MAJOR 2
+#define CATCH_VERSION_MINOR 13
+#define CATCH_VERSION_PATCH 10
+
+#ifdef __clang__
+# pragma clang system_header
+#elif defined __GNUC__
+# pragma GCC system_header
+#endif
+
+// start catch_suppress_warnings.h
+
+#ifdef __clang__
+# ifdef __ICC // icpc defines the __clang__ macro
+# pragma warning(push)
+# pragma warning(disable: 161 1682)
+# else // __ICC
+# pragma clang diagnostic push
+# pragma clang diagnostic ignored "-Wpadded"
+# pragma clang diagnostic ignored "-Wswitch-enum"
+# pragma clang diagnostic ignored "-Wcovered-switch-default"
+# endif
+#elif defined __GNUC__
+ // Because REQUIREs trigger GCC's -Wparentheses, and because still
+ // supported version of g++ have only buggy support for _Pragmas,
+ // Wparentheses have to be suppressed globally.
+# pragma GCC diagnostic ignored "-Wparentheses" // See #674 for details
+
+# pragma GCC diagnostic push
+# pragma GCC diagnostic ignored "-Wunused-variable"
+# pragma GCC diagnostic ignored "-Wpadded"
+#endif
+// end catch_suppress_warnings.h
+#if defined(CATCH_CONFIG_MAIN) || defined(CATCH_CONFIG_RUNNER)
+# define CATCH_IMPL
+# define CATCH_CONFIG_ALL_PARTS
+#endif
+
+// In the impl file, we want to have access to all parts of the headers
+// Can also be used to sanely support PCHs
+#if defined(CATCH_CONFIG_ALL_PARTS)
+# define CATCH_CONFIG_EXTERNAL_INTERFACES
+# if defined(CATCH_CONFIG_DISABLE_MATCHERS)
+# undef CATCH_CONFIG_DISABLE_MATCHERS
+# endif
+# if !defined(CATCH_CONFIG_ENABLE_CHRONO_STRINGMAKER)
+# define CATCH_CONFIG_ENABLE_CHRONO_STRINGMAKER
+# endif
+#endif
+
+#if !defined(CATCH_CONFIG_IMPL_ONLY)
+// start catch_platform.h
+
+// See e.g.:
+// https://opensource.apple.com/source/CarbonHeaders/CarbonHeaders-18.1/TargetConditionals.h.auto.html
+#ifdef __APPLE__
+# include <TargetConditionals.h>
+# if (defined(TARGET_OS_OSX) && TARGET_OS_OSX == 1) || \
+ (defined(TARGET_OS_MAC) && TARGET_OS_MAC == 1)
+# define CATCH_PLATFORM_MAC
+# elif (defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE == 1)
+# define CATCH_PLATFORM_IPHONE
+# endif
+
+#elif defined(linux) || defined(__linux) || defined(__linux__)
+# define CATCH_PLATFORM_LINUX
+
+#elif defined(WIN32) || defined(__WIN32__) || defined(_WIN32) || defined(_MSC_VER) || defined(__MINGW32__)
+# define CATCH_PLATFORM_WINDOWS
+#endif
+
+// end catch_platform.h
+
+#ifdef CATCH_IMPL
+# ifndef CLARA_CONFIG_MAIN
+# define CLARA_CONFIG_MAIN_NOT_DEFINED
+# define CLARA_CONFIG_MAIN
+# endif
+#endif
+
+// start catch_user_interfaces.h
+
+namespace Catch {
+ unsigned int rngSeed();
+}
+
+// end catch_user_interfaces.h
+// start catch_tag_alias_autoregistrar.h
+
+// start catch_common.h
+
+// start catch_compiler_capabilities.h
+
+// Detect a number of compiler features - by compiler
+// The following features are defined:
+//
+// CATCH_CONFIG_COUNTER : is the __COUNTER__ macro supported?
+// CATCH_CONFIG_WINDOWS_SEH : is Windows SEH supported?
+// CATCH_CONFIG_POSIX_SIGNALS : are POSIX signals supported?
+// CATCH_CONFIG_DISABLE_EXCEPTIONS : Are exceptions enabled?
+// ****************
+// Note to maintainers: if new toggles are added please document them
+// in configuration.md, too
+// ****************
+
+// In general each macro has a _NO_<feature name> form
+// (e.g. CATCH_CONFIG_NO_POSIX_SIGNALS) which disables the feature.
+// Many features, at point of detection, define an _INTERNAL_ macro, so they
+// can be combined, en-mass, with the _NO_ forms later.
+
+#ifdef __cplusplus
+
+# if (__cplusplus >= 201402L) || (defined(_MSVC_LANG) && _MSVC_LANG >= 201402L)
+# define CATCH_CPP14_OR_GREATER
+# endif
+
+# if (__cplusplus >= 201703L) || (defined(_MSVC_LANG) && _MSVC_LANG >= 201703L)
+# define CATCH_CPP17_OR_GREATER
+# endif
+
+#endif
+
+// Only GCC compiler should be used in this block, so other compilers trying to
+// mask themselves as GCC should be ignored.
+#if defined(__GNUC__) && !defined(__clang__) && !defined(__ICC) && !defined(__CUDACC__) && !defined(__LCC__)
+# define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION _Pragma( "GCC diagnostic push" )
+# define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION _Pragma( "GCC diagnostic pop" )
+
+# define CATCH_INTERNAL_IGNORE_BUT_WARN(...) (void)__builtin_constant_p(__VA_ARGS__)
+
+#endif
+
+#if defined(__clang__)
+
+# define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION _Pragma( "clang diagnostic push" )
+# define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION _Pragma( "clang diagnostic pop" )
+
+// As of this writing, IBM XL's implementation of __builtin_constant_p has a bug
+// which results in calls to destructors being emitted for each temporary,
+// without a matching initialization. In practice, this can result in something
+// like `std::string::~string` being called on an uninitialized value.
+//
+// For example, this code will likely segfault under IBM XL:
+// ```
+// REQUIRE(std::string("12") + "34" == "1234")
+// ```
+//
+// Therefore, `CATCH_INTERNAL_IGNORE_BUT_WARN` is not implemented.
+# if !defined(__ibmxl__) && !defined(__CUDACC__)
+# define CATCH_INTERNAL_IGNORE_BUT_WARN(...) (void)__builtin_constant_p(__VA_ARGS__) /* NOLINT(cppcoreguidelines-pro-type-vararg, hicpp-vararg) */
+# endif
+
+# define CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
+ _Pragma( "clang diagnostic ignored \"-Wexit-time-destructors\"" ) \
+ _Pragma( "clang diagnostic ignored \"-Wglobal-constructors\"")
+
+# define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS \
+ _Pragma( "clang diagnostic ignored \"-Wparentheses\"" )
+
+# define CATCH_INTERNAL_SUPPRESS_UNUSED_WARNINGS \
+ _Pragma( "clang diagnostic ignored \"-Wunused-variable\"" )
+
+# define CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS \
+ _Pragma( "clang diagnostic ignored \"-Wgnu-zero-variadic-macro-arguments\"" )
+
+# define CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS \
+ _Pragma( "clang diagnostic ignored \"-Wunused-template\"" )
+
+#endif // __clang__
+
+////////////////////////////////////////////////////////////////////////////////
+// Assume that non-Windows platforms support posix signals by default
+#if !defined(CATCH_PLATFORM_WINDOWS)
+ #define CATCH_INTERNAL_CONFIG_POSIX_SIGNALS
+#endif
+
+////////////////////////////////////////////////////////////////////////////////
+// We know some environments not to support full POSIX signals
+#if defined(__CYGWIN__) || defined(__QNX__) || defined(__EMSCRIPTEN__) || defined(__DJGPP__)
+ #define CATCH_INTERNAL_CONFIG_NO_POSIX_SIGNALS
+#endif
+
+#ifdef __OS400__
+# define CATCH_INTERNAL_CONFIG_NO_POSIX_SIGNALS
+# define CATCH_CONFIG_COLOUR_NONE
+#endif
+
+////////////////////////////////////////////////////////////////////////////////
+// Android somehow still does not support std::to_string
+#if defined(__ANDROID__)
+# define CATCH_INTERNAL_CONFIG_NO_CPP11_TO_STRING
+# define CATCH_INTERNAL_CONFIG_ANDROID_LOGWRITE
+#endif
+
+////////////////////////////////////////////////////////////////////////////////
+// Not all Windows environments support SEH properly
+#if defined(__MINGW32__)
+# define CATCH_INTERNAL_CONFIG_NO_WINDOWS_SEH
+#endif
+
+////////////////////////////////////////////////////////////////////////////////
+// PS4
+#if defined(__ORBIS__)
+# define CATCH_INTERNAL_CONFIG_NO_NEW_CAPTURE
+#endif
+
+////////////////////////////////////////////////////////////////////////////////
+// Cygwin
+#ifdef __CYGWIN__
+
+// Required for some versions of Cygwin to declare gettimeofday
+// see: http://stackoverflow.com/questions/36901803/gettimeofday-not-declared-in-this-scope-cygwin
+# define _BSD_SOURCE
+// some versions of cygwin (most) do not support std::to_string. Use the libstd check.
+// https://gcc.gnu.org/onlinedocs/gcc-4.8.2/libstdc++/api/a01053_source.html line 2812-2813
+# if !((__cplusplus >= 201103L) && defined(_GLIBCXX_USE_C99) \
+ && !defined(_GLIBCXX_HAVE_BROKEN_VSWPRINTF))
+
+# define CATCH_INTERNAL_CONFIG_NO_CPP11_TO_STRING
+
+# endif
+#endif // __CYGWIN__
+
+////////////////////////////////////////////////////////////////////////////////
+// Visual C++
+#if defined(_MSC_VER)
+
+// Universal Windows platform does not support SEH
+// Or console colours (or console at all...)
+# if defined(WINAPI_FAMILY) && (WINAPI_FAMILY == WINAPI_FAMILY_APP)
+# define CATCH_CONFIG_COLOUR_NONE
+# else
+# define CATCH_INTERNAL_CONFIG_WINDOWS_SEH
+# endif
+
+# if !defined(__clang__) // Handle Clang masquerading for msvc
+
+// MSVC traditional preprocessor needs some workaround for __VA_ARGS__
+// _MSVC_TRADITIONAL == 0 means new conformant preprocessor
+// _MSVC_TRADITIONAL == 1 means old traditional non-conformant preprocessor
+# if !defined(_MSVC_TRADITIONAL) || (defined(_MSVC_TRADITIONAL) && _MSVC_TRADITIONAL)
+# define CATCH_INTERNAL_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
+# endif // MSVC_TRADITIONAL
+
+// Only do this if we're not using clang on Windows, which uses `diagnostic push` & `diagnostic pop`
+# define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION __pragma( warning(push) )
+# define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION __pragma( warning(pop) )
+# endif // __clang__
+
+#endif // _MSC_VER
+
+#if defined(_REENTRANT) || defined(_MSC_VER)
+// Enable async processing, as -pthread is specified or no additional linking is required
+# define CATCH_INTERNAL_CONFIG_USE_ASYNC
+#endif // _MSC_VER
+
+////////////////////////////////////////////////////////////////////////////////
+// Check if we are compiled with -fno-exceptions or equivalent
+#if defined(__EXCEPTIONS) || defined(__cpp_exceptions) || defined(_CPPUNWIND)
+# define CATCH_INTERNAL_CONFIG_EXCEPTIONS_ENABLED
+#endif
+
+////////////////////////////////////////////////////////////////////////////////
+// DJGPP
+#ifdef __DJGPP__
+# define CATCH_INTERNAL_CONFIG_NO_WCHAR
+#endif // __DJGPP__
+
+////////////////////////////////////////////////////////////////////////////////
+// Embarcadero C++Build
+#if defined(__BORLANDC__)
+ #define CATCH_INTERNAL_CONFIG_POLYFILL_ISNAN
+#endif
+
+////////////////////////////////////////////////////////////////////////////////
+
+// Use of __COUNTER__ is suppressed during code analysis in
+// CLion/AppCode 2017.2.x and former, because __COUNTER__ is not properly
+// handled by it.
+// Otherwise all supported compilers support COUNTER macro,
+// but user still might want to turn it off
+#if ( !defined(__JETBRAINS_IDE__) || __JETBRAINS_IDE__ >= 20170300L )
+ #define CATCH_INTERNAL_CONFIG_COUNTER
+#endif
+
+////////////////////////////////////////////////////////////////////////////////
+
+// RTX is a special version of Windows that is real time.
+// This means that it is detected as Windows, but does not provide
+// the same set of capabilities as real Windows does.
+#if defined(UNDER_RTSS) || defined(RTX64_BUILD)
+ #define CATCH_INTERNAL_CONFIG_NO_WINDOWS_SEH
+ #define CATCH_INTERNAL_CONFIG_NO_ASYNC
+ #define CATCH_CONFIG_COLOUR_NONE
+#endif
+
+#if !defined(_GLIBCXX_USE_C99_MATH_TR1)
+#define CATCH_INTERNAL_CONFIG_GLOBAL_NEXTAFTER
+#endif
+
+// Various stdlib support checks that require __has_include
+#if defined(__has_include)
+ // Check if string_view is available and usable
+ #if __has_include(<string_view>) && defined(CATCH_CPP17_OR_GREATER)
+ # define CATCH_INTERNAL_CONFIG_CPP17_STRING_VIEW
+ #endif
+
+ // Check if optional is available and usable
+ # if __has_include(<optional>) && defined(CATCH_CPP17_OR_GREATER)
+ # define CATCH_INTERNAL_CONFIG_CPP17_OPTIONAL
+ # endif // __has_include(<optional>) && defined(CATCH_CPP17_OR_GREATER)
+
+ // Check if byte is available and usable
+ # if __has_include(<cstddef>) && defined(CATCH_CPP17_OR_GREATER)
+ # include <cstddef>
+ # if defined(__cpp_lib_byte) && (__cpp_lib_byte > 0)
+ # define CATCH_INTERNAL_CONFIG_CPP17_BYTE
+ # endif
+ # endif // __has_include(<cstddef>) && defined(CATCH_CPP17_OR_GREATER)
+
+ // Check if variant is available and usable
+ # if __has_include(<variant>) && defined(CATCH_CPP17_OR_GREATER)
+ # if defined(__clang__) && (__clang_major__ < 8)
+ // work around clang bug with libstdc++ https://bugs.llvm.org/show_bug.cgi?id=31852
+ // fix should be in clang 8, workaround in libstdc++ 8.2
+ # include <ciso646>
+ # if defined(__GLIBCXX__) && defined(_GLIBCXX_RELEASE) && (_GLIBCXX_RELEASE < 9)
+ # define CATCH_CONFIG_NO_CPP17_VARIANT
+ # else
+ # define CATCH_INTERNAL_CONFIG_CPP17_VARIANT
+ # endif // defined(__GLIBCXX__) && defined(_GLIBCXX_RELEASE) && (_GLIBCXX_RELEASE < 9)
+ # else
+ # define CATCH_INTERNAL_CONFIG_CPP17_VARIANT
+ # endif // defined(__clang__) && (__clang_major__ < 8)
+ # endif // __has_include(<variant>) && defined(CATCH_CPP17_OR_GREATER)
+#endif // defined(__has_include)
+
+#if defined(CATCH_INTERNAL_CONFIG_COUNTER) && !defined(CATCH_CONFIG_NO_COUNTER) && !defined(CATCH_CONFIG_COUNTER)
+# define CATCH_CONFIG_COUNTER
+#endif
+#if defined(CATCH_INTERNAL_CONFIG_WINDOWS_SEH) && !defined(CATCH_CONFIG_NO_WINDOWS_SEH) && !defined(CATCH_CONFIG_WINDOWS_SEH) && !defined(CATCH_INTERNAL_CONFIG_NO_WINDOWS_SEH)
+# define CATCH_CONFIG_WINDOWS_SEH
+#endif
+// This is set by default, because we assume that unix compilers are posix-signal-compatible by default.
+#if defined(CATCH_INTERNAL_CONFIG_POSIX_SIGNALS) && !defined(CATCH_INTERNAL_CONFIG_NO_POSIX_SIGNALS) && !defined(CATCH_CONFIG_NO_POSIX_SIGNALS) && !defined(CATCH_CONFIG_POSIX_SIGNALS)
+# define CATCH_CONFIG_POSIX_SIGNALS
+#endif
+// This is set by default, because we assume that compilers with no wchar_t support are just rare exceptions.
+#if !defined(CATCH_INTERNAL_CONFIG_NO_WCHAR) && !defined(CATCH_CONFIG_NO_WCHAR) && !defined(CATCH_CONFIG_WCHAR)
+# define CATCH_CONFIG_WCHAR
+#endif
+
+#if !defined(CATCH_INTERNAL_CONFIG_NO_CPP11_TO_STRING) && !defined(CATCH_CONFIG_NO_CPP11_TO_STRING) && !defined(CATCH_CONFIG_CPP11_TO_STRING)
+# define CATCH_CONFIG_CPP11_TO_STRING
+#endif
+
+#if defined(CATCH_INTERNAL_CONFIG_CPP17_OPTIONAL) && !defined(CATCH_CONFIG_NO_CPP17_OPTIONAL) && !defined(CATCH_CONFIG_CPP17_OPTIONAL)
+# define CATCH_CONFIG_CPP17_OPTIONAL
+#endif
+
+#if defined(CATCH_INTERNAL_CONFIG_CPP17_STRING_VIEW) && !defined(CATCH_CONFIG_NO_CPP17_STRING_VIEW) && !defined(CATCH_CONFIG_CPP17_STRING_VIEW)
+# define CATCH_CONFIG_CPP17_STRING_VIEW
+#endif
+
+#if defined(CATCH_INTERNAL_CONFIG_CPP17_VARIANT) && !defined(CATCH_CONFIG_NO_CPP17_VARIANT) && !defined(CATCH_CONFIG_CPP17_VARIANT)
+# define CATCH_CONFIG_CPP17_VARIANT
+#endif
+
+#if defined(CATCH_INTERNAL_CONFIG_CPP17_BYTE) && !defined(CATCH_CONFIG_NO_CPP17_BYTE) && !defined(CATCH_CONFIG_CPP17_BYTE)
+# define CATCH_CONFIG_CPP17_BYTE
+#endif
+
+#if defined(CATCH_CONFIG_EXPERIMENTAL_REDIRECT)
+# define CATCH_INTERNAL_CONFIG_NEW_CAPTURE
+#endif
+
+#if defined(CATCH_INTERNAL_CONFIG_NEW_CAPTURE) && !defined(CATCH_INTERNAL_CONFIG_NO_NEW_CAPTURE) && !defined(CATCH_CONFIG_NO_NEW_CAPTURE) && !defined(CATCH_CONFIG_NEW_CAPTURE)
+# define CATCH_CONFIG_NEW_CAPTURE
+#endif
+
+#if !defined(CATCH_INTERNAL_CONFIG_EXCEPTIONS_ENABLED) && !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS)
+# define CATCH_CONFIG_DISABLE_EXCEPTIONS
+#endif
+
+#if defined(CATCH_INTERNAL_CONFIG_POLYFILL_ISNAN) && !defined(CATCH_CONFIG_NO_POLYFILL_ISNAN) && !defined(CATCH_CONFIG_POLYFILL_ISNAN)
+# define CATCH_CONFIG_POLYFILL_ISNAN
+#endif
+
+#if defined(CATCH_INTERNAL_CONFIG_USE_ASYNC) && !defined(CATCH_INTERNAL_CONFIG_NO_ASYNC) && !defined(CATCH_CONFIG_NO_USE_ASYNC) && !defined(CATCH_CONFIG_USE_ASYNC)
+# define CATCH_CONFIG_USE_ASYNC
+#endif
+
+#if defined(CATCH_INTERNAL_CONFIG_ANDROID_LOGWRITE) && !defined(CATCH_CONFIG_NO_ANDROID_LOGWRITE) && !defined(CATCH_CONFIG_ANDROID_LOGWRITE)
+# define CATCH_CONFIG_ANDROID_LOGWRITE
+#endif
+
+#if defined(CATCH_INTERNAL_CONFIG_GLOBAL_NEXTAFTER) && !defined(CATCH_CONFIG_NO_GLOBAL_NEXTAFTER) && !defined(CATCH_CONFIG_GLOBAL_NEXTAFTER)
+# define CATCH_CONFIG_GLOBAL_NEXTAFTER
+#endif
+
+// Even if we do not think the compiler has that warning, we still have
+// to provide a macro that can be used by the code.
+#if !defined(CATCH_INTERNAL_START_WARNINGS_SUPPRESSION)
+# define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION
+#endif
+#if !defined(CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION)
+# define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION
+#endif
+#if !defined(CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS)
+# define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS
+#endif
+#if !defined(CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS)
+# define CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS
+#endif
+#if !defined(CATCH_INTERNAL_SUPPRESS_UNUSED_WARNINGS)
+# define CATCH_INTERNAL_SUPPRESS_UNUSED_WARNINGS
+#endif
+#if !defined(CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS)
+# define CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS
+#endif
+
+// The goal of this macro is to avoid evaluation of the arguments, but
+// still have the compiler warn on problems inside...
+#if !defined(CATCH_INTERNAL_IGNORE_BUT_WARN)
+# define CATCH_INTERNAL_IGNORE_BUT_WARN(...)
+#endif
+
+#if defined(__APPLE__) && defined(__apple_build_version__) && (__clang_major__ < 10)
+# undef CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS
+#elif defined(__clang__) && (__clang_major__ < 5)
+# undef CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS
+#endif
+
+#if !defined(CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS)
+# define CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS
+#endif
+
+#if defined(CATCH_CONFIG_DISABLE_EXCEPTIONS)
+#define CATCH_TRY if ((true))
+#define CATCH_CATCH_ALL if ((false))
+#define CATCH_CATCH_ANON(type) if ((false))
+#else
+#define CATCH_TRY try
+#define CATCH_CATCH_ALL catch (...)
+#define CATCH_CATCH_ANON(type) catch (type)
+#endif
+
+#if defined(CATCH_INTERNAL_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR) && !defined(CATCH_CONFIG_NO_TRADITIONAL_MSVC_PREPROCESSOR) && !defined(CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR)
+#define CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
+#endif
+
+// end catch_compiler_capabilities.h
+#define INTERNAL_CATCH_UNIQUE_NAME_LINE2( name, line ) name##line
+#define INTERNAL_CATCH_UNIQUE_NAME_LINE( name, line ) INTERNAL_CATCH_UNIQUE_NAME_LINE2( name, line )
+#ifdef CATCH_CONFIG_COUNTER
+# define INTERNAL_CATCH_UNIQUE_NAME( name ) INTERNAL_CATCH_UNIQUE_NAME_LINE( name, __COUNTER__ )
+#else
+# define INTERNAL_CATCH_UNIQUE_NAME( name ) INTERNAL_CATCH_UNIQUE_NAME_LINE( name, __LINE__ )
+#endif
+
+#include <iosfwd>
+#include <string>
+#include <cstdint>
+
+// We need a dummy global operator<< so we can bring it into Catch namespace later
+struct Catch_global_namespace_dummy {};
+std::ostream& operator<<(std::ostream&, Catch_global_namespace_dummy);
+
+namespace Catch {
+
+ struct CaseSensitive { enum Choice {
+ Yes,
+ No
+ }; };
+
+ class NonCopyable {
+ NonCopyable( NonCopyable const& ) = delete;
+ NonCopyable( NonCopyable && ) = delete;
+ NonCopyable& operator = ( NonCopyable const& ) = delete;
+ NonCopyable& operator = ( NonCopyable && ) = delete;
+
+ protected:
+ NonCopyable();
+ virtual ~NonCopyable();
+ };
+
+ struct SourceLineInfo {
+
+ SourceLineInfo() = delete;
+ SourceLineInfo( char const* _file, std::size_t _line ) noexcept
+ : file( _file ),
+ line( _line )
+ {}
+
+ SourceLineInfo( SourceLineInfo const& other ) = default;
+ SourceLineInfo& operator = ( SourceLineInfo const& ) = default;
+ SourceLineInfo( SourceLineInfo&& ) noexcept = default;
+ SourceLineInfo& operator = ( SourceLineInfo&& ) noexcept = default;
+
+ bool empty() const noexcept { return file[0] == '\0'; }
+ bool operator == ( SourceLineInfo const& other ) const noexcept;
+ bool operator < ( SourceLineInfo const& other ) const noexcept;
+
+ char const* file;
+ std::size_t line;
+ };
+
+ std::ostream& operator << ( std::ostream& os, SourceLineInfo const& info );
+
+ // Bring in operator<< from global namespace into Catch namespace
+ // This is necessary because the overload of operator<< above makes
+ // lookup stop at namespace Catch
+ using ::operator<<;
+
+ // Use this in variadic streaming macros to allow
+ // >> +StreamEndStop
+ // as well as
+ // >> stuff +StreamEndStop
+ struct StreamEndStop {
+ std::string operator+() const;
+ };
+ template<typename T>
+ T const& operator + ( T const& value, StreamEndStop ) {
+ return value;
+ }
+}
+
+#define CATCH_INTERNAL_LINEINFO \
+ ::Catch::SourceLineInfo( __FILE__, static_cast<std::size_t>( __LINE__ ) )
+
+// end catch_common.h
+namespace Catch {
+
+ struct RegistrarForTagAliases {
+ RegistrarForTagAliases( char const* alias, char const* tag, SourceLineInfo const& lineInfo );
+ };
+
+} // end namespace Catch
+
+#define CATCH_REGISTER_TAG_ALIAS( alias, spec ) \
+ CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
+ CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
+ namespace{ Catch::RegistrarForTagAliases INTERNAL_CATCH_UNIQUE_NAME( AutoRegisterTagAlias )( alias, spec, CATCH_INTERNAL_LINEINFO ); } \
+ CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION
+
+// end catch_tag_alias_autoregistrar.h
+// start catch_test_registry.h
+
+// start catch_interfaces_testcase.h
+
+#include <vector>
+
+namespace Catch {
+
+ class TestSpec;
+
+ struct ITestInvoker {
+ virtual void invoke () const = 0;
+ virtual ~ITestInvoker();
+ };
+
+ class TestCase;
+ struct IConfig;
+
+ struct ITestCaseRegistry {
+ virtual ~ITestCaseRegistry();
+ virtual std::vector<TestCase> const& getAllTests() const = 0;
+ virtual std::vector<TestCase> const& getAllTestsSorted( IConfig const& config ) const = 0;
+ };
+
+ bool isThrowSafe( TestCase const& testCase, IConfig const& config );
+ bool matchTest( TestCase const& testCase, TestSpec const& testSpec, IConfig const& config );
+ std::vector<TestCase> filterTests( std::vector<TestCase> const& testCases, TestSpec const& testSpec, IConfig const& config );
+ std::vector<TestCase> const& getAllTestCasesSorted( IConfig const& config );
+
+}
+
+// end catch_interfaces_testcase.h
+// start catch_stringref.h
+
+#include <cstddef>
+#include <string>
+#include <iosfwd>
+#include <cassert>
+
+namespace Catch {
+
+ /// A non-owning string class (similar to the forthcoming std::string_view)
+ /// Note that, because a StringRef may be a substring of another string,
+ /// it may not be null terminated.
+ class StringRef {
+ public:
+ using size_type = std::size_t;
+ using const_iterator = const char*;
+
+ private:
+ static constexpr char const* const s_empty = "";
+
+ char const* m_start = s_empty;
+ size_type m_size = 0;
+
+ public: // construction
+ constexpr StringRef() noexcept = default;
+
+ StringRef( char const* rawChars ) noexcept;
+
+ constexpr StringRef( char const* rawChars, size_type size ) noexcept
+ : m_start( rawChars ),
+ m_size( size )
+ {}
+
+ StringRef( std::string const& stdString ) noexcept
+ : m_start( stdString.c_str() ),
+ m_size( stdString.size() )
+ {}
+
+ explicit operator std::string() const {
+ return std::string(m_start, m_size);
+ }
+
+ public: // operators
+ auto operator == ( StringRef const& other ) const noexcept -> bool;
+ auto operator != (StringRef const& other) const noexcept -> bool {
+ return !(*this == other);
+ }
+
+ auto operator[] ( size_type index ) const noexcept -> char {
+ assert(index < m_size);
+ return m_start[index];
+ }
+
+ public: // named queries
+ constexpr auto empty() const noexcept -> bool {
+ return m_size == 0;
+ }
+ constexpr auto size() const noexcept -> size_type {
+ return m_size;
+ }
+
+ // Returns the current start pointer. If the StringRef is not
+ // null-terminated, throws std::domain_exception
+ auto c_str() const -> char const*;
+
+ public: // substrings and searches
+ // Returns a substring of [start, start + length).
+ // If start + length > size(), then the substring is [start, size()).
+ // If start > size(), then the substring is empty.
+ auto substr( size_type start, size_type length ) const noexcept -> StringRef;
+
+ // Returns the current start pointer. May not be null-terminated.
+ auto data() const noexcept -> char const*;
+
+ constexpr auto isNullTerminated() const noexcept -> bool {
+ return m_start[m_size] == '\0';
+ }
+
+ public: // iterators
+ constexpr const_iterator begin() const { return m_start; }
+ constexpr const_iterator end() const { return m_start + m_size; }
+ };
+
+ auto operator += ( std::string& lhs, StringRef const& sr ) -> std::string&;
+ auto operator << ( std::ostream& os, StringRef const& sr ) -> std::ostream&;
+
+ constexpr auto operator "" _sr( char const* rawChars, std::size_t size ) noexcept -> StringRef {
+ return StringRef( rawChars, size );
+ }
+} // namespace Catch
+
+constexpr auto operator "" _catch_sr( char const* rawChars, std::size_t size ) noexcept -> Catch::StringRef {
+ return Catch::StringRef( rawChars, size );
+}
+
+// end catch_stringref.h
+// start catch_preprocessor.hpp
+
+
+#define CATCH_RECURSION_LEVEL0(...) __VA_ARGS__
+#define CATCH_RECURSION_LEVEL1(...) CATCH_RECURSION_LEVEL0(CATCH_RECURSION_LEVEL0(CATCH_RECURSION_LEVEL0(__VA_ARGS__)))
+#define CATCH_RECURSION_LEVEL2(...) CATCH_RECURSION_LEVEL1(CATCH_RECURSION_LEVEL1(CATCH_RECURSION_LEVEL1(__VA_ARGS__)))
+#define CATCH_RECURSION_LEVEL3(...) CATCH_RECURSION_LEVEL2(CATCH_RECURSION_LEVEL2(CATCH_RECURSION_LEVEL2(__VA_ARGS__)))
+#define CATCH_RECURSION_LEVEL4(...) CATCH_RECURSION_LEVEL3(CATCH_RECURSION_LEVEL3(CATCH_RECURSION_LEVEL3(__VA_ARGS__)))
+#define CATCH_RECURSION_LEVEL5(...) CATCH_RECURSION_LEVEL4(CATCH_RECURSION_LEVEL4(CATCH_RECURSION_LEVEL4(__VA_ARGS__)))
+
+#ifdef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
+#define INTERNAL_CATCH_EXPAND_VARGS(...) __VA_ARGS__
+// MSVC needs more evaluations
+#define CATCH_RECURSION_LEVEL6(...) CATCH_RECURSION_LEVEL5(CATCH_RECURSION_LEVEL5(CATCH_RECURSION_LEVEL5(__VA_ARGS__)))
+#define CATCH_RECURSE(...) CATCH_RECURSION_LEVEL6(CATCH_RECURSION_LEVEL6(__VA_ARGS__))
+#else
+#define CATCH_RECURSE(...) CATCH_RECURSION_LEVEL5(__VA_ARGS__)
+#endif
+
+#define CATCH_REC_END(...)
+#define CATCH_REC_OUT
+
+#define CATCH_EMPTY()
+#define CATCH_DEFER(id) id CATCH_EMPTY()
+
+#define CATCH_REC_GET_END2() 0, CATCH_REC_END
+#define CATCH_REC_GET_END1(...) CATCH_REC_GET_END2
+#define CATCH_REC_GET_END(...) CATCH_REC_GET_END1
+#define CATCH_REC_NEXT0(test, next, ...) next CATCH_REC_OUT
+#define CATCH_REC_NEXT1(test, next) CATCH_DEFER ( CATCH_REC_NEXT0 ) ( test, next, 0)
+#define CATCH_REC_NEXT(test, next) CATCH_REC_NEXT1(CATCH_REC_GET_END test, next)
+
+#define CATCH_REC_LIST0(f, x, peek, ...) , f(x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST1) ) ( f, peek, __VA_ARGS__ )
+#define CATCH_REC_LIST1(f, x, peek, ...) , f(x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST0) ) ( f, peek, __VA_ARGS__ )
+#define CATCH_REC_LIST2(f, x, peek, ...) f(x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST1) ) ( f, peek, __VA_ARGS__ )
+
+#define CATCH_REC_LIST0_UD(f, userdata, x, peek, ...) , f(userdata, x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST1_UD) ) ( f, userdata, peek, __VA_ARGS__ )
+#define CATCH_REC_LIST1_UD(f, userdata, x, peek, ...) , f(userdata, x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST0_UD) ) ( f, userdata, peek, __VA_ARGS__ )
+#define CATCH_REC_LIST2_UD(f, userdata, x, peek, ...) f(userdata, x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST1_UD) ) ( f, userdata, peek, __VA_ARGS__ )
+
+// Applies the function macro `f` to each of the remaining parameters, inserts commas between the results,
+// and passes userdata as the first parameter to each invocation,
+// e.g. CATCH_REC_LIST_UD(f, x, a, b, c) evaluates to f(x, a), f(x, b), f(x, c)
+#define CATCH_REC_LIST_UD(f, userdata, ...) CATCH_RECURSE(CATCH_REC_LIST2_UD(f, userdata, __VA_ARGS__, ()()(), ()()(), ()()(), 0))
+
+#define CATCH_REC_LIST(f, ...) CATCH_RECURSE(CATCH_REC_LIST2(f, __VA_ARGS__, ()()(), ()()(), ()()(), 0))
+
+#define INTERNAL_CATCH_EXPAND1(param) INTERNAL_CATCH_EXPAND2(param)
+#define INTERNAL_CATCH_EXPAND2(...) INTERNAL_CATCH_NO## __VA_ARGS__
+#define INTERNAL_CATCH_DEF(...) INTERNAL_CATCH_DEF __VA_ARGS__
+#define INTERNAL_CATCH_NOINTERNAL_CATCH_DEF
+#define INTERNAL_CATCH_STRINGIZE(...) INTERNAL_CATCH_STRINGIZE2(__VA_ARGS__)
+#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
+#define INTERNAL_CATCH_STRINGIZE2(...) #__VA_ARGS__
+#define INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS(param) INTERNAL_CATCH_STRINGIZE(INTERNAL_CATCH_REMOVE_PARENS(param))
+#else
+// MSVC is adding extra space and needs another indirection to expand INTERNAL_CATCH_NOINTERNAL_CATCH_DEF
+#define INTERNAL_CATCH_STRINGIZE2(...) INTERNAL_CATCH_STRINGIZE3(__VA_ARGS__)
+#define INTERNAL_CATCH_STRINGIZE3(...) #__VA_ARGS__
+#define INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS(param) (INTERNAL_CATCH_STRINGIZE(INTERNAL_CATCH_REMOVE_PARENS(param)) + 1)
+#endif
+
+#define INTERNAL_CATCH_MAKE_NAMESPACE2(...) ns_##__VA_ARGS__
+#define INTERNAL_CATCH_MAKE_NAMESPACE(name) INTERNAL_CATCH_MAKE_NAMESPACE2(name)
+
+#define INTERNAL_CATCH_REMOVE_PARENS(...) INTERNAL_CATCH_EXPAND1(INTERNAL_CATCH_DEF __VA_ARGS__)
+
+#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
+#define INTERNAL_CATCH_MAKE_TYPE_LIST2(...) decltype(get_wrapper<INTERNAL_CATCH_REMOVE_PARENS_GEN(__VA_ARGS__)>())
+#define INTERNAL_CATCH_MAKE_TYPE_LIST(...) INTERNAL_CATCH_MAKE_TYPE_LIST2(INTERNAL_CATCH_REMOVE_PARENS(__VA_ARGS__))
+#else
+#define INTERNAL_CATCH_MAKE_TYPE_LIST2(...) INTERNAL_CATCH_EXPAND_VARGS(decltype(get_wrapper<INTERNAL_CATCH_REMOVE_PARENS_GEN(__VA_ARGS__)>()))
+#define INTERNAL_CATCH_MAKE_TYPE_LIST(...) INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_MAKE_TYPE_LIST2(INTERNAL_CATCH_REMOVE_PARENS(__VA_ARGS__)))
+#endif
+
+#define INTERNAL_CATCH_MAKE_TYPE_LISTS_FROM_TYPES(...)\
+ CATCH_REC_LIST(INTERNAL_CATCH_MAKE_TYPE_LIST,__VA_ARGS__)
+
+#define INTERNAL_CATCH_REMOVE_PARENS_1_ARG(_0) INTERNAL_CATCH_REMOVE_PARENS(_0)
+#define INTERNAL_CATCH_REMOVE_PARENS_2_ARG(_0, _1) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_1_ARG(_1)
+#define INTERNAL_CATCH_REMOVE_PARENS_3_ARG(_0, _1, _2) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_2_ARG(_1, _2)
+#define INTERNAL_CATCH_REMOVE_PARENS_4_ARG(_0, _1, _2, _3) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_3_ARG(_1, _2, _3)
+#define INTERNAL_CATCH_REMOVE_PARENS_5_ARG(_0, _1, _2, _3, _4) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_4_ARG(_1, _2, _3, _4)
+#define INTERNAL_CATCH_REMOVE_PARENS_6_ARG(_0, _1, _2, _3, _4, _5) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_5_ARG(_1, _2, _3, _4, _5)
+#define INTERNAL_CATCH_REMOVE_PARENS_7_ARG(_0, _1, _2, _3, _4, _5, _6) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_6_ARG(_1, _2, _3, _4, _5, _6)
+#define INTERNAL_CATCH_REMOVE_PARENS_8_ARG(_0, _1, _2, _3, _4, _5, _6, _7) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_7_ARG(_1, _2, _3, _4, _5, _6, _7)
+#define INTERNAL_CATCH_REMOVE_PARENS_9_ARG(_0, _1, _2, _3, _4, _5, _6, _7, _8) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_8_ARG(_1, _2, _3, _4, _5, _6, _7, _8)
+#define INTERNAL_CATCH_REMOVE_PARENS_10_ARG(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_9_ARG(_1, _2, _3, _4, _5, _6, _7, _8, _9)
+#define INTERNAL_CATCH_REMOVE_PARENS_11_ARG(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_10_ARG(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10)
+
+#define INTERNAL_CATCH_VA_NARGS_IMPL(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, N, ...) N
+
+#define INTERNAL_CATCH_TYPE_GEN\
+ template<typename...> struct TypeList {};\
+ template<typename...Ts>\
+ constexpr auto get_wrapper() noexcept -> TypeList<Ts...> { return {}; }\
+ template<template<typename...> class...> struct TemplateTypeList{};\
+ template<template<typename...> class...Cs>\
+ constexpr auto get_wrapper() noexcept -> TemplateTypeList<Cs...> { return {}; }\
+ template<typename...>\
+ struct append;\
+ template<typename...>\
+ struct rewrap;\
+ template<template<typename...> class, typename...>\
+ struct create;\
+ template<template<typename...> class, typename>\
+ struct convert;\
+ \
+ template<typename T> \
+ struct append<T> { using type = T; };\
+ template< template<typename...> class L1, typename...E1, template<typename...> class L2, typename...E2, typename...Rest>\
+ struct append<L1<E1...>, L2<E2...>, Rest...> { using type = typename append<L1<E1...,E2...>, Rest...>::type; };\
+ template< template<typename...> class L1, typename...E1, typename...Rest>\
+ struct append<L1<E1...>, TypeList<mpl_::na>, Rest...> { using type = L1<E1...>; };\
+ \
+ template< template<typename...> class Container, template<typename...> class List, typename...elems>\
+ struct rewrap<TemplateTypeList<Container>, List<elems...>> { using type = TypeList<Container<elems...>>; };\
+ template< template<typename...> class Container, template<typename...> class List, class...Elems, typename...Elements>\
+ struct rewrap<TemplateTypeList<Container>, List<Elems...>, Elements...> { using type = typename append<TypeList<Container<Elems...>>, typename rewrap<TemplateTypeList<Container>, Elements...>::type>::type; };\
+ \
+ template<template <typename...> class Final, template< typename...> class...Containers, typename...Types>\
+ struct create<Final, TemplateTypeList<Containers...>, TypeList<Types...>> { using type = typename append<Final<>, typename rewrap<TemplateTypeList<Containers>, Types...>::type...>::type; };\
+ template<template <typename...> class Final, template <typename...> class List, typename...Ts>\
+ struct convert<Final, List<Ts...>> { using type = typename append<Final<>,TypeList<Ts>...>::type; };
+
+#define INTERNAL_CATCH_NTTP_1(signature, ...)\
+ template<INTERNAL_CATCH_REMOVE_PARENS(signature)> struct Nttp{};\
+ template<INTERNAL_CATCH_REMOVE_PARENS(signature)>\
+ constexpr auto get_wrapper() noexcept -> Nttp<__VA_ARGS__> { return {}; } \
+ template<template<INTERNAL_CATCH_REMOVE_PARENS(signature)> class...> struct NttpTemplateTypeList{};\
+ template<template<INTERNAL_CATCH_REMOVE_PARENS(signature)> class...Cs>\
+ constexpr auto get_wrapper() noexcept -> NttpTemplateTypeList<Cs...> { return {}; } \
+ \
+ template< template<INTERNAL_CATCH_REMOVE_PARENS(signature)> class Container, template<INTERNAL_CATCH_REMOVE_PARENS(signature)> class List, INTERNAL_CATCH_REMOVE_PARENS(signature)>\
+ struct rewrap<NttpTemplateTypeList<Container>, List<__VA_ARGS__>> { using type = TypeList<Container<__VA_ARGS__>>; };\
+ template< template<INTERNAL_CATCH_REMOVE_PARENS(signature)> class Container, template<INTERNAL_CATCH_REMOVE_PARENS(signature)> class List, INTERNAL_CATCH_REMOVE_PARENS(signature), typename...Elements>\
+ struct rewrap<NttpTemplateTypeList<Container>, List<__VA_ARGS__>, Elements...> { using type = typename append<TypeList<Container<__VA_ARGS__>>, typename rewrap<NttpTemplateTypeList<Container>, Elements...>::type>::type; };\
+ template<template <typename...> class Final, template<INTERNAL_CATCH_REMOVE_PARENS(signature)> class...Containers, typename...Types>\
+ struct create<Final, NttpTemplateTypeList<Containers...>, TypeList<Types...>> { using type = typename append<Final<>, typename rewrap<NttpTemplateTypeList<Containers>, Types...>::type...>::type; };
+
+#define INTERNAL_CATCH_DECLARE_SIG_TEST0(TestName)
+#define INTERNAL_CATCH_DECLARE_SIG_TEST1(TestName, signature)\
+ template<INTERNAL_CATCH_REMOVE_PARENS(signature)>\
+ static void TestName()
+#define INTERNAL_CATCH_DECLARE_SIG_TEST_X(TestName, signature, ...)\
+ template<INTERNAL_CATCH_REMOVE_PARENS(signature)>\
+ static void TestName()
+
+#define INTERNAL_CATCH_DEFINE_SIG_TEST0(TestName)
+#define INTERNAL_CATCH_DEFINE_SIG_TEST1(TestName, signature)\
+ template<INTERNAL_CATCH_REMOVE_PARENS(signature)>\
+ static void TestName()
+#define INTERNAL_CATCH_DEFINE_SIG_TEST_X(TestName, signature,...)\
+ template<INTERNAL_CATCH_REMOVE_PARENS(signature)>\
+ static void TestName()
+
+#define INTERNAL_CATCH_NTTP_REGISTER0(TestFunc, signature)\
+ template<typename Type>\
+ void reg_test(TypeList<Type>, Catch::NameAndTags nameAndTags)\
+ {\
+ Catch::AutoReg( Catch::makeTestInvoker(&TestFunc<Type>), CATCH_INTERNAL_LINEINFO, Catch::StringRef(), nameAndTags);\
+ }
+
+#define INTERNAL_CATCH_NTTP_REGISTER(TestFunc, signature, ...)\
+ template<INTERNAL_CATCH_REMOVE_PARENS(signature)>\
+ void reg_test(Nttp<__VA_ARGS__>, Catch::NameAndTags nameAndTags)\
+ {\
+ Catch::AutoReg( Catch::makeTestInvoker(&TestFunc<__VA_ARGS__>), CATCH_INTERNAL_LINEINFO, Catch::StringRef(), nameAndTags);\
+ }
+
+#define INTERNAL_CATCH_NTTP_REGISTER_METHOD0(TestName, signature, ...)\
+ template<typename Type>\
+ void reg_test(TypeList<Type>, Catch::StringRef className, Catch::NameAndTags nameAndTags)\
+ {\
+ Catch::AutoReg( Catch::makeTestInvoker(&TestName<Type>::test), CATCH_INTERNAL_LINEINFO, className, nameAndTags);\
+ }
+
+#define INTERNAL_CATCH_NTTP_REGISTER_METHOD(TestName, signature, ...)\
+ template<INTERNAL_CATCH_REMOVE_PARENS(signature)>\
+ void reg_test(Nttp<__VA_ARGS__>, Catch::StringRef className, Catch::NameAndTags nameAndTags)\
+ {\
+ Catch::AutoReg( Catch::makeTestInvoker(&TestName<__VA_ARGS__>::test), CATCH_INTERNAL_LINEINFO, className, nameAndTags);\
+ }
+
+#define INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD0(TestName, ClassName)
+#define INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD1(TestName, ClassName, signature)\
+ template<typename TestType> \
+ struct TestName : INTERNAL_CATCH_REMOVE_PARENS(ClassName)<TestType> { \
+ void test();\
+ }
+
+#define INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X(TestName, ClassName, signature, ...)\
+ template<INTERNAL_CATCH_REMOVE_PARENS(signature)> \
+ struct TestName : INTERNAL_CATCH_REMOVE_PARENS(ClassName)<__VA_ARGS__> { \
+ void test();\
+ }
+
+#define INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD0(TestName)
+#define INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD1(TestName, signature)\
+ template<typename TestType> \
+ void INTERNAL_CATCH_MAKE_NAMESPACE(TestName)::TestName<TestType>::test()
+#define INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X(TestName, signature, ...)\
+ template<INTERNAL_CATCH_REMOVE_PARENS(signature)> \
+ void INTERNAL_CATCH_MAKE_NAMESPACE(TestName)::TestName<__VA_ARGS__>::test()
+
+#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
+#define INTERNAL_CATCH_NTTP_0
+#define INTERNAL_CATCH_NTTP_GEN(...) INTERNAL_CATCH_VA_NARGS_IMPL(__VA_ARGS__, INTERNAL_CATCH_NTTP_1(__VA_ARGS__), INTERNAL_CATCH_NTTP_1(__VA_ARGS__), INTERNAL_CATCH_NTTP_1(__VA_ARGS__), INTERNAL_CATCH_NTTP_1(__VA_ARGS__), INTERNAL_CATCH_NTTP_1(__VA_ARGS__), INTERNAL_CATCH_NTTP_1( __VA_ARGS__), INTERNAL_CATCH_NTTP_1( __VA_ARGS__), INTERNAL_CATCH_NTTP_1( __VA_ARGS__), INTERNAL_CATCH_NTTP_1( __VA_ARGS__),INTERNAL_CATCH_NTTP_1( __VA_ARGS__), INTERNAL_CATCH_NTTP_0)
+#define INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD(TestName, ...) INTERNAL_CATCH_VA_NARGS_IMPL( "dummy", __VA_ARGS__, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X,INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X,INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X,INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD1, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD0)(TestName, __VA_ARGS__)
+#define INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD(TestName, ClassName, ...) INTERNAL_CATCH_VA_NARGS_IMPL( "dummy", __VA_ARGS__, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X,INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X,INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X,INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD1, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD0)(TestName, ClassName, __VA_ARGS__)
+#define INTERNAL_CATCH_NTTP_REG_METHOD_GEN(TestName, ...) INTERNAL_CATCH_VA_NARGS_IMPL( "dummy", __VA_ARGS__, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD0, INTERNAL_CATCH_NTTP_REGISTER_METHOD0)(TestName, __VA_ARGS__)
+#define INTERNAL_CATCH_NTTP_REG_GEN(TestFunc, ...) INTERNAL_CATCH_VA_NARGS_IMPL( "dummy", __VA_ARGS__, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER0, INTERNAL_CATCH_NTTP_REGISTER0)(TestFunc, __VA_ARGS__)
+#define INTERNAL_CATCH_DEFINE_SIG_TEST(TestName, ...) INTERNAL_CATCH_VA_NARGS_IMPL( "dummy", __VA_ARGS__, INTERNAL_CATCH_DEFINE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X,INTERNAL_CATCH_DEFINE_SIG_TEST_X,INTERNAL_CATCH_DEFINE_SIG_TEST1, INTERNAL_CATCH_DEFINE_SIG_TEST0)(TestName, __VA_ARGS__)
+#define INTERNAL_CATCH_DECLARE_SIG_TEST(TestName, ...) INTERNAL_CATCH_VA_NARGS_IMPL( "dummy", __VA_ARGS__, INTERNAL_CATCH_DECLARE_SIG_TEST_X,INTERNAL_CATCH_DECLARE_SIG_TEST_X, INTERNAL_CATCH_DECLARE_SIG_TEST_X, INTERNAL_CATCH_DECLARE_SIG_TEST_X, INTERNAL_CATCH_DECLARE_SIG_TEST_X, INTERNAL_CATCH_DECLARE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X,INTERNAL_CATCH_DECLARE_SIG_TEST_X,INTERNAL_CATCH_DECLARE_SIG_TEST_X, INTERNAL_CATCH_DECLARE_SIG_TEST1, INTERNAL_CATCH_DECLARE_SIG_TEST0)(TestName, __VA_ARGS__)
+#define INTERNAL_CATCH_REMOVE_PARENS_GEN(...) INTERNAL_CATCH_VA_NARGS_IMPL(__VA_ARGS__, INTERNAL_CATCH_REMOVE_PARENS_11_ARG,INTERNAL_CATCH_REMOVE_PARENS_10_ARG,INTERNAL_CATCH_REMOVE_PARENS_9_ARG,INTERNAL_CATCH_REMOVE_PARENS_8_ARG,INTERNAL_CATCH_REMOVE_PARENS_7_ARG,INTERNAL_CATCH_REMOVE_PARENS_6_ARG,INTERNAL_CATCH_REMOVE_PARENS_5_ARG,INTERNAL_CATCH_REMOVE_PARENS_4_ARG,INTERNAL_CATCH_REMOVE_PARENS_3_ARG,INTERNAL_CATCH_REMOVE_PARENS_2_ARG,INTERNAL_CATCH_REMOVE_PARENS_1_ARG)(__VA_ARGS__)
+#else
+#define INTERNAL_CATCH_NTTP_0(signature)
+#define INTERNAL_CATCH_NTTP_GEN(...) INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_VA_NARGS_IMPL(__VA_ARGS__, INTERNAL_CATCH_NTTP_1, INTERNAL_CATCH_NTTP_1, INTERNAL_CATCH_NTTP_1, INTERNAL_CATCH_NTTP_1, INTERNAL_CATCH_NTTP_1, INTERNAL_CATCH_NTTP_1, INTERNAL_CATCH_NTTP_1, INTERNAL_CATCH_NTTP_1, INTERNAL_CATCH_NTTP_1,INTERNAL_CATCH_NTTP_1, INTERNAL_CATCH_NTTP_0)( __VA_ARGS__))
+#define INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD(TestName, ...) INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_VA_NARGS_IMPL( "dummy", __VA_ARGS__, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X,INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X,INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X,INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD1, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD0)(TestName, __VA_ARGS__))
+#define INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD(TestName, ClassName, ...) INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_VA_NARGS_IMPL( "dummy", __VA_ARGS__, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X,INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X,INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X,INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD1, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD0)(TestName, ClassName, __VA_ARGS__))
+#define INTERNAL_CATCH_NTTP_REG_METHOD_GEN(TestName, ...) INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_VA_NARGS_IMPL( "dummy", __VA_ARGS__, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD0, INTERNAL_CATCH_NTTP_REGISTER_METHOD0)(TestName, __VA_ARGS__))
+#define INTERNAL_CATCH_NTTP_REG_GEN(TestFunc, ...) INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_VA_NARGS_IMPL( "dummy", __VA_ARGS__, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER0, INTERNAL_CATCH_NTTP_REGISTER0)(TestFunc, __VA_ARGS__))
+#define INTERNAL_CATCH_DEFINE_SIG_TEST(TestName, ...) INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_VA_NARGS_IMPL( "dummy", __VA_ARGS__, INTERNAL_CATCH_DEFINE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X,INTERNAL_CATCH_DEFINE_SIG_TEST_X,INTERNAL_CATCH_DEFINE_SIG_TEST1, INTERNAL_CATCH_DEFINE_SIG_TEST0)(TestName, __VA_ARGS__))
+#define INTERNAL_CATCH_DECLARE_SIG_TEST(TestName, ...) INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_VA_NARGS_IMPL( "dummy", __VA_ARGS__, INTERNAL_CATCH_DECLARE_SIG_TEST_X,INTERNAL_CATCH_DECLARE_SIG_TEST_X, INTERNAL_CATCH_DECLARE_SIG_TEST_X, INTERNAL_CATCH_DECLARE_SIG_TEST_X, INTERNAL_CATCH_DECLARE_SIG_TEST_X, INTERNAL_CATCH_DECLARE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X,INTERNAL_CATCH_DECLARE_SIG_TEST_X,INTERNAL_CATCH_DECLARE_SIG_TEST_X, INTERNAL_CATCH_DECLARE_SIG_TEST1, INTERNAL_CATCH_DECLARE_SIG_TEST0)(TestName, __VA_ARGS__))
+#define INTERNAL_CATCH_REMOVE_PARENS_GEN(...) INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_VA_NARGS_IMPL(__VA_ARGS__, INTERNAL_CATCH_REMOVE_PARENS_11_ARG,INTERNAL_CATCH_REMOVE_PARENS_10_ARG,INTERNAL_CATCH_REMOVE_PARENS_9_ARG,INTERNAL_CATCH_REMOVE_PARENS_8_ARG,INTERNAL_CATCH_REMOVE_PARENS_7_ARG,INTERNAL_CATCH_REMOVE_PARENS_6_ARG,INTERNAL_CATCH_REMOVE_PARENS_5_ARG,INTERNAL_CATCH_REMOVE_PARENS_4_ARG,INTERNAL_CATCH_REMOVE_PARENS_3_ARG,INTERNAL_CATCH_REMOVE_PARENS_2_ARG,INTERNAL_CATCH_REMOVE_PARENS_1_ARG)(__VA_ARGS__))
+#endif
+
+// end catch_preprocessor.hpp
+// start catch_meta.hpp
+
+
+#include <type_traits>
+
+namespace Catch {
+ template<typename T>
+ struct always_false : std::false_type {};
+
+ template <typename> struct true_given : std::true_type {};
+ struct is_callable_tester {
+ template <typename Fun, typename... Args>
+ true_given<decltype(std::declval<Fun>()(std::declval<Args>()...))> static test(int);
+ template <typename...>
+ std::false_type static test(...);
+ };
+
+ template <typename T>
+ struct is_callable;
+
+ template <typename Fun, typename... Args>
+ struct is_callable<Fun(Args...)> : decltype(is_callable_tester::test<Fun, Args...>(0)) {};
+
+#if defined(__cpp_lib_is_invocable) && __cpp_lib_is_invocable >= 201703
+ // std::result_of is deprecated in C++17 and removed in C++20. Hence, it is
+ // replaced with std::invoke_result here.
+ template <typename Func, typename... U>
+ using FunctionReturnType = std::remove_reference_t<std::remove_cv_t<std::invoke_result_t<Func, U...>>>;
+#else
+ // Keep ::type here because we still support C++11
+ template <typename Func, typename... U>
+ using FunctionReturnType = typename std::remove_reference<typename std::remove_cv<typename std::result_of<Func(U...)>::type>::type>::type;
+#endif
+
+} // namespace Catch
+
+namespace mpl_{
+ struct na;
+}
+
+// end catch_meta.hpp
+namespace Catch {
+
+template<typename C>
+class TestInvokerAsMethod : public ITestInvoker {
+ void (C::*m_testAsMethod)();
+public:
+ TestInvokerAsMethod( void (C::*testAsMethod)() ) noexcept : m_testAsMethod( testAsMethod ) {}
+
+ void invoke() const override {
+ C obj;
+ (obj.*m_testAsMethod)();
+ }
+};
+
+auto makeTestInvoker( void(*testAsFunction)() ) noexcept -> ITestInvoker*;
+
+template<typename C>
+auto makeTestInvoker( void (C::*testAsMethod)() ) noexcept -> ITestInvoker* {
+ return new(std::nothrow) TestInvokerAsMethod<C>( testAsMethod );
+}
+
+struct NameAndTags {
+ NameAndTags( StringRef const& name_ = StringRef(), StringRef const& tags_ = StringRef() ) noexcept;
+ StringRef name;
+ StringRef tags;
+};
+
+struct AutoReg : NonCopyable {
+ AutoReg( ITestInvoker* invoker, SourceLineInfo const& lineInfo, StringRef const& classOrMethod, NameAndTags const& nameAndTags ) noexcept;
+ ~AutoReg();
+};
+
+} // end namespace Catch
+
+#if defined(CATCH_CONFIG_DISABLE)
+ #define INTERNAL_CATCH_TESTCASE_NO_REGISTRATION( TestName, ... ) \
+ static void TestName()
+ #define INTERNAL_CATCH_TESTCASE_METHOD_NO_REGISTRATION( TestName, ClassName, ... ) \
+ namespace{ \
+ struct TestName : INTERNAL_CATCH_REMOVE_PARENS(ClassName) { \
+ void test(); \
+ }; \
+ } \
+ void TestName::test()
+ #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION_2( TestName, TestFunc, Name, Tags, Signature, ... ) \
+ INTERNAL_CATCH_DEFINE_SIG_TEST(TestFunc, INTERNAL_CATCH_REMOVE_PARENS(Signature))
+ #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION_2( TestNameClass, TestName, ClassName, Name, Tags, Signature, ... ) \
+ namespace{ \
+ namespace INTERNAL_CATCH_MAKE_NAMESPACE(TestName) { \
+ INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD(TestName, ClassName, INTERNAL_CATCH_REMOVE_PARENS(Signature));\
+ } \
+ } \
+ INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD(TestName, INTERNAL_CATCH_REMOVE_PARENS(Signature))
+
+ #ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
+ #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION(Name, Tags, ...) \
+ INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION_2( INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_ ), INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_F_U_N_C_ ), Name, Tags, typename TestType, __VA_ARGS__ )
+ #else
+ #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION(Name, Tags, ...) \
+ INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION_2( INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_ ), INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_F_U_N_C_ ), Name, Tags, typename TestType, __VA_ARGS__ ) )
+ #endif
+
+ #ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
+ #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_SIG_NO_REGISTRATION(Name, Tags, Signature, ...) \
+ INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION_2( INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_ ), INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_F_U_N_C_ ), Name, Tags, Signature, __VA_ARGS__ )
+ #else
+ #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_SIG_NO_REGISTRATION(Name, Tags, Signature, ...) \
+ INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION_2( INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_ ), INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_F_U_N_C_ ), Name, Tags, Signature, __VA_ARGS__ ) )
+ #endif
+
+ #ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
+ #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION( ClassName, Name, Tags,... ) \
+ INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION_2( INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_C_L_A_S_S_ ), INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_ ) , ClassName, Name, Tags, typename T, __VA_ARGS__ )
+ #else
+ #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION( ClassName, Name, Tags,... ) \
+ INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION_2( INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_C_L_A_S_S_ ), INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_ ) , ClassName, Name, Tags, typename T, __VA_ARGS__ ) )
+ #endif
+
+ #ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
+ #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_SIG_NO_REGISTRATION( ClassName, Name, Tags, Signature, ... ) \
+ INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION_2( INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_C_L_A_S_S_ ), INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_ ) , ClassName, Name, Tags, Signature, __VA_ARGS__ )
+ #else
+ #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_SIG_NO_REGISTRATION( ClassName, Name, Tags, Signature, ... ) \
+ INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION_2( INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_C_L_A_S_S_ ), INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_ ) , ClassName, Name, Tags, Signature, __VA_ARGS__ ) )
+ #endif
+#endif
+
+ ///////////////////////////////////////////////////////////////////////////////
+ #define INTERNAL_CATCH_TESTCASE2( TestName, ... ) \
+ static void TestName(); \
+ CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
+ CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
+ namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( Catch::makeTestInvoker( &TestName ), CATCH_INTERNAL_LINEINFO, Catch::StringRef(), Catch::NameAndTags{ __VA_ARGS__ } ); } /* NOLINT */ \
+ CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \
+ static void TestName()
+ #define INTERNAL_CATCH_TESTCASE( ... ) \
+ INTERNAL_CATCH_TESTCASE2( INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_S_T_ ), __VA_ARGS__ )
+
+ ///////////////////////////////////////////////////////////////////////////////
+ #define INTERNAL_CATCH_METHOD_AS_TEST_CASE( QualifiedMethod, ... ) \
+ CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
+ CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
+ namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( Catch::makeTestInvoker( &QualifiedMethod ), CATCH_INTERNAL_LINEINFO, "&" #QualifiedMethod, Catch::NameAndTags{ __VA_ARGS__ } ); } /* NOLINT */ \
+ CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION
+
+ ///////////////////////////////////////////////////////////////////////////////
+ #define INTERNAL_CATCH_TEST_CASE_METHOD2( TestName, ClassName, ... )\
+ CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
+ CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
+ namespace{ \
+ struct TestName : INTERNAL_CATCH_REMOVE_PARENS(ClassName) { \
+ void test(); \
+ }; \
+ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar ) ( Catch::makeTestInvoker( &TestName::test ), CATCH_INTERNAL_LINEINFO, #ClassName, Catch::NameAndTags{ __VA_ARGS__ } ); /* NOLINT */ \
+ } \
+ CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \
+ void TestName::test()
+ #define INTERNAL_CATCH_TEST_CASE_METHOD( ClassName, ... ) \
+ INTERNAL_CATCH_TEST_CASE_METHOD2( INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_S_T_ ), ClassName, __VA_ARGS__ )
+
+ ///////////////////////////////////////////////////////////////////////////////
+ #define INTERNAL_CATCH_REGISTER_TESTCASE( Function, ... ) \
+ CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
+ CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
+ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( Catch::makeTestInvoker( Function ), CATCH_INTERNAL_LINEINFO, Catch::StringRef(), Catch::NameAndTags{ __VA_ARGS__ } ); /* NOLINT */ \
+ CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION
+
+ ///////////////////////////////////////////////////////////////////////////////
+ #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_2(TestName, TestFunc, Name, Tags, Signature, ... )\
+ CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
+ CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
+ CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS \
+ CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS \
+ INTERNAL_CATCH_DECLARE_SIG_TEST(TestFunc, INTERNAL_CATCH_REMOVE_PARENS(Signature));\
+ namespace {\
+ namespace INTERNAL_CATCH_MAKE_NAMESPACE(TestName){\
+ INTERNAL_CATCH_TYPE_GEN\
+ INTERNAL_CATCH_NTTP_GEN(INTERNAL_CATCH_REMOVE_PARENS(Signature))\
+ INTERNAL_CATCH_NTTP_REG_GEN(TestFunc,INTERNAL_CATCH_REMOVE_PARENS(Signature))\
+ template<typename...Types> \
+ struct TestName{\
+ TestName(){\
+ int index = 0; \
+ constexpr char const* tmpl_types[] = {CATCH_REC_LIST(INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS, __VA_ARGS__)};\
+ using expander = int[];\
+ (void)expander{(reg_test(Types{}, Catch::NameAndTags{ Name " - " + std::string(tmpl_types[index]), Tags } ), index++)... };/* NOLINT */ \
+ }\
+ };\
+ static int INTERNAL_CATCH_UNIQUE_NAME( globalRegistrar ) = [](){\
+ TestName<INTERNAL_CATCH_MAKE_TYPE_LISTS_FROM_TYPES(__VA_ARGS__)>();\
+ return 0;\
+ }();\
+ }\
+ }\
+ CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \
+ INTERNAL_CATCH_DEFINE_SIG_TEST(TestFunc,INTERNAL_CATCH_REMOVE_PARENS(Signature))
+
+#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
+ #define INTERNAL_CATCH_TEMPLATE_TEST_CASE(Name, Tags, ...) \
+ INTERNAL_CATCH_TEMPLATE_TEST_CASE_2( INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_ ), INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_F_U_N_C_ ), Name, Tags, typename TestType, __VA_ARGS__ )
+#else
+ #define INTERNAL_CATCH_TEMPLATE_TEST_CASE(Name, Tags, ...) \
+ INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_2( INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_ ), INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_F_U_N_C_ ), Name, Tags, typename TestType, __VA_ARGS__ ) )
+#endif
+
+#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
+ #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_SIG(Name, Tags, Signature, ...) \
+ INTERNAL_CATCH_TEMPLATE_TEST_CASE_2( INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_ ), INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_F_U_N_C_ ), Name, Tags, Signature, __VA_ARGS__ )
+#else
+ #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_SIG(Name, Tags, Signature, ...) \
+ INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_2( INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_ ), INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_F_U_N_C_ ), Name, Tags, Signature, __VA_ARGS__ ) )
+#endif
+
+ #define INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE2(TestName, TestFuncName, Name, Tags, Signature, TmplTypes, TypesList) \
+ CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
+ CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
+ CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS \
+ CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS \
+ template<typename TestType> static void TestFuncName(); \
+ namespace {\
+ namespace INTERNAL_CATCH_MAKE_NAMESPACE(TestName) { \
+ INTERNAL_CATCH_TYPE_GEN \
+ INTERNAL_CATCH_NTTP_GEN(INTERNAL_CATCH_REMOVE_PARENS(Signature)) \
+ template<typename... Types> \
+ struct TestName { \
+ void reg_tests() { \
+ int index = 0; \
+ using expander = int[]; \
+ constexpr char const* tmpl_types[] = {CATCH_REC_LIST(INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS, INTERNAL_CATCH_REMOVE_PARENS(TmplTypes))};\
+ constexpr char const* types_list[] = {CATCH_REC_LIST(INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS, INTERNAL_CATCH_REMOVE_PARENS(TypesList))};\
+ constexpr auto num_types = sizeof(types_list) / sizeof(types_list[0]);\
+ (void)expander{(Catch::AutoReg( Catch::makeTestInvoker( &TestFuncName<Types> ), CATCH_INTERNAL_LINEINFO, Catch::StringRef(), Catch::NameAndTags{ Name " - " + std::string(tmpl_types[index / num_types]) + "<" + std::string(types_list[index % num_types]) + ">", Tags } ), index++)... };/* NOLINT */\
+ } \
+ }; \
+ static int INTERNAL_CATCH_UNIQUE_NAME( globalRegistrar ) = [](){ \
+ using TestInit = typename create<TestName, decltype(get_wrapper<INTERNAL_CATCH_REMOVE_PARENS(TmplTypes)>()), TypeList<INTERNAL_CATCH_MAKE_TYPE_LISTS_FROM_TYPES(INTERNAL_CATCH_REMOVE_PARENS(TypesList))>>::type; \
+ TestInit t; \
+ t.reg_tests(); \
+ return 0; \
+ }(); \
+ } \
+ } \
+ CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \
+ template<typename TestType> \
+ static void TestFuncName()
+
+#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
+ #define INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE(Name, Tags, ...)\
+ INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE2(INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_ ), INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_F_U_N_C_ ), Name, Tags, typename T,__VA_ARGS__)
+#else
+ #define INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE(Name, Tags, ...)\
+ INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE2( INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_ ), INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_F_U_N_C_ ), Name, Tags, typename T, __VA_ARGS__ ) )
+#endif
+
+#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
+ #define INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_SIG(Name, Tags, Signature, ...)\
+ INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE2(INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_ ), INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_F_U_N_C_ ), Name, Tags, Signature, __VA_ARGS__)
+#else
+ #define INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_SIG(Name, Tags, Signature, ...)\
+ INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE2( INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_ ), INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_F_U_N_C_ ), Name, Tags, Signature, __VA_ARGS__ ) )
+#endif
+
+ #define INTERNAL_CATCH_TEMPLATE_LIST_TEST_CASE_2(TestName, TestFunc, Name, Tags, TmplList)\
+ CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
+ CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
+ CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS \
+ template<typename TestType> static void TestFunc(); \
+ namespace {\
+ namespace INTERNAL_CATCH_MAKE_NAMESPACE(TestName){\
+ INTERNAL_CATCH_TYPE_GEN\
+ template<typename... Types> \
+ struct TestName { \
+ void reg_tests() { \
+ int index = 0; \
+ using expander = int[]; \
+ (void)expander{(Catch::AutoReg( Catch::makeTestInvoker( &TestFunc<Types> ), CATCH_INTERNAL_LINEINFO, Catch::StringRef(), Catch::NameAndTags{ Name " - " + std::string(INTERNAL_CATCH_STRINGIZE(TmplList)) + " - " + std::to_string(index), Tags } ), index++)... };/* NOLINT */\
+ } \
+ };\
+ static int INTERNAL_CATCH_UNIQUE_NAME( globalRegistrar ) = [](){ \
+ using TestInit = typename convert<TestName, TmplList>::type; \
+ TestInit t; \
+ t.reg_tests(); \
+ return 0; \
+ }(); \
+ }}\
+ CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \
+ template<typename TestType> \
+ static void TestFunc()
+
+ #define INTERNAL_CATCH_TEMPLATE_LIST_TEST_CASE(Name, Tags, TmplList) \
+ INTERNAL_CATCH_TEMPLATE_LIST_TEST_CASE_2( INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_ ), INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_F_U_N_C_ ), Name, Tags, TmplList )
+
+ #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_2( TestNameClass, TestName, ClassName, Name, Tags, Signature, ... ) \
+ CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
+ CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
+ CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS \
+ CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS \
+ namespace {\
+ namespace INTERNAL_CATCH_MAKE_NAMESPACE(TestName){ \
+ INTERNAL_CATCH_TYPE_GEN\
+ INTERNAL_CATCH_NTTP_GEN(INTERNAL_CATCH_REMOVE_PARENS(Signature))\
+ INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD(TestName, ClassName, INTERNAL_CATCH_REMOVE_PARENS(Signature));\
+ INTERNAL_CATCH_NTTP_REG_METHOD_GEN(TestName, INTERNAL_CATCH_REMOVE_PARENS(Signature))\
+ template<typename...Types> \
+ struct TestNameClass{\
+ TestNameClass(){\
+ int index = 0; \
+ constexpr char const* tmpl_types[] = {CATCH_REC_LIST(INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS, __VA_ARGS__)};\
+ using expander = int[];\
+ (void)expander{(reg_test(Types{}, #ClassName, Catch::NameAndTags{ Name " - " + std::string(tmpl_types[index]), Tags } ), index++)... };/* NOLINT */ \
+ }\
+ };\
+ static int INTERNAL_CATCH_UNIQUE_NAME( globalRegistrar ) = [](){\
+ TestNameClass<INTERNAL_CATCH_MAKE_TYPE_LISTS_FROM_TYPES(__VA_ARGS__)>();\
+ return 0;\
+ }();\
+ }\
+ }\
+ CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \
+ INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD(TestName, INTERNAL_CATCH_REMOVE_PARENS(Signature))
+
+#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
+ #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD( ClassName, Name, Tags,... ) \
+ INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_2( INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_C_L_A_S_S_ ), INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_ ) , ClassName, Name, Tags, typename T, __VA_ARGS__ )
+#else
+ #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD( ClassName, Name, Tags,... ) \
+ INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_2( INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_C_L_A_S_S_ ), INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_ ) , ClassName, Name, Tags, typename T, __VA_ARGS__ ) )
+#endif
+
+#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
+ #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_SIG( ClassName, Name, Tags, Signature, ... ) \
+ INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_2( INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_C_L_A_S_S_ ), INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_ ) , ClassName, Name, Tags, Signature, __VA_ARGS__ )
+#else
+ #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_SIG( ClassName, Name, Tags, Signature, ... ) \
+ INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_2( INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_C_L_A_S_S_ ), INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_ ) , ClassName, Name, Tags, Signature, __VA_ARGS__ ) )
+#endif
+
+ #define INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_2(TestNameClass, TestName, ClassName, Name, Tags, Signature, TmplTypes, TypesList)\
+ CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
+ CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
+ CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS \
+ CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS \
+ template<typename TestType> \
+ struct TestName : INTERNAL_CATCH_REMOVE_PARENS(ClassName <TestType>) { \
+ void test();\
+ };\
+ namespace {\
+ namespace INTERNAL_CATCH_MAKE_NAMESPACE(TestNameClass) {\
+ INTERNAL_CATCH_TYPE_GEN \
+ INTERNAL_CATCH_NTTP_GEN(INTERNAL_CATCH_REMOVE_PARENS(Signature))\
+ template<typename...Types>\
+ struct TestNameClass{\
+ void reg_tests(){\
+ int index = 0;\
+ using expander = int[];\
+ constexpr char const* tmpl_types[] = {CATCH_REC_LIST(INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS, INTERNAL_CATCH_REMOVE_PARENS(TmplTypes))};\
+ constexpr char const* types_list[] = {CATCH_REC_LIST(INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS, INTERNAL_CATCH_REMOVE_PARENS(TypesList))};\
+ constexpr auto num_types = sizeof(types_list) / sizeof(types_list[0]);\
+ (void)expander{(Catch::AutoReg( Catch::makeTestInvoker( &TestName<Types>::test ), CATCH_INTERNAL_LINEINFO, #ClassName, Catch::NameAndTags{ Name " - " + std::string(tmpl_types[index / num_types]) + "<" + std::string(types_list[index % num_types]) + ">", Tags } ), index++)... };/* NOLINT */ \
+ }\
+ };\
+ static int INTERNAL_CATCH_UNIQUE_NAME( globalRegistrar ) = [](){\
+ using TestInit = typename create<TestNameClass, decltype(get_wrapper<INTERNAL_CATCH_REMOVE_PARENS(TmplTypes)>()), TypeList<INTERNAL_CATCH_MAKE_TYPE_LISTS_FROM_TYPES(INTERNAL_CATCH_REMOVE_PARENS(TypesList))>>::type;\
+ TestInit t;\
+ t.reg_tests();\
+ return 0;\
+ }(); \
+ }\
+ }\
+ CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \
+ template<typename TestType> \
+ void TestName<TestType>::test()
+
+#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
+ #define INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD( ClassName, Name, Tags, ... )\
+ INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_2( INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_ ), INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_F_U_N_C_ ), ClassName, Name, Tags, typename T, __VA_ARGS__ )
+#else
+ #define INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD( ClassName, Name, Tags, ... )\
+ INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_2( INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_ ), INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_F_U_N_C_ ), ClassName, Name, Tags, typename T,__VA_ARGS__ ) )
+#endif
+
+#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
+ #define INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG( ClassName, Name, Tags, Signature, ... )\
+ INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_2( INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_ ), INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_F_U_N_C_ ), ClassName, Name, Tags, Signature, __VA_ARGS__ )
+#else
+ #define INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG( ClassName, Name, Tags, Signature, ... )\
+ INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_2( INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_ ), INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_F_U_N_C_ ), ClassName, Name, Tags, Signature,__VA_ARGS__ ) )
+#endif
+
+ #define INTERNAL_CATCH_TEMPLATE_LIST_TEST_CASE_METHOD_2( TestNameClass, TestName, ClassName, Name, Tags, TmplList) \
+ CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
+ CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
+ CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS \
+ template<typename TestType> \
+ struct TestName : INTERNAL_CATCH_REMOVE_PARENS(ClassName <TestType>) { \
+ void test();\
+ };\
+ namespace {\
+ namespace INTERNAL_CATCH_MAKE_NAMESPACE(TestName){ \
+ INTERNAL_CATCH_TYPE_GEN\
+ template<typename...Types>\
+ struct TestNameClass{\
+ void reg_tests(){\
+ int index = 0;\
+ using expander = int[];\
+ (void)expander{(Catch::AutoReg( Catch::makeTestInvoker( &TestName<Types>::test ), CATCH_INTERNAL_LINEINFO, #ClassName, Catch::NameAndTags{ Name " - " + std::string(INTERNAL_CATCH_STRINGIZE(TmplList)) + " - " + std::to_string(index), Tags } ), index++)... };/* NOLINT */ \
+ }\
+ };\
+ static int INTERNAL_CATCH_UNIQUE_NAME( globalRegistrar ) = [](){\
+ using TestInit = typename convert<TestNameClass, TmplList>::type;\
+ TestInit t;\
+ t.reg_tests();\
+ return 0;\
+ }(); \
+ }}\
+ CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \
+ template<typename TestType> \
+ void TestName<TestType>::test()
+
+#define INTERNAL_CATCH_TEMPLATE_LIST_TEST_CASE_METHOD(ClassName, Name, Tags, TmplList) \
+ INTERNAL_CATCH_TEMPLATE_LIST_TEST_CASE_METHOD_2( INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_ ), INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_F_U_N_C_ ), ClassName, Name, Tags, TmplList )
+
+// end catch_test_registry.h
+// start catch_capture.hpp
+
+// start catch_assertionhandler.h
+
+// start catch_assertioninfo.h
+
+// start catch_result_type.h
+
+namespace Catch {
+
+ // ResultWas::OfType enum
+ struct ResultWas { enum OfType {
+ Unknown = -1,
+ Ok = 0,
+ Info = 1,
+ Warning = 2,
+
+ FailureBit = 0x10,
+
+ ExpressionFailed = FailureBit | 1,
+ ExplicitFailure = FailureBit | 2,
+
+ Exception = 0x100 | FailureBit,
+
+ ThrewException = Exception | 1,
+ DidntThrowException = Exception | 2,
+
+ FatalErrorCondition = 0x200 | FailureBit
+
+ }; };
+
+ bool isOk( ResultWas::OfType resultType );
+ bool isJustInfo( int flags );
+
+ // ResultDisposition::Flags enum
+ struct ResultDisposition { enum Flags {
+ Normal = 0x01,
+
+ ContinueOnFailure = 0x02, // Failures fail test, but execution continues
+ FalseTest = 0x04, // Prefix expression with !
+ SuppressFail = 0x08 // Failures are reported but do not fail the test
+ }; };
+
+ ResultDisposition::Flags operator | ( ResultDisposition::Flags lhs, ResultDisposition::Flags rhs );
+
+ bool shouldContinueOnFailure( int flags );
+ inline bool isFalseTest( int flags ) { return ( flags & ResultDisposition::FalseTest ) != 0; }
+ bool shouldSuppressFailure( int flags );
+
+} // end namespace Catch
+
+// end catch_result_type.h
+namespace Catch {
+
+ struct AssertionInfo
+ {
+ StringRef macroName;
+ SourceLineInfo lineInfo;
+ StringRef capturedExpression;
+ ResultDisposition::Flags resultDisposition;
+
+ // We want to delete this constructor but a compiler bug in 4.8 means
+ // the struct is then treated as non-aggregate
+ //AssertionInfo() = delete;
+ };
+
+} // end namespace Catch
+
+// end catch_assertioninfo.h
+// start catch_decomposer.h
+
+// start catch_tostring.h
+
+#include <vector>
+#include <cstddef>
+#include <type_traits>
+#include <string>
+// start catch_stream.h
+
+#include <iosfwd>
+#include <cstddef>
+#include <ostream>
+
+namespace Catch {
+
+ std::ostream& cout();
+ std::ostream& cerr();
+ std::ostream& clog();
+
+ class StringRef;
+
+ struct IStream {
+ virtual ~IStream();
+ virtual std::ostream& stream() const = 0;
+ };
+
+ auto makeStream( StringRef const &filename ) -> IStream const*;
+
+ class ReusableStringStream : NonCopyable {
+ std::size_t m_index;
+ std::ostream* m_oss;
+ public:
+ ReusableStringStream();
+ ~ReusableStringStream();
+
+ auto str() const -> std::string;
+
+ template<typename T>
+ auto operator << ( T const& value ) -> ReusableStringStream& {
+ *m_oss << value;
+ return *this;
+ }
+ auto get() -> std::ostream& { return *m_oss; }
+ };
+}
+
+// end catch_stream.h
+// start catch_interfaces_enum_values_registry.h
+
+#include <vector>
+
+namespace Catch {
+
+ namespace Detail {
+ struct EnumInfo {
+ StringRef m_name;
+ std::vector<std::pair<int, StringRef>> m_values;
+
+ ~EnumInfo();
+
+ StringRef lookup( int value ) const;
+ };
+ } // namespace Detail
+
+ struct IMutableEnumValuesRegistry {
+ virtual ~IMutableEnumValuesRegistry();
+
+ virtual Detail::EnumInfo const& registerEnum( StringRef enumName, StringRef allEnums, std::vector<int> const& values ) = 0;
+
+ template<typename E>
+ Detail::EnumInfo const& registerEnum( StringRef enumName, StringRef allEnums, std::initializer_list<E> values ) {
+ static_assert(sizeof(int) >= sizeof(E), "Cannot serialize enum to int");
+ std::vector<int> intValues;
+ intValues.reserve( values.size() );
+ for( auto enumValue : values )
+ intValues.push_back( static_cast<int>( enumValue ) );
+ return registerEnum( enumName, allEnums, intValues );
+ }
+ };
+
+} // Catch
+
+// end catch_interfaces_enum_values_registry.h
+
+#ifdef CATCH_CONFIG_CPP17_STRING_VIEW
+#include <string_view>
+#endif
+
+#ifdef __OBJC__
+// start catch_objc_arc.hpp
+
+#import <Foundation/Foundation.h>
+
+#ifdef __has_feature
+#define CATCH_ARC_ENABLED __has_feature(objc_arc)
+#else
+#define CATCH_ARC_ENABLED 0
+#endif
+
+void arcSafeRelease( NSObject* obj );
+id performOptionalSelector( id obj, SEL sel );
+
+#if !CATCH_ARC_ENABLED
+inline void arcSafeRelease( NSObject* obj ) {
+ [obj release];
+}
+inline id performOptionalSelector( id obj, SEL sel ) {
+ if( [obj respondsToSelector: sel] )
+ return [obj performSelector: sel];
+ return nil;
+}
+#define CATCH_UNSAFE_UNRETAINED
+#define CATCH_ARC_STRONG
+#else
+inline void arcSafeRelease( NSObject* ){}
+inline id performOptionalSelector( id obj, SEL sel ) {
+#ifdef __clang__
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
+#endif
+ if( [obj respondsToSelector: sel] )
+ return [obj performSelector: sel];
+#ifdef __clang__
+#pragma clang diagnostic pop
+#endif
+ return nil;
+}
+#define CATCH_UNSAFE_UNRETAINED __unsafe_unretained
+#define CATCH_ARC_STRONG __strong
+#endif
+
+// end catch_objc_arc.hpp
+#endif
+
+#ifdef _MSC_VER
+#pragma warning(push)
+#pragma warning(disable:4180) // We attempt to stream a function (address) by const&, which MSVC complains about but is harmless
+#endif
+
+namespace Catch {
+ namespace Detail {
+
+ extern const std::string unprintableString;
+
+ std::string rawMemoryToString( const void *object, std::size_t size );
+
+ template<typename T>
+ std::string rawMemoryToString( const T& object ) {
+ return rawMemoryToString( &object, sizeof(object) );
+ }
+
+ template<typename T>
+ class IsStreamInsertable {
+ template<typename Stream, typename U>
+ static auto test(int)
+ -> decltype(std::declval<Stream&>() << std::declval<U>(), std::true_type());
+
+ template<typename, typename>
+ static auto test(...)->std::false_type;
+
+ public:
+ static const bool value = decltype(test<std::ostream, const T&>(0))::value;
+ };
+
+ template<typename E>
+ std::string convertUnknownEnumToString( E e );
+
+ template<typename T>
+ typename std::enable_if<
+ !std::is_enum<T>::value && !std::is_base_of<std::exception, T>::value,
+ std::string>::type convertUnstreamable( T const& ) {
+ return Detail::unprintableString;
+ }
+ template<typename T>
+ typename std::enable_if<
+ !std::is_enum<T>::value && std::is_base_of<std::exception, T>::value,
+ std::string>::type convertUnstreamable(T const& ex) {
+ return ex.what();
+ }
+
+ template<typename T>
+ typename std::enable_if<
+ std::is_enum<T>::value
+ , std::string>::type convertUnstreamable( T const& value ) {
+ return convertUnknownEnumToString( value );
+ }
+
+#if defined(_MANAGED)
+ //! Convert a CLR string to a utf8 std::string
+ template<typename T>
+ std::string clrReferenceToString( T^ ref ) {
+ if (ref == nullptr)
+ return std::string("null");
+ auto bytes = System::Text::Encoding::UTF8->GetBytes(ref->ToString());
+ cli::pin_ptr<System::Byte> p = &bytes[0];
+ return std::string(reinterpret_cast<char const *>(p), bytes->Length);
+ }
+#endif
+
+ } // namespace Detail
+
+ // If we decide for C++14, change these to enable_if_ts
+ template <typename T, typename = void>
+ struct StringMaker {
+ template <typename Fake = T>
+ static
+ typename std::enable_if<::Catch::Detail::IsStreamInsertable<Fake>::value, std::string>::type
+ convert(const Fake& value) {
+ ReusableStringStream rss;
+ // NB: call using the function-like syntax to avoid ambiguity with
+ // user-defined templated operator<< under clang.
+ rss.operator<<(value);
+ return rss.str();
+ }
+
+ template <typename Fake = T>
+ static
+ typename std::enable_if<!::Catch::Detail::IsStreamInsertable<Fake>::value, std::string>::type
+ convert( const Fake& value ) {
+#if !defined(CATCH_CONFIG_FALLBACK_STRINGIFIER)
+ return Detail::convertUnstreamable(value);
+#else
+ return CATCH_CONFIG_FALLBACK_STRINGIFIER(value);
+#endif
+ }
+ };
+
+ namespace Detail {
+
+ // This function dispatches all stringification requests inside of Catch.
+ // Should be preferably called fully qualified, like ::Catch::Detail::stringify
+ template <typename T>
+ std::string stringify(const T& e) {
+ return ::Catch::StringMaker<typename std::remove_cv<typename std::remove_reference<T>::type>::type>::convert(e);
+ }
+
+ template<typename E>
+ std::string convertUnknownEnumToString( E e ) {
+ return ::Catch::Detail::stringify(static_cast<typename std::underlying_type<E>::type>(e));
+ }
+
+#if defined(_MANAGED)
+ template <typename T>
+ std::string stringify( T^ e ) {
+ return ::Catch::StringMaker<T^>::convert(e);
+ }
+#endif
+
+ } // namespace Detail
+
+ // Some predefined specializations
+
+ template<>
+ struct StringMaker<std::string> {
+ static std::string convert(const std::string& str);
+ };
+
+#ifdef CATCH_CONFIG_CPP17_STRING_VIEW
+ template<>
+ struct StringMaker<std::string_view> {
+ static std::string convert(std::string_view str);
+ };
+#endif
+
+ template<>
+ struct StringMaker<char const *> {
+ static std::string convert(char const * str);
+ };
+ template<>
+ struct StringMaker<char *> {
+ static std::string convert(char * str);
+ };
+
+#ifdef CATCH_CONFIG_WCHAR
+ template<>
+ struct StringMaker<std::wstring> {
+ static std::string convert(const std::wstring& wstr);
+ };
+
+# ifdef CATCH_CONFIG_CPP17_STRING_VIEW
+ template<>
+ struct StringMaker<std::wstring_view> {
+ static std::string convert(std::wstring_view str);
+ };
+# endif
+
+ template<>
+ struct StringMaker<wchar_t const *> {
+ static std::string convert(wchar_t const * str);
+ };
+ template<>
+ struct StringMaker<wchar_t *> {
+ static std::string convert(wchar_t * str);
+ };
+#endif
+
+ // TBD: Should we use `strnlen` to ensure that we don't go out of the buffer,
+ // while keeping string semantics?
+ template<int SZ>
+ struct StringMaker<char[SZ]> {
+ static std::string convert(char const* str) {
+ return ::Catch::Detail::stringify(std::string{ str });
+ }
+ };
+ template<int SZ>
+ struct StringMaker<signed char[SZ]> {
+ static std::string convert(signed char const* str) {
+ return ::Catch::Detail::stringify(std::string{ reinterpret_cast<char const *>(str) });
+ }
+ };
+ template<int SZ>
+ struct StringMaker<unsigned char[SZ]> {
+ static std::string convert(unsigned char const* str) {
+ return ::Catch::Detail::stringify(std::string{ reinterpret_cast<char const *>(str) });
+ }
+ };
+
+#if defined(CATCH_CONFIG_CPP17_BYTE)
+ template<>
+ struct StringMaker<std::byte> {
+ static std::string convert(std::byte value);
+ };
+#endif // defined(CATCH_CONFIG_CPP17_BYTE)
+ template<>
+ struct StringMaker<int> {
+ static std::string convert(int value);
+ };
+ template<>
+ struct StringMaker<long> {
+ static std::string convert(long value);
+ };
+ template<>
+ struct StringMaker<long long> {
+ static std::string convert(long long value);
+ };
+ template<>
+ struct StringMaker<unsigned int> {
+ static std::string convert(unsigned int value);
+ };
+ template<>
+ struct StringMaker<unsigned long> {
+ static std::string convert(unsigned long value);
+ };
+ template<>
+ struct StringMaker<unsigned long long> {
+ static std::string convert(unsigned long long value);
+ };
+
+ template<>
+ struct StringMaker<bool> {
+ static std::string convert(bool b);
+ };
+
+ template<>
+ struct StringMaker<char> {
+ static std::string convert(char c);
+ };
+ template<>
+ struct StringMaker<signed char> {
+ static std::string convert(signed char c);
+ };
+ template<>
+ struct StringMaker<unsigned char> {
+ static std::string convert(unsigned char c);
+ };
+
+ template<>
+ struct StringMaker<std::nullptr_t> {
+ static std::string convert(std::nullptr_t);
+ };
+
+ template<>
+ struct StringMaker<float> {
+ static std::string convert(float value);
+ static int precision;
+ };
+
+ template<>
+ struct StringMaker<double> {
+ static std::string convert(double value);
+ static int precision;
+ };
+
+ template <typename T>
+ struct StringMaker<T*> {
+ template <typename U>
+ static std::string convert(U* p) {
+ if (p) {
+ return ::Catch::Detail::rawMemoryToString(p);
+ } else {
+ return "nullptr";
+ }
+ }
+ };
+
+ template <typename R, typename C>
+ struct StringMaker<R C::*> {
+ static std::string convert(R C::* p) {
+ if (p) {
+ return ::Catch::Detail::rawMemoryToString(p);
+ } else {
+ return "nullptr";
+ }
+ }
+ };
+
+#if defined(_MANAGED)
+ template <typename T>
+ struct StringMaker<T^> {
+ static std::string convert( T^ ref ) {
+ return ::Catch::Detail::clrReferenceToString(ref);
+ }
+ };
+#endif
+
+ namespace Detail {
+ template<typename InputIterator, typename Sentinel = InputIterator>
+ std::string rangeToString(InputIterator first, Sentinel last) {
+ ReusableStringStream rss;
+ rss << "{ ";
+ if (first != last) {
+ rss << ::Catch::Detail::stringify(*first);
+ for (++first; first != last; ++first)
+ rss << ", " << ::Catch::Detail::stringify(*first);
+ }
+ rss << " }";
+ return rss.str();
+ }
+ }
+
+#ifdef __OBJC__
+ template<>
+ struct StringMaker<NSString*> {
+ static std::string convert(NSString * nsstring) {
+ if (!nsstring)
+ return "nil";
+ return std::string("@") + [nsstring UTF8String];
+ }
+ };
+ template<>
+ struct StringMaker<NSObject*> {
+ static std::string convert(NSObject* nsObject) {
+ return ::Catch::Detail::stringify([nsObject description]);
+ }
+
+ };
+ namespace Detail {
+ inline std::string stringify( NSString* nsstring ) {
+ return StringMaker<NSString*>::convert( nsstring );
+ }
+
+ } // namespace Detail
+#endif // __OBJC__
+
+} // namespace Catch
+
+//////////////////////////////////////////////////////
+// Separate std-lib types stringification, so it can be selectively enabled
+// This means that we do not bring in
+
+#if defined(CATCH_CONFIG_ENABLE_ALL_STRINGMAKERS)
+# define CATCH_CONFIG_ENABLE_PAIR_STRINGMAKER
+# define CATCH_CONFIG_ENABLE_TUPLE_STRINGMAKER
+# define CATCH_CONFIG_ENABLE_VARIANT_STRINGMAKER
+# define CATCH_CONFIG_ENABLE_CHRONO_STRINGMAKER
+# define CATCH_CONFIG_ENABLE_OPTIONAL_STRINGMAKER
+#endif
+
+// Separate std::pair specialization
+#if defined(CATCH_CONFIG_ENABLE_PAIR_STRINGMAKER)
+#include <utility>
+namespace Catch {
+ template<typename T1, typename T2>
+ struct StringMaker<std::pair<T1, T2> > {
+ static std::string convert(const std::pair<T1, T2>& pair) {
+ ReusableStringStream rss;
+ rss << "{ "
+ << ::Catch::Detail::stringify(pair.first)
+ << ", "
+ << ::Catch::Detail::stringify(pair.second)
+ << " }";
+ return rss.str();
+ }
+ };
+}
+#endif // CATCH_CONFIG_ENABLE_PAIR_STRINGMAKER
+
+#if defined(CATCH_CONFIG_ENABLE_OPTIONAL_STRINGMAKER) && defined(CATCH_CONFIG_CPP17_OPTIONAL)
+#include <optional>
+namespace Catch {
+ template<typename T>
+ struct StringMaker<std::optional<T> > {
+ static std::string convert(const std::optional<T>& optional) {
+ ReusableStringStream rss;
+ if (optional.has_value()) {
+ rss << ::Catch::Detail::stringify(*optional);
+ } else {
+ rss << "{ }";
+ }
+ return rss.str();
+ }
+ };
+}
+#endif // CATCH_CONFIG_ENABLE_OPTIONAL_STRINGMAKER
+
+// Separate std::tuple specialization
+#if defined(CATCH_CONFIG_ENABLE_TUPLE_STRINGMAKER)
+#include <tuple>
+namespace Catch {
+ namespace Detail {
+ template<
+ typename Tuple,
+ std::size_t N = 0,
+ bool = (N < std::tuple_size<Tuple>::value)
+ >
+ struct TupleElementPrinter {
+ static void print(const Tuple& tuple, std::ostream& os) {
+ os << (N ? ", " : " ")
+ << ::Catch::Detail::stringify(std::get<N>(tuple));
+ TupleElementPrinter<Tuple, N + 1>::print(tuple, os);
+ }
+ };
+
+ template<
+ typename Tuple,
+ std::size_t N
+ >
+ struct TupleElementPrinter<Tuple, N, false> {
+ static void print(const Tuple&, std::ostream&) {}
+ };
+
+ }
+
+ template<typename ...Types>
+ struct StringMaker<std::tuple<Types...>> {
+ static std::string convert(const std::tuple<Types...>& tuple) {
+ ReusableStringStream rss;
+ rss << '{';
+ Detail::TupleElementPrinter<std::tuple<Types...>>::print(tuple, rss.get());
+ rss << " }";
+ return rss.str();
+ }
+ };
+}
+#endif // CATCH_CONFIG_ENABLE_TUPLE_STRINGMAKER
+
+#if defined(CATCH_CONFIG_ENABLE_VARIANT_STRINGMAKER) && defined(CATCH_CONFIG_CPP17_VARIANT)
+#include <variant>
+namespace Catch {
+ template<>
+ struct StringMaker<std::monostate> {
+ static std::string convert(const std::monostate&) {
+ return "{ }";
+ }
+ };
+
+ template<typename... Elements>
+ struct StringMaker<std::variant<Elements...>> {
+ static std::string convert(const std::variant<Elements...>& variant) {
+ if (variant.valueless_by_exception()) {
+ return "{valueless variant}";
+ } else {
+ return std::visit(
+ [](const auto& value) {
+ return ::Catch::Detail::stringify(value);
+ },
+ variant
+ );
+ }
+ }
+ };
+}
+#endif // CATCH_CONFIG_ENABLE_VARIANT_STRINGMAKER
+
+namespace Catch {
+ // Import begin/ end from std here
+ using std::begin;
+ using std::end;
+
+ namespace detail {
+ template <typename...>
+ struct void_type {
+ using type = void;
+ };
+
+ template <typename T, typename = void>
+ struct is_range_impl : std::false_type {
+ };
+
+ template <typename T>
+ struct is_range_impl<T, typename void_type<decltype(begin(std::declval<T>()))>::type> : std::true_type {
+ };
+ } // namespace detail
+
+ template <typename T>
+ struct is_range : detail::is_range_impl<T> {
+ };
+
+#if defined(_MANAGED) // Managed types are never ranges
+ template <typename T>
+ struct is_range<T^> {
+ static const bool value = false;
+ };
+#endif
+
+ template<typename Range>
+ std::string rangeToString( Range const& range ) {
+ return ::Catch::Detail::rangeToString( begin( range ), end( range ) );
+ }
+
+ // Handle vector<bool> specially
+ template<typename Allocator>
+ std::string rangeToString( std::vector<bool, Allocator> const& v ) {
+ ReusableStringStream rss;
+ rss << "{ ";
+ bool first = true;
+ for( bool b : v ) {
+ if( first )
+ first = false;
+ else
+ rss << ", ";
+ rss << ::Catch::Detail::stringify( b );
+ }
+ rss << " }";
+ return rss.str();
+ }
+
+ template<typename R>
+ struct StringMaker<R, typename std::enable_if<is_range<R>::value && !::Catch::Detail::IsStreamInsertable<R>::value>::type> {
+ static std::string convert( R const& range ) {
+ return rangeToString( range );
+ }
+ };
+
+ template <typename T, int SZ>
+ struct StringMaker<T[SZ]> {
+ static std::string convert(T const(&arr)[SZ]) {
+ return rangeToString(arr);
+ }
+ };
+
+} // namespace Catch
+
+// Separate std::chrono::duration specialization
+#if defined(CATCH_CONFIG_ENABLE_CHRONO_STRINGMAKER)
+#include <ctime>
+#include <ratio>
+#include <chrono>
+
+namespace Catch {
+
+template <class Ratio>
+struct ratio_string {
+ static std::string symbol();
+};
+
+template <class Ratio>
+std::string ratio_string<Ratio>::symbol() {
+ Catch::ReusableStringStream rss;
+ rss << '[' << Ratio::num << '/'
+ << Ratio::den << ']';
+ return rss.str();
+}
+template <>
+struct ratio_string<std::atto> {
+ static std::string symbol();
+};
+template <>
+struct ratio_string<std::femto> {
+ static std::string symbol();
+};
+template <>
+struct ratio_string<std::pico> {
+ static std::string symbol();
+};
+template <>
+struct ratio_string<std::nano> {
+ static std::string symbol();
+};
+template <>
+struct ratio_string<std::micro> {
+ static std::string symbol();
+};
+template <>
+struct ratio_string<std::milli> {
+ static std::string symbol();
+};
+
+ ////////////
+ // std::chrono::duration specializations
+ template<typename Value, typename Ratio>
+ struct StringMaker<std::chrono::duration<Value, Ratio>> {
+ static std::string convert(std::chrono::duration<Value, Ratio> const& duration) {
+ ReusableStringStream rss;
+ rss << duration.count() << ' ' << ratio_string<Ratio>::symbol() << 's';
+ return rss.str();
+ }
+ };
+ template<typename Value>
+ struct StringMaker<std::chrono::duration<Value, std::ratio<1>>> {
+ static std::string convert(std::chrono::duration<Value, std::ratio<1>> const& duration) {
+ ReusableStringStream rss;
+ rss << duration.count() << " s";
+ return rss.str();
+ }
+ };
+ template<typename Value>
+ struct StringMaker<std::chrono::duration<Value, std::ratio<60>>> {
+ static std::string convert(std::chrono::duration<Value, std::ratio<60>> const& duration) {
+ ReusableStringStream rss;
+ rss << duration.count() << " m";
+ return rss.str();
+ }
+ };
+ template<typename Value>
+ struct StringMaker<std::chrono::duration<Value, std::ratio<3600>>> {
+ static std::string convert(std::chrono::duration<Value, std::ratio<3600>> const& duration) {
+ ReusableStringStream rss;
+ rss << duration.count() << " h";
+ return rss.str();
+ }
+ };
+
+ ////////////
+ // std::chrono::time_point specialization
+ // Generic time_point cannot be specialized, only std::chrono::time_point<system_clock>
+ template<typename Clock, typename Duration>
+ struct StringMaker<std::chrono::time_point<Clock, Duration>> {
+ static std::string convert(std::chrono::time_point<Clock, Duration> const& time_point) {
+ return ::Catch::Detail::stringify(time_point.time_since_epoch()) + " since epoch";
+ }
+ };
+ // std::chrono::time_point<system_clock> specialization
+ template<typename Duration>
+ struct StringMaker<std::chrono::time_point<std::chrono::system_clock, Duration>> {
+ static std::string convert(std::chrono::time_point<std::chrono::system_clock, Duration> const& time_point) {
+ auto converted = std::chrono::system_clock::to_time_t(time_point);
+
+#ifdef _MSC_VER
+ std::tm timeInfo = {};
+ gmtime_s(&timeInfo, &converted);
+#else
+ std::tm* timeInfo = std::gmtime(&converted);
+#endif
+
+ auto const timeStampSize = sizeof("2017-01-16T17:06:45Z");
+ char timeStamp[timeStampSize];
+ const char * const fmt = "%Y-%m-%dT%H:%M:%SZ";
+
+#ifdef _MSC_VER
+ std::strftime(timeStamp, timeStampSize, fmt, &timeInfo);
+#else
+ std::strftime(timeStamp, timeStampSize, fmt, timeInfo);
+#endif
+ return std::string(timeStamp);
+ }
+ };
+}
+#endif // CATCH_CONFIG_ENABLE_CHRONO_STRINGMAKER
+
+#define INTERNAL_CATCH_REGISTER_ENUM( enumName, ... ) \
+namespace Catch { \
+ template<> struct StringMaker<enumName> { \
+ static std::string convert( enumName value ) { \
+ static const auto& enumInfo = ::Catch::getMutableRegistryHub().getMutableEnumValuesRegistry().registerEnum( #enumName, #__VA_ARGS__, { __VA_ARGS__ } ); \
+ return static_cast<std::string>(enumInfo.lookup( static_cast<int>( value ) )); \
+ } \
+ }; \
+}
+
+#define CATCH_REGISTER_ENUM( enumName, ... ) INTERNAL_CATCH_REGISTER_ENUM( enumName, __VA_ARGS__ )
+
+#ifdef _MSC_VER
+#pragma warning(pop)
+#endif
+
+// end catch_tostring.h
+#include <iosfwd>
+
+#ifdef _MSC_VER
+#pragma warning(push)
+#pragma warning(disable:4389) // '==' : signed/unsigned mismatch
+#pragma warning(disable:4018) // more "signed/unsigned mismatch"
+#pragma warning(disable:4312) // Converting int to T* using reinterpret_cast (issue on x64 platform)
+#pragma warning(disable:4180) // qualifier applied to function type has no meaning
+#pragma warning(disable:4800) // Forcing result to true or false
+#endif
+
+namespace Catch {
+
+ struct ITransientExpression {
+ auto isBinaryExpression() const -> bool { return m_isBinaryExpression; }
+ auto getResult() const -> bool { return m_result; }
+ virtual void streamReconstructedExpression( std::ostream &os ) const = 0;
+
+ ITransientExpression( bool isBinaryExpression, bool result )
+ : m_isBinaryExpression( isBinaryExpression ),
+ m_result( result )
+ {}
+
+ // We don't actually need a virtual destructor, but many static analysers
+ // complain if it's not here :-(
+ virtual ~ITransientExpression();
+
+ bool m_isBinaryExpression;
+ bool m_result;
+
+ };
+
+ void formatReconstructedExpression( std::ostream &os, std::string const& lhs, StringRef op, std::string const& rhs );
+
+ template<typename LhsT, typename RhsT>
+ class BinaryExpr : public ITransientExpression {
+ LhsT m_lhs;
+ StringRef m_op;
+ RhsT m_rhs;
+
+ void streamReconstructedExpression( std::ostream &os ) const override {
+ formatReconstructedExpression
+ ( os, Catch::Detail::stringify( m_lhs ), m_op, Catch::Detail::stringify( m_rhs ) );
+ }
+
+ public:
+ BinaryExpr( bool comparisonResult, LhsT lhs, StringRef op, RhsT rhs )
+ : ITransientExpression{ true, comparisonResult },
+ m_lhs( lhs ),
+ m_op( op ),
+ m_rhs( rhs )
+ {}
+
+ template<typename T>
+ auto operator && ( T ) const -> BinaryExpr<LhsT, RhsT const&> const {
+ static_assert(always_false<T>::value,
+ "chained comparisons are not supported inside assertions, "
+ "wrap the expression inside parentheses, or decompose it");
+ }
+
+ template<typename T>
+ auto operator || ( T ) const -> BinaryExpr<LhsT, RhsT const&> const {
+ static_assert(always_false<T>::value,
+ "chained comparisons are not supported inside assertions, "
+ "wrap the expression inside parentheses, or decompose it");
+ }
+
+ template<typename T>
+ auto operator == ( T ) const -> BinaryExpr<LhsT, RhsT const&> const {
+ static_assert(always_false<T>::value,
+ "chained comparisons are not supported inside assertions, "
+ "wrap the expression inside parentheses, or decompose it");
+ }
+
+ template<typename T>
+ auto operator != ( T ) const -> BinaryExpr<LhsT, RhsT const&> const {
+ static_assert(always_false<T>::value,
+ "chained comparisons are not supported inside assertions, "
+ "wrap the expression inside parentheses, or decompose it");
+ }
+
+ template<typename T>
+ auto operator > ( T ) const -> BinaryExpr<LhsT, RhsT const&> const {
+ static_assert(always_false<T>::value,
+ "chained comparisons are not supported inside assertions, "
+ "wrap the expression inside parentheses, or decompose it");
+ }
+
+ template<typename T>
+ auto operator < ( T ) const -> BinaryExpr<LhsT, RhsT const&> const {
+ static_assert(always_false<T>::value,
+ "chained comparisons are not supported inside assertions, "
+ "wrap the expression inside parentheses, or decompose it");
+ }
+
+ template<typename T>
+ auto operator >= ( T ) const -> BinaryExpr<LhsT, RhsT const&> const {
+ static_assert(always_false<T>::value,
+ "chained comparisons are not supported inside assertions, "
+ "wrap the expression inside parentheses, or decompose it");
+ }
+
+ template<typename T>
+ auto operator <= ( T ) const -> BinaryExpr<LhsT, RhsT const&> const {
+ static_assert(always_false<T>::value,
+ "chained comparisons are not supported inside assertions, "
+ "wrap the expression inside parentheses, or decompose it");
+ }
+ };
+
+ template<typename LhsT>
+ class UnaryExpr : public ITransientExpression {
+ LhsT m_lhs;
+
+ void streamReconstructedExpression( std::ostream &os ) const override {
+ os << Catch::Detail::stringify( m_lhs );
+ }
+
+ public:
+ explicit UnaryExpr( LhsT lhs )
+ : ITransientExpression{ false, static_cast<bool>(lhs) },
+ m_lhs( lhs )
+ {}
+ };
+
+ // Specialised comparison functions to handle equality comparisons between ints and pointers (NULL deduces as an int)
+ template<typename LhsT, typename RhsT>
+ auto compareEqual( LhsT const& lhs, RhsT const& rhs ) -> bool { return static_cast<bool>(lhs == rhs); }
+ template<typename T>
+ auto compareEqual( T* const& lhs, int rhs ) -> bool { return lhs == reinterpret_cast<void const*>( rhs ); }
+ template<typename T>
+ auto compareEqual( T* const& lhs, long rhs ) -> bool { return lhs == reinterpret_cast<void const*>( rhs ); }
+ template<typename T>
+ auto compareEqual( int lhs, T* const& rhs ) -> bool { return reinterpret_cast<void const*>( lhs ) == rhs; }
+ template<typename T>
+ auto compareEqual( long lhs, T* const& rhs ) -> bool { return reinterpret_cast<void const*>( lhs ) == rhs; }
+
+ template<typename LhsT, typename RhsT>
+ auto compareNotEqual( LhsT const& lhs, RhsT&& rhs ) -> bool { return static_cast<bool>(lhs != rhs); }
+ template<typename T>
+ auto compareNotEqual( T* const& lhs, int rhs ) -> bool { return lhs != reinterpret_cast<void const*>( rhs ); }
+ template<typename T>
+ auto compareNotEqual( T* const& lhs, long rhs ) -> bool { return lhs != reinterpret_cast<void const*>( rhs ); }
+ template<typename T>
+ auto compareNotEqual( int lhs, T* const& rhs ) -> bool { return reinterpret_cast<void const*>( lhs ) != rhs; }
+ template<typename T>
+ auto compareNotEqual( long lhs, T* const& rhs ) -> bool { return reinterpret_cast<void const*>( lhs ) != rhs; }
+
+ template<typename LhsT>
+ class ExprLhs {
+ LhsT m_lhs;
+ public:
+ explicit ExprLhs( LhsT lhs ) : m_lhs( lhs ) {}
+
+ template<typename RhsT>
+ auto operator == ( RhsT const& rhs ) -> BinaryExpr<LhsT, RhsT const&> const {
+ return { compareEqual( m_lhs, rhs ), m_lhs, "==", rhs };
+ }
+ auto operator == ( bool rhs ) -> BinaryExpr<LhsT, bool> const {
+ return { m_lhs == rhs, m_lhs, "==", rhs };
+ }
+
+ template<typename RhsT>
+ auto operator != ( RhsT const& rhs ) -> BinaryExpr<LhsT, RhsT const&> const {
+ return { compareNotEqual( m_lhs, rhs ), m_lhs, "!=", rhs };
+ }
+ auto operator != ( bool rhs ) -> BinaryExpr<LhsT, bool> const {
+ return { m_lhs != rhs, m_lhs, "!=", rhs };
+ }
+
+ template<typename RhsT>
+ auto operator > ( RhsT const& rhs ) -> BinaryExpr<LhsT, RhsT const&> const {
+ return { static_cast<bool>(m_lhs > rhs), m_lhs, ">", rhs };
+ }
+ template<typename RhsT>
+ auto operator < ( RhsT const& rhs ) -> BinaryExpr<LhsT, RhsT const&> const {
+ return { static_cast<bool>(m_lhs < rhs), m_lhs, "<", rhs };
+ }
+ template<typename RhsT>
+ auto operator >= ( RhsT const& rhs ) -> BinaryExpr<LhsT, RhsT const&> const {
+ return { static_cast<bool>(m_lhs >= rhs), m_lhs, ">=", rhs };
+ }
+ template<typename RhsT>
+ auto operator <= ( RhsT const& rhs ) -> BinaryExpr<LhsT, RhsT const&> const {
+ return { static_cast<bool>(m_lhs <= rhs), m_lhs, "<=", rhs };
+ }
+ template <typename RhsT>
+ auto operator | (RhsT const& rhs) -> BinaryExpr<LhsT, RhsT const&> const {
+ return { static_cast<bool>(m_lhs | rhs), m_lhs, "|", rhs };
+ }
+ template <typename RhsT>
+ auto operator & (RhsT const& rhs) -> BinaryExpr<LhsT, RhsT const&> const {
+ return { static_cast<bool>(m_lhs & rhs), m_lhs, "&", rhs };
+ }
+ template <typename RhsT>
+ auto operator ^ (RhsT const& rhs) -> BinaryExpr<LhsT, RhsT const&> const {
+ return { static_cast<bool>(m_lhs ^ rhs), m_lhs, "^", rhs };
+ }
+
+ template<typename RhsT>
+ auto operator && ( RhsT const& ) -> BinaryExpr<LhsT, RhsT const&> const {
+ static_assert(always_false<RhsT>::value,
+ "operator&& is not supported inside assertions, "
+ "wrap the expression inside parentheses, or decompose it");
+ }
+
+ template<typename RhsT>
+ auto operator || ( RhsT const& ) -> BinaryExpr<LhsT, RhsT const&> const {
+ static_assert(always_false<RhsT>::value,
+ "operator|| is not supported inside assertions, "
+ "wrap the expression inside parentheses, or decompose it");
+ }
+
+ auto makeUnaryExpr() const -> UnaryExpr<LhsT> {
+ return UnaryExpr<LhsT>{ m_lhs };
+ }
+ };
+
+ void handleExpression( ITransientExpression const& expr );
+
+ template<typename T>
+ void handleExpression( ExprLhs<T> const& expr ) {
+ handleExpression( expr.makeUnaryExpr() );
+ }
+
+ struct Decomposer {
+ template<typename T>
+ auto operator <= ( T const& lhs ) -> ExprLhs<T const&> {
+ return ExprLhs<T const&>{ lhs };
+ }
+
+ auto operator <=( bool value ) -> ExprLhs<bool> {
+ return ExprLhs<bool>{ value };
+ }
+ };
+
+} // end namespace Catch
+
+#ifdef _MSC_VER
+#pragma warning(pop)
+#endif
+
+// end catch_decomposer.h
+// start catch_interfaces_capture.h
+
+#include <string>
+#include <chrono>
+
+namespace Catch {
+
+ class AssertionResult;
+ struct AssertionInfo;
+ struct SectionInfo;
+ struct SectionEndInfo;
+ struct MessageInfo;
+ struct MessageBuilder;
+ struct Counts;
+ struct AssertionReaction;
+ struct SourceLineInfo;
+
+ struct ITransientExpression;
+ struct IGeneratorTracker;
+
+#if defined(CATCH_CONFIG_ENABLE_BENCHMARKING)
+ struct BenchmarkInfo;
+ template <typename Duration = std::chrono::duration<double, std::nano>>
+ struct BenchmarkStats;
+#endif // CATCH_CONFIG_ENABLE_BENCHMARKING
+
+ struct IResultCapture {
+
+ virtual ~IResultCapture();
+
+ virtual bool sectionStarted( SectionInfo const& sectionInfo,
+ Counts& assertions ) = 0;
+ virtual void sectionEnded( SectionEndInfo const& endInfo ) = 0;
+ virtual void sectionEndedEarly( SectionEndInfo const& endInfo ) = 0;
+
+ virtual auto acquireGeneratorTracker( StringRef generatorName, SourceLineInfo const& lineInfo ) -> IGeneratorTracker& = 0;
+
+#if defined(CATCH_CONFIG_ENABLE_BENCHMARKING)
+ virtual void benchmarkPreparing( std::string const& name ) = 0;
+ virtual void benchmarkStarting( BenchmarkInfo const& info ) = 0;
+ virtual void benchmarkEnded( BenchmarkStats<> const& stats ) = 0;
+ virtual void benchmarkFailed( std::string const& error ) = 0;
+#endif // CATCH_CONFIG_ENABLE_BENCHMARKING
+
+ virtual void pushScopedMessage( MessageInfo const& message ) = 0;
+ virtual void popScopedMessage( MessageInfo const& message ) = 0;
+
+ virtual void emplaceUnscopedMessage( MessageBuilder const& builder ) = 0;
+
+ virtual void handleFatalErrorCondition( StringRef message ) = 0;
+
+ virtual void handleExpr
+ ( AssertionInfo const& info,
+ ITransientExpression const& expr,
+ AssertionReaction& reaction ) = 0;
+ virtual void handleMessage
+ ( AssertionInfo const& info,
+ ResultWas::OfType resultType,
+ StringRef const& message,
+ AssertionReaction& reaction ) = 0;
+ virtual void handleUnexpectedExceptionNotThrown
+ ( AssertionInfo const& info,
+ AssertionReaction& reaction ) = 0;
+ virtual void handleUnexpectedInflightException
+ ( AssertionInfo const& info,
+ std::string const& message,
+ AssertionReaction& reaction ) = 0;
+ virtual void handleIncomplete
+ ( AssertionInfo const& info ) = 0;
+ virtual void handleNonExpr
+ ( AssertionInfo const &info,
+ ResultWas::OfType resultType,
+ AssertionReaction &reaction ) = 0;
+
+ virtual bool lastAssertionPassed() = 0;
+ virtual void assertionPassed() = 0;
+
+ // Deprecated, do not use:
+ virtual std::string getCurrentTestName() const = 0;
+ virtual const AssertionResult* getLastResult() const = 0;
+ virtual void exceptionEarlyReported() = 0;
+ };
+
+ IResultCapture& getResultCapture();
+}
+
+// end catch_interfaces_capture.h
+namespace Catch {
+
+ struct TestFailureException{};
+ struct AssertionResultData;
+ struct IResultCapture;
+ class RunContext;
+
+ class LazyExpression {
+ friend class AssertionHandler;
+ friend struct AssertionStats;
+ friend class RunContext;
+
+ ITransientExpression const* m_transientExpression = nullptr;
+ bool m_isNegated;
+ public:
+ LazyExpression( bool isNegated );
+ LazyExpression( LazyExpression const& other );
+ LazyExpression& operator = ( LazyExpression const& ) = delete;
+
+ explicit operator bool() const;
+
+ friend auto operator << ( std::ostream& os, LazyExpression const& lazyExpr ) -> std::ostream&;
+ };
+
+ struct AssertionReaction {
+ bool shouldDebugBreak = false;
+ bool shouldThrow = false;
+ };
+
+ class AssertionHandler {
+ AssertionInfo m_assertionInfo;
+ AssertionReaction m_reaction;
+ bool m_completed = false;
+ IResultCapture& m_resultCapture;
+
+ public:
+ AssertionHandler
+ ( StringRef const& macroName,
+ SourceLineInfo const& lineInfo,
+ StringRef capturedExpression,
+ ResultDisposition::Flags resultDisposition );
+ ~AssertionHandler() {
+ if ( !m_completed ) {
+ m_resultCapture.handleIncomplete( m_assertionInfo );
+ }
+ }
+
+ template<typename T>
+ void handleExpr( ExprLhs<T> const& expr ) {
+ handleExpr( expr.makeUnaryExpr() );
+ }
+ void handleExpr( ITransientExpression const& expr );
+
+ void handleMessage(ResultWas::OfType resultType, StringRef const& message);
+
+ void handleExceptionThrownAsExpected();
+ void handleUnexpectedExceptionNotThrown();
+ void handleExceptionNotThrownAsExpected();
+ void handleThrowingCallSkipped();
+ void handleUnexpectedInflightException();
+
+ void complete();
+ void setCompleted();
+
+ // query
+ auto allowThrows() const -> bool;
+ };
+
+ void handleExceptionMatchExpr( AssertionHandler& handler, std::string const& str, StringRef const& matcherString );
+
+} // namespace Catch
+
+// end catch_assertionhandler.h
+// start catch_message.h
+
+#include <string>
+#include <vector>
+
+namespace Catch {
+
+ struct MessageInfo {
+ MessageInfo( StringRef const& _macroName,
+ SourceLineInfo const& _lineInfo,
+ ResultWas::OfType _type );
+
+ StringRef macroName;
+ std::string message;
+ SourceLineInfo lineInfo;
+ ResultWas::OfType type;
+ unsigned int sequence;
+
+ bool operator == ( MessageInfo const& other ) const;
+ bool operator < ( MessageInfo const& other ) const;
+ private:
+ static unsigned int globalCount;
+ };
+
+ struct MessageStream {
+
+ template<typename T>
+ MessageStream& operator << ( T const& value ) {
+ m_stream << value;
+ return *this;
+ }
+
+ ReusableStringStream m_stream;
+ };
+
+ struct MessageBuilder : MessageStream {
+ MessageBuilder( StringRef const& macroName,
+ SourceLineInfo const& lineInfo,
+ ResultWas::OfType type );
+
+ template<typename T>
+ MessageBuilder& operator << ( T const& value ) {
+ m_stream << value;
+ return *this;
+ }
+
+ MessageInfo m_info;
+ };
+
+ class ScopedMessage {
+ public:
+ explicit ScopedMessage( MessageBuilder const& builder );
+ ScopedMessage( ScopedMessage& duplicate ) = delete;
+ ScopedMessage( ScopedMessage&& old );
+ ~ScopedMessage();
+
+ MessageInfo m_info;
+ bool m_moved;
+ };
+
+ class Capturer {
+ std::vector<MessageInfo> m_messages;
+ IResultCapture& m_resultCapture = getResultCapture();
+ size_t m_captured = 0;
+ public:
+ Capturer( StringRef macroName, SourceLineInfo const& lineInfo, ResultWas::OfType resultType, StringRef names );
+ ~Capturer();
+
+ void captureValue( size_t index, std::string const& value );
+
+ template<typename T>
+ void captureValues( size_t index, T const& value ) {
+ captureValue( index, Catch::Detail::stringify( value ) );
+ }
+
+ template<typename T, typename... Ts>
+ void captureValues( size_t index, T const& value, Ts const&... values ) {
+ captureValue( index, Catch::Detail::stringify(value) );
+ captureValues( index+1, values... );
+ }
+ };
+
+} // end namespace Catch
+
+// end catch_message.h
+#if !defined(CATCH_CONFIG_DISABLE)
+
+#if !defined(CATCH_CONFIG_DISABLE_STRINGIFICATION)
+ #define CATCH_INTERNAL_STRINGIFY(...) #__VA_ARGS__
+#else
+ #define CATCH_INTERNAL_STRINGIFY(...) "Disabled by CATCH_CONFIG_DISABLE_STRINGIFICATION"
+#endif
+
+#if defined(CATCH_CONFIG_FAST_COMPILE) || defined(CATCH_CONFIG_DISABLE_EXCEPTIONS)
+
+///////////////////////////////////////////////////////////////////////////////
+// Another way to speed-up compilation is to omit local try-catch for REQUIRE*
+// macros.
+#define INTERNAL_CATCH_TRY
+#define INTERNAL_CATCH_CATCH( capturer )
+
+#else // CATCH_CONFIG_FAST_COMPILE
+
+#define INTERNAL_CATCH_TRY try
+#define INTERNAL_CATCH_CATCH( handler ) catch(...) { handler.handleUnexpectedInflightException(); }
+
+#endif
+
+#define INTERNAL_CATCH_REACT( handler ) handler.complete();
+
+///////////////////////////////////////////////////////////////////////////////
+#define INTERNAL_CATCH_TEST( macroName, resultDisposition, ... ) \
+ do { \
+ CATCH_INTERNAL_IGNORE_BUT_WARN(__VA_ARGS__); \
+ Catch::AssertionHandler catchAssertionHandler( macroName##_catch_sr, CATCH_INTERNAL_LINEINFO, CATCH_INTERNAL_STRINGIFY(__VA_ARGS__), resultDisposition ); \
+ INTERNAL_CATCH_TRY { \
+ CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
+ CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS \
+ catchAssertionHandler.handleExpr( Catch::Decomposer() <= __VA_ARGS__ ); \
+ CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \
+ } INTERNAL_CATCH_CATCH( catchAssertionHandler ) \
+ INTERNAL_CATCH_REACT( catchAssertionHandler ) \
+ } while( (void)0, (false) && static_cast<bool>( !!(__VA_ARGS__) ) )
+
+///////////////////////////////////////////////////////////////////////////////
+#define INTERNAL_CATCH_IF( macroName, resultDisposition, ... ) \
+ INTERNAL_CATCH_TEST( macroName, resultDisposition, __VA_ARGS__ ); \
+ if( Catch::getResultCapture().lastAssertionPassed() )
+
+///////////////////////////////////////////////////////////////////////////////
+#define INTERNAL_CATCH_ELSE( macroName, resultDisposition, ... ) \
+ INTERNAL_CATCH_TEST( macroName, resultDisposition, __VA_ARGS__ ); \
+ if( !Catch::getResultCapture().lastAssertionPassed() )
+
+///////////////////////////////////////////////////////////////////////////////
+#define INTERNAL_CATCH_NO_THROW( macroName, resultDisposition, ... ) \
+ do { \
+ Catch::AssertionHandler catchAssertionHandler( macroName##_catch_sr, CATCH_INTERNAL_LINEINFO, CATCH_INTERNAL_STRINGIFY(__VA_ARGS__), resultDisposition ); \
+ try { \
+ static_cast<void>(__VA_ARGS__); \
+ catchAssertionHandler.handleExceptionNotThrownAsExpected(); \
+ } \
+ catch( ... ) { \
+ catchAssertionHandler.handleUnexpectedInflightException(); \
+ } \
+ INTERNAL_CATCH_REACT( catchAssertionHandler ) \
+ } while( false )
+
+///////////////////////////////////////////////////////////////////////////////
+#define INTERNAL_CATCH_THROWS( macroName, resultDisposition, ... ) \
+ do { \
+ Catch::AssertionHandler catchAssertionHandler( macroName##_catch_sr, CATCH_INTERNAL_LINEINFO, CATCH_INTERNAL_STRINGIFY(__VA_ARGS__), resultDisposition); \
+ if( catchAssertionHandler.allowThrows() ) \
+ try { \
+ static_cast<void>(__VA_ARGS__); \
+ catchAssertionHandler.handleUnexpectedExceptionNotThrown(); \
+ } \
+ catch( ... ) { \
+ catchAssertionHandler.handleExceptionThrownAsExpected(); \
+ } \
+ else \
+ catchAssertionHandler.handleThrowingCallSkipped(); \
+ INTERNAL_CATCH_REACT( catchAssertionHandler ) \
+ } while( false )
+
+///////////////////////////////////////////////////////////////////////////////
+#define INTERNAL_CATCH_THROWS_AS( macroName, exceptionType, resultDisposition, expr ) \
+ do { \
+ Catch::AssertionHandler catchAssertionHandler( macroName##_catch_sr, CATCH_INTERNAL_LINEINFO, CATCH_INTERNAL_STRINGIFY(expr) ", " CATCH_INTERNAL_STRINGIFY(exceptionType), resultDisposition ); \
+ if( catchAssertionHandler.allowThrows() ) \
+ try { \
+ static_cast<void>(expr); \
+ catchAssertionHandler.handleUnexpectedExceptionNotThrown(); \
+ } \
+ catch( exceptionType const& ) { \
+ catchAssertionHandler.handleExceptionThrownAsExpected(); \
+ } \
+ catch( ... ) { \
+ catchAssertionHandler.handleUnexpectedInflightException(); \
+ } \
+ else \
+ catchAssertionHandler.handleThrowingCallSkipped(); \
+ INTERNAL_CATCH_REACT( catchAssertionHandler ) \
+ } while( false )
+
+///////////////////////////////////////////////////////////////////////////////
+#define INTERNAL_CATCH_MSG( macroName, messageType, resultDisposition, ... ) \
+ do { \
+ Catch::AssertionHandler catchAssertionHandler( macroName##_catch_sr, CATCH_INTERNAL_LINEINFO, Catch::StringRef(), resultDisposition ); \
+ catchAssertionHandler.handleMessage( messageType, ( Catch::MessageStream() << __VA_ARGS__ + ::Catch::StreamEndStop() ).m_stream.str() ); \
+ INTERNAL_CATCH_REACT( catchAssertionHandler ) \
+ } while( false )
+
+///////////////////////////////////////////////////////////////////////////////
+#define INTERNAL_CATCH_CAPTURE( varName, macroName, ... ) \
+ auto varName = Catch::Capturer( macroName, CATCH_INTERNAL_LINEINFO, Catch::ResultWas::Info, #__VA_ARGS__ ); \
+ varName.captureValues( 0, __VA_ARGS__ )
+
+///////////////////////////////////////////////////////////////////////////////
+#define INTERNAL_CATCH_INFO( macroName, log ) \
+ Catch::ScopedMessage INTERNAL_CATCH_UNIQUE_NAME( scopedMessage )( Catch::MessageBuilder( macroName##_catch_sr, CATCH_INTERNAL_LINEINFO, Catch::ResultWas::Info ) << log );
+
+///////////////////////////////////////////////////////////////////////////////
+#define INTERNAL_CATCH_UNSCOPED_INFO( macroName, log ) \
+ Catch::getResultCapture().emplaceUnscopedMessage( Catch::MessageBuilder( macroName##_catch_sr, CATCH_INTERNAL_LINEINFO, Catch::ResultWas::Info ) << log )
+
+///////////////////////////////////////////////////////////////////////////////
+// Although this is matcher-based, it can be used with just a string
+#define INTERNAL_CATCH_THROWS_STR_MATCHES( macroName, resultDisposition, matcher, ... ) \
+ do { \
+ Catch::AssertionHandler catchAssertionHandler( macroName##_catch_sr, CATCH_INTERNAL_LINEINFO, CATCH_INTERNAL_STRINGIFY(__VA_ARGS__) ", " CATCH_INTERNAL_STRINGIFY(matcher), resultDisposition ); \
+ if( catchAssertionHandler.allowThrows() ) \
+ try { \
+ static_cast<void>(__VA_ARGS__); \
+ catchAssertionHandler.handleUnexpectedExceptionNotThrown(); \
+ } \
+ catch( ... ) { \
+ Catch::handleExceptionMatchExpr( catchAssertionHandler, matcher, #matcher##_catch_sr ); \
+ } \
+ else \
+ catchAssertionHandler.handleThrowingCallSkipped(); \
+ INTERNAL_CATCH_REACT( catchAssertionHandler ) \
+ } while( false )
+
+#endif // CATCH_CONFIG_DISABLE
+
+// end catch_capture.hpp
+// start catch_section.h
+
+// start catch_section_info.h
+
+// start catch_totals.h
+
+#include <cstddef>
+
+namespace Catch {
+
+ struct Counts {
+ Counts operator - ( Counts const& other ) const;
+ Counts& operator += ( Counts const& other );
+
+ std::size_t total() const;
+ bool allPassed() const;
+ bool allOk() const;
+
+ std::size_t passed = 0;
+ std::size_t failed = 0;
+ std::size_t failedButOk = 0;
+ };
+
+ struct Totals {
+
+ Totals operator - ( Totals const& other ) const;
+ Totals& operator += ( Totals const& other );
+
+ Totals delta( Totals const& prevTotals ) const;
+
+ int error = 0;
+ Counts assertions;
+ Counts testCases;
+ };
+}
+
+// end catch_totals.h
+#include <string>
+
+namespace Catch {
+
+ struct SectionInfo {
+ SectionInfo
+ ( SourceLineInfo const& _lineInfo,
+ std::string const& _name );
+
+ // Deprecated
+ SectionInfo
+ ( SourceLineInfo const& _lineInfo,
+ std::string const& _name,
+ std::string const& ) : SectionInfo( _lineInfo, _name ) {}
+
+ std::string name;
+ std::string description; // !Deprecated: this will always be empty
+ SourceLineInfo lineInfo;
+ };
+
+ struct SectionEndInfo {
+ SectionInfo sectionInfo;
+ Counts prevAssertions;
+ double durationInSeconds;
+ };
+
+} // end namespace Catch
+
+// end catch_section_info.h
+// start catch_timer.h
+
+#include <cstdint>
+
+namespace Catch {
+
+ auto getCurrentNanosecondsSinceEpoch() -> uint64_t;
+ auto getEstimatedClockResolution() -> uint64_t;
+
+ class Timer {
+ uint64_t m_nanoseconds = 0;
+ public:
+ void start();
+ auto getElapsedNanoseconds() const -> uint64_t;
+ auto getElapsedMicroseconds() const -> uint64_t;
+ auto getElapsedMilliseconds() const -> unsigned int;
+ auto getElapsedSeconds() const -> double;
+ };
+
+} // namespace Catch
+
+// end catch_timer.h
+#include <string>
+
+namespace Catch {
+
+ class Section : NonCopyable {
+ public:
+ Section( SectionInfo const& info );
+ ~Section();
+
+ // This indicates whether the section should be executed or not
+ explicit operator bool() const;
+
+ private:
+ SectionInfo m_info;
+
+ std::string m_name;
+ Counts m_assertions;
+ bool m_sectionIncluded;
+ Timer m_timer;
+ };
+
+} // end namespace Catch
+
+#define INTERNAL_CATCH_SECTION( ... ) \
+ CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
+ CATCH_INTERNAL_SUPPRESS_UNUSED_WARNINGS \
+ if( Catch::Section const& INTERNAL_CATCH_UNIQUE_NAME( catch_internal_Section ) = Catch::SectionInfo( CATCH_INTERNAL_LINEINFO, __VA_ARGS__ ) ) \
+ CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION
+
+#define INTERNAL_CATCH_DYNAMIC_SECTION( ... ) \
+ CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
+ CATCH_INTERNAL_SUPPRESS_UNUSED_WARNINGS \
+ if( Catch::Section const& INTERNAL_CATCH_UNIQUE_NAME( catch_internal_Section ) = Catch::SectionInfo( CATCH_INTERNAL_LINEINFO, (Catch::ReusableStringStream() << __VA_ARGS__).str() ) ) \
+ CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION
+
+// end catch_section.h
+// start catch_interfaces_exception.h
+
+// start catch_interfaces_registry_hub.h
+
+#include <string>
+#include <memory>
+
+namespace Catch {
+
+ class TestCase;
+ struct ITestCaseRegistry;
+ struct IExceptionTranslatorRegistry;
+ struct IExceptionTranslator;
+ struct IReporterRegistry;
+ struct IReporterFactory;
+ struct ITagAliasRegistry;
+ struct IMutableEnumValuesRegistry;
+
+ class StartupExceptionRegistry;
+
+ using IReporterFactoryPtr = std::shared_ptr<IReporterFactory>;
+
+ struct IRegistryHub {
+ virtual ~IRegistryHub();
+
+ virtual IReporterRegistry const& getReporterRegistry() const = 0;
+ virtual ITestCaseRegistry const& getTestCaseRegistry() const = 0;
+ virtual ITagAliasRegistry const& getTagAliasRegistry() const = 0;
+ virtual IExceptionTranslatorRegistry const& getExceptionTranslatorRegistry() const = 0;
+
+ virtual StartupExceptionRegistry const& getStartupExceptionRegistry() const = 0;
+ };
+
+ struct IMutableRegistryHub {
+ virtual ~IMutableRegistryHub();
+ virtual void registerReporter( std::string const& name, IReporterFactoryPtr const& factory ) = 0;
+ virtual void registerListener( IReporterFactoryPtr const& factory ) = 0;
+ virtual void registerTest( TestCase const& testInfo ) = 0;
+ virtual void registerTranslator( const IExceptionTranslator* translator ) = 0;
+ virtual void registerTagAlias( std::string const& alias, std::string const& tag, SourceLineInfo const& lineInfo ) = 0;
+ virtual void registerStartupException() noexcept = 0;
+ virtual IMutableEnumValuesRegistry& getMutableEnumValuesRegistry() = 0;
+ };
+
+ IRegistryHub const& getRegistryHub();
+ IMutableRegistryHub& getMutableRegistryHub();
+ void cleanUp();
+ std::string translateActiveException();
+
+}
+
+// end catch_interfaces_registry_hub.h
+#if defined(CATCH_CONFIG_DISABLE)
+ #define INTERNAL_CATCH_TRANSLATE_EXCEPTION_NO_REG( translatorName, signature) \
+ static std::string translatorName( signature )
+#endif
+
+#include <exception>
+#include <string>
+#include <vector>
+
+namespace Catch {
+ using exceptionTranslateFunction = std::string(*)();
+
+ struct IExceptionTranslator;
+ using ExceptionTranslators = std::vector<std::unique_ptr<IExceptionTranslator const>>;
+
+ struct IExceptionTranslator {
+ virtual ~IExceptionTranslator();
+ virtual std::string translate( ExceptionTranslators::const_iterator it, ExceptionTranslators::const_iterator itEnd ) const = 0;
+ };
+
+ struct IExceptionTranslatorRegistry {
+ virtual ~IExceptionTranslatorRegistry();
+
+ virtual std::string translateActiveException() const = 0;
+ };
+
+ class ExceptionTranslatorRegistrar {
+ template<typename T>
+ class ExceptionTranslator : public IExceptionTranslator {
+ public:
+
+ ExceptionTranslator( std::string(*translateFunction)( T& ) )
+ : m_translateFunction( translateFunction )
+ {}
+
+ std::string translate( ExceptionTranslators::const_iterator it, ExceptionTranslators::const_iterator itEnd ) const override {
+#if defined(CATCH_CONFIG_DISABLE_EXCEPTIONS)
+ return "";
+#else
+ try {
+ if( it == itEnd )
+ std::rethrow_exception(std::current_exception());
+ else
+ return (*it)->translate( it+1, itEnd );
+ }
+ catch( T& ex ) {
+ return m_translateFunction( ex );
+ }
+#endif
+ }
+
+ protected:
+ std::string(*m_translateFunction)( T& );
+ };
+
+ public:
+ template<typename T>
+ ExceptionTranslatorRegistrar( std::string(*translateFunction)( T& ) ) {
+ getMutableRegistryHub().registerTranslator
+ ( new ExceptionTranslator<T>( translateFunction ) );
+ }
+ };
+}
+
+///////////////////////////////////////////////////////////////////////////////
+#define INTERNAL_CATCH_TRANSLATE_EXCEPTION2( translatorName, signature ) \
+ static std::string translatorName( signature ); \
+ CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
+ CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
+ namespace{ Catch::ExceptionTranslatorRegistrar INTERNAL_CATCH_UNIQUE_NAME( catch_internal_ExceptionRegistrar )( &translatorName ); } \
+ CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \
+ static std::string translatorName( signature )
+
+#define INTERNAL_CATCH_TRANSLATE_EXCEPTION( signature ) INTERNAL_CATCH_TRANSLATE_EXCEPTION2( INTERNAL_CATCH_UNIQUE_NAME( catch_internal_ExceptionTranslator ), signature )
+
+// end catch_interfaces_exception.h
+// start catch_approx.h
+
+#include <type_traits>
+
+namespace Catch {
+namespace Detail {
+
+ class Approx {
+ private:
+ bool equalityComparisonImpl(double other) const;
+ // Validates the new margin (margin >= 0)
+ // out-of-line to avoid including stdexcept in the header
+ void setMargin(double margin);
+ // Validates the new epsilon (0 < epsilon < 1)
+ // out-of-line to avoid including stdexcept in the header
+ void setEpsilon(double epsilon);
+
+ public:
+ explicit Approx ( double value );
+
+ static Approx custom();
+
+ Approx operator-() const;
+
+ template <typename T, typename = typename std::enable_if<std::is_constructible<double, T>::value>::type>
+ Approx operator()( T const& value ) const {
+ Approx approx( static_cast<double>(value) );
+ approx.m_epsilon = m_epsilon;
+ approx.m_margin = m_margin;
+ approx.m_scale = m_scale;
+ return approx;
+ }
+
+ template <typename T, typename = typename std::enable_if<std::is_constructible<double, T>::value>::type>
+ explicit Approx( T const& value ): Approx(static_cast<double>(value))
+ {}
+
+ template <typename T, typename = typename std::enable_if<std::is_constructible<double, T>::value>::type>
+ friend bool operator == ( const T& lhs, Approx const& rhs ) {
+ auto lhs_v = static_cast<double>(lhs);
+ return rhs.equalityComparisonImpl(lhs_v);
+ }
+
+ template <typename T, typename = typename std::enable_if<std::is_constructible<double, T>::value>::type>
+ friend bool operator == ( Approx const& lhs, const T& rhs ) {
+ return operator==( rhs, lhs );
+ }
+
+ template <typename T, typename = typename std::enable_if<std::is_constructible<double, T>::value>::type>
+ friend bool operator != ( T const& lhs, Approx const& rhs ) {
+ return !operator==( lhs, rhs );
+ }
+
+ template <typename T, typename = typename std::enable_if<std::is_constructible<double, T>::value>::type>
+ friend bool operator != ( Approx const& lhs, T const& rhs ) {
+ return !operator==( rhs, lhs );
+ }
+
+ template <typename T, typename = typename std::enable_if<std::is_constructible<double, T>::value>::type>
+ friend bool operator <= ( T const& lhs, Approx const& rhs ) {
+ return static_cast<double>(lhs) < rhs.m_value || lhs == rhs;
+ }
+
+ template <typename T, typename = typename std::enable_if<std::is_constructible<double, T>::value>::type>
+ friend bool operator <= ( Approx const& lhs, T const& rhs ) {
+ return lhs.m_value < static_cast<double>(rhs) || lhs == rhs;
+ }
+
+ template <typename T, typename = typename std::enable_if<std::is_constructible<double, T>::value>::type>
+ friend bool operator >= ( T const& lhs, Approx const& rhs ) {
+ return static_cast<double>(lhs) > rhs.m_value || lhs == rhs;
+ }
+
+ template <typename T, typename = typename std::enable_if<std::is_constructible<double, T>::value>::type>
+ friend bool operator >= ( Approx const& lhs, T const& rhs ) {
+ return lhs.m_value > static_cast<double>(rhs) || lhs == rhs;
+ }
+
+ template <typename T, typename = typename std::enable_if<std::is_constructible<double, T>::value>::type>
+ Approx& epsilon( T const& newEpsilon ) {
+ double epsilonAsDouble = static_cast<double>(newEpsilon);
+ setEpsilon(epsilonAsDouble);
+ return *this;
+ }
+
+ template <typename T, typename = typename std::enable_if<std::is_constructible<double, T>::value>::type>
+ Approx& margin( T const& newMargin ) {
+ double marginAsDouble = static_cast<double>(newMargin);
+ setMargin(marginAsDouble);
+ return *this;
+ }
+
+ template <typename T, typename = typename std::enable_if<std::is_constructible<double, T>::value>::type>
+ Approx& scale( T const& newScale ) {
+ m_scale = static_cast<double>(newScale);
+ return *this;
+ }
+
+ std::string toString() const;
+
+ private:
+ double m_epsilon;
+ double m_margin;
+ double m_scale;
+ double m_value;
+ };
+} // end namespace Detail
+
+namespace literals {
+ Detail::Approx operator "" _a(long double val);
+ Detail::Approx operator "" _a(unsigned long long val);
+} // end namespace literals
+
+template<>
+struct StringMaker<Catch::Detail::Approx> {
+ static std::string convert(Catch::Detail::Approx const& value);
+};
+
+} // end namespace Catch
+
+// end catch_approx.h
+// start catch_string_manip.h
+
+#include <string>
+#include <iosfwd>
+#include <vector>
+
+namespace Catch {
+
+ bool startsWith( std::string const& s, std::string const& prefix );
+ bool startsWith( std::string const& s, char prefix );
+ bool endsWith( std::string const& s, std::string const& suffix );
+ bool endsWith( std::string const& s, char suffix );
+ bool contains( std::string const& s, std::string const& infix );
+ void toLowerInPlace( std::string& s );
+ std::string toLower( std::string const& s );
+ //! Returns a new string without whitespace at the start/end
+ std::string trim( std::string const& str );
+ //! Returns a substring of the original ref without whitespace. Beware lifetimes!
+ StringRef trim(StringRef ref);
+
+ // !!! Be aware, returns refs into original string - make sure original string outlives them
+ std::vector<StringRef> splitStringRef( StringRef str, char delimiter );
+ bool replaceInPlace( std::string& str, std::string const& replaceThis, std::string const& withThis );
+
+ struct pluralise {
+ pluralise( std::size_t count, std::string const& label );
+
+ friend std::ostream& operator << ( std::ostream& os, pluralise const& pluraliser );
+
+ std::size_t m_count;
+ std::string m_label;
+ };
+}
+
+// end catch_string_manip.h
+#ifndef CATCH_CONFIG_DISABLE_MATCHERS
+// start catch_capture_matchers.h
+
+// start catch_matchers.h
+
+#include <string>
+#include <vector>
+
+namespace Catch {
+namespace Matchers {
+ namespace Impl {
+
+ template<typename ArgT> struct MatchAllOf;
+ template<typename ArgT> struct MatchAnyOf;
+ template<typename ArgT> struct MatchNotOf;
+
+ class MatcherUntypedBase {
+ public:
+ MatcherUntypedBase() = default;
+ MatcherUntypedBase ( MatcherUntypedBase const& ) = default;
+ MatcherUntypedBase& operator = ( MatcherUntypedBase const& ) = delete;
+ std::string toString() const;
+
+ protected:
+ virtual ~MatcherUntypedBase();
+ virtual std::string describe() const = 0;
+ mutable std::string m_cachedToString;
+ };
+
+#ifdef __clang__
+# pragma clang diagnostic push
+# pragma clang diagnostic ignored "-Wnon-virtual-dtor"
+#endif
+
+ template<typename ObjectT>
+ struct MatcherMethod {
+ virtual bool match( ObjectT const& arg ) const = 0;
+ };
+
+#if defined(__OBJC__)
+ // Hack to fix Catch GH issue #1661. Could use id for generic Object support.
+ // use of const for Object pointers is very uncommon and under ARC it causes some kind of signature mismatch that breaks compilation
+ template<>
+ struct MatcherMethod<NSString*> {
+ virtual bool match( NSString* arg ) const = 0;
+ };
+#endif
+
+#ifdef __clang__
+# pragma clang diagnostic pop
+#endif
+
+ template<typename T>
+ struct MatcherBase : MatcherUntypedBase, MatcherMethod<T> {
+
+ MatchAllOf<T> operator && ( MatcherBase const& other ) const;
+ MatchAnyOf<T> operator || ( MatcherBase const& other ) const;
+ MatchNotOf<T> operator ! () const;
+ };
+
+ template<typename ArgT>
+ struct MatchAllOf : MatcherBase<ArgT> {
+ bool match( ArgT const& arg ) const override {
+ for( auto matcher : m_matchers ) {
+ if (!matcher->match(arg))
+ return false;
+ }
+ return true;
+ }
+ std::string describe() const override {
+ std::string description;
+ description.reserve( 4 + m_matchers.size()*32 );
+ description += "( ";
+ bool first = true;
+ for( auto matcher : m_matchers ) {
+ if( first )
+ first = false;
+ else
+ description += " and ";
+ description += matcher->toString();
+ }
+ description += " )";
+ return description;
+ }
+
+ MatchAllOf<ArgT> operator && ( MatcherBase<ArgT> const& other ) {
+ auto copy(*this);
+ copy.m_matchers.push_back( &other );
+ return copy;
+ }
+
+ std::vector<MatcherBase<ArgT> const*> m_matchers;
+ };
+ template<typename ArgT>
+ struct MatchAnyOf : MatcherBase<ArgT> {
+
+ bool match( ArgT const& arg ) const override {
+ for( auto matcher : m_matchers ) {
+ if (matcher->match(arg))
+ return true;
+ }
+ return false;
+ }
+ std::string describe() const override {
+ std::string description;
+ description.reserve( 4 + m_matchers.size()*32 );
+ description += "( ";
+ bool first = true;
+ for( auto matcher : m_matchers ) {
+ if( first )
+ first = false;
+ else
+ description += " or ";
+ description += matcher->toString();
+ }
+ description += " )";
+ return description;
+ }
+
+ MatchAnyOf<ArgT> operator || ( MatcherBase<ArgT> const& other ) {
+ auto copy(*this);
+ copy.m_matchers.push_back( &other );
+ return copy;
+ }
+
+ std::vector<MatcherBase<ArgT> const*> m_matchers;
+ };
+
+ template<typename ArgT>
+ struct MatchNotOf : MatcherBase<ArgT> {
+
+ MatchNotOf( MatcherBase<ArgT> const& underlyingMatcher ) : m_underlyingMatcher( underlyingMatcher ) {}
+
+ bool match( ArgT const& arg ) const override {
+ return !m_underlyingMatcher.match( arg );
+ }
+
+ std::string describe() const override {
+ return "not " + m_underlyingMatcher.toString();
+ }
+ MatcherBase<ArgT> const& m_underlyingMatcher;
+ };
+
+ template<typename T>
+ MatchAllOf<T> MatcherBase<T>::operator && ( MatcherBase const& other ) const {
+ return MatchAllOf<T>() && *this && other;
+ }
+ template<typename T>
+ MatchAnyOf<T> MatcherBase<T>::operator || ( MatcherBase const& other ) const {
+ return MatchAnyOf<T>() || *this || other;
+ }
+ template<typename T>
+ MatchNotOf<T> MatcherBase<T>::operator ! () const {
+ return MatchNotOf<T>( *this );
+ }
+
+ } // namespace Impl
+
+} // namespace Matchers
+
+using namespace Matchers;
+using Matchers::Impl::MatcherBase;
+
+} // namespace Catch
+
+// end catch_matchers.h
+// start catch_matchers_exception.hpp
+
+namespace Catch {
+namespace Matchers {
+namespace Exception {
+
+class ExceptionMessageMatcher : public MatcherBase<std::exception> {
+ std::string m_message;
+public:
+
+ ExceptionMessageMatcher(std::string const& message):
+ m_message(message)
+ {}
+
+ bool match(std::exception const& ex) const override;
+
+ std::string describe() const override;
+};
+
+} // namespace Exception
+
+Exception::ExceptionMessageMatcher Message(std::string const& message);
+
+} // namespace Matchers
+} // namespace Catch
+
+// end catch_matchers_exception.hpp
+// start catch_matchers_floating.h
+
+namespace Catch {
+namespace Matchers {
+
+ namespace Floating {
+
+ enum class FloatingPointKind : uint8_t;
+
+ struct WithinAbsMatcher : MatcherBase<double> {
+ WithinAbsMatcher(double target, double margin);
+ bool match(double const& matchee) const override;
+ std::string describe() const override;
+ private:
+ double m_target;
+ double m_margin;
+ };
+
+ struct WithinUlpsMatcher : MatcherBase<double> {
+ WithinUlpsMatcher(double target, uint64_t ulps, FloatingPointKind baseType);
+ bool match(double const& matchee) const override;
+ std::string describe() const override;
+ private:
+ double m_target;
+ uint64_t m_ulps;
+ FloatingPointKind m_type;
+ };
+
+ // Given IEEE-754 format for floats and doubles, we can assume
+ // that float -> double promotion is lossless. Given this, we can
+ // assume that if we do the standard relative comparison of
+ // |lhs - rhs| <= epsilon * max(fabs(lhs), fabs(rhs)), then we get
+ // the same result if we do this for floats, as if we do this for
+ // doubles that were promoted from floats.
+ struct WithinRelMatcher : MatcherBase<double> {
+ WithinRelMatcher(double target, double epsilon);
+ bool match(double const& matchee) const override;
+ std::string describe() const override;
+ private:
+ double m_target;
+ double m_epsilon;
+ };
+
+ } // namespace Floating
+
+ // The following functions create the actual matcher objects.
+ // This allows the types to be inferred
+ Floating::WithinUlpsMatcher WithinULP(double target, uint64_t maxUlpDiff);
+ Floating::WithinUlpsMatcher WithinULP(float target, uint64_t maxUlpDiff);
+ Floating::WithinAbsMatcher WithinAbs(double target, double margin);
+ Floating::WithinRelMatcher WithinRel(double target, double eps);
+ // defaults epsilon to 100*numeric_limits<double>::epsilon()
+ Floating::WithinRelMatcher WithinRel(double target);
+ Floating::WithinRelMatcher WithinRel(float target, float eps);
+ // defaults epsilon to 100*numeric_limits<float>::epsilon()
+ Floating::WithinRelMatcher WithinRel(float target);
+
+} // namespace Matchers
+} // namespace Catch
+
+// end catch_matchers_floating.h
+// start catch_matchers_generic.hpp
+
+#include <functional>
+#include <string>
+
+namespace Catch {
+namespace Matchers {
+namespace Generic {
+
+namespace Detail {
+ std::string finalizeDescription(const std::string& desc);
+}
+
+template <typename T>
+class PredicateMatcher : public MatcherBase<T> {
+ std::function<bool(T const&)> m_predicate;
+ std::string m_description;
+public:
+
+ PredicateMatcher(std::function<bool(T const&)> const& elem, std::string const& descr)
+ :m_predicate(std::move(elem)),
+ m_description(Detail::finalizeDescription(descr))
+ {}
+
+ bool match( T const& item ) const override {
+ return m_predicate(item);
+ }
+
+ std::string describe() const override {
+ return m_description;
+ }
+};
+
+} // namespace Generic
+
+ // The following functions create the actual matcher objects.
+ // The user has to explicitly specify type to the function, because
+ // inferring std::function<bool(T const&)> is hard (but possible) and
+ // requires a lot of TMP.
+ template<typename T>
+ Generic::PredicateMatcher<T> Predicate(std::function<bool(T const&)> const& predicate, std::string const& description = "") {
+ return Generic::PredicateMatcher<T>(predicate, description);
+ }
+
+} // namespace Matchers
+} // namespace Catch
+
+// end catch_matchers_generic.hpp
+// start catch_matchers_string.h
+
+#include <string>
+
+namespace Catch {
+namespace Matchers {
+
+ namespace StdString {
+
+ struct CasedString
+ {
+ CasedString( std::string const& str, CaseSensitive::Choice caseSensitivity );
+ std::string adjustString( std::string const& str ) const;
+ std::string caseSensitivitySuffix() const;
+
+ CaseSensitive::Choice m_caseSensitivity;
+ std::string m_str;
+ };
+
+ struct StringMatcherBase : MatcherBase<std::string> {
+ StringMatcherBase( std::string const& operation, CasedString const& comparator );
+ std::string describe() const override;
+
+ CasedString m_comparator;
+ std::string m_operation;
+ };
+
+ struct EqualsMatcher : StringMatcherBase {
+ EqualsMatcher( CasedString const& comparator );
+ bool match( std::string const& source ) const override;
+ };
+ struct ContainsMatcher : StringMatcherBase {
+ ContainsMatcher( CasedString const& comparator );
+ bool match( std::string const& source ) const override;
+ };
+ struct StartsWithMatcher : StringMatcherBase {
+ StartsWithMatcher( CasedString const& comparator );
+ bool match( std::string const& source ) const override;
+ };
+ struct EndsWithMatcher : StringMatcherBase {
+ EndsWithMatcher( CasedString const& comparator );
+ bool match( std::string const& source ) const override;
+ };
+
+ struct RegexMatcher : MatcherBase<std::string> {
+ RegexMatcher( std::string regex, CaseSensitive::Choice caseSensitivity );
+ bool match( std::string const& matchee ) const override;
+ std::string describe() const override;
+
+ private:
+ std::string m_regex;
+ CaseSensitive::Choice m_caseSensitivity;
+ };
+
+ } // namespace StdString
+
+ // The following functions create the actual matcher objects.
+ // This allows the types to be inferred
+
+ StdString::EqualsMatcher Equals( std::string const& str, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes );
+ StdString::ContainsMatcher Contains( std::string const& str, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes );
+ StdString::EndsWithMatcher EndsWith( std::string const& str, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes );
+ StdString::StartsWithMatcher StartsWith( std::string const& str, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes );
+ StdString::RegexMatcher Matches( std::string const& regex, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes );
+
+} // namespace Matchers
+} // namespace Catch
+
+// end catch_matchers_string.h
+// start catch_matchers_vector.h
+
+#include <algorithm>
+
+namespace Catch {
+namespace Matchers {
+
+ namespace Vector {
+ template<typename T, typename Alloc>
+ struct ContainsElementMatcher : MatcherBase<std::vector<T, Alloc>> {
+
+ ContainsElementMatcher(T const &comparator) : m_comparator( comparator) {}
+
+ bool match(std::vector<T, Alloc> const &v) const override {
+ for (auto const& el : v) {
+ if (el == m_comparator) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ std::string describe() const override {
+ return "Contains: " + ::Catch::Detail::stringify( m_comparator );
+ }
+
+ T const& m_comparator;
+ };
+
+ template<typename T, typename AllocComp, typename AllocMatch>
+ struct ContainsMatcher : MatcherBase<std::vector<T, AllocMatch>> {
+
+ ContainsMatcher(std::vector<T, AllocComp> const &comparator) : m_comparator( comparator ) {}
+
+ bool match(std::vector<T, AllocMatch> const &v) const override {
+ // !TBD: see note in EqualsMatcher
+ if (m_comparator.size() > v.size())
+ return false;
+ for (auto const& comparator : m_comparator) {
+ auto present = false;
+ for (const auto& el : v) {
+ if (el == comparator) {
+ present = true;
+ break;
+ }
+ }
+ if (!present) {
+ return false;
+ }
+ }
+ return true;
+ }
+ std::string describe() const override {
+ return "Contains: " + ::Catch::Detail::stringify( m_comparator );
+ }
+
+ std::vector<T, AllocComp> const& m_comparator;
+ };
+
+ template<typename T, typename AllocComp, typename AllocMatch>
+ struct EqualsMatcher : MatcherBase<std::vector<T, AllocMatch>> {
+
+ EqualsMatcher(std::vector<T, AllocComp> const &comparator) : m_comparator( comparator ) {}
+
+ bool match(std::vector<T, AllocMatch> const &v) const override {
+ // !TBD: This currently works if all elements can be compared using !=
+ // - a more general approach would be via a compare template that defaults
+ // to using !=. but could be specialised for, e.g. std::vector<T, Alloc> etc
+ // - then just call that directly
+ if (m_comparator.size() != v.size())
+ return false;
+ for (std::size_t i = 0; i < v.size(); ++i)
+ if (m_comparator[i] != v[i])
+ return false;
+ return true;
+ }
+ std::string describe() const override {
+ return "Equals: " + ::Catch::Detail::stringify( m_comparator );
+ }
+ std::vector<T, AllocComp> const& m_comparator;
+ };
+
+ template<typename T, typename AllocComp, typename AllocMatch>
+ struct ApproxMatcher : MatcherBase<std::vector<T, AllocMatch>> {
+
+ ApproxMatcher(std::vector<T, AllocComp> const& comparator) : m_comparator( comparator ) {}
+
+ bool match(std::vector<T, AllocMatch> const &v) const override {
+ if (m_comparator.size() != v.size())
+ return false;
+ for (std::size_t i = 0; i < v.size(); ++i)
+ if (m_comparator[i] != approx(v[i]))
+ return false;
+ return true;
+ }
+ std::string describe() const override {
+ return "is approx: " + ::Catch::Detail::stringify( m_comparator );
+ }
+ template <typename = typename std::enable_if<std::is_constructible<double, T>::value>::type>
+ ApproxMatcher& epsilon( T const& newEpsilon ) {
+ approx.epsilon(newEpsilon);
+ return *this;
+ }
+ template <typename = typename std::enable_if<std::is_constructible<double, T>::value>::type>
+ ApproxMatcher& margin( T const& newMargin ) {
+ approx.margin(newMargin);
+ return *this;
+ }
+ template <typename = typename std::enable_if<std::is_constructible<double, T>::value>::type>
+ ApproxMatcher& scale( T const& newScale ) {
+ approx.scale(newScale);
+ return *this;
+ }
+
+ std::vector<T, AllocComp> const& m_comparator;
+ mutable Catch::Detail::Approx approx = Catch::Detail::Approx::custom();
+ };
+
+ template<typename T, typename AllocComp, typename AllocMatch>
+ struct UnorderedEqualsMatcher : MatcherBase<std::vector<T, AllocMatch>> {
+ UnorderedEqualsMatcher(std::vector<T, AllocComp> const& target) : m_target(target) {}
+ bool match(std::vector<T, AllocMatch> const& vec) const override {
+ if (m_target.size() != vec.size()) {
+ return false;
+ }
+ return std::is_permutation(m_target.begin(), m_target.end(), vec.begin());
+ }
+
+ std::string describe() const override {
+ return "UnorderedEquals: " + ::Catch::Detail::stringify(m_target);
+ }
+ private:
+ std::vector<T, AllocComp> const& m_target;
+ };
+
+ } // namespace Vector
+
+ // The following functions create the actual matcher objects.
+ // This allows the types to be inferred
+
+ template<typename T, typename AllocComp = std::allocator<T>, typename AllocMatch = AllocComp>
+ Vector::ContainsMatcher<T, AllocComp, AllocMatch> Contains( std::vector<T, AllocComp> const& comparator ) {
+ return Vector::ContainsMatcher<T, AllocComp, AllocMatch>( comparator );
+ }
+
+ template<typename T, typename Alloc = std::allocator<T>>
+ Vector::ContainsElementMatcher<T, Alloc> VectorContains( T const& comparator ) {
+ return Vector::ContainsElementMatcher<T, Alloc>( comparator );
+ }
+
+ template<typename T, typename AllocComp = std::allocator<T>, typename AllocMatch = AllocComp>
+ Vector::EqualsMatcher<T, AllocComp, AllocMatch> Equals( std::vector<T, AllocComp> const& comparator ) {
+ return Vector::EqualsMatcher<T, AllocComp, AllocMatch>( comparator );
+ }
+
+ template<typename T, typename AllocComp = std::allocator<T>, typename AllocMatch = AllocComp>
+ Vector::ApproxMatcher<T, AllocComp, AllocMatch> Approx( std::vector<T, AllocComp> const& comparator ) {
+ return Vector::ApproxMatcher<T, AllocComp, AllocMatch>( comparator );
+ }
+
+ template<typename T, typename AllocComp = std::allocator<T>, typename AllocMatch = AllocComp>
+ Vector::UnorderedEqualsMatcher<T, AllocComp, AllocMatch> UnorderedEquals(std::vector<T, AllocComp> const& target) {
+ return Vector::UnorderedEqualsMatcher<T, AllocComp, AllocMatch>( target );
+ }
+
+} // namespace Matchers
+} // namespace Catch
+
+// end catch_matchers_vector.h
+namespace Catch {
+
+ template<typename ArgT, typename MatcherT>
+ class MatchExpr : public ITransientExpression {
+ ArgT const& m_arg;
+ MatcherT m_matcher;
+ StringRef m_matcherString;
+ public:
+ MatchExpr( ArgT const& arg, MatcherT const& matcher, StringRef const& matcherString )
+ : ITransientExpression{ true, matcher.match( arg ) },
+ m_arg( arg ),
+ m_matcher( matcher ),
+ m_matcherString( matcherString )
+ {}
+
+ void streamReconstructedExpression( std::ostream &os ) const override {
+ auto matcherAsString = m_matcher.toString();
+ os << Catch::Detail::stringify( m_arg ) << ' ';
+ if( matcherAsString == Detail::unprintableString )
+ os << m_matcherString;
+ else
+ os << matcherAsString;
+ }
+ };
+
+ using StringMatcher = Matchers::Impl::MatcherBase<std::string>;
+
+ void handleExceptionMatchExpr( AssertionHandler& handler, StringMatcher const& matcher, StringRef const& matcherString );
+
+ template<typename ArgT, typename MatcherT>
+ auto makeMatchExpr( ArgT const& arg, MatcherT const& matcher, StringRef const& matcherString ) -> MatchExpr<ArgT, MatcherT> {
+ return MatchExpr<ArgT, MatcherT>( arg, matcher, matcherString );
+ }
+
+} // namespace Catch
+
+///////////////////////////////////////////////////////////////////////////////
+#define INTERNAL_CHECK_THAT( macroName, matcher, resultDisposition, arg ) \
+ do { \
+ Catch::AssertionHandler catchAssertionHandler( macroName##_catch_sr, CATCH_INTERNAL_LINEINFO, CATCH_INTERNAL_STRINGIFY(arg) ", " CATCH_INTERNAL_STRINGIFY(matcher), resultDisposition ); \
+ INTERNAL_CATCH_TRY { \
+ catchAssertionHandler.handleExpr( Catch::makeMatchExpr( arg, matcher, #matcher##_catch_sr ) ); \
+ } INTERNAL_CATCH_CATCH( catchAssertionHandler ) \
+ INTERNAL_CATCH_REACT( catchAssertionHandler ) \
+ } while( false )
+
+///////////////////////////////////////////////////////////////////////////////
+#define INTERNAL_CATCH_THROWS_MATCHES( macroName, exceptionType, resultDisposition, matcher, ... ) \
+ do { \
+ Catch::AssertionHandler catchAssertionHandler( macroName##_catch_sr, CATCH_INTERNAL_LINEINFO, CATCH_INTERNAL_STRINGIFY(__VA_ARGS__) ", " CATCH_INTERNAL_STRINGIFY(exceptionType) ", " CATCH_INTERNAL_STRINGIFY(matcher), resultDisposition ); \
+ if( catchAssertionHandler.allowThrows() ) \
+ try { \
+ static_cast<void>(__VA_ARGS__ ); \
+ catchAssertionHandler.handleUnexpectedExceptionNotThrown(); \
+ } \
+ catch( exceptionType const& ex ) { \
+ catchAssertionHandler.handleExpr( Catch::makeMatchExpr( ex, matcher, #matcher##_catch_sr ) ); \
+ } \
+ catch( ... ) { \
+ catchAssertionHandler.handleUnexpectedInflightException(); \
+ } \
+ else \
+ catchAssertionHandler.handleThrowingCallSkipped(); \
+ INTERNAL_CATCH_REACT( catchAssertionHandler ) \
+ } while( false )
+
+// end catch_capture_matchers.h
+#endif
+// start catch_generators.hpp
+
+// start catch_interfaces_generatortracker.h
+
+
+#include <memory>
+
+namespace Catch {
+
+ namespace Generators {
+ class GeneratorUntypedBase {
+ public:
+ GeneratorUntypedBase() = default;
+ virtual ~GeneratorUntypedBase();
+ // Attempts to move the generator to the next element
+ //
+ // Returns true iff the move succeeded (and a valid element
+ // can be retrieved).
+ virtual bool next() = 0;
+ };
+ using GeneratorBasePtr = std::unique_ptr<GeneratorUntypedBase>;
+
+ } // namespace Generators
+
+ struct IGeneratorTracker {
+ virtual ~IGeneratorTracker();
+ virtual auto hasGenerator() const -> bool = 0;
+ virtual auto getGenerator() const -> Generators::GeneratorBasePtr const& = 0;
+ virtual void setGenerator( Generators::GeneratorBasePtr&& generator ) = 0;
+ };
+
+} // namespace Catch
+
+// end catch_interfaces_generatortracker.h
+// start catch_enforce.h
+
+#include <exception>
+
+namespace Catch {
+#if !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS)
+ template <typename Ex>
+ [[noreturn]]
+ void throw_exception(Ex const& e) {
+ throw e;
+ }
+#else // ^^ Exceptions are enabled // Exceptions are disabled vv
+ [[noreturn]]
+ void throw_exception(std::exception const& e);
+#endif
+
+ [[noreturn]]
+ void throw_logic_error(std::string const& msg);
+ [[noreturn]]
+ void throw_domain_error(std::string const& msg);
+ [[noreturn]]
+ void throw_runtime_error(std::string const& msg);
+
+} // namespace Catch;
+
+#define CATCH_MAKE_MSG(...) \
+ (Catch::ReusableStringStream() << __VA_ARGS__).str()
+
+#define CATCH_INTERNAL_ERROR(...) \
+ Catch::throw_logic_error(CATCH_MAKE_MSG( CATCH_INTERNAL_LINEINFO << ": Internal Catch2 error: " << __VA_ARGS__))
+
+#define CATCH_ERROR(...) \
+ Catch::throw_domain_error(CATCH_MAKE_MSG( __VA_ARGS__ ))
+
+#define CATCH_RUNTIME_ERROR(...) \
+ Catch::throw_runtime_error(CATCH_MAKE_MSG( __VA_ARGS__ ))
+
+#define CATCH_ENFORCE( condition, ... ) \
+ do{ if( !(condition) ) CATCH_ERROR( __VA_ARGS__ ); } while(false)
+
+// end catch_enforce.h
+#include <memory>
+#include <vector>
+#include <cassert>
+
+#include <utility>
+#include <exception>
+
+namespace Catch {
+
+class GeneratorException : public std::exception {
+ const char* const m_msg = "";
+
+public:
+ GeneratorException(const char* msg):
+ m_msg(msg)
+ {}
+
+ const char* what() const noexcept override final;
+};
+
+namespace Generators {
+
+ // !TBD move this into its own location?
+ namespace pf{
+ template<typename T, typename... Args>
+ std::unique_ptr<T> make_unique( Args&&... args ) {
+ return std::unique_ptr<T>(new T(std::forward<Args>(args)...));
+ }
+ }
+
+ template<typename T>
+ struct IGenerator : GeneratorUntypedBase {
+ virtual ~IGenerator() = default;
+
+ // Returns the current element of the generator
+ //
+ // \Precondition The generator is either freshly constructed,
+ // or the last call to `next()` returned true
+ virtual T const& get() const = 0;
+ using type = T;
+ };
+
+ template<typename T>
+ class SingleValueGenerator final : public IGenerator<T> {
+ T m_value;
+ public:
+ SingleValueGenerator(T&& value) : m_value(std::move(value)) {}
+
+ T const& get() const override {
+ return m_value;
+ }
+ bool next() override {
+ return false;
+ }
+ };
+
+ template<typename T>
+ class FixedValuesGenerator final : public IGenerator<T> {
+ static_assert(!std::is_same<T, bool>::value,
+ "FixedValuesGenerator does not support bools because of std::vector<bool>"
+ "specialization, use SingleValue Generator instead.");
+ std::vector<T> m_values;
+ size_t m_idx = 0;
+ public:
+ FixedValuesGenerator( std::initializer_list<T> values ) : m_values( values ) {}
+
+ T const& get() const override {
+ return m_values[m_idx];
+ }
+ bool next() override {
+ ++m_idx;
+ return m_idx < m_values.size();
+ }
+ };
+
+ template <typename T>
+ class GeneratorWrapper final {
+ std::unique_ptr<IGenerator<T>> m_generator;
+ public:
+ GeneratorWrapper(std::unique_ptr<IGenerator<T>> generator):
+ m_generator(std::move(generator))
+ {}
+ T const& get() const {
+ return m_generator->get();
+ }
+ bool next() {
+ return m_generator->next();
+ }
+ };
+
+ template <typename T>
+ GeneratorWrapper<T> value(T&& value) {
+ return GeneratorWrapper<T>(pf::make_unique<SingleValueGenerator<T>>(std::forward<T>(value)));
+ }
+ template <typename T>
+ GeneratorWrapper<T> values(std::initializer_list<T> values) {
+ return GeneratorWrapper<T>(pf::make_unique<FixedValuesGenerator<T>>(values));
+ }
+
+ template<typename T>
+ class Generators : public IGenerator<T> {
+ std::vector<GeneratorWrapper<T>> m_generators;
+ size_t m_current = 0;
+
+ void populate(GeneratorWrapper<T>&& generator) {
+ m_generators.emplace_back(std::move(generator));
+ }
+ void populate(T&& val) {
+ m_generators.emplace_back(value(std::forward<T>(val)));
+ }
+ template<typename U>
+ void populate(U&& val) {
+ populate(T(std::forward<U>(val)));
+ }
+ template<typename U, typename... Gs>
+ void populate(U&& valueOrGenerator, Gs &&... moreGenerators) {
+ populate(std::forward<U>(valueOrGenerator));
+ populate(std::forward<Gs>(moreGenerators)...);
+ }
+
+ public:
+ template <typename... Gs>
+ Generators(Gs &&... moreGenerators) {
+ m_generators.reserve(sizeof...(Gs));
+ populate(std::forward<Gs>(moreGenerators)...);
+ }
+
+ T const& get() const override {
+ return m_generators[m_current].get();
+ }
+
+ bool next() override {
+ if (m_current >= m_generators.size()) {
+ return false;
+ }
+ const bool current_status = m_generators[m_current].next();
+ if (!current_status) {
+ ++m_current;
+ }
+ return m_current < m_generators.size();
+ }
+ };
+
+ template<typename... Ts>
+ GeneratorWrapper<std::tuple<Ts...>> table( std::initializer_list<std::tuple<typename std::decay<Ts>::type...>> tuples ) {
+ return values<std::tuple<Ts...>>( tuples );
+ }
+
+ // Tag type to signal that a generator sequence should convert arguments to a specific type
+ template <typename T>
+ struct as {};
+
+ template<typename T, typename... Gs>
+ auto makeGenerators( GeneratorWrapper<T>&& generator, Gs &&... moreGenerators ) -> Generators<T> {
+ return Generators<T>(std::move(generator), std::forward<Gs>(moreGenerators)...);
+ }
+ template<typename T>
+ auto makeGenerators( GeneratorWrapper<T>&& generator ) -> Generators<T> {
+ return Generators<T>(std::move(generator));
+ }
+ template<typename T, typename... Gs>
+ auto makeGenerators( T&& val, Gs &&... moreGenerators ) -> Generators<T> {
+ return makeGenerators( value( std::forward<T>( val ) ), std::forward<Gs>( moreGenerators )... );
+ }
+ template<typename T, typename U, typename... Gs>
+ auto makeGenerators( as<T>, U&& val, Gs &&... moreGenerators ) -> Generators<T> {
+ return makeGenerators( value( T( std::forward<U>( val ) ) ), std::forward<Gs>( moreGenerators )... );
+ }
+
+ auto acquireGeneratorTracker( StringRef generatorName, SourceLineInfo const& lineInfo ) -> IGeneratorTracker&;
+
+ template<typename L>
+ // Note: The type after -> is weird, because VS2015 cannot parse
+ // the expression used in the typedef inside, when it is in
+ // return type. Yeah.
+ auto generate( StringRef generatorName, SourceLineInfo const& lineInfo, L const& generatorExpression ) -> decltype(std::declval<decltype(generatorExpression())>().get()) {
+ using UnderlyingType = typename decltype(generatorExpression())::type;
+
+ IGeneratorTracker& tracker = acquireGeneratorTracker( generatorName, lineInfo );
+ if (!tracker.hasGenerator()) {
+ tracker.setGenerator(pf::make_unique<Generators<UnderlyingType>>(generatorExpression()));
+ }
+
+ auto const& generator = static_cast<IGenerator<UnderlyingType> const&>( *tracker.getGenerator() );
+ return generator.get();
+ }
+
+} // namespace Generators
+} // namespace Catch
+
+#define GENERATE( ... ) \
+ Catch::Generators::generate( INTERNAL_CATCH_STRINGIZE(INTERNAL_CATCH_UNIQUE_NAME(generator)), \
+ CATCH_INTERNAL_LINEINFO, \
+ [ ]{ using namespace Catch::Generators; return makeGenerators( __VA_ARGS__ ); } ) //NOLINT(google-build-using-namespace)
+#define GENERATE_COPY( ... ) \
+ Catch::Generators::generate( INTERNAL_CATCH_STRINGIZE(INTERNAL_CATCH_UNIQUE_NAME(generator)), \
+ CATCH_INTERNAL_LINEINFO, \
+ [=]{ using namespace Catch::Generators; return makeGenerators( __VA_ARGS__ ); } ) //NOLINT(google-build-using-namespace)
+#define GENERATE_REF( ... ) \
+ Catch::Generators::generate( INTERNAL_CATCH_STRINGIZE(INTERNAL_CATCH_UNIQUE_NAME(generator)), \
+ CATCH_INTERNAL_LINEINFO, \
+ [&]{ using namespace Catch::Generators; return makeGenerators( __VA_ARGS__ ); } ) //NOLINT(google-build-using-namespace)
+
+// end catch_generators.hpp
+// start catch_generators_generic.hpp
+
+namespace Catch {
+namespace Generators {
+
+ template <typename T>
+ class TakeGenerator : public IGenerator<T> {
+ GeneratorWrapper<T> m_generator;
+ size_t m_returned = 0;
+ size_t m_target;
+ public:
+ TakeGenerator(size_t target, GeneratorWrapper<T>&& generator):
+ m_generator(std::move(generator)),
+ m_target(target)
+ {
+ assert(target != 0 && "Empty generators are not allowed");
+ }
+ T const& get() const override {
+ return m_generator.get();
+ }
+ bool next() override {
+ ++m_returned;
+ if (m_returned >= m_target) {
+ return false;
+ }
+
+ const auto success = m_generator.next();
+ // If the underlying generator does not contain enough values
+ // then we cut short as well
+ if (!success) {
+ m_returned = m_target;
+ }
+ return success;
+ }
+ };
+
+ template <typename T>
+ GeneratorWrapper<T> take(size_t target, GeneratorWrapper<T>&& generator) {
+ return GeneratorWrapper<T>(pf::make_unique<TakeGenerator<T>>(target, std::move(generator)));
+ }
+
+ template <typename T, typename Predicate>
+ class FilterGenerator : public IGenerator<T> {
+ GeneratorWrapper<T> m_generator;
+ Predicate m_predicate;
+ public:
+ template <typename P = Predicate>
+ FilterGenerator(P&& pred, GeneratorWrapper<T>&& generator):
+ m_generator(std::move(generator)),
+ m_predicate(std::forward<P>(pred))
+ {
+ if (!m_predicate(m_generator.get())) {
+ // It might happen that there are no values that pass the
+ // filter. In that case we throw an exception.
+ auto has_initial_value = nextImpl();
+ if (!has_initial_value) {
+ Catch::throw_exception(GeneratorException("No valid value found in filtered generator"));
+ }
+ }
+ }
+
+ T const& get() const override {
+ return m_generator.get();
+ }
+
+ bool next() override {
+ return nextImpl();
+ }
+
+ private:
+ bool nextImpl() {
+ bool success = m_generator.next();
+ if (!success) {
+ return false;
+ }
+ while (!m_predicate(m_generator.get()) && (success = m_generator.next()) == true);
+ return success;
+ }
+ };
+
+ template <typename T, typename Predicate>
+ GeneratorWrapper<T> filter(Predicate&& pred, GeneratorWrapper<T>&& generator) {
+ return GeneratorWrapper<T>(std::unique_ptr<IGenerator<T>>(pf::make_unique<FilterGenerator<T, Predicate>>(std::forward<Predicate>(pred), std::move(generator))));
+ }
+
+ template <typename T>
+ class RepeatGenerator : public IGenerator<T> {
+ static_assert(!std::is_same<T, bool>::value,
+ "RepeatGenerator currently does not support bools"
+ "because of std::vector<bool> specialization");
+ GeneratorWrapper<T> m_generator;
+ mutable std::vector<T> m_returned;
+ size_t m_target_repeats;
+ size_t m_current_repeat = 0;
+ size_t m_repeat_index = 0;
+ public:
+ RepeatGenerator(size_t repeats, GeneratorWrapper<T>&& generator):
+ m_generator(std::move(generator)),
+ m_target_repeats(repeats)
+ {
+ assert(m_target_repeats > 0 && "Repeat generator must repeat at least once");
+ }
+
+ T const& get() const override {
+ if (m_current_repeat == 0) {
+ m_returned.push_back(m_generator.get());
+ return m_returned.back();
+ }
+ return m_returned[m_repeat_index];
+ }
+
+ bool next() override {
+ // There are 2 basic cases:
+ // 1) We are still reading the generator
+ // 2) We are reading our own cache
+
+ // In the first case, we need to poke the underlying generator.
+ // If it happily moves, we are left in that state, otherwise it is time to start reading from our cache
+ if (m_current_repeat == 0) {
+ const auto success = m_generator.next();
+ if (!success) {
+ ++m_current_repeat;
+ }
+ return m_current_repeat < m_target_repeats;
+ }
+
+ // In the second case, we need to move indices forward and check that we haven't run up against the end
+ ++m_repeat_index;
+ if (m_repeat_index == m_returned.size()) {
+ m_repeat_index = 0;
+ ++m_current_repeat;
+ }
+ return m_current_repeat < m_target_repeats;
+ }
+ };
+
+ template <typename T>
+ GeneratorWrapper<T> repeat(size_t repeats, GeneratorWrapper<T>&& generator) {
+ return GeneratorWrapper<T>(pf::make_unique<RepeatGenerator<T>>(repeats, std::move(generator)));
+ }
+
+ template <typename T, typename U, typename Func>
+ class MapGenerator : public IGenerator<T> {
+ // TBD: provide static assert for mapping function, for friendly error message
+ GeneratorWrapper<U> m_generator;
+ Func m_function;
+ // To avoid returning dangling reference, we have to save the values
+ T m_cache;
+ public:
+ template <typename F2 = Func>
+ MapGenerator(F2&& function, GeneratorWrapper<U>&& generator) :
+ m_generator(std::move(generator)),
+ m_function(std::forward<F2>(function)),
+ m_cache(m_function(m_generator.get()))
+ {}
+
+ T const& get() const override {
+ return m_cache;
+ }
+ bool next() override {
+ const auto success = m_generator.next();
+ if (success) {
+ m_cache = m_function(m_generator.get());
+ }
+ return success;
+ }
+ };
+
+ template <typename Func, typename U, typename T = FunctionReturnType<Func, U>>
+ GeneratorWrapper<T> map(Func&& function, GeneratorWrapper<U>&& generator) {
+ return GeneratorWrapper<T>(
+ pf::make_unique<MapGenerator<T, U, Func>>(std::forward<Func>(function), std::move(generator))
+ );
+ }
+
+ template <typename T, typename U, typename Func>
+ GeneratorWrapper<T> map(Func&& function, GeneratorWrapper<U>&& generator) {
+ return GeneratorWrapper<T>(
+ pf::make_unique<MapGenerator<T, U, Func>>(std::forward<Func>(function), std::move(generator))
+ );
+ }
+
+ template <typename T>
+ class ChunkGenerator final : public IGenerator<std::vector<T>> {
+ std::vector<T> m_chunk;
+ size_t m_chunk_size;
+ GeneratorWrapper<T> m_generator;
+ bool m_used_up = false;
+ public:
+ ChunkGenerator(size_t size, GeneratorWrapper<T> generator) :
+ m_chunk_size(size), m_generator(std::move(generator))
+ {
+ m_chunk.reserve(m_chunk_size);
+ if (m_chunk_size != 0) {
+ m_chunk.push_back(m_generator.get());
+ for (size_t i = 1; i < m_chunk_size; ++i) {
+ if (!m_generator.next()) {
+ Catch::throw_exception(GeneratorException("Not enough values to initialize the first chunk"));
+ }
+ m_chunk.push_back(m_generator.get());
+ }
+ }
+ }
+ std::vector<T> const& get() const override {
+ return m_chunk;
+ }
+ bool next() override {
+ m_chunk.clear();
+ for (size_t idx = 0; idx < m_chunk_size; ++idx) {
+ if (!m_generator.next()) {
+ return false;
+ }
+ m_chunk.push_back(m_generator.get());
+ }
+ return true;
+ }
+ };
+
+ template <typename T>
+ GeneratorWrapper<std::vector<T>> chunk(size_t size, GeneratorWrapper<T>&& generator) {
+ return GeneratorWrapper<std::vector<T>>(
+ pf::make_unique<ChunkGenerator<T>>(size, std::move(generator))
+ );
+ }
+
+} // namespace Generators
+} // namespace Catch
+
+// end catch_generators_generic.hpp
+// start catch_generators_specific.hpp
+
+// start catch_context.h
+
+#include <memory>
+
+namespace Catch {
+
+ struct IResultCapture;
+ struct IRunner;
+ struct IConfig;
+ struct IMutableContext;
+
+ using IConfigPtr = std::shared_ptr<IConfig const>;
+
+ struct IContext
+ {
+ virtual ~IContext();
+
+ virtual IResultCapture* getResultCapture() = 0;
+ virtual IRunner* getRunner() = 0;
+ virtual IConfigPtr const& getConfig() const = 0;
+ };
+
+ struct IMutableContext : IContext
+ {
+ virtual ~IMutableContext();
+ virtual void setResultCapture( IResultCapture* resultCapture ) = 0;
+ virtual void setRunner( IRunner* runner ) = 0;
+ virtual void setConfig( IConfigPtr const& config ) = 0;
+
+ private:
+ static IMutableContext *currentContext;
+ friend IMutableContext& getCurrentMutableContext();
+ friend void cleanUpContext();
+ static void createContext();
+ };
+
+ inline IMutableContext& getCurrentMutableContext()
+ {
+ if( !IMutableContext::currentContext )
+ IMutableContext::createContext();
+ // NOLINTNEXTLINE(clang-analyzer-core.uninitialized.UndefReturn)
+ return *IMutableContext::currentContext;
+ }
+
+ inline IContext& getCurrentContext()
+ {
+ return getCurrentMutableContext();
+ }
+
+ void cleanUpContext();
+
+ class SimplePcg32;
+ SimplePcg32& rng();
+}
+
+// end catch_context.h
+// start catch_interfaces_config.h
+
+// start catch_option.hpp
+
+namespace Catch {
+
+ // An optional type
+ template<typename T>
+ class Option {
+ public:
+ Option() : nullableValue( nullptr ) {}
+ Option( T const& _value )
+ : nullableValue( new( storage ) T( _value ) )
+ {}
+ Option( Option const& _other )
+ : nullableValue( _other ? new( storage ) T( *_other ) : nullptr )
+ {}
+
+ ~Option() {
+ reset();
+ }
+
+ Option& operator= ( Option const& _other ) {
+ if( &_other != this ) {
+ reset();
+ if( _other )
+ nullableValue = new( storage ) T( *_other );
+ }
+ return *this;
+ }
+ Option& operator = ( T const& _value ) {
+ reset();
+ nullableValue = new( storage ) T( _value );
+ return *this;
+ }
+
+ void reset() {
+ if( nullableValue )
+ nullableValue->~T();
+ nullableValue = nullptr;
+ }
+
+ T& operator*() { return *nullableValue; }
+ T const& operator*() const { return *nullableValue; }
+ T* operator->() { return nullableValue; }
+ const T* operator->() const { return nullableValue; }
+
+ T valueOr( T const& defaultValue ) const {
+ return nullableValue ? *nullableValue : defaultValue;
+ }
+
+ bool some() const { return nullableValue != nullptr; }
+ bool none() const { return nullableValue == nullptr; }
+
+ bool operator !() const { return nullableValue == nullptr; }
+ explicit operator bool() const {
+ return some();
+ }
+
+ private:
+ T *nullableValue;
+ alignas(alignof(T)) char storage[sizeof(T)];
+ };
+
+} // end namespace Catch
+
+// end catch_option.hpp
+#include <chrono>
+#include <iosfwd>
+#include <string>
+#include <vector>
+#include <memory>
+
+namespace Catch {
+
+ enum class Verbosity {
+ Quiet = 0,
+ Normal,
+ High
+ };
+
+ struct WarnAbout { enum What {
+ Nothing = 0x00,
+ NoAssertions = 0x01,
+ NoTests = 0x02
+ }; };
+
+ struct ShowDurations { enum OrNot {
+ DefaultForReporter,
+ Always,
+ Never
+ }; };
+ struct RunTests { enum InWhatOrder {
+ InDeclarationOrder,
+ InLexicographicalOrder,
+ InRandomOrder
+ }; };
+ struct UseColour { enum YesOrNo {
+ Auto,
+ Yes,
+ No
+ }; };
+ struct WaitForKeypress { enum When {
+ Never,
+ BeforeStart = 1,
+ BeforeExit = 2,
+ BeforeStartAndExit = BeforeStart | BeforeExit
+ }; };
+
+ class TestSpec;
+
+ struct IConfig : NonCopyable {
+
+ virtual ~IConfig();
+
+ virtual bool allowThrows() const = 0;
+ virtual std::ostream& stream() const = 0;
+ virtual std::string name() const = 0;
+ virtual bool includeSuccessfulResults() const = 0;
+ virtual bool shouldDebugBreak() const = 0;
+ virtual bool warnAboutMissingAssertions() const = 0;
+ virtual bool warnAboutNoTests() const = 0;
+ virtual int abortAfter() const = 0;
+ virtual bool showInvisibles() const = 0;
+ virtual ShowDurations::OrNot showDurations() const = 0;
+ virtual double minDuration() const = 0;
+ virtual TestSpec const& testSpec() const = 0;
+ virtual bool hasTestFilters() const = 0;
+ virtual std::vector<std::string> const& getTestsOrTags() const = 0;
+ virtual RunTests::InWhatOrder runOrder() const = 0;
+ virtual unsigned int rngSeed() const = 0;
+ virtual UseColour::YesOrNo useColour() const = 0;
+ virtual std::vector<std::string> const& getSectionsToRun() const = 0;
+ virtual Verbosity verbosity() const = 0;
+
+ virtual bool benchmarkNoAnalysis() const = 0;
+ virtual int benchmarkSamples() const = 0;
+ virtual double benchmarkConfidenceInterval() const = 0;
+ virtual unsigned int benchmarkResamples() const = 0;
+ virtual std::chrono::milliseconds benchmarkWarmupTime() const = 0;
+ };
+
+ using IConfigPtr = std::shared_ptr<IConfig const>;
+}
+
+// end catch_interfaces_config.h
+// start catch_random_number_generator.h
+
+#include <cstdint>
+
+namespace Catch {
+
+ // This is a simple implementation of C++11 Uniform Random Number
+ // Generator. It does not provide all operators, because Catch2
+ // does not use it, but it should behave as expected inside stdlib's
+ // distributions.
+ // The implementation is based on the PCG family (http://pcg-random.org)
+ class SimplePcg32 {
+ using state_type = std::uint64_t;
+ public:
+ using result_type = std::uint32_t;
+ static constexpr result_type (min)() {
+ return 0;
+ }
+ static constexpr result_type (max)() {
+ return static_cast<result_type>(-1);
+ }
+
+ // Provide some default initial state for the default constructor
+ SimplePcg32():SimplePcg32(0xed743cc4U) {}
+
+ explicit SimplePcg32(result_type seed_);
+
+ void seed(result_type seed_);
+ void discard(uint64_t skip);
+
+ result_type operator()();
+
+ private:
+ friend bool operator==(SimplePcg32 const& lhs, SimplePcg32 const& rhs);
+ friend bool operator!=(SimplePcg32 const& lhs, SimplePcg32 const& rhs);
+
+ // In theory we also need operator<< and operator>>
+ // In practice we do not use them, so we will skip them for now
+
+ std::uint64_t m_state;
+ // This part of the state determines which "stream" of the numbers
+ // is chosen -- we take it as a constant for Catch2, so we only
+ // need to deal with seeding the main state.
+ // Picked by reading 8 bytes from `/dev/random` :-)
+ static const std::uint64_t s_inc = (0x13ed0cc53f939476ULL << 1ULL) | 1ULL;
+ };
+
+} // end namespace Catch
+
+// end catch_random_number_generator.h
+#include <random>
+
+namespace Catch {
+namespace Generators {
+
+template <typename Float>
+class RandomFloatingGenerator final : public IGenerator<Float> {
+ Catch::SimplePcg32& m_rng;
+ std::uniform_real_distribution<Float> m_dist;
+ Float m_current_number;
+public:
+
+ RandomFloatingGenerator(Float a, Float b):
+ m_rng(rng()),
+ m_dist(a, b) {
+ static_cast<void>(next());
+ }
+
+ Float const& get() const override {
+ return m_current_number;
+ }
+ bool next() override {
+ m_current_number = m_dist(m_rng);
+ return true;
+ }
+};
+
+template <typename Integer>
+class RandomIntegerGenerator final : public IGenerator<Integer> {
+ Catch::SimplePcg32& m_rng;
+ std::uniform_int_distribution<Integer> m_dist;
+ Integer m_current_number;
+public:
+
+ RandomIntegerGenerator(Integer a, Integer b):
+ m_rng(rng()),
+ m_dist(a, b) {
+ static_cast<void>(next());
+ }
+
+ Integer const& get() const override {
+ return m_current_number;
+ }
+ bool next() override {
+ m_current_number = m_dist(m_rng);
+ return true;
+ }
+};
+
+// TODO: Ideally this would be also constrained against the various char types,
+// but I don't expect users to run into that in practice.
+template <typename T>
+typename std::enable_if<std::is_integral<T>::value && !std::is_same<T, bool>::value,
+GeneratorWrapper<T>>::type
+random(T a, T b) {
+ return GeneratorWrapper<T>(
+ pf::make_unique<RandomIntegerGenerator<T>>(a, b)
+ );
+}
+
+template <typename T>
+typename std::enable_if<std::is_floating_point<T>::value,
+GeneratorWrapper<T>>::type
+random(T a, T b) {
+ return GeneratorWrapper<T>(
+ pf::make_unique<RandomFloatingGenerator<T>>(a, b)
+ );
+}
+
+template <typename T>
+class RangeGenerator final : public IGenerator<T> {
+ T m_current;
+ T m_end;
+ T m_step;
+ bool m_positive;
+
+public:
+ RangeGenerator(T const& start, T const& end, T const& step):
+ m_current(start),
+ m_end(end),
+ m_step(step),
+ m_positive(m_step > T(0))
+ {
+ assert(m_current != m_end && "Range start and end cannot be equal");
+ assert(m_step != T(0) && "Step size cannot be zero");
+ assert(((m_positive && m_current <= m_end) || (!m_positive && m_current >= m_end)) && "Step moves away from end");
+ }
+
+ RangeGenerator(T const& start, T const& end):
+ RangeGenerator(start, end, (start < end) ? T(1) : T(-1))
+ {}
+
+ T const& get() const override {
+ return m_current;
+ }
+
+ bool next() override {
+ m_current += m_step;
+ return (m_positive) ? (m_current < m_end) : (m_current > m_end);
+ }
+};
+
+template <typename T>
+GeneratorWrapper<T> range(T const& start, T const& end, T const& step) {
+ static_assert(std::is_arithmetic<T>::value && !std::is_same<T, bool>::value, "Type must be numeric");
+ return GeneratorWrapper<T>(pf::make_unique<RangeGenerator<T>>(start, end, step));
+}
+
+template <typename T>
+GeneratorWrapper<T> range(T const& start, T const& end) {
+ static_assert(std::is_integral<T>::value && !std::is_same<T, bool>::value, "Type must be an integer");
+ return GeneratorWrapper<T>(pf::make_unique<RangeGenerator<T>>(start, end));
+}
+
+template <typename T>
+class IteratorGenerator final : public IGenerator<T> {
+ static_assert(!std::is_same<T, bool>::value,
+ "IteratorGenerator currently does not support bools"
+ "because of std::vector<bool> specialization");
+
+ std::vector<T> m_elems;
+ size_t m_current = 0;
+public:
+ template <typename InputIterator, typename InputSentinel>
+ IteratorGenerator(InputIterator first, InputSentinel last):m_elems(first, last) {
+ if (m_elems.empty()) {
+ Catch::throw_exception(GeneratorException("IteratorGenerator received no valid values"));
+ }
+ }
+
+ T const& get() const override {
+ return m_elems[m_current];
+ }
+
+ bool next() override {
+ ++m_current;
+ return m_current != m_elems.size();
+ }
+};
+
+template <typename InputIterator,
+ typename InputSentinel,
+ typename ResultType = typename std::iterator_traits<InputIterator>::value_type>
+GeneratorWrapper<ResultType> from_range(InputIterator from, InputSentinel to) {
+ return GeneratorWrapper<ResultType>(pf::make_unique<IteratorGenerator<ResultType>>(from, to));
+}
+
+template <typename Container,
+ typename ResultType = typename Container::value_type>
+GeneratorWrapper<ResultType> from_range(Container const& cnt) {
+ return GeneratorWrapper<ResultType>(pf::make_unique<IteratorGenerator<ResultType>>(cnt.begin(), cnt.end()));
+}
+
+} // namespace Generators
+} // namespace Catch
+
+// end catch_generators_specific.hpp
+
+// These files are included here so the single_include script doesn't put them
+// in the conditionally compiled sections
+// start catch_test_case_info.h
+
+#include <string>
+#include <vector>
+#include <memory>
+
+#ifdef __clang__
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wpadded"
+#endif
+
+namespace Catch {
+
+ struct ITestInvoker;
+
+ struct TestCaseInfo {
+ enum SpecialProperties{
+ None = 0,
+ IsHidden = 1 << 1,
+ ShouldFail = 1 << 2,
+ MayFail = 1 << 3,
+ Throws = 1 << 4,
+ NonPortable = 1 << 5,
+ Benchmark = 1 << 6
+ };
+
+ TestCaseInfo( std::string const& _name,
+ std::string const& _className,
+ std::string const& _description,
+ std::vector<std::string> const& _tags,
+ SourceLineInfo const& _lineInfo );
+
+ friend void setTags( TestCaseInfo& testCaseInfo, std::vector<std::string> tags );
+
+ bool isHidden() const;
+ bool throws() const;
+ bool okToFail() const;
+ bool expectedToFail() const;
+
+ std::string tagsAsString() const;
+
+ std::string name;
+ std::string className;
+ std::string description;
+ std::vector<std::string> tags;
+ std::vector<std::string> lcaseTags;
+ SourceLineInfo lineInfo;
+ SpecialProperties properties;
+ };
+
+ class TestCase : public TestCaseInfo {
+ public:
+
+ TestCase( ITestInvoker* testCase, TestCaseInfo&& info );
+
+ TestCase withName( std::string const& _newName ) const;
+
+ void invoke() const;
+
+ TestCaseInfo const& getTestCaseInfo() const;
+
+ bool operator == ( TestCase const& other ) const;
+ bool operator < ( TestCase const& other ) const;
+
+ private:
+ std::shared_ptr<ITestInvoker> test;
+ };
+
+ TestCase makeTestCase( ITestInvoker* testCase,
+ std::string const& className,
+ NameAndTags const& nameAndTags,
+ SourceLineInfo const& lineInfo );
+}
+
+#ifdef __clang__
+#pragma clang diagnostic pop
+#endif
+
+// end catch_test_case_info.h
+// start catch_interfaces_runner.h
+
+namespace Catch {
+
+ struct IRunner {
+ virtual ~IRunner();
+ virtual bool aborting() const = 0;
+ };
+}
+
+// end catch_interfaces_runner.h
+
+#ifdef __OBJC__
+// start catch_objc.hpp
+
+#import <objc/runtime.h>
+
+#include <string>
+
+// NB. Any general catch headers included here must be included
+// in catch.hpp first to make sure they are included by the single
+// header for non obj-usage
+
+///////////////////////////////////////////////////////////////////////////////
+// This protocol is really only here for (self) documenting purposes, since
+// all its methods are optional.
+@protocol OcFixture
+
+@optional
+
+-(void) setUp;
+-(void) tearDown;
+
+@end
+
+namespace Catch {
+
+ class OcMethod : public ITestInvoker {
+
+ public:
+ OcMethod( Class cls, SEL sel ) : m_cls( cls ), m_sel( sel ) {}
+
+ virtual void invoke() const {
+ id obj = [[m_cls alloc] init];
+
+ performOptionalSelector( obj, @selector(setUp) );
+ performOptionalSelector( obj, m_sel );
+ performOptionalSelector( obj, @selector(tearDown) );
+
+ arcSafeRelease( obj );
+ }
+ private:
+ virtual ~OcMethod() {}
+
+ Class m_cls;
+ SEL m_sel;
+ };
+
+ namespace Detail{
+
+ inline std::string getAnnotation( Class cls,
+ std::string const& annotationName,
+ std::string const& testCaseName ) {
+ NSString* selStr = [[NSString alloc] initWithFormat:@"Catch_%s_%s", annotationName.c_str(), testCaseName.c_str()];
+ SEL sel = NSSelectorFromString( selStr );
+ arcSafeRelease( selStr );
+ id value = performOptionalSelector( cls, sel );
+ if( value )
+ return [(NSString*)value UTF8String];
+ return "";
+ }
+ }
+
+ inline std::size_t registerTestMethods() {
+ std::size_t noTestMethods = 0;
+ int noClasses = objc_getClassList( nullptr, 0 );
+
+ Class* classes = (CATCH_UNSAFE_UNRETAINED Class *)malloc( sizeof(Class) * noClasses);
+ objc_getClassList( classes, noClasses );
+
+ for( int c = 0; c < noClasses; c++ ) {
+ Class cls = classes[c];
+ {
+ u_int count;
+ Method* methods = class_copyMethodList( cls, &count );
+ for( u_int m = 0; m < count ; m++ ) {
+ SEL selector = method_getName(methods[m]);
+ std::string methodName = sel_getName(selector);
+ if( startsWith( methodName, "Catch_TestCase_" ) ) {
+ std::string testCaseName = methodName.substr( 15 );
+ std::string name = Detail::getAnnotation( cls, "Name", testCaseName );
+ std::string desc = Detail::getAnnotation( cls, "Description", testCaseName );
+ const char* className = class_getName( cls );
+
+ getMutableRegistryHub().registerTest( makeTestCase( new OcMethod( cls, selector ), className, NameAndTags( name.c_str(), desc.c_str() ), SourceLineInfo("",0) ) );
+ noTestMethods++;
+ }
+ }
+ free(methods);
+ }
+ }
+ return noTestMethods;
+ }
+
+#if !defined(CATCH_CONFIG_DISABLE_MATCHERS)
+
+ namespace Matchers {
+ namespace Impl {
+ namespace NSStringMatchers {
+
+ struct StringHolder : MatcherBase<NSString*>{
+ StringHolder( NSString* substr ) : m_substr( [substr copy] ){}
+ StringHolder( StringHolder const& other ) : m_substr( [other.m_substr copy] ){}
+ StringHolder() {
+ arcSafeRelease( m_substr );
+ }
+
+ bool match( NSString* str ) const override {
+ return false;
+ }
+
+ NSString* CATCH_ARC_STRONG m_substr;
+ };
+
+ struct Equals : StringHolder {
+ Equals( NSString* substr ) : StringHolder( substr ){}
+
+ bool match( NSString* str ) const override {
+ return (str != nil || m_substr == nil ) &&
+ [str isEqualToString:m_substr];
+ }
+
+ std::string describe() const override {
+ return "equals string: " + Catch::Detail::stringify( m_substr );
+ }
+ };
+
+ struct Contains : StringHolder {
+ Contains( NSString* substr ) : StringHolder( substr ){}
+
+ bool match( NSString* str ) const override {
+ return (str != nil || m_substr == nil ) &&
+ [str rangeOfString:m_substr].location != NSNotFound;
+ }
+
+ std::string describe() const override {
+ return "contains string: " + Catch::Detail::stringify( m_substr );
+ }
+ };
+
+ struct StartsWith : StringHolder {
+ StartsWith( NSString* substr ) : StringHolder( substr ){}
+
+ bool match( NSString* str ) const override {
+ return (str != nil || m_substr == nil ) &&
+ [str rangeOfString:m_substr].location == 0;
+ }
+
+ std::string describe() const override {
+ return "starts with: " + Catch::Detail::stringify( m_substr );
+ }
+ };
+ struct EndsWith : StringHolder {
+ EndsWith( NSString* substr ) : StringHolder( substr ){}
+
+ bool match( NSString* str ) const override {
+ return (str != nil || m_substr == nil ) &&
+ [str rangeOfString:m_substr].location == [str length] - [m_substr length];
+ }
+
+ std::string describe() const override {
+ return "ends with: " + Catch::Detail::stringify( m_substr );
+ }
+ };
+
+ } // namespace NSStringMatchers
+ } // namespace Impl
+
+ inline Impl::NSStringMatchers::Equals
+ Equals( NSString* substr ){ return Impl::NSStringMatchers::Equals( substr ); }
+
+ inline Impl::NSStringMatchers::Contains
+ Contains( NSString* substr ){ return Impl::NSStringMatchers::Contains( substr ); }
+
+ inline Impl::NSStringMatchers::StartsWith
+ StartsWith( NSString* substr ){ return Impl::NSStringMatchers::StartsWith( substr ); }
+
+ inline Impl::NSStringMatchers::EndsWith
+ EndsWith( NSString* substr ){ return Impl::NSStringMatchers::EndsWith( substr ); }
+
+ } // namespace Matchers
+
+ using namespace Matchers;
+
+#endif // CATCH_CONFIG_DISABLE_MATCHERS
+
+} // namespace Catch
+
+///////////////////////////////////////////////////////////////////////////////
+#define OC_MAKE_UNIQUE_NAME( root, uniqueSuffix ) root##uniqueSuffix
+#define OC_TEST_CASE2( name, desc, uniqueSuffix ) \
++(NSString*) OC_MAKE_UNIQUE_NAME( Catch_Name_test_, uniqueSuffix ) \
+{ \
+return @ name; \
+} \
++(NSString*) OC_MAKE_UNIQUE_NAME( Catch_Description_test_, uniqueSuffix ) \
+{ \
+return @ desc; \
+} \
+-(void) OC_MAKE_UNIQUE_NAME( Catch_TestCase_test_, uniqueSuffix )
+
+#define OC_TEST_CASE( name, desc ) OC_TEST_CASE2( name, desc, __LINE__ )
+
+// end catch_objc.hpp
+#endif
+
+// Benchmarking needs the externally-facing parts of reporters to work
+#if defined(CATCH_CONFIG_EXTERNAL_INTERFACES) || defined(CATCH_CONFIG_ENABLE_BENCHMARKING)
+// start catch_external_interfaces.h
+
+// start catch_reporter_bases.hpp
+
+// start catch_interfaces_reporter.h
+
+// start catch_config.hpp
+
+// start catch_test_spec_parser.h
+
+#ifdef __clang__
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wpadded"
+#endif
+
+// start catch_test_spec.h
+
+#ifdef __clang__
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wpadded"
+#endif
+
+// start catch_wildcard_pattern.h
+
+namespace Catch
+{
+ class WildcardPattern {
+ enum WildcardPosition {
+ NoWildcard = 0,
+ WildcardAtStart = 1,
+ WildcardAtEnd = 2,
+ WildcardAtBothEnds = WildcardAtStart | WildcardAtEnd
+ };
+
+ public:
+
+ WildcardPattern( std::string const& pattern, CaseSensitive::Choice caseSensitivity );
+ virtual ~WildcardPattern() = default;
+ virtual bool matches( std::string const& str ) const;
+
+ private:
+ std::string normaliseString( std::string const& str ) const;
+ CaseSensitive::Choice m_caseSensitivity;
+ WildcardPosition m_wildcard = NoWildcard;
+ std::string m_pattern;
+ };
+}
+
+// end catch_wildcard_pattern.h
+#include <string>
+#include <vector>
+#include <memory>
+
+namespace Catch {
+
+ struct IConfig;
+
+ class TestSpec {
+ class Pattern {
+ public:
+ explicit Pattern( std::string const& name );
+ virtual ~Pattern();
+ virtual bool matches( TestCaseInfo const& testCase ) const = 0;
+ std::string const& name() const;
+ private:
+ std::string const m_name;
+ };
+ using PatternPtr = std::shared_ptr<Pattern>;
+
+ class NamePattern : public Pattern {
+ public:
+ explicit NamePattern( std::string const& name, std::string const& filterString );
+ bool matches( TestCaseInfo const& testCase ) const override;
+ private:
+ WildcardPattern m_wildcardPattern;
+ };
+
+ class TagPattern : public Pattern {
+ public:
+ explicit TagPattern( std::string const& tag, std::string const& filterString );
+ bool matches( TestCaseInfo const& testCase ) const override;
+ private:
+ std::string m_tag;
+ };
+
+ class ExcludedPattern : public Pattern {
+ public:
+ explicit ExcludedPattern( PatternPtr const& underlyingPattern );
+ bool matches( TestCaseInfo const& testCase ) const override;
+ private:
+ PatternPtr m_underlyingPattern;
+ };
+
+ struct Filter {
+ std::vector<PatternPtr> m_patterns;
+
+ bool matches( TestCaseInfo const& testCase ) const;
+ std::string name() const;
+ };
+
+ public:
+ struct FilterMatch {
+ std::string name;
+ std::vector<TestCase const*> tests;
+ };
+ using Matches = std::vector<FilterMatch>;
+ using vectorStrings = std::vector<std::string>;
+
+ bool hasFilters() const;
+ bool matches( TestCaseInfo const& testCase ) const;
+ Matches matchesByFilter( std::vector<TestCase> const& testCases, IConfig const& config ) const;
+ const vectorStrings & getInvalidArgs() const;
+
+ private:
+ std::vector<Filter> m_filters;
+ std::vector<std::string> m_invalidArgs;
+ friend class TestSpecParser;
+ };
+}
+
+#ifdef __clang__
+#pragma clang diagnostic pop
+#endif
+
+// end catch_test_spec.h
+// start catch_interfaces_tag_alias_registry.h
+
+#include <string>
+
+namespace Catch {
+
+ struct TagAlias;
+
+ struct ITagAliasRegistry {
+ virtual ~ITagAliasRegistry();
+ // Nullptr if not present
+ virtual TagAlias const* find( std::string const& alias ) const = 0;
+ virtual std::string expandAliases( std::string const& unexpandedTestSpec ) const = 0;
+
+ static ITagAliasRegistry const& get();
+ };
+
+} // end namespace Catch
+
+// end catch_interfaces_tag_alias_registry.h
+namespace Catch {
+
+ class TestSpecParser {
+ enum Mode{ None, Name, QuotedName, Tag, EscapedName };
+ Mode m_mode = None;
+ Mode lastMode = None;
+ bool m_exclusion = false;
+ std::size_t m_pos = 0;
+ std::size_t m_realPatternPos = 0;
+ std::string m_arg;
+ std::string m_substring;
+ std::string m_patternName;
+ std::vector<std::size_t> m_escapeChars;
+ TestSpec::Filter m_currentFilter;
+ TestSpec m_testSpec;
+ ITagAliasRegistry const* m_tagAliases = nullptr;
+
+ public:
+ TestSpecParser( ITagAliasRegistry const& tagAliases );
+
+ TestSpecParser& parse( std::string const& arg );
+ TestSpec testSpec();
+
+ private:
+ bool visitChar( char c );
+ void startNewMode( Mode mode );
+ bool processNoneChar( char c );
+ void processNameChar( char c );
+ bool processOtherChar( char c );
+ void endMode();
+ void escape();
+ bool isControlChar( char c ) const;
+ void saveLastMode();
+ void revertBackToLastMode();
+ void addFilter();
+ bool separate();
+
+ // Handles common preprocessing of the pattern for name/tag patterns
+ std::string preprocessPattern();
+ // Adds the current pattern as a test name
+ void addNamePattern();
+ // Adds the current pattern as a tag
+ void addTagPattern();
+
+ inline void addCharToPattern(char c) {
+ m_substring += c;
+ m_patternName += c;
+ m_realPatternPos++;
+ }
+
+ };
+ TestSpec parseTestSpec( std::string const& arg );
+
+} // namespace Catch
+
+#ifdef __clang__
+#pragma clang diagnostic pop
+#endif
+
+// end catch_test_spec_parser.h
+// Libstdc++ doesn't like incomplete classes for unique_ptr
+
+#include <memory>
+#include <vector>
+#include <string>
+
+#ifndef CATCH_CONFIG_CONSOLE_WIDTH
+#define CATCH_CONFIG_CONSOLE_WIDTH 80
+#endif
+
+namespace Catch {
+
+ struct IStream;
+
+ struct ConfigData {
+ bool listTests = false;
+ bool listTags = false;
+ bool listReporters = false;
+ bool listTestNamesOnly = false;
+
+ bool showSuccessfulTests = false;
+ bool shouldDebugBreak = false;
+ bool noThrow = false;
+ bool showHelp = false;
+ bool showInvisibles = false;
+ bool filenamesAsTags = false;
+ bool libIdentify = false;
+
+ int abortAfter = -1;
+ unsigned int rngSeed = 0;
+
+ bool benchmarkNoAnalysis = false;
+ unsigned int benchmarkSamples = 100;
+ double benchmarkConfidenceInterval = 0.95;
+ unsigned int benchmarkResamples = 100000;
+ std::chrono::milliseconds::rep benchmarkWarmupTime = 100;
+
+ Verbosity verbosity = Verbosity::Normal;
+ WarnAbout::What warnings = WarnAbout::Nothing;
+ ShowDurations::OrNot showDurations = ShowDurations::DefaultForReporter;
+ double minDuration = -1;
+ RunTests::InWhatOrder runOrder = RunTests::InDeclarationOrder;
+ UseColour::YesOrNo useColour = UseColour::Auto;
+ WaitForKeypress::When waitForKeypress = WaitForKeypress::Never;
+
+ std::string outputFilename;
+ std::string name;
+ std::string processName;
+#ifndef CATCH_CONFIG_DEFAULT_REPORTER
+#define CATCH_CONFIG_DEFAULT_REPORTER "console"
+#endif
+ std::string reporterName = CATCH_CONFIG_DEFAULT_REPORTER;
+#undef CATCH_CONFIG_DEFAULT_REPORTER
+
+ std::vector<std::string> testsOrTags;
+ std::vector<std::string> sectionsToRun;
+ };
+
+ class Config : public IConfig {
+ public:
+
+ Config() = default;
+ Config( ConfigData const& data );
+ virtual ~Config() = default;
+
+ std::string const& getFilename() const;
+
+ bool listTests() const;
+ bool listTestNamesOnly() const;
+ bool listTags() const;
+ bool listReporters() const;
+
+ std::string getProcessName() const;
+ std::string const& getReporterName() const;
+
+ std::vector<std::string> const& getTestsOrTags() const override;
+ std::vector<std::string> const& getSectionsToRun() const override;
+
+ TestSpec const& testSpec() const override;
+ bool hasTestFilters() const override;
+
+ bool showHelp() const;
+
+ // IConfig interface
+ bool allowThrows() const override;
+ std::ostream& stream() const override;
+ std::string name() const override;
+ bool includeSuccessfulResults() const override;
+ bool warnAboutMissingAssertions() const override;
+ bool warnAboutNoTests() const override;
+ ShowDurations::OrNot showDurations() const override;
+ double minDuration() const override;
+ RunTests::InWhatOrder runOrder() const override;
+ unsigned int rngSeed() const override;
+ UseColour::YesOrNo useColour() const override;
+ bool shouldDebugBreak() const override;
+ int abortAfter() const override;
+ bool showInvisibles() const override;
+ Verbosity verbosity() const override;
+ bool benchmarkNoAnalysis() const override;
+ int benchmarkSamples() const override;
+ double benchmarkConfidenceInterval() const override;
+ unsigned int benchmarkResamples() const override;
+ std::chrono::milliseconds benchmarkWarmupTime() const override;
+
+ private:
+
+ IStream const* openStream();
+ ConfigData m_data;
+
+ std::unique_ptr<IStream const> m_stream;
+ TestSpec m_testSpec;
+ bool m_hasTestFilters = false;
+ };
+
+} // end namespace Catch
+
+// end catch_config.hpp
+// start catch_assertionresult.h
+
+#include <string>
+
+namespace Catch {
+
+ struct AssertionResultData
+ {
+ AssertionResultData() = delete;
+
+ AssertionResultData( ResultWas::OfType _resultType, LazyExpression const& _lazyExpression );
+
+ std::string message;
+ mutable std::string reconstructedExpression;
+ LazyExpression lazyExpression;
+ ResultWas::OfType resultType;
+
+ std::string reconstructExpression() const;
+ };
+
+ class AssertionResult {
+ public:
+ AssertionResult() = delete;
+ AssertionResult( AssertionInfo const& info, AssertionResultData const& data );
+
+ bool isOk() const;
+ bool succeeded() const;
+ ResultWas::OfType getResultType() const;
+ bool hasExpression() const;
+ bool hasMessage() const;
+ std::string getExpression() const;
+ std::string getExpressionInMacro() const;
+ bool hasExpandedExpression() const;
+ std::string getExpandedExpression() const;
+ std::string getMessage() const;
+ SourceLineInfo getSourceInfo() const;
+ StringRef getTestMacroName() const;
+
+ //protected:
+ AssertionInfo m_info;
+ AssertionResultData m_resultData;
+ };
+
+} // end namespace Catch
+
+// end catch_assertionresult.h
+#if defined(CATCH_CONFIG_ENABLE_BENCHMARKING)
+// start catch_estimate.hpp
+
+ // Statistics estimates
+
+
+namespace Catch {
+ namespace Benchmark {
+ template <typename Duration>
+ struct Estimate {
+ Duration point;
+ Duration lower_bound;
+ Duration upper_bound;
+ double confidence_interval;
+
+ template <typename Duration2>
+ operator Estimate<Duration2>() const {
+ return { point, lower_bound, upper_bound, confidence_interval };
+ }
+ };
+ } // namespace Benchmark
+} // namespace Catch
+
+// end catch_estimate.hpp
+// start catch_outlier_classification.hpp
+
+// Outlier information
+
+namespace Catch {
+ namespace Benchmark {
+ struct OutlierClassification {
+ int samples_seen = 0;
+ int low_severe = 0; // more than 3 times IQR below Q1
+ int low_mild = 0; // 1.5 to 3 times IQR below Q1
+ int high_mild = 0; // 1.5 to 3 times IQR above Q3
+ int high_severe = 0; // more than 3 times IQR above Q3
+
+ int total() const {
+ return low_severe + low_mild + high_mild + high_severe;
+ }
+ };
+ } // namespace Benchmark
+} // namespace Catch
+
+// end catch_outlier_classification.hpp
+
+#include <iterator>
+#endif // CATCH_CONFIG_ENABLE_BENCHMARKING
+
+#include <string>
+#include <iosfwd>
+#include <map>
+#include <set>
+#include <memory>
+#include <algorithm>
+
+namespace Catch {
+
+ struct ReporterConfig {
+ explicit ReporterConfig( IConfigPtr const& _fullConfig );
+
+ ReporterConfig( IConfigPtr const& _fullConfig, std::ostream& _stream );
+
+ std::ostream& stream() const;
+ IConfigPtr fullConfig() const;
+
+ private:
+ std::ostream* m_stream;
+ IConfigPtr m_fullConfig;
+ };
+
+ struct ReporterPreferences {
+ bool shouldRedirectStdOut = false;
+ bool shouldReportAllAssertions = false;
+ };
+
+ template<typename T>
+ struct LazyStat : Option<T> {
+ LazyStat& operator=( T const& _value ) {
+ Option<T>::operator=( _value );
+ used = false;
+ return *this;
+ }
+ void reset() {
+ Option<T>::reset();
+ used = false;
+ }
+ bool used = false;
+ };
+
+ struct TestRunInfo {
+ TestRunInfo( std::string const& _name );
+ std::string name;
+ };
+ struct GroupInfo {
+ GroupInfo( std::string const& _name,
+ std::size_t _groupIndex,
+ std::size_t _groupsCount );
+
+ std::string name;
+ std::size_t groupIndex;
+ std::size_t groupsCounts;
+ };
+
+ struct AssertionStats {
+ AssertionStats( AssertionResult const& _assertionResult,
+ std::vector<MessageInfo> const& _infoMessages,
+ Totals const& _totals );
+
+ AssertionStats( AssertionStats const& ) = default;
+ AssertionStats( AssertionStats && ) = default;
+ AssertionStats& operator = ( AssertionStats const& ) = delete;
+ AssertionStats& operator = ( AssertionStats && ) = delete;
+ virtual ~AssertionStats();
+
+ AssertionResult assertionResult;
+ std::vector<MessageInfo> infoMessages;
+ Totals totals;
+ };
+
+ struct SectionStats {
+ SectionStats( SectionInfo const& _sectionInfo,
+ Counts const& _assertions,
+ double _durationInSeconds,
+ bool _missingAssertions );
+ SectionStats( SectionStats const& ) = default;
+ SectionStats( SectionStats && ) = default;
+ SectionStats& operator = ( SectionStats const& ) = default;
+ SectionStats& operator = ( SectionStats && ) = default;
+ virtual ~SectionStats();
+
+ SectionInfo sectionInfo;
+ Counts assertions;
+ double durationInSeconds;
+ bool missingAssertions;
+ };
+
+ struct TestCaseStats {
+ TestCaseStats( TestCaseInfo const& _testInfo,
+ Totals const& _totals,
+ std::string const& _stdOut,
+ std::string const& _stdErr,
+ bool _aborting );
+
+ TestCaseStats( TestCaseStats const& ) = default;
+ TestCaseStats( TestCaseStats && ) = default;
+ TestCaseStats& operator = ( TestCaseStats const& ) = default;
+ TestCaseStats& operator = ( TestCaseStats && ) = default;
+ virtual ~TestCaseStats();
+
+ TestCaseInfo testInfo;
+ Totals totals;
+ std::string stdOut;
+ std::string stdErr;
+ bool aborting;
+ };
+
+ struct TestGroupStats {
+ TestGroupStats( GroupInfo const& _groupInfo,
+ Totals const& _totals,
+ bool _aborting );
+ TestGroupStats( GroupInfo const& _groupInfo );
+
+ TestGroupStats( TestGroupStats const& ) = default;
+ TestGroupStats( TestGroupStats && ) = default;
+ TestGroupStats& operator = ( TestGroupStats const& ) = default;
+ TestGroupStats& operator = ( TestGroupStats && ) = default;
+ virtual ~TestGroupStats();
+
+ GroupInfo groupInfo;
+ Totals totals;
+ bool aborting;
+ };
+
+ struct TestRunStats {
+ TestRunStats( TestRunInfo const& _runInfo,
+ Totals const& _totals,
+ bool _aborting );
+
+ TestRunStats( TestRunStats const& ) = default;
+ TestRunStats( TestRunStats && ) = default;
+ TestRunStats& operator = ( TestRunStats const& ) = default;
+ TestRunStats& operator = ( TestRunStats && ) = default;
+ virtual ~TestRunStats();
+
+ TestRunInfo runInfo;
+ Totals totals;
+ bool aborting;
+ };
+
+#if defined(CATCH_CONFIG_ENABLE_BENCHMARKING)
+ struct BenchmarkInfo {
+ std::string name;
+ double estimatedDuration;
+ int iterations;
+ int samples;
+ unsigned int resamples;
+ double clockResolution;
+ double clockCost;
+ };
+
+ template <class Duration>
+ struct BenchmarkStats {
+ BenchmarkInfo info;
+
+ std::vector<Duration> samples;
+ Benchmark::Estimate<Duration> mean;
+ Benchmark::Estimate<Duration> standardDeviation;
+ Benchmark::OutlierClassification outliers;
+ double outlierVariance;
+
+ template <typename Duration2>
+ operator BenchmarkStats<Duration2>() const {
+ std::vector<Duration2> samples2;
+ samples2.reserve(samples.size());
+ std::transform(samples.begin(), samples.end(), std::back_inserter(samples2), [](Duration d) { return Duration2(d); });
+ return {
+ info,
+ std::move(samples2),
+ mean,
+ standardDeviation,
+ outliers,
+ outlierVariance,
+ };
+ }
+ };
+#endif // CATCH_CONFIG_ENABLE_BENCHMARKING
+
+ struct IStreamingReporter {
+ virtual ~IStreamingReporter() = default;
+
+ // Implementing class must also provide the following static methods:
+ // static std::string getDescription();
+ // static std::set<Verbosity> getSupportedVerbosities()
+
+ virtual ReporterPreferences getPreferences() const = 0;
+
+ virtual void noMatchingTestCases( std::string const& spec ) = 0;
+
+ virtual void reportInvalidArguments(std::string const&) {}
+
+ virtual void testRunStarting( TestRunInfo const& testRunInfo ) = 0;
+ virtual void testGroupStarting( GroupInfo const& groupInfo ) = 0;
+
+ virtual void testCaseStarting( TestCaseInfo const& testInfo ) = 0;
+ virtual void sectionStarting( SectionInfo const& sectionInfo ) = 0;
+
+#if defined(CATCH_CONFIG_ENABLE_BENCHMARKING)
+ virtual void benchmarkPreparing( std::string const& ) {}
+ virtual void benchmarkStarting( BenchmarkInfo const& ) {}
+ virtual void benchmarkEnded( BenchmarkStats<> const& ) {}
+ virtual void benchmarkFailed( std::string const& ) {}
+#endif // CATCH_CONFIG_ENABLE_BENCHMARKING
+
+ virtual void assertionStarting( AssertionInfo const& assertionInfo ) = 0;
+
+ // The return value indicates if the messages buffer should be cleared:
+ virtual bool assertionEnded( AssertionStats const& assertionStats ) = 0;
+
+ virtual void sectionEnded( SectionStats const& sectionStats ) = 0;
+ virtual void testCaseEnded( TestCaseStats const& testCaseStats ) = 0;
+ virtual void testGroupEnded( TestGroupStats const& testGroupStats ) = 0;
+ virtual void testRunEnded( TestRunStats const& testRunStats ) = 0;
+
+ virtual void skipTest( TestCaseInfo const& testInfo ) = 0;
+
+ // Default empty implementation provided
+ virtual void fatalErrorEncountered( StringRef name );
+
+ virtual bool isMulti() const;
+ };
+ using IStreamingReporterPtr = std::unique_ptr<IStreamingReporter>;
+
+ struct IReporterFactory {
+ virtual ~IReporterFactory();
+ virtual IStreamingReporterPtr create( ReporterConfig const& config ) const = 0;
+ virtual std::string getDescription() const = 0;
+ };
+ using IReporterFactoryPtr = std::shared_ptr<IReporterFactory>;
+
+ struct IReporterRegistry {
+ using FactoryMap = std::map<std::string, IReporterFactoryPtr>;
+ using Listeners = std::vector<IReporterFactoryPtr>;
+
+ virtual ~IReporterRegistry();
+ virtual IStreamingReporterPtr create( std::string const& name, IConfigPtr const& config ) const = 0;
+ virtual FactoryMap const& getFactories() const = 0;
+ virtual Listeners const& getListeners() const = 0;
+ };
+
+} // end namespace Catch
+
+// end catch_interfaces_reporter.h
+#include <algorithm>
+#include <cstring>
+#include <cfloat>
+#include <cstdio>
+#include <cassert>
+#include <memory>
+#include <ostream>
+
+namespace Catch {
+ void prepareExpandedExpression(AssertionResult& result);
+
+ // Returns double formatted as %.3f (format expected on output)
+ std::string getFormattedDuration( double duration );
+
+ //! Should the reporter show
+ bool shouldShowDuration( IConfig const& config, double duration );
+
+ std::string serializeFilters( std::vector<std::string> const& container );
+
+ template<typename DerivedT>
+ struct StreamingReporterBase : IStreamingReporter {
+
+ StreamingReporterBase( ReporterConfig const& _config )
+ : m_config( _config.fullConfig() ),
+ stream( _config.stream() )
+ {
+ m_reporterPrefs.shouldRedirectStdOut = false;
+ if( !DerivedT::getSupportedVerbosities().count( m_config->verbosity() ) )
+ CATCH_ERROR( "Verbosity level not supported by this reporter" );
+ }
+
+ ReporterPreferences getPreferences() const override {
+ return m_reporterPrefs;
+ }
+
+ static std::set<Verbosity> getSupportedVerbosities() {
+ return { Verbosity::Normal };
+ }
+
+ ~StreamingReporterBase() override = default;
+
+ void noMatchingTestCases(std::string const&) override {}
+
+ void reportInvalidArguments(std::string const&) override {}
+
+ void testRunStarting(TestRunInfo const& _testRunInfo) override {
+ currentTestRunInfo = _testRunInfo;
+ }
+
+ void testGroupStarting(GroupInfo const& _groupInfo) override {
+ currentGroupInfo = _groupInfo;
+ }
+
+ void testCaseStarting(TestCaseInfo const& _testInfo) override {
+ currentTestCaseInfo = _testInfo;
+ }
+ void sectionStarting(SectionInfo const& _sectionInfo) override {
+ m_sectionStack.push_back(_sectionInfo);
+ }
+
+ void sectionEnded(SectionStats const& /* _sectionStats */) override {
+ m_sectionStack.pop_back();
+ }
+ void testCaseEnded(TestCaseStats const& /* _testCaseStats */) override {
+ currentTestCaseInfo.reset();
+ }
+ void testGroupEnded(TestGroupStats const& /* _testGroupStats */) override {
+ currentGroupInfo.reset();
+ }
+ void testRunEnded(TestRunStats const& /* _testRunStats */) override {
+ currentTestCaseInfo.reset();
+ currentGroupInfo.reset();
+ currentTestRunInfo.reset();
+ }
+
+ void skipTest(TestCaseInfo const&) override {
+ // Don't do anything with this by default.
+ // It can optionally be overridden in the derived class.
+ }
+
+ IConfigPtr m_config;
+ std::ostream& stream;
+
+ LazyStat<TestRunInfo> currentTestRunInfo;
+ LazyStat<GroupInfo> currentGroupInfo;
+ LazyStat<TestCaseInfo> currentTestCaseInfo;
+
+ std::vector<SectionInfo> m_sectionStack;
+ ReporterPreferences m_reporterPrefs;
+ };
+
+ template<typename DerivedT>
+ struct CumulativeReporterBase : IStreamingReporter {
+ template<typename T, typename ChildNodeT>
+ struct Node {
+ explicit Node( T const& _value ) : value( _value ) {}
+ virtual ~Node() {}
+
+ using ChildNodes = std::vector<std::shared_ptr<ChildNodeT>>;
+ T value;
+ ChildNodes children;
+ };
+ struct SectionNode {
+ explicit SectionNode(SectionStats const& _stats) : stats(_stats) {}
+ virtual ~SectionNode() = default;
+
+ bool operator == (SectionNode const& other) const {
+ return stats.sectionInfo.lineInfo == other.stats.sectionInfo.lineInfo;
+ }
+ bool operator == (std::shared_ptr<SectionNode> const& other) const {
+ return operator==(*other);
+ }
+
+ SectionStats stats;
+ using ChildSections = std::vector<std::shared_ptr<SectionNode>>;
+ using Assertions = std::vector<AssertionStats>;
+ ChildSections childSections;
+ Assertions assertions;
+ std::string stdOut;
+ std::string stdErr;
+ };
+
+ struct BySectionInfo {
+ BySectionInfo( SectionInfo const& other ) : m_other( other ) {}
+ BySectionInfo( BySectionInfo const& other ) : m_other( other.m_other ) {}
+ bool operator() (std::shared_ptr<SectionNode> const& node) const {
+ return ((node->stats.sectionInfo.name == m_other.name) &&
+ (node->stats.sectionInfo.lineInfo == m_other.lineInfo));
+ }
+ void operator=(BySectionInfo const&) = delete;
+
+ private:
+ SectionInfo const& m_other;
+ };
+
+ using TestCaseNode = Node<TestCaseStats, SectionNode>;
+ using TestGroupNode = Node<TestGroupStats, TestCaseNode>;
+ using TestRunNode = Node<TestRunStats, TestGroupNode>;
+
+ CumulativeReporterBase( ReporterConfig const& _config )
+ : m_config( _config.fullConfig() ),
+ stream( _config.stream() )
+ {
+ m_reporterPrefs.shouldRedirectStdOut = false;
+ if( !DerivedT::getSupportedVerbosities().count( m_config->verbosity() ) )
+ CATCH_ERROR( "Verbosity level not supported by this reporter" );
+ }
+ ~CumulativeReporterBase() override = default;
+
+ ReporterPreferences getPreferences() const override {
+ return m_reporterPrefs;
+ }
+
+ static std::set<Verbosity> getSupportedVerbosities() {
+ return { Verbosity::Normal };
+ }
+
+ void testRunStarting( TestRunInfo const& ) override {}
+ void testGroupStarting( GroupInfo const& ) override {}
+
+ void testCaseStarting( TestCaseInfo const& ) override {}
+
+ void sectionStarting( SectionInfo const& sectionInfo ) override {
+ SectionStats incompleteStats( sectionInfo, Counts(), 0, false );
+ std::shared_ptr<SectionNode> node;
+ if( m_sectionStack.empty() ) {
+ if( !m_rootSection )
+ m_rootSection = std::make_shared<SectionNode>( incompleteStats );
+ node = m_rootSection;
+ }
+ else {
+ SectionNode& parentNode = *m_sectionStack.back();
+ auto it =
+ std::find_if( parentNode.childSections.begin(),
+ parentNode.childSections.end(),
+ BySectionInfo( sectionInfo ) );
+ if( it == parentNode.childSections.end() ) {
+ node = std::make_shared<SectionNode>( incompleteStats );
+ parentNode.childSections.push_back( node );
+ }
+ else
+ node = *it;
+ }
+ m_sectionStack.push_back( node );
+ m_deepestSection = std::move(node);
+ }
+
+ void assertionStarting(AssertionInfo const&) override {}
+
+ bool assertionEnded(AssertionStats const& assertionStats) override {
+ assert(!m_sectionStack.empty());
+ // AssertionResult holds a pointer to a temporary DecomposedExpression,
+ // which getExpandedExpression() calls to build the expression string.
+ // Our section stack copy of the assertionResult will likely outlive the
+ // temporary, so it must be expanded or discarded now to avoid calling
+ // a destroyed object later.
+ prepareExpandedExpression(const_cast<AssertionResult&>( assertionStats.assertionResult ) );
+ SectionNode& sectionNode = *m_sectionStack.back();
+ sectionNode.assertions.push_back(assertionStats);
+ return true;
+ }
+ void sectionEnded(SectionStats const& sectionStats) override {
+ assert(!m_sectionStack.empty());
+ SectionNode& node = *m_sectionStack.back();
+ node.stats = sectionStats;
+ m_sectionStack.pop_back();
+ }
+ void testCaseEnded(TestCaseStats const& testCaseStats) override {
+ auto node = std::make_shared<TestCaseNode>(testCaseStats);
+ assert(m_sectionStack.size() == 0);
+ node->children.push_back(m_rootSection);
+ m_testCases.push_back(node);
+ m_rootSection.reset();
+
+ assert(m_deepestSection);
+ m_deepestSection->stdOut = testCaseStats.stdOut;
+ m_deepestSection->stdErr = testCaseStats.stdErr;
+ }
+ void testGroupEnded(TestGroupStats const& testGroupStats) override {
+ auto node = std::make_shared<TestGroupNode>(testGroupStats);
+ node->children.swap(m_testCases);
+ m_testGroups.push_back(node);
+ }
+ void testRunEnded(TestRunStats const& testRunStats) override {
+ auto node = std::make_shared<TestRunNode>(testRunStats);
+ node->children.swap(m_testGroups);
+ m_testRuns.push_back(node);
+ testRunEndedCumulative();
+ }
+ virtual void testRunEndedCumulative() = 0;
+
+ void skipTest(TestCaseInfo const&) override {}
+
+ IConfigPtr m_config;
+ std::ostream& stream;
+ std::vector<AssertionStats> m_assertions;
+ std::vector<std::vector<std::shared_ptr<SectionNode>>> m_sections;
+ std::vector<std::shared_ptr<TestCaseNode>> m_testCases;
+ std::vector<std::shared_ptr<TestGroupNode>> m_testGroups;
+
+ std::vector<std::shared_ptr<TestRunNode>> m_testRuns;
+
+ std::shared_ptr<SectionNode> m_rootSection;
+ std::shared_ptr<SectionNode> m_deepestSection;
+ std::vector<std::shared_ptr<SectionNode>> m_sectionStack;
+ ReporterPreferences m_reporterPrefs;
+ };
+
+ template<char C>
+ char const* getLineOfChars() {
+ static char line[CATCH_CONFIG_CONSOLE_WIDTH] = {0};
+ if( !*line ) {
+ std::memset( line, C, CATCH_CONFIG_CONSOLE_WIDTH-1 );
+ line[CATCH_CONFIG_CONSOLE_WIDTH-1] = 0;
+ }
+ return line;
+ }
+
+ struct TestEventListenerBase : StreamingReporterBase<TestEventListenerBase> {
+ TestEventListenerBase( ReporterConfig const& _config );
+
+ static std::set<Verbosity> getSupportedVerbosities();
+
+ void assertionStarting(AssertionInfo const&) override;
+ bool assertionEnded(AssertionStats const&) override;
+ };
+
+} // end namespace Catch
+
+// end catch_reporter_bases.hpp
+// start catch_console_colour.h
+
+namespace Catch {
+
+ struct Colour {
+ enum Code {
+ None = 0,
+
+ White,
+ Red,
+ Green,
+ Blue,
+ Cyan,
+ Yellow,
+ Grey,
+
+ Bright = 0x10,
+
+ BrightRed = Bright | Red,
+ BrightGreen = Bright | Green,
+ LightGrey = Bright | Grey,
+ BrightWhite = Bright | White,
+ BrightYellow = Bright | Yellow,
+
+ // By intention
+ FileName = LightGrey,
+ Warning = BrightYellow,
+ ResultError = BrightRed,
+ ResultSuccess = BrightGreen,
+ ResultExpectedFailure = Warning,
+
+ Error = BrightRed,
+ Success = Green,
+
+ OriginalExpression = Cyan,
+ ReconstructedExpression = BrightYellow,
+
+ SecondaryText = LightGrey,
+ Headers = White
+ };
+
+ // Use constructed object for RAII guard
+ Colour( Code _colourCode );
+ Colour( Colour&& other ) noexcept;
+ Colour& operator=( Colour&& other ) noexcept;
+ ~Colour();
+
+ // Use static method for one-shot changes
+ static void use( Code _colourCode );
+
+ private:
+ bool m_moved = false;
+ };
+
+ std::ostream& operator << ( std::ostream& os, Colour const& );
+
+} // end namespace Catch
+
+// end catch_console_colour.h
+// start catch_reporter_registrars.hpp
+
+
+namespace Catch {
+
+ template<typename T>
+ class ReporterRegistrar {
+
+ class ReporterFactory : public IReporterFactory {
+
+ IStreamingReporterPtr create( ReporterConfig const& config ) const override {
+ return std::unique_ptr<T>( new T( config ) );
+ }
+
+ std::string getDescription() const override {
+ return T::getDescription();
+ }
+ };
+
+ public:
+
+ explicit ReporterRegistrar( std::string const& name ) {
+ getMutableRegistryHub().registerReporter( name, std::make_shared<ReporterFactory>() );
+ }
+ };
+
+ template<typename T>
+ class ListenerRegistrar {
+
+ class ListenerFactory : public IReporterFactory {
+
+ IStreamingReporterPtr create( ReporterConfig const& config ) const override {
+ return std::unique_ptr<T>( new T( config ) );
+ }
+ std::string getDescription() const override {
+ return std::string();
+ }
+ };
+
+ public:
+
+ ListenerRegistrar() {
+ getMutableRegistryHub().registerListener( std::make_shared<ListenerFactory>() );
+ }
+ };
+}
+
+#if !defined(CATCH_CONFIG_DISABLE)
+
+#define CATCH_REGISTER_REPORTER( name, reporterType ) \
+ CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
+ CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
+ namespace{ Catch::ReporterRegistrar<reporterType> catch_internal_RegistrarFor##reporterType( name ); } \
+ CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION
+
+#define CATCH_REGISTER_LISTENER( listenerType ) \
+ CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
+ CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
+ namespace{ Catch::ListenerRegistrar<listenerType> catch_internal_RegistrarFor##listenerType; } \
+ CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION
+#else // CATCH_CONFIG_DISABLE
+
+#define CATCH_REGISTER_REPORTER(name, reporterType)
+#define CATCH_REGISTER_LISTENER(listenerType)
+
+#endif // CATCH_CONFIG_DISABLE
+
+// end catch_reporter_registrars.hpp
+// Allow users to base their work off existing reporters
+// start catch_reporter_compact.h
+
+namespace Catch {
+
+ struct CompactReporter : StreamingReporterBase<CompactReporter> {
+
+ using StreamingReporterBase::StreamingReporterBase;
+
+ ~CompactReporter() override;
+
+ static std::string getDescription();
+
+ void noMatchingTestCases(std::string const& spec) override;
+
+ void assertionStarting(AssertionInfo const&) override;
+
+ bool assertionEnded(AssertionStats const& _assertionStats) override;
+
+ void sectionEnded(SectionStats const& _sectionStats) override;
+
+ void testRunEnded(TestRunStats const& _testRunStats) override;
+
+ };
+
+} // end namespace Catch
+
+// end catch_reporter_compact.h
+// start catch_reporter_console.h
+
+#if defined(_MSC_VER)
+#pragma warning(push)
+#pragma warning(disable:4061) // Not all labels are EXPLICITLY handled in switch
+ // Note that 4062 (not all labels are handled
+ // and default is missing) is enabled
+#endif
+
+namespace Catch {
+ // Fwd decls
+ struct SummaryColumn;
+ class TablePrinter;
+
+ struct ConsoleReporter : StreamingReporterBase<ConsoleReporter> {
+ std::unique_ptr<TablePrinter> m_tablePrinter;
+
+ ConsoleReporter(ReporterConfig const& config);
+ ~ConsoleReporter() override;
+ static std::string getDescription();
+
+ void noMatchingTestCases(std::string const& spec) override;
+
+ void reportInvalidArguments(std::string const&arg) override;
+
+ void assertionStarting(AssertionInfo const&) override;
+
+ bool assertionEnded(AssertionStats const& _assertionStats) override;
+
+ void sectionStarting(SectionInfo const& _sectionInfo) override;
+ void sectionEnded(SectionStats const& _sectionStats) override;
+
+#if defined(CATCH_CONFIG_ENABLE_BENCHMARKING)
+ void benchmarkPreparing(std::string const& name) override;
+ void benchmarkStarting(BenchmarkInfo const& info) override;
+ void benchmarkEnded(BenchmarkStats<> const& stats) override;
+ void benchmarkFailed(std::string const& error) override;
+#endif // CATCH_CONFIG_ENABLE_BENCHMARKING
+
+ void testCaseEnded(TestCaseStats const& _testCaseStats) override;
+ void testGroupEnded(TestGroupStats const& _testGroupStats) override;
+ void testRunEnded(TestRunStats const& _testRunStats) override;
+ void testRunStarting(TestRunInfo const& _testRunInfo) override;
+ private:
+
+ void lazyPrint();
+
+ void lazyPrintWithoutClosingBenchmarkTable();
+ void lazyPrintRunInfo();
+ void lazyPrintGroupInfo();
+ void printTestCaseAndSectionHeader();
+
+ void printClosedHeader(std::string const& _name);
+ void printOpenHeader(std::string const& _name);
+
+ // if string has a : in first line will set indent to follow it on
+ // subsequent lines
+ void printHeaderString(std::string const& _string, std::size_t indent = 0);
+
+ void printTotals(Totals const& totals);
+ void printSummaryRow(std::string const& label, std::vector<SummaryColumn> const& cols, std::size_t row);
+
+ void printTotalsDivider(Totals const& totals);
+ void printSummaryDivider();
+ void printTestFilters();
+
+ private:
+ bool m_headerPrinted = false;
+ };
+
+} // end namespace Catch
+
+#if defined(_MSC_VER)
+#pragma warning(pop)
+#endif
+
+// end catch_reporter_console.h
+// start catch_reporter_junit.h
+
+// start catch_xmlwriter.h
+
+#include <vector>
+
+namespace Catch {
+ enum class XmlFormatting {
+ None = 0x00,
+ Indent = 0x01,
+ Newline = 0x02,
+ };
+
+ XmlFormatting operator | (XmlFormatting lhs, XmlFormatting rhs);
+ XmlFormatting operator & (XmlFormatting lhs, XmlFormatting rhs);
+
+ class XmlEncode {
+ public:
+ enum ForWhat { ForTextNodes, ForAttributes };
+
+ XmlEncode( std::string const& str, ForWhat forWhat = ForTextNodes );
+
+ void encodeTo( std::ostream& os ) const;
+
+ friend std::ostream& operator << ( std::ostream& os, XmlEncode const& xmlEncode );
+
+ private:
+ std::string m_str;
+ ForWhat m_forWhat;
+ };
+
+ class XmlWriter {
+ public:
+
+ class ScopedElement {
+ public:
+ ScopedElement( XmlWriter* writer, XmlFormatting fmt );
+
+ ScopedElement( ScopedElement&& other ) noexcept;
+ ScopedElement& operator=( ScopedElement&& other ) noexcept;
+
+ ~ScopedElement();
+
+ ScopedElement& writeText( std::string const& text, XmlFormatting fmt = XmlFormatting::Newline | XmlFormatting::Indent );
+
+ template<typename T>
+ ScopedElement& writeAttribute( std::string const& name, T const& attribute ) {
+ m_writer->writeAttribute( name, attribute );
+ return *this;
+ }
+
+ private:
+ mutable XmlWriter* m_writer = nullptr;
+ XmlFormatting m_fmt;
+ };
+
+ XmlWriter( std::ostream& os = Catch::cout() );
+ ~XmlWriter();
+
+ XmlWriter( XmlWriter const& ) = delete;
+ XmlWriter& operator=( XmlWriter const& ) = delete;
+
+ XmlWriter& startElement( std::string const& name, XmlFormatting fmt = XmlFormatting::Newline | XmlFormatting::Indent);
+
+ ScopedElement scopedElement( std::string const& name, XmlFormatting fmt = XmlFormatting::Newline | XmlFormatting::Indent);
+
+ XmlWriter& endElement(XmlFormatting fmt = XmlFormatting::Newline | XmlFormatting::Indent);
+
+ XmlWriter& writeAttribute( std::string const& name, std::string const& attribute );
+
+ XmlWriter& writeAttribute( std::string const& name, bool attribute );
+
+ template<typename T>
+ XmlWriter& writeAttribute( std::string const& name, T const& attribute ) {
+ ReusableStringStream rss;
+ rss << attribute;
+ return writeAttribute( name, rss.str() );
+ }
+
+ XmlWriter& writeText( std::string const& text, XmlFormatting fmt = XmlFormatting::Newline | XmlFormatting::Indent);
+
+ XmlWriter& writeComment(std::string const& text, XmlFormatting fmt = XmlFormatting::Newline | XmlFormatting::Indent);
+
+ void writeStylesheetRef( std::string const& url );
+
+ XmlWriter& writeBlankLine();
+
+ void ensureTagClosed();
+
+ private:
+
+ void applyFormatting(XmlFormatting fmt);
+
+ void writeDeclaration();
+
+ void newlineIfNecessary();
+
+ bool m_tagIsOpen = false;
+ bool m_needsNewline = false;
+ std::vector<std::string> m_tags;
+ std::string m_indent;
+ std::ostream& m_os;
+ };
+
+}
+
+// end catch_xmlwriter.h
+namespace Catch {
+
+ class JunitReporter : public CumulativeReporterBase<JunitReporter> {
+ public:
+ JunitReporter(ReporterConfig const& _config);
+
+ ~JunitReporter() override;
+
+ static std::string getDescription();
+
+ void noMatchingTestCases(std::string const& /*spec*/) override;
+
+ void testRunStarting(TestRunInfo const& runInfo) override;
+
+ void testGroupStarting(GroupInfo const& groupInfo) override;
+
+ void testCaseStarting(TestCaseInfo const& testCaseInfo) override;
+ bool assertionEnded(AssertionStats const& assertionStats) override;
+
+ void testCaseEnded(TestCaseStats const& testCaseStats) override;
+
+ void testGroupEnded(TestGroupStats const& testGroupStats) override;
+
+ void testRunEndedCumulative() override;
+
+ void writeGroup(TestGroupNode const& groupNode, double suiteTime);
+
+ void writeTestCase(TestCaseNode const& testCaseNode);
+
+ void writeSection( std::string const& className,
+ std::string const& rootName,
+ SectionNode const& sectionNode,
+ bool testOkToFail );
+
+ void writeAssertions(SectionNode const& sectionNode);
+ void writeAssertion(AssertionStats const& stats);
+
+ XmlWriter xml;
+ Timer suiteTimer;
+ std::string stdOutForSuite;
+ std::string stdErrForSuite;
+ unsigned int unexpectedExceptions = 0;
+ bool m_okToFail = false;
+ };
+
+} // end namespace Catch
+
+// end catch_reporter_junit.h
+// start catch_reporter_xml.h
+
+namespace Catch {
+ class XmlReporter : public StreamingReporterBase<XmlReporter> {
+ public:
+ XmlReporter(ReporterConfig const& _config);
+
+ ~XmlReporter() override;
+
+ static std::string getDescription();
+
+ virtual std::string getStylesheetRef() const;
+
+ void writeSourceInfo(SourceLineInfo const& sourceInfo);
+
+ public: // StreamingReporterBase
+
+ void noMatchingTestCases(std::string const& s) override;
+
+ void testRunStarting(TestRunInfo const& testInfo) override;
+
+ void testGroupStarting(GroupInfo const& groupInfo) override;
+
+ void testCaseStarting(TestCaseInfo const& testInfo) override;
+
+ void sectionStarting(SectionInfo const& sectionInfo) override;
+
+ void assertionStarting(AssertionInfo const&) override;
+
+ bool assertionEnded(AssertionStats const& assertionStats) override;
+
+ void sectionEnded(SectionStats const& sectionStats) override;
+
+ void testCaseEnded(TestCaseStats const& testCaseStats) override;
+
+ void testGroupEnded(TestGroupStats const& testGroupStats) override;
+
+ void testRunEnded(TestRunStats const& testRunStats) override;
+
+#if defined(CATCH_CONFIG_ENABLE_BENCHMARKING)
+ void benchmarkPreparing(std::string const& name) override;
+ void benchmarkStarting(BenchmarkInfo const&) override;
+ void benchmarkEnded(BenchmarkStats<> const&) override;
+ void benchmarkFailed(std::string const&) override;
+#endif // CATCH_CONFIG_ENABLE_BENCHMARKING
+
+ private:
+ Timer m_testCaseTimer;
+ XmlWriter m_xml;
+ int m_sectionDepth = 0;
+ };
+
+} // end namespace Catch
+
+// end catch_reporter_xml.h
+
+// end catch_external_interfaces.h
+#endif
+
+#if defined(CATCH_CONFIG_ENABLE_BENCHMARKING)
+// start catch_benchmarking_all.hpp
+
+// A proxy header that includes all of the benchmarking headers to allow
+// concise include of the benchmarking features. You should prefer the
+// individual includes in standard use.
+
+// start catch_benchmark.hpp
+
+ // Benchmark
+
+// start catch_chronometer.hpp
+
+// User-facing chronometer
+
+
+// start catch_clock.hpp
+
+// Clocks
+
+
+#include <chrono>
+#include <ratio>
+
+namespace Catch {
+ namespace Benchmark {
+ template <typename Clock>
+ using ClockDuration = typename Clock::duration;
+ template <typename Clock>
+ using FloatDuration = std::chrono::duration<double, typename Clock::period>;
+
+ template <typename Clock>
+ using TimePoint = typename Clock::time_point;
+
+ using default_clock = std::chrono::steady_clock;
+
+ template <typename Clock>
+ struct now {
+ TimePoint<Clock> operator()() const {
+ return Clock::now();
+ }
+ };
+
+ using fp_seconds = std::chrono::duration<double, std::ratio<1>>;
+ } // namespace Benchmark
+} // namespace Catch
+
+// end catch_clock.hpp
+// start catch_optimizer.hpp
+
+ // Hinting the optimizer
+
+
+#if defined(_MSC_VER)
+# include <atomic> // atomic_thread_fence
+#endif
+
+namespace Catch {
+ namespace Benchmark {
+#if defined(__GNUC__) || defined(__clang__)
+ template <typename T>
+ inline void keep_memory(T* p) {
+ asm volatile("" : : "g"(p) : "memory");
+ }
+ inline void keep_memory() {
+ asm volatile("" : : : "memory");
+ }
+
+ namespace Detail {
+ inline void optimizer_barrier() { keep_memory(); }
+ } // namespace Detail
+#elif defined(_MSC_VER)
+
+#pragma optimize("", off)
+ template <typename T>
+ inline void keep_memory(T* p) {
+ // thanks @milleniumbug
+ *reinterpret_cast<char volatile*>(p) = *reinterpret_cast<char const volatile*>(p);
+ }
+ // TODO equivalent keep_memory()
+#pragma optimize("", on)
+
+ namespace Detail {
+ inline void optimizer_barrier() {
+ std::atomic_thread_fence(std::memory_order_seq_cst);
+ }
+ } // namespace Detail
+
+#endif
+
+ template <typename T>
+ inline void deoptimize_value(T&& x) {
+ keep_memory(&x);
+ }
+
+ template <typename Fn, typename... Args>
+ inline auto invoke_deoptimized(Fn&& fn, Args&&... args) -> typename std::enable_if<!std::is_same<void, decltype(fn(args...))>::value>::type {
+ deoptimize_value(std::forward<Fn>(fn) (std::forward<Args...>(args...)));
+ }
+
+ template <typename Fn, typename... Args>
+ inline auto invoke_deoptimized(Fn&& fn, Args&&... args) -> typename std::enable_if<std::is_same<void, decltype(fn(args...))>::value>::type {
+ std::forward<Fn>(fn) (std::forward<Args...>(args...));
+ }
+ } // namespace Benchmark
+} // namespace Catch
+
+// end catch_optimizer.hpp
+// start catch_complete_invoke.hpp
+
+// Invoke with a special case for void
+
+
+#include <type_traits>
+#include <utility>
+
+namespace Catch {
+ namespace Benchmark {
+ namespace Detail {
+ template <typename T>
+ struct CompleteType { using type = T; };
+ template <>
+ struct CompleteType<void> { struct type {}; };
+
+ template <typename T>
+ using CompleteType_t = typename CompleteType<T>::type;
+
+ template <typename Result>
+ struct CompleteInvoker {
+ template <typename Fun, typename... Args>
+ static Result invoke(Fun&& fun, Args&&... args) {
+ return std::forward<Fun>(fun)(std::forward<Args>(args)...);
+ }
+ };
+ template <>
+ struct CompleteInvoker<void> {
+ template <typename Fun, typename... Args>
+ static CompleteType_t<void> invoke(Fun&& fun, Args&&... args) {
+ std::forward<Fun>(fun)(std::forward<Args>(args)...);
+ return {};
+ }
+ };
+
+ // invoke and not return void :(
+ template <typename Fun, typename... Args>
+ CompleteType_t<FunctionReturnType<Fun, Args...>> complete_invoke(Fun&& fun, Args&&... args) {
+ return CompleteInvoker<FunctionReturnType<Fun, Args...>>::invoke(std::forward<Fun>(fun), std::forward<Args>(args)...);
+ }
+
+ const std::string benchmarkErrorMsg = "a benchmark failed to run successfully";
+ } // namespace Detail
+
+ template <typename Fun>
+ Detail::CompleteType_t<FunctionReturnType<Fun>> user_code(Fun&& fun) {
+ CATCH_TRY{
+ return Detail::complete_invoke(std::forward<Fun>(fun));
+ } CATCH_CATCH_ALL{
+ getResultCapture().benchmarkFailed(translateActiveException());
+ CATCH_RUNTIME_ERROR(Detail::benchmarkErrorMsg);
+ }
+ }
+ } // namespace Benchmark
+} // namespace Catch
+
+// end catch_complete_invoke.hpp
+namespace Catch {
+ namespace Benchmark {
+ namespace Detail {
+ struct ChronometerConcept {
+ virtual void start() = 0;
+ virtual void finish() = 0;
+ virtual ~ChronometerConcept() = default;
+ };
+ template <typename Clock>
+ struct ChronometerModel final : public ChronometerConcept {
+ void start() override { started = Clock::now(); }
+ void finish() override { finished = Clock::now(); }
+
+ ClockDuration<Clock> elapsed() const { return finished - started; }
+
+ TimePoint<Clock> started;
+ TimePoint<Clock> finished;
+ };
+ } // namespace Detail
+
+ struct Chronometer {
+ public:
+ template <typename Fun>
+ void measure(Fun&& fun) { measure(std::forward<Fun>(fun), is_callable<Fun(int)>()); }
+
+ int runs() const { return k; }
+
+ Chronometer(Detail::ChronometerConcept& meter, int k)
+ : impl(&meter)
+ , k(k) {}
+
+ private:
+ template <typename Fun>
+ void measure(Fun&& fun, std::false_type) {
+ measure([&fun](int) { return fun(); }, std::true_type());
+ }
+
+ template <typename Fun>
+ void measure(Fun&& fun, std::true_type) {
+ Detail::optimizer_barrier();
+ impl->start();
+ for (int i = 0; i < k; ++i) invoke_deoptimized(fun, i);
+ impl->finish();
+ Detail::optimizer_barrier();
+ }
+
+ Detail::ChronometerConcept* impl;
+ int k;
+ };
+ } // namespace Benchmark
+} // namespace Catch
+
+// end catch_chronometer.hpp
+// start catch_environment.hpp
+
+// Environment information
+
+
+namespace Catch {
+ namespace Benchmark {
+ template <typename Duration>
+ struct EnvironmentEstimate {
+ Duration mean;
+ OutlierClassification outliers;
+
+ template <typename Duration2>
+ operator EnvironmentEstimate<Duration2>() const {
+ return { mean, outliers };
+ }
+ };
+ template <typename Clock>
+ struct Environment {
+ using clock_type = Clock;
+ EnvironmentEstimate<FloatDuration<Clock>> clock_resolution;
+ EnvironmentEstimate<FloatDuration<Clock>> clock_cost;
+ };
+ } // namespace Benchmark
+} // namespace Catch
+
+// end catch_environment.hpp
+// start catch_execution_plan.hpp
+
+ // Execution plan
+
+
+// start catch_benchmark_function.hpp
+
+ // Dumb std::function implementation for consistent call overhead
+
+
+#include <cassert>
+#include <type_traits>
+#include <utility>
+#include <memory>
+
+namespace Catch {
+ namespace Benchmark {
+ namespace Detail {
+ template <typename T>
+ using Decay = typename std::decay<T>::type;
+ template <typename T, typename U>
+ struct is_related
+ : std::is_same<Decay<T>, Decay<U>> {};
+
+ /// We need to reinvent std::function because every piece of code that might add overhead
+ /// in a measurement context needs to have consistent performance characteristics so that we
+ /// can account for it in the measurement.
+ /// Implementations of std::function with optimizations that aren't always applicable, like
+ /// small buffer optimizations, are not uncommon.
+ /// This is effectively an implementation of std::function without any such optimizations;
+ /// it may be slow, but it is consistently slow.
+ struct BenchmarkFunction {
+ private:
+ struct callable {
+ virtual void call(Chronometer meter) const = 0;
+ virtual callable* clone() const = 0;
+ virtual ~callable() = default;
+ };
+ template <typename Fun>
+ struct model : public callable {
+ model(Fun&& fun) : fun(std::move(fun)) {}
+ model(Fun const& fun) : fun(fun) {}
+
+ model<Fun>* clone() const override { return new model<Fun>(*this); }
+
+ void call(Chronometer meter) const override {
+ call(meter, is_callable<Fun(Chronometer)>());
+ }
+ void call(Chronometer meter, std::true_type) const {
+ fun(meter);
+ }
+ void call(Chronometer meter, std::false_type) const {
+ meter.measure(fun);
+ }
+
+ Fun fun;
+ };
+
+ struct do_nothing { void operator()() const {} };
+
+ template <typename T>
+ BenchmarkFunction(model<T>* c) : f(c) {}
+
+ public:
+ BenchmarkFunction()
+ : f(new model<do_nothing>{ {} }) {}
+
+ template <typename Fun,
+ typename std::enable_if<!is_related<Fun, BenchmarkFunction>::value, int>::type = 0>
+ BenchmarkFunction(Fun&& fun)
+ : f(new model<typename std::decay<Fun>::type>(std::forward<Fun>(fun))) {}
+
+ BenchmarkFunction(BenchmarkFunction&& that)
+ : f(std::move(that.f)) {}
+
+ BenchmarkFunction(BenchmarkFunction const& that)
+ : f(that.f->clone()) {}
+
+ BenchmarkFunction& operator=(BenchmarkFunction&& that) {
+ f = std::move(that.f);
+ return *this;
+ }
+
+ BenchmarkFunction& operator=(BenchmarkFunction const& that) {
+ f.reset(that.f->clone());
+ return *this;
+ }
+
+ void operator()(Chronometer meter) const { f->call(meter); }
+
+ private:
+ std::unique_ptr<callable> f;
+ };
+ } // namespace Detail
+ } // namespace Benchmark
+} // namespace Catch
+
+// end catch_benchmark_function.hpp
+// start catch_repeat.hpp
+
+// repeat algorithm
+
+
+#include <type_traits>
+#include <utility>
+
+namespace Catch {
+ namespace Benchmark {
+ namespace Detail {
+ template <typename Fun>
+ struct repeater {
+ void operator()(int k) const {
+ for (int i = 0; i < k; ++i) {
+ fun();
+ }
+ }
+ Fun fun;
+ };
+ template <typename Fun>
+ repeater<typename std::decay<Fun>::type> repeat(Fun&& fun) {
+ return { std::forward<Fun>(fun) };
+ }
+ } // namespace Detail
+ } // namespace Benchmark
+} // namespace Catch
+
+// end catch_repeat.hpp
+// start catch_run_for_at_least.hpp
+
+// Run a function for a minimum amount of time
+
+
+// start catch_measure.hpp
+
+// Measure
+
+
+// start catch_timing.hpp
+
+// Timing
+
+
+#include <tuple>
+#include <type_traits>
+
+namespace Catch {
+ namespace Benchmark {
+ template <typename Duration, typename Result>
+ struct Timing {
+ Duration elapsed;
+ Result result;
+ int iterations;
+ };
+ template <typename Clock, typename Func, typename... Args>
+ using TimingOf = Timing<ClockDuration<Clock>, Detail::CompleteType_t<FunctionReturnType<Func, Args...>>>;
+ } // namespace Benchmark
+} // namespace Catch
+
+// end catch_timing.hpp
+#include <utility>
+
+namespace Catch {
+ namespace Benchmark {
+ namespace Detail {
+ template <typename Clock, typename Fun, typename... Args>
+ TimingOf<Clock, Fun, Args...> measure(Fun&& fun, Args&&... args) {
+ auto start = Clock::now();
+ auto&& r = Detail::complete_invoke(fun, std::forward<Args>(args)...);
+ auto end = Clock::now();
+ auto delta = end - start;
+ return { delta, std::forward<decltype(r)>(r), 1 };
+ }
+ } // namespace Detail
+ } // namespace Benchmark
+} // namespace Catch
+
+// end catch_measure.hpp
+#include <utility>
+#include <type_traits>
+
+namespace Catch {
+ namespace Benchmark {
+ namespace Detail {
+ template <typename Clock, typename Fun>
+ TimingOf<Clock, Fun, int> measure_one(Fun&& fun, int iters, std::false_type) {
+ return Detail::measure<Clock>(fun, iters);
+ }
+ template <typename Clock, typename Fun>
+ TimingOf<Clock, Fun, Chronometer> measure_one(Fun&& fun, int iters, std::true_type) {
+ Detail::ChronometerModel<Clock> meter;
+ auto&& result = Detail::complete_invoke(fun, Chronometer(meter, iters));
+
+ return { meter.elapsed(), std::move(result), iters };
+ }
+
+ template <typename Clock, typename Fun>
+ using run_for_at_least_argument_t = typename std::conditional<is_callable<Fun(Chronometer)>::value, Chronometer, int>::type;
+
+ struct optimized_away_error : std::exception {
+ const char* what() const noexcept override {
+ return "could not measure benchmark, maybe it was optimized away";
+ }
+ };
+
+ template <typename Clock, typename Fun>
+ TimingOf<Clock, Fun, run_for_at_least_argument_t<Clock, Fun>> run_for_at_least(ClockDuration<Clock> how_long, int seed, Fun&& fun) {
+ auto iters = seed;
+ while (iters < (1 << 30)) {
+ auto&& Timing = measure_one<Clock>(fun, iters, is_callable<Fun(Chronometer)>());
+
+ if (Timing.elapsed >= how_long) {
+ return { Timing.elapsed, std::move(Timing.result), iters };
+ }
+ iters *= 2;
+ }
+ Catch::throw_exception(optimized_away_error{});
+ }
+ } // namespace Detail
+ } // namespace Benchmark
+} // namespace Catch
+
+// end catch_run_for_at_least.hpp
+#include <algorithm>
+#include <iterator>
+
+namespace Catch {
+ namespace Benchmark {
+ template <typename Duration>
+ struct ExecutionPlan {
+ int iterations_per_sample;
+ Duration estimated_duration;
+ Detail::BenchmarkFunction benchmark;
+ Duration warmup_time;
+ int warmup_iterations;
+
+ template <typename Duration2>
+ operator ExecutionPlan<Duration2>() const {
+ return { iterations_per_sample, estimated_duration, benchmark, warmup_time, warmup_iterations };
+ }
+
+ template <typename Clock>
+ std::vector<FloatDuration<Clock>> run(const IConfig &cfg, Environment<FloatDuration<Clock>> env) const {
+ // warmup a bit
+ Detail::run_for_at_least<Clock>(std::chrono::duration_cast<ClockDuration<Clock>>(warmup_time), warmup_iterations, Detail::repeat(now<Clock>{}));
+
+ std::vector<FloatDuration<Clock>> times;
+ times.reserve(cfg.benchmarkSamples());
+ std::generate_n(std::back_inserter(times), cfg.benchmarkSamples(), [this, env] {
+ Detail::ChronometerModel<Clock> model;
+ this->benchmark(Chronometer(model, iterations_per_sample));
+ auto sample_time = model.elapsed() - env.clock_cost.mean;
+ if (sample_time < FloatDuration<Clock>::zero()) sample_time = FloatDuration<Clock>::zero();
+ return sample_time / iterations_per_sample;
+ });
+ return times;
+ }
+ };
+ } // namespace Benchmark
+} // namespace Catch
+
+// end catch_execution_plan.hpp
+// start catch_estimate_clock.hpp
+
+ // Environment measurement
+
+
+// start catch_stats.hpp
+
+// Statistical analysis tools
+
+
+#include <algorithm>
+#include <functional>
+#include <vector>
+#include <iterator>
+#include <numeric>
+#include <tuple>
+#include <cmath>
+#include <utility>
+#include <cstddef>
+#include <random>
+
+namespace Catch {
+ namespace Benchmark {
+ namespace Detail {
+ using sample = std::vector<double>;
+
+ double weighted_average_quantile(int k, int q, std::vector<double>::iterator first, std::vector<double>::iterator last);
+
+ template <typename Iterator>
+ OutlierClassification classify_outliers(Iterator first, Iterator last) {
+ std::vector<double> copy(first, last);
+
+ auto q1 = weighted_average_quantile(1, 4, copy.begin(), copy.end());
+ auto q3 = weighted_average_quantile(3, 4, copy.begin(), copy.end());
+ auto iqr = q3 - q1;
+ auto los = q1 - (iqr * 3.);
+ auto lom = q1 - (iqr * 1.5);
+ auto him = q3 + (iqr * 1.5);
+ auto his = q3 + (iqr * 3.);
+
+ OutlierClassification o;
+ for (; first != last; ++first) {
+ auto&& t = *first;
+ if (t < los) ++o.low_severe;
+ else if (t < lom) ++o.low_mild;
+ else if (t > his) ++o.high_severe;
+ else if (t > him) ++o.high_mild;
+ ++o.samples_seen;
+ }
+ return o;
+ }
+
+ template <typename Iterator>
+ double mean(Iterator first, Iterator last) {
+ auto count = last - first;
+ double sum = std::accumulate(first, last, 0.);
+ return sum / count;
+ }
+
+ template <typename URng, typename Iterator, typename Estimator>
+ sample resample(URng& rng, int resamples, Iterator first, Iterator last, Estimator& estimator) {
+ auto n = last - first;
+ std::uniform_int_distribution<decltype(n)> dist(0, n - 1);
+
+ sample out;
+ out.reserve(resamples);
+ std::generate_n(std::back_inserter(out), resamples, [n, first, &estimator, &dist, &rng] {
+ std::vector<double> resampled;
+ resampled.reserve(n);
+ std::generate_n(std::back_inserter(resampled), n, [first, &dist, &rng] { return first[dist(rng)]; });
+ return estimator(resampled.begin(), resampled.end());
+ });
+ std::sort(out.begin(), out.end());
+ return out;
+ }
+
+ template <typename Estimator, typename Iterator>
+ sample jackknife(Estimator&& estimator, Iterator first, Iterator last) {
+ auto n = last - first;
+ auto second = std::next(first);
+ sample results;
+ results.reserve(n);
+
+ for (auto it = first; it != last; ++it) {
+ std::iter_swap(it, first);
+ results.push_back(estimator(second, last));
+ }
+
+ return results;
+ }
+
+ inline double normal_cdf(double x) {
+ return std::erfc(-x / std::sqrt(2.0)) / 2.0;
+ }
+
+ double erfc_inv(double x);
+
+ double normal_quantile(double p);
+
+ template <typename Iterator, typename Estimator>
+ Estimate<double> bootstrap(double confidence_level, Iterator first, Iterator last, sample const& resample, Estimator&& estimator) {
+ auto n_samples = last - first;
+
+ double point = estimator(first, last);
+ // Degenerate case with a single sample
+ if (n_samples == 1) return { point, point, point, confidence_level };
+
+ sample jack = jackknife(estimator, first, last);
+ double jack_mean = mean(jack.begin(), jack.end());
+ double sum_squares, sum_cubes;
+ std::tie(sum_squares, sum_cubes) = std::accumulate(jack.begin(), jack.end(), std::make_pair(0., 0.), [jack_mean](std::pair<double, double> sqcb, double x) -> std::pair<double, double> {
+ auto d = jack_mean - x;
+ auto d2 = d * d;
+ auto d3 = d2 * d;
+ return { sqcb.first + d2, sqcb.second + d3 };
+ });
+
+ double accel = sum_cubes / (6 * std::pow(sum_squares, 1.5));
+ int n = static_cast<int>(resample.size());
+ double prob_n = std::count_if(resample.begin(), resample.end(), [point](double x) { return x < point; }) / (double)n;
+ // degenerate case with uniform samples
+ if (prob_n == 0) return { point, point, point, confidence_level };
+
+ double bias = normal_quantile(prob_n);
+ double z1 = normal_quantile((1. - confidence_level) / 2.);
+
+ auto cumn = [n](double x) -> int {
+ return std::lround(normal_cdf(x) * n); };
+ auto a = [bias, accel](double b) { return bias + b / (1. - accel * b); };
+ double b1 = bias + z1;
+ double b2 = bias - z1;
+ double a1 = a(b1);
+ double a2 = a(b2);
+ auto lo = (std::max)(cumn(a1), 0);
+ auto hi = (std::min)(cumn(a2), n - 1);
+
+ return { point, resample[lo], resample[hi], confidence_level };
+ }
+
+ double outlier_variance(Estimate<double> mean, Estimate<double> stddev, int n);
+
+ struct bootstrap_analysis {
+ Estimate<double> mean;
+ Estimate<double> standard_deviation;
+ double outlier_variance;
+ };
+
+ bootstrap_analysis analyse_samples(double confidence_level, int n_resamples, std::vector<double>::iterator first, std::vector<double>::iterator last);
+ } // namespace Detail
+ } // namespace Benchmark
+} // namespace Catch
+
+// end catch_stats.hpp
+#include <algorithm>
+#include <iterator>
+#include <tuple>
+#include <vector>
+#include <cmath>
+
+namespace Catch {
+ namespace Benchmark {
+ namespace Detail {
+ template <typename Clock>
+ std::vector<double> resolution(int k) {
+ std::vector<TimePoint<Clock>> times;
+ times.reserve(k + 1);
+ std::generate_n(std::back_inserter(times), k + 1, now<Clock>{});
+
+ std::vector<double> deltas;
+ deltas.reserve(k);
+ std::transform(std::next(times.begin()), times.end(), times.begin(),
+ std::back_inserter(deltas),
+ [](TimePoint<Clock> a, TimePoint<Clock> b) { return static_cast<double>((a - b).count()); });
+
+ return deltas;
+ }
+
+ const auto warmup_iterations = 10000;
+ const auto warmup_time = std::chrono::milliseconds(100);
+ const auto minimum_ticks = 1000;
+ const auto warmup_seed = 10000;
+ const auto clock_resolution_estimation_time = std::chrono::milliseconds(500);
+ const auto clock_cost_estimation_time_limit = std::chrono::seconds(1);
+ const auto clock_cost_estimation_tick_limit = 100000;
+ const auto clock_cost_estimation_time = std::chrono::milliseconds(10);
+ const auto clock_cost_estimation_iterations = 10000;
+
+ template <typename Clock>
+ int warmup() {
+ return run_for_at_least<Clock>(std::chrono::duration_cast<ClockDuration<Clock>>(warmup_time), warmup_seed, &resolution<Clock>)
+ .iterations;
+ }
+ template <typename Clock>
+ EnvironmentEstimate<FloatDuration<Clock>> estimate_clock_resolution(int iterations) {
+ auto r = run_for_at_least<Clock>(std::chrono::duration_cast<ClockDuration<Clock>>(clock_resolution_estimation_time), iterations, &resolution<Clock>)
+ .result;
+ return {
+ FloatDuration<Clock>(mean(r.begin(), r.end())),
+ classify_outliers(r.begin(), r.end()),
+ };
+ }
+ template <typename Clock>
+ EnvironmentEstimate<FloatDuration<Clock>> estimate_clock_cost(FloatDuration<Clock> resolution) {
+ auto time_limit = (std::min)(
+ resolution * clock_cost_estimation_tick_limit,
+ FloatDuration<Clock>(clock_cost_estimation_time_limit));
+ auto time_clock = [](int k) {
+ return Detail::measure<Clock>([k] {
+ for (int i = 0; i < k; ++i) {
+ volatile auto ignored = Clock::now();
+ (void)ignored;
+ }
+ }).elapsed;
+ };
+ time_clock(1);
+ int iters = clock_cost_estimation_iterations;
+ auto&& r = run_for_at_least<Clock>(std::chrono::duration_cast<ClockDuration<Clock>>(clock_cost_estimation_time), iters, time_clock);
+ std::vector<double> times;
+ int nsamples = static_cast<int>(std::ceil(time_limit / r.elapsed));
+ times.reserve(nsamples);
+ std::generate_n(std::back_inserter(times), nsamples, [time_clock, &r] {
+ return static_cast<double>((time_clock(r.iterations) / r.iterations).count());
+ });
+ return {
+ FloatDuration<Clock>(mean(times.begin(), times.end())),
+ classify_outliers(times.begin(), times.end()),
+ };
+ }
+
+ template <typename Clock>
+ Environment<FloatDuration<Clock>> measure_environment() {
+ static Environment<FloatDuration<Clock>>* env = nullptr;
+ if (env) {
+ return *env;
+ }
+
+ auto iters = Detail::warmup<Clock>();
+ auto resolution = Detail::estimate_clock_resolution<Clock>(iters);
+ auto cost = Detail::estimate_clock_cost<Clock>(resolution.mean);
+
+ env = new Environment<FloatDuration<Clock>>{ resolution, cost };
+ return *env;
+ }
+ } // namespace Detail
+ } // namespace Benchmark
+} // namespace Catch
+
+// end catch_estimate_clock.hpp
+// start catch_analyse.hpp
+
+ // Run and analyse one benchmark
+
+
+// start catch_sample_analysis.hpp
+
+// Benchmark results
+
+
+#include <algorithm>
+#include <vector>
+#include <string>
+#include <iterator>
+
+namespace Catch {
+ namespace Benchmark {
+ template <typename Duration>
+ struct SampleAnalysis {
+ std::vector<Duration> samples;
+ Estimate<Duration> mean;
+ Estimate<Duration> standard_deviation;
+ OutlierClassification outliers;
+ double outlier_variance;
+
+ template <typename Duration2>
+ operator SampleAnalysis<Duration2>() const {
+ std::vector<Duration2> samples2;
+ samples2.reserve(samples.size());
+ std::transform(samples.begin(), samples.end(), std::back_inserter(samples2), [](Duration d) { return Duration2(d); });
+ return {
+ std::move(samples2),
+ mean,
+ standard_deviation,
+ outliers,
+ outlier_variance,
+ };
+ }
+ };
+ } // namespace Benchmark
+} // namespace Catch
+
+// end catch_sample_analysis.hpp
+#include <algorithm>
+#include <iterator>
+#include <vector>
+
+namespace Catch {
+ namespace Benchmark {
+ namespace Detail {
+ template <typename Duration, typename Iterator>
+ SampleAnalysis<Duration> analyse(const IConfig &cfg, Environment<Duration>, Iterator first, Iterator last) {
+ if (!cfg.benchmarkNoAnalysis()) {
+ std::vector<double> samples;
+ samples.reserve(last - first);
+ std::transform(first, last, std::back_inserter(samples), [](Duration d) { return d.count(); });
+
+ auto analysis = Catch::Benchmark::Detail::analyse_samples(cfg.benchmarkConfidenceInterval(), cfg.benchmarkResamples(), samples.begin(), samples.end());
+ auto outliers = Catch::Benchmark::Detail::classify_outliers(samples.begin(), samples.end());
+
+ auto wrap_estimate = [](Estimate<double> e) {
+ return Estimate<Duration> {
+ Duration(e.point),
+ Duration(e.lower_bound),
+ Duration(e.upper_bound),
+ e.confidence_interval,
+ };
+ };
+ std::vector<Duration> samples2;
+ samples2.reserve(samples.size());
+ std::transform(samples.begin(), samples.end(), std::back_inserter(samples2), [](double d) { return Duration(d); });
+ return {
+ std::move(samples2),
+ wrap_estimate(analysis.mean),
+ wrap_estimate(analysis.standard_deviation),
+ outliers,
+ analysis.outlier_variance,
+ };
+ } else {
+ std::vector<Duration> samples;
+ samples.reserve(last - first);
+
+ Duration mean = Duration(0);
+ int i = 0;
+ for (auto it = first; it < last; ++it, ++i) {
+ samples.push_back(Duration(*it));
+ mean += Duration(*it);
+ }
+ mean /= i;
+
+ return {
+ std::move(samples),
+ Estimate<Duration>{mean, mean, mean, 0.0},
+ Estimate<Duration>{Duration(0), Duration(0), Duration(0), 0.0},
+ OutlierClassification{},
+ 0.0
+ };
+ }
+ }
+ } // namespace Detail
+ } // namespace Benchmark
+} // namespace Catch
+
+// end catch_analyse.hpp
+#include <algorithm>
+#include <functional>
+#include <string>
+#include <vector>
+#include <cmath>
+
+namespace Catch {
+ namespace Benchmark {
+ struct Benchmark {
+ Benchmark(std::string &&name)
+ : name(std::move(name)) {}
+
+ template <class FUN>
+ Benchmark(std::string &&name, FUN &&func)
+ : fun(std::move(func)), name(std::move(name)) {}
+
+ template <typename Clock>
+ ExecutionPlan<FloatDuration<Clock>> prepare(const IConfig &cfg, Environment<FloatDuration<Clock>> env) const {
+ auto min_time = env.clock_resolution.mean * Detail::minimum_ticks;
+ auto run_time = std::max(min_time, std::chrono::duration_cast<decltype(min_time)>(cfg.benchmarkWarmupTime()));
+ auto&& test = Detail::run_for_at_least<Clock>(std::chrono::duration_cast<ClockDuration<Clock>>(run_time), 1, fun);
+ int new_iters = static_cast<int>(std::ceil(min_time * test.iterations / test.elapsed));
+ return { new_iters, test.elapsed / test.iterations * new_iters * cfg.benchmarkSamples(), fun, std::chrono::duration_cast<FloatDuration<Clock>>(cfg.benchmarkWarmupTime()), Detail::warmup_iterations };
+ }
+
+ template <typename Clock = default_clock>
+ void run() {
+ IConfigPtr cfg = getCurrentContext().getConfig();
+
+ auto env = Detail::measure_environment<Clock>();
+
+ getResultCapture().benchmarkPreparing(name);
+ CATCH_TRY{
+ auto plan = user_code([&] {
+ return prepare<Clock>(*cfg, env);
+ });
+
+ BenchmarkInfo info {
+ name,
+ plan.estimated_duration.count(),
+ plan.iterations_per_sample,
+ cfg->benchmarkSamples(),
+ cfg->benchmarkResamples(),
+ env.clock_resolution.mean.count(),
+ env.clock_cost.mean.count()
+ };
+
+ getResultCapture().benchmarkStarting(info);
+
+ auto samples = user_code([&] {
+ return plan.template run<Clock>(*cfg, env);
+ });
+
+ auto analysis = Detail::analyse(*cfg, env, samples.begin(), samples.end());
+ BenchmarkStats<FloatDuration<Clock>> stats{ info, analysis.samples, analysis.mean, analysis.standard_deviation, analysis.outliers, analysis.outlier_variance };
+ getResultCapture().benchmarkEnded(stats);
+
+ } CATCH_CATCH_ALL{
+ if (translateActiveException() != Detail::benchmarkErrorMsg) // benchmark errors have been reported, otherwise rethrow.
+ std::rethrow_exception(std::current_exception());
+ }
+ }
+
+ // sets lambda to be used in fun *and* executes benchmark!
+ template <typename Fun,
+ typename std::enable_if<!Detail::is_related<Fun, Benchmark>::value, int>::type = 0>
+ Benchmark & operator=(Fun func) {
+ fun = Detail::BenchmarkFunction(func);
+ run();
+ return *this;
+ }
+
+ explicit operator bool() {
+ return true;
+ }
+
+ private:
+ Detail::BenchmarkFunction fun;
+ std::string name;
+ };
+ }
+} // namespace Catch
+
+#define INTERNAL_CATCH_GET_1_ARG(arg1, arg2, ...) arg1
+#define INTERNAL_CATCH_GET_2_ARG(arg1, arg2, ...) arg2
+
+#define INTERNAL_CATCH_BENCHMARK(BenchmarkName, name, benchmarkIndex)\
+ if( Catch::Benchmark::Benchmark BenchmarkName{name} ) \
+ BenchmarkName = [&](int benchmarkIndex)
+
+#define INTERNAL_CATCH_BENCHMARK_ADVANCED(BenchmarkName, name)\
+ if( Catch::Benchmark::Benchmark BenchmarkName{name} ) \
+ BenchmarkName = [&]
+
+// end catch_benchmark.hpp
+// start catch_constructor.hpp
+
+// Constructor and destructor helpers
+
+
+#include <type_traits>
+
+namespace Catch {
+ namespace Benchmark {
+ namespace Detail {
+ template <typename T, bool Destruct>
+ struct ObjectStorage
+ {
+ ObjectStorage() : data() {}
+
+ ObjectStorage(const ObjectStorage& other)
+ {
+ new(&data) T(other.stored_object());
+ }
+
+ ObjectStorage(ObjectStorage&& other)
+ {
+ new(&data) T(std::move(other.stored_object()));
+ }
+
+ ~ObjectStorage() { destruct_on_exit<T>(); }
+
+ template <typename... Args>
+ void construct(Args&&... args)
+ {
+ new (&data) T(std::forward<Args>(args)...);
+ }
+
+ template <bool AllowManualDestruction = !Destruct>
+ typename std::enable_if<AllowManualDestruction>::type destruct()
+ {
+ stored_object().~T();
+ }
+
+ private:
+ // If this is a constructor benchmark, destruct the underlying object
+ template <typename U>
+ void destruct_on_exit(typename std::enable_if<Destruct, U>::type* = 0) { destruct<true>(); }
+ // Otherwise, don't
+ template <typename U>
+ void destruct_on_exit(typename std::enable_if<!Destruct, U>::type* = 0) { }
+
+ T& stored_object() {
+ return *static_cast<T*>(static_cast<void*>(&data));
+ }
+
+ T const& stored_object() const {
+ return *static_cast<T*>(static_cast<void*>(&data));
+ }
+
+ struct { alignas(T) unsigned char data[sizeof(T)]; } data;
+ };
+ }
+
+ template <typename T>
+ using storage_for = Detail::ObjectStorage<T, true>;
+
+ template <typename T>
+ using destructable_object = Detail::ObjectStorage<T, false>;
+ }
+}
+
+// end catch_constructor.hpp
+// end catch_benchmarking_all.hpp
+#endif
+
+#endif // ! CATCH_CONFIG_IMPL_ONLY
+
+#ifdef CATCH_IMPL
+// start catch_impl.hpp
+
+#ifdef __clang__
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wweak-vtables"
+#endif
+
+// Keep these here for external reporters
+// start catch_test_case_tracker.h
+
+#include <string>
+#include <vector>
+#include <memory>
+
+namespace Catch {
+namespace TestCaseTracking {
+
+ struct NameAndLocation {
+ std::string name;
+ SourceLineInfo location;
+
+ NameAndLocation( std::string const& _name, SourceLineInfo const& _location );
+ friend bool operator==(NameAndLocation const& lhs, NameAndLocation const& rhs) {
+ return lhs.name == rhs.name
+ && lhs.location == rhs.location;
+ }
+ };
+
+ class ITracker;
+
+ using ITrackerPtr = std::shared_ptr<ITracker>;
+
+ class ITracker {
+ NameAndLocation m_nameAndLocation;
+
+ public:
+ ITracker(NameAndLocation const& nameAndLoc) :
+ m_nameAndLocation(nameAndLoc)
+ {}
+
+ // static queries
+ NameAndLocation const& nameAndLocation() const {
+ return m_nameAndLocation;
+ }
+
+ virtual ~ITracker();
+
+ // dynamic queries
+ virtual bool isComplete() const = 0; // Successfully completed or failed
+ virtual bool isSuccessfullyCompleted() const = 0;
+ virtual bool isOpen() const = 0; // Started but not complete
+ virtual bool hasChildren() const = 0;
+ virtual bool hasStarted() const = 0;
+
+ virtual ITracker& parent() = 0;
+
+ // actions
+ virtual void close() = 0; // Successfully complete
+ virtual void fail() = 0;
+ virtual void markAsNeedingAnotherRun() = 0;
+
+ virtual void addChild( ITrackerPtr const& child ) = 0;
+ virtual ITrackerPtr findChild( NameAndLocation const& nameAndLocation ) = 0;
+ virtual void openChild() = 0;
+
+ // Debug/ checking
+ virtual bool isSectionTracker() const = 0;
+ virtual bool isGeneratorTracker() const = 0;
+ };
+
+ class TrackerContext {
+
+ enum RunState {
+ NotStarted,
+ Executing,
+ CompletedCycle
+ };
+
+ ITrackerPtr m_rootTracker;
+ ITracker* m_currentTracker = nullptr;
+ RunState m_runState = NotStarted;
+
+ public:
+
+ ITracker& startRun();
+ void endRun();
+
+ void startCycle();
+ void completeCycle();
+
+ bool completedCycle() const;
+ ITracker& currentTracker();
+ void setCurrentTracker( ITracker* tracker );
+ };
+
+ class TrackerBase : public ITracker {
+ protected:
+ enum CycleState {
+ NotStarted,
+ Executing,
+ ExecutingChildren,
+ NeedsAnotherRun,
+ CompletedSuccessfully,
+ Failed
+ };
+
+ using Children = std::vector<ITrackerPtr>;
+ TrackerContext& m_ctx;
+ ITracker* m_parent;
+ Children m_children;
+ CycleState m_runState = NotStarted;
+
+ public:
+ TrackerBase( NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent );
+
+ bool isComplete() const override;
+ bool isSuccessfullyCompleted() const override;
+ bool isOpen() const override;
+ bool hasChildren() const override;
+ bool hasStarted() const override {
+ return m_runState != NotStarted;
+ }
+
+ void addChild( ITrackerPtr const& child ) override;
+
+ ITrackerPtr findChild( NameAndLocation const& nameAndLocation ) override;
+ ITracker& parent() override;
+
+ void openChild() override;
+
+ bool isSectionTracker() const override;
+ bool isGeneratorTracker() const override;
+
+ void open();
+
+ void close() override;
+ void fail() override;
+ void markAsNeedingAnotherRun() override;
+
+ private:
+ void moveToParent();
+ void moveToThis();
+ };
+
+ class SectionTracker : public TrackerBase {
+ std::vector<std::string> m_filters;
+ std::string m_trimmed_name;
+ public:
+ SectionTracker( NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent );
+
+ bool isSectionTracker() const override;
+
+ bool isComplete() const override;
+
+ static SectionTracker& acquire( TrackerContext& ctx, NameAndLocation const& nameAndLocation );
+
+ void tryOpen();
+
+ void addInitialFilters( std::vector<std::string> const& filters );
+ void addNextFilters( std::vector<std::string> const& filters );
+ //! Returns filters active in this tracker
+ std::vector<std::string> const& getFilters() const;
+ //! Returns whitespace-trimmed name of the tracked section
+ std::string const& trimmedName() const;
+ };
+
+} // namespace TestCaseTracking
+
+using TestCaseTracking::ITracker;
+using TestCaseTracking::TrackerContext;
+using TestCaseTracking::SectionTracker;
+
+} // namespace Catch
+
+// end catch_test_case_tracker.h
+
+// start catch_leak_detector.h
+
+namespace Catch {
+
+ struct LeakDetector {
+ LeakDetector();
+ ~LeakDetector();
+ };
+
+}
+// end catch_leak_detector.h
+// Cpp files will be included in the single-header file here
+// start catch_stats.cpp
+
+// Statistical analysis tools
+
+#if defined(CATCH_CONFIG_ENABLE_BENCHMARKING)
+
+#include <cassert>
+#include <random>
+
+#if defined(CATCH_CONFIG_USE_ASYNC)
+#include <future>
+#endif
+
+namespace {
+ double erf_inv(double x) {
+ // Code accompanying the article "Approximating the erfinv function" in GPU Computing Gems, Volume 2
+ double w, p;
+
+ w = -log((1.0 - x) * (1.0 + x));
+
+ if (w < 6.250000) {
+ w = w - 3.125000;
+ p = -3.6444120640178196996e-21;
+ p = -1.685059138182016589e-19 + p * w;
+ p = 1.2858480715256400167e-18 + p * w;
+ p = 1.115787767802518096e-17 + p * w;
+ p = -1.333171662854620906e-16 + p * w;
+ p = 2.0972767875968561637e-17 + p * w;
+ p = 6.6376381343583238325e-15 + p * w;
+ p = -4.0545662729752068639e-14 + p * w;
+ p = -8.1519341976054721522e-14 + p * w;
+ p = 2.6335093153082322977e-12 + p * w;
+ p = -1.2975133253453532498e-11 + p * w;
+ p = -5.4154120542946279317e-11 + p * w;
+ p = 1.051212273321532285e-09 + p * w;
+ p = -4.1126339803469836976e-09 + p * w;
+ p = -2.9070369957882005086e-08 + p * w;
+ p = 4.2347877827932403518e-07 + p * w;
+ p = -1.3654692000834678645e-06 + p * w;
+ p = -1.3882523362786468719e-05 + p * w;
+ p = 0.0001867342080340571352 + p * w;
+ p = -0.00074070253416626697512 + p * w;
+ p = -0.0060336708714301490533 + p * w;
+ p = 0.24015818242558961693 + p * w;
+ p = 1.6536545626831027356 + p * w;
+ } else if (w < 16.000000) {
+ w = sqrt(w) - 3.250000;
+ p = 2.2137376921775787049e-09;
+ p = 9.0756561938885390979e-08 + p * w;
+ p = -2.7517406297064545428e-07 + p * w;
+ p = 1.8239629214389227755e-08 + p * w;
+ p = 1.5027403968909827627e-06 + p * w;
+ p = -4.013867526981545969e-06 + p * w;
+ p = 2.9234449089955446044e-06 + p * w;
+ p = 1.2475304481671778723e-05 + p * w;
+ p = -4.7318229009055733981e-05 + p * w;
+ p = 6.8284851459573175448e-05 + p * w;
+ p = 2.4031110387097893999e-05 + p * w;
+ p = -0.0003550375203628474796 + p * w;
+ p = 0.00095328937973738049703 + p * w;
+ p = -0.0016882755560235047313 + p * w;
+ p = 0.0024914420961078508066 + p * w;
+ p = -0.0037512085075692412107 + p * w;
+ p = 0.005370914553590063617 + p * w;
+ p = 1.0052589676941592334 + p * w;
+ p = 3.0838856104922207635 + p * w;
+ } else {
+ w = sqrt(w) - 5.000000;
+ p = -2.7109920616438573243e-11;
+ p = -2.5556418169965252055e-10 + p * w;
+ p = 1.5076572693500548083e-09 + p * w;
+ p = -3.7894654401267369937e-09 + p * w;
+ p = 7.6157012080783393804e-09 + p * w;
+ p = -1.4960026627149240478e-08 + p * w;
+ p = 2.9147953450901080826e-08 + p * w;
+ p = -6.7711997758452339498e-08 + p * w;
+ p = 2.2900482228026654717e-07 + p * w;
+ p = -9.9298272942317002539e-07 + p * w;
+ p = 4.5260625972231537039e-06 + p * w;
+ p = -1.9681778105531670567e-05 + p * w;
+ p = 7.5995277030017761139e-05 + p * w;
+ p = -0.00021503011930044477347 + p * w;
+ p = -0.00013871931833623122026 + p * w;
+ p = 1.0103004648645343977 + p * w;
+ p = 4.8499064014085844221 + p * w;
+ }
+ return p * x;
+ }
+
+ double standard_deviation(std::vector<double>::iterator first, std::vector<double>::iterator last) {
+ auto m = Catch::Benchmark::Detail::mean(first, last);
+ double variance = std::accumulate(first, last, 0., [m](double a, double b) {
+ double diff = b - m;
+ return a + diff * diff;
+ }) / (last - first);
+ return std::sqrt(variance);
+ }
+
+}
+
+namespace Catch {
+ namespace Benchmark {
+ namespace Detail {
+
+ double weighted_average_quantile(int k, int q, std::vector<double>::iterator first, std::vector<double>::iterator last) {
+ auto count = last - first;
+ double idx = (count - 1) * k / static_cast<double>(q);
+ int j = static_cast<int>(idx);
+ double g = idx - j;
+ std::nth_element(first, first + j, last);
+ auto xj = first[j];
+ if (g == 0) return xj;
+
+ auto xj1 = *std::min_element(first + (j + 1), last);
+ return xj + g * (xj1 - xj);
+ }
+
+ double erfc_inv(double x) {
+ return erf_inv(1.0 - x);
+ }
+
+ double normal_quantile(double p) {
+ static const double ROOT_TWO = std::sqrt(2.0);
+
+ double result = 0.0;
+ assert(p >= 0 && p <= 1);
+ if (p < 0 || p > 1) {
+ return result;
+ }
+
+ result = -erfc_inv(2.0 * p);
+ // result *= normal distribution standard deviation (1.0) * sqrt(2)
+ result *= /*sd * */ ROOT_TWO;
+ // result += normal disttribution mean (0)
+ return result;
+ }
+
+ double outlier_variance(Estimate<double> mean, Estimate<double> stddev, int n) {
+ double sb = stddev.point;
+ double mn = mean.point / n;
+ double mg_min = mn / 2.;
+ double sg = (std::min)(mg_min / 4., sb / std::sqrt(n));
+ double sg2 = sg * sg;
+ double sb2 = sb * sb;
+
+ auto c_max = [n, mn, sb2, sg2](double x) -> double {
+ double k = mn - x;
+ double d = k * k;
+ double nd = n * d;
+ double k0 = -n * nd;
+ double k1 = sb2 - n * sg2 + nd;
+ double det = k1 * k1 - 4 * sg2 * k0;
+ return (int)(-2. * k0 / (k1 + std::sqrt(det)));
+ };
+
+ auto var_out = [n, sb2, sg2](double c) {
+ double nc = n - c;
+ return (nc / n) * (sb2 - nc * sg2);
+ };
+
+ return (std::min)(var_out(1), var_out((std::min)(c_max(0.), c_max(mg_min)))) / sb2;
+ }
+
+ bootstrap_analysis analyse_samples(double confidence_level, int n_resamples, std::vector<double>::iterator first, std::vector<double>::iterator last) {
+ CATCH_INTERNAL_START_WARNINGS_SUPPRESSION
+ CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS
+ static std::random_device entropy;
+ CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION
+
+ auto n = static_cast<int>(last - first); // seriously, one can't use integral types without hell in C++
+
+ auto mean = &Detail::mean<std::vector<double>::iterator>;
+ auto stddev = &standard_deviation;
+
+#if defined(CATCH_CONFIG_USE_ASYNC)
+ auto Estimate = [=](double(*f)(std::vector<double>::iterator, std::vector<double>::iterator)) {
+ auto seed = entropy();
+ return std::async(std::launch::async, [=] {
+ std::mt19937 rng(seed);
+ auto resampled = resample(rng, n_resamples, first, last, f);
+ return bootstrap(confidence_level, first, last, resampled, f);
+ });
+ };
+
+ auto mean_future = Estimate(mean);
+ auto stddev_future = Estimate(stddev);
+
+ auto mean_estimate = mean_future.get();
+ auto stddev_estimate = stddev_future.get();
+#else
+ auto Estimate = [=](double(*f)(std::vector<double>::iterator, std::vector<double>::iterator)) {
+ auto seed = entropy();
+ std::mt19937 rng(seed);
+ auto resampled = resample(rng, n_resamples, first, last, f);
+ return bootstrap(confidence_level, first, last, resampled, f);
+ };
+
+ auto mean_estimate = Estimate(mean);
+ auto stddev_estimate = Estimate(stddev);
+#endif // CATCH_USE_ASYNC
+
+ double outlier_variance = Detail::outlier_variance(mean_estimate, stddev_estimate, n);
+
+ return { mean_estimate, stddev_estimate, outlier_variance };
+ }
+ } // namespace Detail
+ } // namespace Benchmark
+} // namespace Catch
+
+#endif // CATCH_CONFIG_ENABLE_BENCHMARKING
+// end catch_stats.cpp
+// start catch_approx.cpp
+
+#include <cmath>
+#include <limits>
+
+namespace {
+
+// Performs equivalent check of std::fabs(lhs - rhs) <= margin
+// But without the subtraction to allow for INFINITY in comparison
+bool marginComparison(double lhs, double rhs, double margin) {
+ return (lhs + margin >= rhs) && (rhs + margin >= lhs);
+}
+
+}
+
+namespace Catch {
+namespace Detail {
+
+ Approx::Approx ( double value )
+ : m_epsilon( std::numeric_limits<float>::epsilon()*100 ),
+ m_margin( 0.0 ),
+ m_scale( 0.0 ),
+ m_value( value )
+ {}
+
+ Approx Approx::custom() {
+ return Approx( 0 );
+ }
+
+ Approx Approx::operator-() const {
+ auto temp(*this);
+ temp.m_value = -temp.m_value;
+ return temp;
+ }
+
+ std::string Approx::toString() const {
+ ReusableStringStream rss;
+ rss << "Approx( " << ::Catch::Detail::stringify( m_value ) << " )";
+ return rss.str();
+ }
+
+ bool Approx::equalityComparisonImpl(const double other) const {
+ // First try with fixed margin, then compute margin based on epsilon, scale and Approx's value
+ // Thanks to Richard Harris for his help refining the scaled margin value
+ return marginComparison(m_value, other, m_margin)
+ || marginComparison(m_value, other, m_epsilon * (m_scale + std::fabs(std::isinf(m_value)? 0 : m_value)));
+ }
+
+ void Approx::setMargin(double newMargin) {
+ CATCH_ENFORCE(newMargin >= 0,
+ "Invalid Approx::margin: " << newMargin << '.'
+ << " Approx::Margin has to be non-negative.");
+ m_margin = newMargin;
+ }
+
+ void Approx::setEpsilon(double newEpsilon) {
+ CATCH_ENFORCE(newEpsilon >= 0 && newEpsilon <= 1.0,
+ "Invalid Approx::epsilon: " << newEpsilon << '.'
+ << " Approx::epsilon has to be in [0, 1]");
+ m_epsilon = newEpsilon;
+ }
+
+} // end namespace Detail
+
+namespace literals {
+ Detail::Approx operator "" _a(long double val) {
+ return Detail::Approx(val);
+ }
+ Detail::Approx operator "" _a(unsigned long long val) {
+ return Detail::Approx(val);
+ }
+} // end namespace literals
+
+std::string StringMaker<Catch::Detail::Approx>::convert(Catch::Detail::Approx const& value) {
+ return value.toString();
+}
+
+} // end namespace Catch
+// end catch_approx.cpp
+// start catch_assertionhandler.cpp
+
+// start catch_debugger.h
+
+namespace Catch {
+ bool isDebuggerActive();
+}
+
+#ifdef CATCH_PLATFORM_MAC
+
+ #if defined(__i386__) || defined(__x86_64__)
+ #define CATCH_TRAP() __asm__("int $3\n" : : ) /* NOLINT */
+ #elif defined(__aarch64__)
+ #define CATCH_TRAP() __asm__(".inst 0xd43e0000")
+ #endif
+
+#elif defined(CATCH_PLATFORM_IPHONE)
+
+ // use inline assembler
+ #if defined(__i386__) || defined(__x86_64__)
+ #define CATCH_TRAP() __asm__("int $3")
+ #elif defined(__aarch64__)
+ #define CATCH_TRAP() __asm__(".inst 0xd4200000")
+ #elif defined(__arm__) && !defined(__thumb__)
+ #define CATCH_TRAP() __asm__(".inst 0xe7f001f0")
+ #elif defined(__arm__) && defined(__thumb__)
+ #define CATCH_TRAP() __asm__(".inst 0xde01")
+ #endif
+
+#elif defined(CATCH_PLATFORM_LINUX)
+ // If we can use inline assembler, do it because this allows us to break
+ // directly at the location of the failing check instead of breaking inside
+ // raise() called from it, i.e. one stack frame below.
+ #if defined(__GNUC__) && (defined(__i386) || defined(__x86_64))
+ #define CATCH_TRAP() asm volatile ("int $3") /* NOLINT */
+ #else // Fall back to the generic way.
+ #include <signal.h>
+
+ #define CATCH_TRAP() raise(SIGTRAP)
+ #endif
+#elif defined(_MSC_VER)
+ #define CATCH_TRAP() __debugbreak()
+#elif defined(__MINGW32__)
+ extern "C" __declspec(dllimport) void __stdcall DebugBreak();
+ #define CATCH_TRAP() DebugBreak()
+#endif
+
+#ifndef CATCH_BREAK_INTO_DEBUGGER
+ #ifdef CATCH_TRAP
+ #define CATCH_BREAK_INTO_DEBUGGER() []{ if( Catch::isDebuggerActive() ) { CATCH_TRAP(); } }()
+ #else
+ #define CATCH_BREAK_INTO_DEBUGGER() []{}()
+ #endif
+#endif
+
+// end catch_debugger.h
+// start catch_run_context.h
+
+// start catch_fatal_condition.h
+
+#include <cassert>
+
+namespace Catch {
+
+ // Wrapper for platform-specific fatal error (signals/SEH) handlers
+ //
+ // Tries to be cooperative with other handlers, and not step over
+ // other handlers. This means that unknown structured exceptions
+ // are passed on, previous signal handlers are called, and so on.
+ //
+ // Can only be instantiated once, and assumes that once a signal
+ // is caught, the binary will end up terminating. Thus, there
+ class FatalConditionHandler {
+ bool m_started = false;
+
+ // Install/disengage implementation for specific platform.
+ // Should be if-defed to work on current platform, can assume
+ // engage-disengage 1:1 pairing.
+ void engage_platform();
+ void disengage_platform();
+ public:
+ // Should also have platform-specific implementations as needed
+ FatalConditionHandler();
+ ~FatalConditionHandler();
+
+ void engage() {
+ assert(!m_started && "Handler cannot be installed twice.");
+ m_started = true;
+ engage_platform();
+ }
+
+ void disengage() {
+ assert(m_started && "Handler cannot be uninstalled without being installed first");
+ m_started = false;
+ disengage_platform();
+ }
+ };
+
+ //! Simple RAII guard for (dis)engaging the FatalConditionHandler
+ class FatalConditionHandlerGuard {
+ FatalConditionHandler* m_handler;
+ public:
+ FatalConditionHandlerGuard(FatalConditionHandler* handler):
+ m_handler(handler) {
+ m_handler->engage();
+ }
+ ~FatalConditionHandlerGuard() {
+ m_handler->disengage();
+ }
+ };
+
+} // end namespace Catch
+
+// end catch_fatal_condition.h
+#include <string>
+
+namespace Catch {
+
+ struct IMutableContext;
+
+ ///////////////////////////////////////////////////////////////////////////
+
+ class RunContext : public IResultCapture, public IRunner {
+
+ public:
+ RunContext( RunContext const& ) = delete;
+ RunContext& operator =( RunContext const& ) = delete;
+
+ explicit RunContext( IConfigPtr const& _config, IStreamingReporterPtr&& reporter );
+
+ ~RunContext() override;
+
+ void testGroupStarting( std::string const& testSpec, std::size_t groupIndex, std::size_t groupsCount );
+ void testGroupEnded( std::string const& testSpec, Totals const& totals, std::size_t groupIndex, std::size_t groupsCount );
+
+ Totals runTest(TestCase const& testCase);
+
+ IConfigPtr config() const;
+ IStreamingReporter& reporter() const;
+
+ public: // IResultCapture
+
+ // Assertion handlers
+ void handleExpr
+ ( AssertionInfo const& info,
+ ITransientExpression const& expr,
+ AssertionReaction& reaction ) override;
+ void handleMessage
+ ( AssertionInfo const& info,
+ ResultWas::OfType resultType,
+ StringRef const& message,
+ AssertionReaction& reaction ) override;
+ void handleUnexpectedExceptionNotThrown
+ ( AssertionInfo const& info,
+ AssertionReaction& reaction ) override;
+ void handleUnexpectedInflightException
+ ( AssertionInfo const& info,
+ std::string const& message,
+ AssertionReaction& reaction ) override;
+ void handleIncomplete
+ ( AssertionInfo const& info ) override;
+ void handleNonExpr
+ ( AssertionInfo const &info,
+ ResultWas::OfType resultType,
+ AssertionReaction &reaction ) override;
+
+ bool sectionStarted( SectionInfo const& sectionInfo, Counts& assertions ) override;
+
+ void sectionEnded( SectionEndInfo const& endInfo ) override;
+ void sectionEndedEarly( SectionEndInfo const& endInfo ) override;
+
+ auto acquireGeneratorTracker( StringRef generatorName, SourceLineInfo const& lineInfo ) -> IGeneratorTracker& override;
+
+#if defined(CATCH_CONFIG_ENABLE_BENCHMARKING)
+ void benchmarkPreparing( std::string const& name ) override;
+ void benchmarkStarting( BenchmarkInfo const& info ) override;
+ void benchmarkEnded( BenchmarkStats<> const& stats ) override;
+ void benchmarkFailed( std::string const& error ) override;
+#endif // CATCH_CONFIG_ENABLE_BENCHMARKING
+
+ void pushScopedMessage( MessageInfo const& message ) override;
+ void popScopedMessage( MessageInfo const& message ) override;
+
+ void emplaceUnscopedMessage( MessageBuilder const& builder ) override;
+
+ std::string getCurrentTestName() const override;
+
+ const AssertionResult* getLastResult() const override;
+
+ void exceptionEarlyReported() override;
+
+ void handleFatalErrorCondition( StringRef message ) override;
+
+ bool lastAssertionPassed() override;
+
+ void assertionPassed() override;
+
+ public:
+ // !TBD We need to do this another way!
+ bool aborting() const final;
+
+ private:
+
+ void runCurrentTest( std::string& redirectedCout, std::string& redirectedCerr );
+ void invokeActiveTestCase();
+
+ void resetAssertionInfo();
+ bool testForMissingAssertions( Counts& assertions );
+
+ void assertionEnded( AssertionResult const& result );
+ void reportExpr
+ ( AssertionInfo const &info,
+ ResultWas::OfType resultType,
+ ITransientExpression const *expr,
+ bool negated );
+
+ void populateReaction( AssertionReaction& reaction );
+
+ private:
+
+ void handleUnfinishedSections();
+
+ TestRunInfo m_runInfo;
+ IMutableContext& m_context;
+ TestCase const* m_activeTestCase = nullptr;
+ ITracker* m_testCaseTracker = nullptr;
+ Option<AssertionResult> m_lastResult;
+
+ IConfigPtr m_config;
+ Totals m_totals;
+ IStreamingReporterPtr m_reporter;
+ std::vector<MessageInfo> m_messages;
+ std::vector<ScopedMessage> m_messageScopes; /* Keeps owners of so-called unscoped messages. */
+ AssertionInfo m_lastAssertionInfo;
+ std::vector<SectionEndInfo> m_unfinishedSections;
+ std::vector<ITracker*> m_activeSections;
+ TrackerContext m_trackerContext;
+ FatalConditionHandler m_fatalConditionhandler;
+ bool m_lastAssertionPassed = false;
+ bool m_shouldReportUnexpected = true;
+ bool m_includeSuccessfulResults;
+ };
+
+ void seedRng(IConfig const& config);
+ unsigned int rngSeed();
+} // end namespace Catch
+
+// end catch_run_context.h
+namespace Catch {
+
+ namespace {
+ auto operator <<( std::ostream& os, ITransientExpression const& expr ) -> std::ostream& {
+ expr.streamReconstructedExpression( os );
+ return os;
+ }
+ }
+
+ LazyExpression::LazyExpression( bool isNegated )
+ : m_isNegated( isNegated )
+ {}
+
+ LazyExpression::LazyExpression( LazyExpression const& other ) : m_isNegated( other.m_isNegated ) {}
+
+ LazyExpression::operator bool() const {
+ return m_transientExpression != nullptr;
+ }
+
+ auto operator << ( std::ostream& os, LazyExpression const& lazyExpr ) -> std::ostream& {
+ if( lazyExpr.m_isNegated )
+ os << "!";
+
+ if( lazyExpr ) {
+ if( lazyExpr.m_isNegated && lazyExpr.m_transientExpression->isBinaryExpression() )
+ os << "(" << *lazyExpr.m_transientExpression << ")";
+ else
+ os << *lazyExpr.m_transientExpression;
+ }
+ else {
+ os << "{** error - unchecked empty expression requested **}";
+ }
+ return os;
+ }
+
+ AssertionHandler::AssertionHandler
+ ( StringRef const& macroName,
+ SourceLineInfo const& lineInfo,
+ StringRef capturedExpression,
+ ResultDisposition::Flags resultDisposition )
+ : m_assertionInfo{ macroName, lineInfo, capturedExpression, resultDisposition },
+ m_resultCapture( getResultCapture() )
+ {}
+
+ void AssertionHandler::handleExpr( ITransientExpression const& expr ) {
+ m_resultCapture.handleExpr( m_assertionInfo, expr, m_reaction );
+ }
+ void AssertionHandler::handleMessage(ResultWas::OfType resultType, StringRef const& message) {
+ m_resultCapture.handleMessage( m_assertionInfo, resultType, message, m_reaction );
+ }
+
+ auto AssertionHandler::allowThrows() const -> bool {
+ return getCurrentContext().getConfig()->allowThrows();
+ }
+
+ void AssertionHandler::complete() {
+ setCompleted();
+ if( m_reaction.shouldDebugBreak ) {
+
+ // If you find your debugger stopping you here then go one level up on the
+ // call-stack for the code that caused it (typically a failed assertion)
+
+ // (To go back to the test and change execution, jump over the throw, next)
+ CATCH_BREAK_INTO_DEBUGGER();
+ }
+ if (m_reaction.shouldThrow) {
+#if !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS)
+ throw Catch::TestFailureException();
+#else
+ CATCH_ERROR( "Test failure requires aborting test!" );
+#endif
+ }
+ }
+ void AssertionHandler::setCompleted() {
+ m_completed = true;
+ }
+
+ void AssertionHandler::handleUnexpectedInflightException() {
+ m_resultCapture.handleUnexpectedInflightException( m_assertionInfo, Catch::translateActiveException(), m_reaction );
+ }
+
+ void AssertionHandler::handleExceptionThrownAsExpected() {
+ m_resultCapture.handleNonExpr(m_assertionInfo, ResultWas::Ok, m_reaction);
+ }
+ void AssertionHandler::handleExceptionNotThrownAsExpected() {
+ m_resultCapture.handleNonExpr(m_assertionInfo, ResultWas::Ok, m_reaction);
+ }
+
+ void AssertionHandler::handleUnexpectedExceptionNotThrown() {
+ m_resultCapture.handleUnexpectedExceptionNotThrown( m_assertionInfo, m_reaction );
+ }
+
+ void AssertionHandler::handleThrowingCallSkipped() {
+ m_resultCapture.handleNonExpr(m_assertionInfo, ResultWas::Ok, m_reaction);
+ }
+
+ // This is the overload that takes a string and infers the Equals matcher from it
+ // The more general overload, that takes any string matcher, is in catch_capture_matchers.cpp
+ void handleExceptionMatchExpr( AssertionHandler& handler, std::string const& str, StringRef const& matcherString ) {
+ handleExceptionMatchExpr( handler, Matchers::Equals( str ), matcherString );
+ }
+
+} // namespace Catch
+// end catch_assertionhandler.cpp
+// start catch_assertionresult.cpp
+
+namespace Catch {
+ AssertionResultData::AssertionResultData(ResultWas::OfType _resultType, LazyExpression const & _lazyExpression):
+ lazyExpression(_lazyExpression),
+ resultType(_resultType) {}
+
+ std::string AssertionResultData::reconstructExpression() const {
+
+ if( reconstructedExpression.empty() ) {
+ if( lazyExpression ) {
+ ReusableStringStream rss;
+ rss << lazyExpression;
+ reconstructedExpression = rss.str();
+ }
+ }
+ return reconstructedExpression;
+ }
+
+ AssertionResult::AssertionResult( AssertionInfo const& info, AssertionResultData const& data )
+ : m_info( info ),
+ m_resultData( data )
+ {}
+
+ // Result was a success
+ bool AssertionResult::succeeded() const {
+ return Catch::isOk( m_resultData.resultType );
+ }
+
+ // Result was a success, or failure is suppressed
+ bool AssertionResult::isOk() const {
+ return Catch::isOk( m_resultData.resultType ) || shouldSuppressFailure( m_info.resultDisposition );
+ }
+
+ ResultWas::OfType AssertionResult::getResultType() const {
+ return m_resultData.resultType;
+ }
+
+ bool AssertionResult::hasExpression() const {
+ return !m_info.capturedExpression.empty();
+ }
+
+ bool AssertionResult::hasMessage() const {
+ return !m_resultData.message.empty();
+ }
+
+ std::string AssertionResult::getExpression() const {
+ // Possibly overallocating by 3 characters should be basically free
+ std::string expr; expr.reserve(m_info.capturedExpression.size() + 3);
+ if (isFalseTest(m_info.resultDisposition)) {
+ expr += "!(";
+ }
+ expr += m_info.capturedExpression;
+ if (isFalseTest(m_info.resultDisposition)) {
+ expr += ')';
+ }
+ return expr;
+ }
+
+ std::string AssertionResult::getExpressionInMacro() const {
+ std::string expr;
+ if( m_info.macroName.empty() )
+ expr = static_cast<std::string>(m_info.capturedExpression);
+ else {
+ expr.reserve( m_info.macroName.size() + m_info.capturedExpression.size() + 4 );
+ expr += m_info.macroName;
+ expr += "( ";
+ expr += m_info.capturedExpression;
+ expr += " )";
+ }
+ return expr;
+ }
+
+ bool AssertionResult::hasExpandedExpression() const {
+ return hasExpression() && getExpandedExpression() != getExpression();
+ }
+
+ std::string AssertionResult::getExpandedExpression() const {
+ std::string expr = m_resultData.reconstructExpression();
+ return expr.empty()
+ ? getExpression()
+ : expr;
+ }
+
+ std::string AssertionResult::getMessage() const {
+ return m_resultData.message;
+ }
+ SourceLineInfo AssertionResult::getSourceInfo() const {
+ return m_info.lineInfo;
+ }
+
+ StringRef AssertionResult::getTestMacroName() const {
+ return m_info.macroName;
+ }
+
+} // end namespace Catch
+// end catch_assertionresult.cpp
+// start catch_capture_matchers.cpp
+
+namespace Catch {
+
+ using StringMatcher = Matchers::Impl::MatcherBase<std::string>;
+
+ // This is the general overload that takes a any string matcher
+ // There is another overload, in catch_assertionhandler.h/.cpp, that only takes a string and infers
+ // the Equals matcher (so the header does not mention matchers)
+ void handleExceptionMatchExpr( AssertionHandler& handler, StringMatcher const& matcher, StringRef const& matcherString ) {
+ std::string exceptionMessage = Catch::translateActiveException();
+ MatchExpr<std::string, StringMatcher const&> expr( exceptionMessage, matcher, matcherString );
+ handler.handleExpr( expr );
+ }
+
+} // namespace Catch
+// end catch_capture_matchers.cpp
+// start catch_commandline.cpp
+
+// start catch_commandline.h
+
+// start catch_clara.h
+
+// Use Catch's value for console width (store Clara's off to the side, if present)
+#ifdef CLARA_CONFIG_CONSOLE_WIDTH
+#define CATCH_TEMP_CLARA_CONFIG_CONSOLE_WIDTH CATCH_CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH
+#undef CATCH_CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH
+#endif
+#define CATCH_CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH CATCH_CONFIG_CONSOLE_WIDTH-1
+
+#ifdef __clang__
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wweak-vtables"
+#pragma clang diagnostic ignored "-Wexit-time-destructors"
+#pragma clang diagnostic ignored "-Wshadow"
+#endif
+
+// start clara.hpp
+// Copyright 2017 Two Blue Cubes Ltd. All rights reserved.
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+// See https://github.com/philsquared/Clara for more details
+
+// Clara v1.1.5
+
+
+#ifndef CATCH_CLARA_CONFIG_CONSOLE_WIDTH
+#define CATCH_CLARA_CONFIG_CONSOLE_WIDTH 80
+#endif
+
+#ifndef CATCH_CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH
+#define CATCH_CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH CATCH_CLARA_CONFIG_CONSOLE_WIDTH
+#endif
+
+#ifndef CLARA_CONFIG_OPTIONAL_TYPE
+#ifdef __has_include
+#if __has_include(<optional>) && __cplusplus >= 201703L
+#include <optional>
+#define CLARA_CONFIG_OPTIONAL_TYPE std::optional
+#endif
+#endif
+#endif
+
+// ----------- #included from clara_textflow.hpp -----------
+
+// TextFlowCpp
+//
+// A single-header library for wrapping and laying out basic text, by Phil Nash
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+// This project is hosted at https://github.com/philsquared/textflowcpp
+
+
+#include <cassert>
+#include <ostream>
+#include <sstream>
+#include <vector>
+
+#ifndef CATCH_CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH
+#define CATCH_CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH 80
+#endif
+
+namespace Catch {
+namespace clara {
+namespace TextFlow {
+
+inline auto isWhitespace(char c) -> bool {
+ static std::string chars = " \t\n\r";
+ return chars.find(c) != std::string::npos;
+}
+inline auto isBreakableBefore(char c) -> bool {
+ static std::string chars = "[({<|";
+ return chars.find(c) != std::string::npos;
+}
+inline auto isBreakableAfter(char c) -> bool {
+ static std::string chars = "])}>.,:;*+-=&/\\";
+ return chars.find(c) != std::string::npos;
+}
+
+class Columns;
+
+class Column {
+ std::vector<std::string> m_strings;
+ size_t m_width = CATCH_CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH;
+ size_t m_indent = 0;
+ size_t m_initialIndent = std::string::npos;
+
+public:
+ class iterator {
+ friend Column;
+
+ Column const& m_column;
+ size_t m_stringIndex = 0;
+ size_t m_pos = 0;
+
+ size_t m_len = 0;
+ size_t m_end = 0;
+ bool m_suffix = false;
+
+ iterator(Column const& column, size_t stringIndex)
+ : m_column(column),
+ m_stringIndex(stringIndex) {}
+
+ auto line() const -> std::string const& { return m_column.m_strings[m_stringIndex]; }
+
+ auto isBoundary(size_t at) const -> bool {
+ assert(at > 0);
+ assert(at <= line().size());
+
+ return at == line().size() ||
+ (isWhitespace(line()[at]) && !isWhitespace(line()[at - 1])) ||
+ isBreakableBefore(line()[at]) ||
+ isBreakableAfter(line()[at - 1]);
+ }
+
+ void calcLength() {
+ assert(m_stringIndex < m_column.m_strings.size());
+
+ m_suffix = false;
+ auto width = m_column.m_width - indent();
+ m_end = m_pos;
+ if (line()[m_pos] == '\n') {
+ ++m_end;
+ }
+ while (m_end < line().size() && line()[m_end] != '\n')
+ ++m_end;
+
+ if (m_end < m_pos + width) {
+ m_len = m_end - m_pos;
+ } else {
+ size_t len = width;
+ while (len > 0 && !isBoundary(m_pos + len))
+ --len;
+ while (len > 0 && isWhitespace(line()[m_pos + len - 1]))
+ --len;
+
+ if (len > 0) {
+ m_len = len;
+ } else {
+ m_suffix = true;
+ m_len = width - 1;
+ }
+ }
+ }
+
+ auto indent() const -> size_t {
+ auto initial = m_pos == 0 && m_stringIndex == 0 ? m_column.m_initialIndent : std::string::npos;
+ return initial == std::string::npos ? m_column.m_indent : initial;
+ }
+
+ auto addIndentAndSuffix(std::string const &plain) const -> std::string {
+ return std::string(indent(), ' ') + (m_suffix ? plain + "-" : plain);
+ }
+
+ public:
+ using difference_type = std::ptrdiff_t;
+ using value_type = std::string;
+ using pointer = value_type * ;
+ using reference = value_type & ;
+ using iterator_category = std::forward_iterator_tag;
+
+ explicit iterator(Column const& column) : m_column(column) {
+ assert(m_column.m_width > m_column.m_indent);
+ assert(m_column.m_initialIndent == std::string::npos || m_column.m_width > m_column.m_initialIndent);
+ calcLength();
+ if (m_len == 0)
+ m_stringIndex++; // Empty string
+ }
+
+ auto operator *() const -> std::string {
+ assert(m_stringIndex < m_column.m_strings.size());
+ assert(m_pos <= m_end);
+ return addIndentAndSuffix(line().substr(m_pos, m_len));
+ }
+
+ auto operator ++() -> iterator& {
+ m_pos += m_len;
+ if (m_pos < line().size() && line()[m_pos] == '\n')
+ m_pos += 1;
+ else
+ while (m_pos < line().size() && isWhitespace(line()[m_pos]))
+ ++m_pos;
+
+ if (m_pos == line().size()) {
+ m_pos = 0;
+ ++m_stringIndex;
+ }
+ if (m_stringIndex < m_column.m_strings.size())
+ calcLength();
+ return *this;
+ }
+ auto operator ++(int) -> iterator {
+ iterator prev(*this);
+ operator++();
+ return prev;
+ }
+
+ auto operator ==(iterator const& other) const -> bool {
+ return
+ m_pos == other.m_pos &&
+ m_stringIndex == other.m_stringIndex &&
+ &m_column == &other.m_column;
+ }
+ auto operator !=(iterator const& other) const -> bool {
+ return !operator==(other);
+ }
+ };
+ using const_iterator = iterator;
+
+ explicit Column(std::string const& text) { m_strings.push_back(text); }
+
+ auto width(size_t newWidth) -> Column& {
+ assert(newWidth > 0);
+ m_width = newWidth;
+ return *this;
+ }
+ auto indent(size_t newIndent) -> Column& {
+ m_indent = newIndent;
+ return *this;
+ }
+ auto initialIndent(size_t newIndent) -> Column& {
+ m_initialIndent = newIndent;
+ return *this;
+ }
+
+ auto width() const -> size_t { return m_width; }
+ auto begin() const -> iterator { return iterator(*this); }
+ auto end() const -> iterator { return { *this, m_strings.size() }; }
+
+ inline friend std::ostream& operator << (std::ostream& os, Column const& col) {
+ bool first = true;
+ for (auto line : col) {
+ if (first)
+ first = false;
+ else
+ os << "\n";
+ os << line;
+ }
+ return os;
+ }
+
+ auto operator + (Column const& other)->Columns;
+
+ auto toString() const -> std::string {
+ std::ostringstream oss;
+ oss << *this;
+ return oss.str();
+ }
+};
+
+class Spacer : public Column {
+
+public:
+ explicit Spacer(size_t spaceWidth) : Column("") {
+ width(spaceWidth);
+ }
+};
+
+class Columns {
+ std::vector<Column> m_columns;
+
+public:
+
+ class iterator {
+ friend Columns;
+ struct EndTag {};
+
+ std::vector<Column> const& m_columns;
+ std::vector<Column::iterator> m_iterators;
+ size_t m_activeIterators;
+
+ iterator(Columns const& columns, EndTag)
+ : m_columns(columns.m_columns),
+ m_activeIterators(0) {
+ m_iterators.reserve(m_columns.size());
+
+ for (auto const& col : m_columns)
+ m_iterators.push_back(col.end());
+ }
+
+ public:
+ using difference_type = std::ptrdiff_t;
+ using value_type = std::string;
+ using pointer = value_type * ;
+ using reference = value_type & ;
+ using iterator_category = std::forward_iterator_tag;
+
+ explicit iterator(Columns const& columns)
+ : m_columns(columns.m_columns),
+ m_activeIterators(m_columns.size()) {
+ m_iterators.reserve(m_columns.size());
+
+ for (auto const& col : m_columns)
+ m_iterators.push_back(col.begin());
+ }
+
+ auto operator ==(iterator const& other) const -> bool {
+ return m_iterators == other.m_iterators;
+ }
+ auto operator !=(iterator const& other) const -> bool {
+ return m_iterators != other.m_iterators;
+ }
+ auto operator *() const -> std::string {
+ std::string row, padding;
+
+ for (size_t i = 0; i < m_columns.size(); ++i) {
+ auto width = m_columns[i].width();
+ if (m_iterators[i] != m_columns[i].end()) {
+ std::string col = *m_iterators[i];
+ row += padding + col;
+ if (col.size() < width)
+ padding = std::string(width - col.size(), ' ');
+ else
+ padding = "";
+ } else {
+ padding += std::string(width, ' ');
+ }
+ }
+ return row;
+ }
+ auto operator ++() -> iterator& {
+ for (size_t i = 0; i < m_columns.size(); ++i) {
+ if (m_iterators[i] != m_columns[i].end())
+ ++m_iterators[i];
+ }
+ return *this;
+ }
+ auto operator ++(int) -> iterator {
+ iterator prev(*this);
+ operator++();
+ return prev;
+ }
+ };
+ using const_iterator = iterator;
+
+ auto begin() const -> iterator { return iterator(*this); }
+ auto end() const -> iterator { return { *this, iterator::EndTag() }; }
+
+ auto operator += (Column const& col) -> Columns& {
+ m_columns.push_back(col);
+ return *this;
+ }
+ auto operator + (Column const& col) -> Columns {
+ Columns combined = *this;
+ combined += col;
+ return combined;
+ }
+
+ inline friend std::ostream& operator << (std::ostream& os, Columns const& cols) {
+
+ bool first = true;
+ for (auto line : cols) {
+ if (first)
+ first = false;
+ else
+ os << "\n";
+ os << line;
+ }
+ return os;
+ }
+
+ auto toString() const -> std::string {
+ std::ostringstream oss;
+ oss << *this;
+ return oss.str();
+ }
+};
+
+inline auto Column::operator + (Column const& other) -> Columns {
+ Columns cols;
+ cols += *this;
+ cols += other;
+ return cols;
+}
+}
+
+}
+}
+
+// ----------- end of #include from clara_textflow.hpp -----------
+// ........... back in clara.hpp
+
+#include <cctype>
+#include <string>
+#include <memory>
+#include <set>
+#include <algorithm>
+
+#if !defined(CATCH_PLATFORM_WINDOWS) && ( defined(WIN32) || defined(__WIN32__) || defined(_WIN32) || defined(_MSC_VER) )
+#define CATCH_PLATFORM_WINDOWS
+#endif
+
+namespace Catch { namespace clara {
+namespace detail {
+
+ // Traits for extracting arg and return type of lambdas (for single argument lambdas)
+ template<typename L>
+ struct UnaryLambdaTraits : UnaryLambdaTraits<decltype( &L::operator() )> {};
+
+ template<typename ClassT, typename ReturnT, typename... Args>
+ struct UnaryLambdaTraits<ReturnT( ClassT::* )( Args... ) const> {
+ static const bool isValid = false;
+ };
+
+ template<typename ClassT, typename ReturnT, typename ArgT>
+ struct UnaryLambdaTraits<ReturnT( ClassT::* )( ArgT ) const> {
+ static const bool isValid = true;
+ using ArgType = typename std::remove_const<typename std::remove_reference<ArgT>::type>::type;
+ using ReturnType = ReturnT;
+ };
+
+ class TokenStream;
+
+ // Transport for raw args (copied from main args, or supplied via init list for testing)
+ class Args {
+ friend TokenStream;
+ std::string m_exeName;
+ std::vector<std::string> m_args;
+
+ public:
+ Args( int argc, char const* const* argv )
+ : m_exeName(argv[0]),
+ m_args(argv + 1, argv + argc) {}
+
+ Args( std::initializer_list<std::string> args )
+ : m_exeName( *args.begin() ),
+ m_args( args.begin()+1, args.end() )
+ {}
+
+ auto exeName() const -> std::string {
+ return m_exeName;
+ }
+ };
+
+ // Wraps a token coming from a token stream. These may not directly correspond to strings as a single string
+ // may encode an option + its argument if the : or = form is used
+ enum class TokenType {
+ Option, Argument
+ };
+ struct Token {
+ TokenType type;
+ std::string token;
+ };
+
+ inline auto isOptPrefix( char c ) -> bool {
+ return c == '-'
+#ifdef CATCH_PLATFORM_WINDOWS
+ || c == '/'
+#endif
+ ;
+ }
+
+ // Abstracts iterators into args as a stream of tokens, with option arguments uniformly handled
+ class TokenStream {
+ using Iterator = std::vector<std::string>::const_iterator;
+ Iterator it;
+ Iterator itEnd;
+ std::vector<Token> m_tokenBuffer;
+
+ void loadBuffer() {
+ m_tokenBuffer.resize( 0 );
+
+ // Skip any empty strings
+ while( it != itEnd && it->empty() )
+ ++it;
+
+ if( it != itEnd ) {
+ auto const &next = *it;
+ if( isOptPrefix( next[0] ) ) {
+ auto delimiterPos = next.find_first_of( " :=" );
+ if( delimiterPos != std::string::npos ) {
+ m_tokenBuffer.push_back( { TokenType::Option, next.substr( 0, delimiterPos ) } );
+ m_tokenBuffer.push_back( { TokenType::Argument, next.substr( delimiterPos + 1 ) } );
+ } else {
+ if( next[1] != '-' && next.size() > 2 ) {
+ std::string opt = "- ";
+ for( size_t i = 1; i < next.size(); ++i ) {
+ opt[1] = next[i];
+ m_tokenBuffer.push_back( { TokenType::Option, opt } );
+ }
+ } else {
+ m_tokenBuffer.push_back( { TokenType::Option, next } );
+ }
+ }
+ } else {
+ m_tokenBuffer.push_back( { TokenType::Argument, next } );
+ }
+ }
+ }
+
+ public:
+ explicit TokenStream( Args const &args ) : TokenStream( args.m_args.begin(), args.m_args.end() ) {}
+
+ TokenStream( Iterator it, Iterator itEnd ) : it( it ), itEnd( itEnd ) {
+ loadBuffer();
+ }
+
+ explicit operator bool() const {
+ return !m_tokenBuffer.empty() || it != itEnd;
+ }
+
+ auto count() const -> size_t { return m_tokenBuffer.size() + (itEnd - it); }
+
+ auto operator*() const -> Token {
+ assert( !m_tokenBuffer.empty() );
+ return m_tokenBuffer.front();
+ }
+
+ auto operator->() const -> Token const * {
+ assert( !m_tokenBuffer.empty() );
+ return &m_tokenBuffer.front();
+ }
+
+ auto operator++() -> TokenStream & {
+ if( m_tokenBuffer.size() >= 2 ) {
+ m_tokenBuffer.erase( m_tokenBuffer.begin() );
+ } else {
+ if( it != itEnd )
+ ++it;
+ loadBuffer();
+ }
+ return *this;
+ }
+ };
+
+ class ResultBase {
+ public:
+ enum Type {
+ Ok, LogicError, RuntimeError
+ };
+
+ protected:
+ ResultBase( Type type ) : m_type( type ) {}
+ virtual ~ResultBase() = default;
+
+ virtual void enforceOk() const = 0;
+
+ Type m_type;
+ };
+
+ template<typename T>
+ class ResultValueBase : public ResultBase {
+ public:
+ auto value() const -> T const & {
+ enforceOk();
+ return m_value;
+ }
+
+ protected:
+ ResultValueBase( Type type ) : ResultBase( type ) {}
+
+ ResultValueBase( ResultValueBase const &other ) : ResultBase( other ) {
+ if( m_type == ResultBase::Ok )
+ new( &m_value ) T( other.m_value );
+ }
+
+ ResultValueBase( Type, T const &value ) : ResultBase( Ok ) {
+ new( &m_value ) T( value );
+ }
+
+ auto operator=( ResultValueBase const &other ) -> ResultValueBase & {
+ if( m_type == ResultBase::Ok )
+ m_value.~T();
+ ResultBase::operator=(other);
+ if( m_type == ResultBase::Ok )
+ new( &m_value ) T( other.m_value );
+ return *this;
+ }
+
+ ~ResultValueBase() override {
+ if( m_type == Ok )
+ m_value.~T();
+ }
+
+ union {
+ T m_value;
+ };
+ };
+
+ template<>
+ class ResultValueBase<void> : public ResultBase {
+ protected:
+ using ResultBase::ResultBase;
+ };
+
+ template<typename T = void>
+ class BasicResult : public ResultValueBase<T> {
+ public:
+ template<typename U>
+ explicit BasicResult( BasicResult<U> const &other )
+ : ResultValueBase<T>( other.type() ),
+ m_errorMessage( other.errorMessage() )
+ {
+ assert( type() != ResultBase::Ok );
+ }
+
+ template<typename U>
+ static auto ok( U const &value ) -> BasicResult { return { ResultBase::Ok, value }; }
+ static auto ok() -> BasicResult { return { ResultBase::Ok }; }
+ static auto logicError( std::string const &message ) -> BasicResult { return { ResultBase::LogicError, message }; }
+ static auto runtimeError( std::string const &message ) -> BasicResult { return { ResultBase::RuntimeError, message }; }
+
+ explicit operator bool() const { return m_type == ResultBase::Ok; }
+ auto type() const -> ResultBase::Type { return m_type; }
+ auto errorMessage() const -> std::string { return m_errorMessage; }
+
+ protected:
+ void enforceOk() const override {
+
+ // Errors shouldn't reach this point, but if they do
+ // the actual error message will be in m_errorMessage
+ assert( m_type != ResultBase::LogicError );
+ assert( m_type != ResultBase::RuntimeError );
+ if( m_type != ResultBase::Ok )
+ std::abort();
+ }
+
+ std::string m_errorMessage; // Only populated if resultType is an error
+
+ BasicResult( ResultBase::Type type, std::string const &message )
+ : ResultValueBase<T>(type),
+ m_errorMessage(message)
+ {
+ assert( m_type != ResultBase::Ok );
+ }
+
+ using ResultValueBase<T>::ResultValueBase;
+ using ResultBase::m_type;
+ };
+
+ enum class ParseResultType {
+ Matched, NoMatch, ShortCircuitAll, ShortCircuitSame
+ };
+
+ class ParseState {
+ public:
+
+ ParseState( ParseResultType type, TokenStream const &remainingTokens )
+ : m_type(type),
+ m_remainingTokens( remainingTokens )
+ {}
+
+ auto type() const -> ParseResultType { return m_type; }
+ auto remainingTokens() const -> TokenStream { return m_remainingTokens; }
+
+ private:
+ ParseResultType m_type;
+ TokenStream m_remainingTokens;
+ };
+
+ using Result = BasicResult<void>;
+ using ParserResult = BasicResult<ParseResultType>;
+ using InternalParseResult = BasicResult<ParseState>;
+
+ struct HelpColumns {
+ std::string left;
+ std::string right;
+ };
+
+ template<typename T>
+ inline auto convertInto( std::string const &source, T& target ) -> ParserResult {
+ std::stringstream ss;
+ ss << source;
+ ss >> target;
+ if( ss.fail() )
+ return ParserResult::runtimeError( "Unable to convert '" + source + "' to destination type" );
+ else
+ return ParserResult::ok( ParseResultType::Matched );
+ }
+ inline auto convertInto( std::string const &source, std::string& target ) -> ParserResult {
+ target = source;
+ return ParserResult::ok( ParseResultType::Matched );
+ }
+ inline auto convertInto( std::string const &source, bool &target ) -> ParserResult {
+ std::string srcLC = source;
+ std::transform( srcLC.begin(), srcLC.end(), srcLC.begin(), []( unsigned char c ) { return static_cast<char>( std::tolower(c) ); } );
+ if (srcLC == "y" || srcLC == "1" || srcLC == "true" || srcLC == "yes" || srcLC == "on")
+ target = true;
+ else if (srcLC == "n" || srcLC == "0" || srcLC == "false" || srcLC == "no" || srcLC == "off")
+ target = false;
+ else
+ return ParserResult::runtimeError( "Expected a boolean value but did not recognise: '" + source + "'" );
+ return ParserResult::ok( ParseResultType::Matched );
+ }
+#ifdef CLARA_CONFIG_OPTIONAL_TYPE
+ template<typename T>
+ inline auto convertInto( std::string const &source, CLARA_CONFIG_OPTIONAL_TYPE<T>& target ) -> ParserResult {
+ T temp;
+ auto result = convertInto( source, temp );
+ if( result )
+ target = std::move(temp);
+ return result;
+ }
+#endif // CLARA_CONFIG_OPTIONAL_TYPE
+
+ struct NonCopyable {
+ NonCopyable() = default;
+ NonCopyable( NonCopyable const & ) = delete;
+ NonCopyable( NonCopyable && ) = delete;
+ NonCopyable &operator=( NonCopyable const & ) = delete;
+ NonCopyable &operator=( NonCopyable && ) = delete;
+ };
+
+ struct BoundRef : NonCopyable {
+ virtual ~BoundRef() = default;
+ virtual auto isContainer() const -> bool { return false; }
+ virtual auto isFlag() const -> bool { return false; }
+ };
+ struct BoundValueRefBase : BoundRef {
+ virtual auto setValue( std::string const &arg ) -> ParserResult = 0;
+ };
+ struct BoundFlagRefBase : BoundRef {
+ virtual auto setFlag( bool flag ) -> ParserResult = 0;
+ virtual auto isFlag() const -> bool { return true; }
+ };
+
+ template<typename T>
+ struct BoundValueRef : BoundValueRefBase {
+ T &m_ref;
+
+ explicit BoundValueRef( T &ref ) : m_ref( ref ) {}
+
+ auto setValue( std::string const &arg ) -> ParserResult override {
+ return convertInto( arg, m_ref );
+ }
+ };
+
+ template<typename T>
+ struct BoundValueRef<std::vector<T>> : BoundValueRefBase {
+ std::vector<T> &m_ref;
+
+ explicit BoundValueRef( std::vector<T> &ref ) : m_ref( ref ) {}
+
+ auto isContainer() const -> bool override { return true; }
+
+ auto setValue( std::string const &arg ) -> ParserResult override {
+ T temp;
+ auto result = convertInto( arg, temp );
+ if( result )
+ m_ref.push_back( temp );
+ return result;
+ }
+ };
+
+ struct BoundFlagRef : BoundFlagRefBase {
+ bool &m_ref;
+
+ explicit BoundFlagRef( bool &ref ) : m_ref( ref ) {}
+
+ auto setFlag( bool flag ) -> ParserResult override {
+ m_ref = flag;
+ return ParserResult::ok( ParseResultType::Matched );
+ }
+ };
+
+ template<typename ReturnType>
+ struct LambdaInvoker {
+ static_assert( std::is_same<ReturnType, ParserResult>::value, "Lambda must return void or clara::ParserResult" );
+
+ template<typename L, typename ArgType>
+ static auto invoke( L const &lambda, ArgType const &arg ) -> ParserResult {
+ return lambda( arg );
+ }
+ };
+
+ template<>
+ struct LambdaInvoker<void> {
+ template<typename L, typename ArgType>
+ static auto invoke( L const &lambda, ArgType const &arg ) -> ParserResult {
+ lambda( arg );
+ return ParserResult::ok( ParseResultType::Matched );
+ }
+ };
+
+ template<typename ArgType, typename L>
+ inline auto invokeLambda( L const &lambda, std::string const &arg ) -> ParserResult {
+ ArgType temp{};
+ auto result = convertInto( arg, temp );
+ return !result
+ ? result
+ : LambdaInvoker<typename UnaryLambdaTraits<L>::ReturnType>::invoke( lambda, temp );
+ }
+
+ template<typename L>
+ struct BoundLambda : BoundValueRefBase {
+ L m_lambda;
+
+ static_assert( UnaryLambdaTraits<L>::isValid, "Supplied lambda must take exactly one argument" );
+ explicit BoundLambda( L const &lambda ) : m_lambda( lambda ) {}
+
+ auto setValue( std::string const &arg ) -> ParserResult override {
+ return invokeLambda<typename UnaryLambdaTraits<L>::ArgType>( m_lambda, arg );
+ }
+ };
+
+ template<typename L>
+ struct BoundFlagLambda : BoundFlagRefBase {
+ L m_lambda;
+
+ static_assert( UnaryLambdaTraits<L>::isValid, "Supplied lambda must take exactly one argument" );
+ static_assert( std::is_same<typename UnaryLambdaTraits<L>::ArgType, bool>::value, "flags must be boolean" );
+
+ explicit BoundFlagLambda( L const &lambda ) : m_lambda( lambda ) {}
+
+ auto setFlag( bool flag ) -> ParserResult override {
+ return LambdaInvoker<typename UnaryLambdaTraits<L>::ReturnType>::invoke( m_lambda, flag );
+ }
+ };
+
+ enum class Optionality { Optional, Required };
+
+ struct Parser;
+
+ class ParserBase {
+ public:
+ virtual ~ParserBase() = default;
+ virtual auto validate() const -> Result { return Result::ok(); }
+ virtual auto parse( std::string const& exeName, TokenStream const &tokens) const -> InternalParseResult = 0;
+ virtual auto cardinality() const -> size_t { return 1; }
+
+ auto parse( Args const &args ) const -> InternalParseResult {
+ return parse( args.exeName(), TokenStream( args ) );
+ }
+ };
+
+ template<typename DerivedT>
+ class ComposableParserImpl : public ParserBase {
+ public:
+ template<typename T>
+ auto operator|( T const &other ) const -> Parser;
+
+ template<typename T>
+ auto operator+( T const &other ) const -> Parser;
+ };
+
+ // Common code and state for Args and Opts
+ template<typename DerivedT>
+ class ParserRefImpl : public ComposableParserImpl<DerivedT> {
+ protected:
+ Optionality m_optionality = Optionality::Optional;
+ std::shared_ptr<BoundRef> m_ref;
+ std::string m_hint;
+ std::string m_description;
+
+ explicit ParserRefImpl( std::shared_ptr<BoundRef> const &ref ) : m_ref( ref ) {}
+
+ public:
+ template<typename T>
+ ParserRefImpl( T &ref, std::string const &hint )
+ : m_ref( std::make_shared<BoundValueRef<T>>( ref ) ),
+ m_hint( hint )
+ {}
+
+ template<typename LambdaT>
+ ParserRefImpl( LambdaT const &ref, std::string const &hint )
+ : m_ref( std::make_shared<BoundLambda<LambdaT>>( ref ) ),
+ m_hint(hint)
+ {}
+
+ auto operator()( std::string const &description ) -> DerivedT & {
+ m_description = description;
+ return static_cast<DerivedT &>( *this );
+ }
+
+ auto optional() -> DerivedT & {
+ m_optionality = Optionality::Optional;
+ return static_cast<DerivedT &>( *this );
+ };
+
+ auto required() -> DerivedT & {
+ m_optionality = Optionality::Required;
+ return static_cast<DerivedT &>( *this );
+ };
+
+ auto isOptional() const -> bool {
+ return m_optionality == Optionality::Optional;
+ }
+
+ auto cardinality() const -> size_t override {
+ if( m_ref->isContainer() )
+ return 0;
+ else
+ return 1;
+ }
+
+ auto hint() const -> std::string { return m_hint; }
+ };
+
+ class ExeName : public ComposableParserImpl<ExeName> {
+ std::shared_ptr<std::string> m_name;
+ std::shared_ptr<BoundValueRefBase> m_ref;
+
+ template<typename LambdaT>
+ static auto makeRef(LambdaT const &lambda) -> std::shared_ptr<BoundValueRefBase> {
+ return std::make_shared<BoundLambda<LambdaT>>( lambda) ;
+ }
+
+ public:
+ ExeName() : m_name( std::make_shared<std::string>( "<executable>" ) ) {}
+
+ explicit ExeName( std::string &ref ) : ExeName() {
+ m_ref = std::make_shared<BoundValueRef<std::string>>( ref );
+ }
+
+ template<typename LambdaT>
+ explicit ExeName( LambdaT const& lambda ) : ExeName() {
+ m_ref = std::make_shared<BoundLambda<LambdaT>>( lambda );
+ }
+
+ // The exe name is not parsed out of the normal tokens, but is handled specially
+ auto parse( std::string const&, TokenStream const &tokens ) const -> InternalParseResult override {
+ return InternalParseResult::ok( ParseState( ParseResultType::NoMatch, tokens ) );
+ }
+
+ auto name() const -> std::string { return *m_name; }
+ auto set( std::string const& newName ) -> ParserResult {
+
+ auto lastSlash = newName.find_last_of( "\\/" );
+ auto filename = ( lastSlash == std::string::npos )
+ ? newName
+ : newName.substr( lastSlash+1 );
+
+ *m_name = filename;
+ if( m_ref )
+ return m_ref->setValue( filename );
+ else
+ return ParserResult::ok( ParseResultType::Matched );
+ }
+ };
+
+ class Arg : public ParserRefImpl<Arg> {
+ public:
+ using ParserRefImpl::ParserRefImpl;
+
+ auto parse( std::string const &, TokenStream const &tokens ) const -> InternalParseResult override {
+ auto validationResult = validate();
+ if( !validationResult )
+ return InternalParseResult( validationResult );
+
+ auto remainingTokens = tokens;
+ auto const &token = *remainingTokens;
+ if( token.type != TokenType::Argument )
+ return InternalParseResult::ok( ParseState( ParseResultType::NoMatch, remainingTokens ) );
+
+ assert( !m_ref->isFlag() );
+ auto valueRef = static_cast<detail::BoundValueRefBase*>( m_ref.get() );
+
+ auto result = valueRef->setValue( remainingTokens->token );
+ if( !result )
+ return InternalParseResult( result );
+ else
+ return InternalParseResult::ok( ParseState( ParseResultType::Matched, ++remainingTokens ) );
+ }
+ };
+
+ inline auto normaliseOpt( std::string const &optName ) -> std::string {
+#ifdef CATCH_PLATFORM_WINDOWS
+ if( optName[0] == '/' )
+ return "-" + optName.substr( 1 );
+ else
+#endif
+ return optName;
+ }
+
+ class Opt : public ParserRefImpl<Opt> {
+ protected:
+ std::vector<std::string> m_optNames;
+
+ public:
+ template<typename LambdaT>
+ explicit Opt( LambdaT const &ref ) : ParserRefImpl( std::make_shared<BoundFlagLambda<LambdaT>>( ref ) ) {}
+
+ explicit Opt( bool &ref ) : ParserRefImpl( std::make_shared<BoundFlagRef>( ref ) ) {}
+
+ template<typename LambdaT>
+ Opt( LambdaT const &ref, std::string const &hint ) : ParserRefImpl( ref, hint ) {}
+
+ template<typename T>
+ Opt( T &ref, std::string const &hint ) : ParserRefImpl( ref, hint ) {}
+
+ auto operator[]( std::string const &optName ) -> Opt & {
+ m_optNames.push_back( optName );
+ return *this;
+ }
+
+ auto getHelpColumns() const -> std::vector<HelpColumns> {
+ std::ostringstream oss;
+ bool first = true;
+ for( auto const &opt : m_optNames ) {
+ if (first)
+ first = false;
+ else
+ oss << ", ";
+ oss << opt;
+ }
+ if( !m_hint.empty() )
+ oss << " <" << m_hint << ">";
+ return { { oss.str(), m_description } };
+ }
+
+ auto isMatch( std::string const &optToken ) const -> bool {
+ auto normalisedToken = normaliseOpt( optToken );
+ for( auto const &name : m_optNames ) {
+ if( normaliseOpt( name ) == normalisedToken )
+ return true;
+ }
+ return false;
+ }
+
+ using ParserBase::parse;
+
+ auto parse( std::string const&, TokenStream const &tokens ) const -> InternalParseResult override {
+ auto validationResult = validate();
+ if( !validationResult )
+ return InternalParseResult( validationResult );
+
+ auto remainingTokens = tokens;
+ if( remainingTokens && remainingTokens->type == TokenType::Option ) {
+ auto const &token = *remainingTokens;
+ if( isMatch(token.token ) ) {
+ if( m_ref->isFlag() ) {
+ auto flagRef = static_cast<detail::BoundFlagRefBase*>( m_ref.get() );
+ auto result = flagRef->setFlag( true );
+ if( !result )
+ return InternalParseResult( result );
+ if( result.value() == ParseResultType::ShortCircuitAll )
+ return InternalParseResult::ok( ParseState( result.value(), remainingTokens ) );
+ } else {
+ auto valueRef = static_cast<detail::BoundValueRefBase*>( m_ref.get() );
+ ++remainingTokens;
+ if( !remainingTokens )
+ return InternalParseResult::runtimeError( "Expected argument following " + token.token );
+ auto const &argToken = *remainingTokens;
+ if( argToken.type != TokenType::Argument )
+ return InternalParseResult::runtimeError( "Expected argument following " + token.token );
+ auto result = valueRef->setValue( argToken.token );
+ if( !result )
+ return InternalParseResult( result );
+ if( result.value() == ParseResultType::ShortCircuitAll )
+ return InternalParseResult::ok( ParseState( result.value(), remainingTokens ) );
+ }
+ return InternalParseResult::ok( ParseState( ParseResultType::Matched, ++remainingTokens ) );
+ }
+ }
+ return InternalParseResult::ok( ParseState( ParseResultType::NoMatch, remainingTokens ) );
+ }
+
+ auto validate() const -> Result override {
+ if( m_optNames.empty() )
+ return Result::logicError( "No options supplied to Opt" );
+ for( auto const &name : m_optNames ) {
+ if( name.empty() )
+ return Result::logicError( "Option name cannot be empty" );
+#ifdef CATCH_PLATFORM_WINDOWS
+ if( name[0] != '-' && name[0] != '/' )
+ return Result::logicError( "Option name must begin with '-' or '/'" );
+#else
+ if( name[0] != '-' )
+ return Result::logicError( "Option name must begin with '-'" );
+#endif
+ }
+ return ParserRefImpl::validate();
+ }
+ };
+
+ struct Help : Opt {
+ Help( bool &showHelpFlag )
+ : Opt([&]( bool flag ) {
+ showHelpFlag = flag;
+ return ParserResult::ok( ParseResultType::ShortCircuitAll );
+ })
+ {
+ static_cast<Opt &>( *this )
+ ("display usage information")
+ ["-?"]["-h"]["--help"]
+ .optional();
+ }
+ };
+
+ struct Parser : ParserBase {
+
+ mutable ExeName m_exeName;
+ std::vector<Opt> m_options;
+ std::vector<Arg> m_args;
+
+ auto operator|=( ExeName const &exeName ) -> Parser & {
+ m_exeName = exeName;
+ return *this;
+ }
+
+ auto operator|=( Arg const &arg ) -> Parser & {
+ m_args.push_back(arg);
+ return *this;
+ }
+
+ auto operator|=( Opt const &opt ) -> Parser & {
+ m_options.push_back(opt);
+ return *this;
+ }
+
+ auto operator|=( Parser const &other ) -> Parser & {
+ m_options.insert(m_options.end(), other.m_options.begin(), other.m_options.end());
+ m_args.insert(m_args.end(), other.m_args.begin(), other.m_args.end());
+ return *this;
+ }
+
+ template<typename T>
+ auto operator|( T const &other ) const -> Parser {
+ return Parser( *this ) |= other;
+ }
+
+ // Forward deprecated interface with '+' instead of '|'
+ template<typename T>
+ auto operator+=( T const &other ) -> Parser & { return operator|=( other ); }
+ template<typename T>
+ auto operator+( T const &other ) const -> Parser { return operator|( other ); }
+
+ auto getHelpColumns() const -> std::vector<HelpColumns> {
+ std::vector<HelpColumns> cols;
+ for (auto const &o : m_options) {
+ auto childCols = o.getHelpColumns();
+ cols.insert( cols.end(), childCols.begin(), childCols.end() );
+ }
+ return cols;
+ }
+
+ void writeToStream( std::ostream &os ) const {
+ if (!m_exeName.name().empty()) {
+ os << "usage:\n" << " " << m_exeName.name() << " ";
+ bool required = true, first = true;
+ for( auto const &arg : m_args ) {
+ if (first)
+ first = false;
+ else
+ os << " ";
+ if( arg.isOptional() && required ) {
+ os << "[";
+ required = false;
+ }
+ os << "<" << arg.hint() << ">";
+ if( arg.cardinality() == 0 )
+ os << " ... ";
+ }
+ if( !required )
+ os << "]";
+ if( !m_options.empty() )
+ os << " options";
+ os << "\n\nwhere options are:" << std::endl;
+ }
+
+ auto rows = getHelpColumns();
+ size_t consoleWidth = CATCH_CLARA_CONFIG_CONSOLE_WIDTH;
+ size_t optWidth = 0;
+ for( auto const &cols : rows )
+ optWidth = (std::max)(optWidth, cols.left.size() + 2);
+
+ optWidth = (std::min)(optWidth, consoleWidth/2);
+
+ for( auto const &cols : rows ) {
+ auto row =
+ TextFlow::Column( cols.left ).width( optWidth ).indent( 2 ) +
+ TextFlow::Spacer(4) +
+ TextFlow::Column( cols.right ).width( consoleWidth - 7 - optWidth );
+ os << row << std::endl;
+ }
+ }
+
+ friend auto operator<<( std::ostream &os, Parser const &parser ) -> std::ostream& {
+ parser.writeToStream( os );
+ return os;
+ }
+
+ auto validate() const -> Result override {
+ for( auto const &opt : m_options ) {
+ auto result = opt.validate();
+ if( !result )
+ return result;
+ }
+ for( auto const &arg : m_args ) {
+ auto result = arg.validate();
+ if( !result )
+ return result;
+ }
+ return Result::ok();
+ }
+
+ using ParserBase::parse;
+
+ auto parse( std::string const& exeName, TokenStream const &tokens ) const -> InternalParseResult override {
+
+ struct ParserInfo {
+ ParserBase const* parser = nullptr;
+ size_t count = 0;
+ };
+ const size_t totalParsers = m_options.size() + m_args.size();
+ assert( totalParsers < 512 );
+ // ParserInfo parseInfos[totalParsers]; // <-- this is what we really want to do
+ ParserInfo parseInfos[512];
+
+ {
+ size_t i = 0;
+ for (auto const &opt : m_options) parseInfos[i++].parser = &opt;
+ for (auto const &arg : m_args) parseInfos[i++].parser = &arg;
+ }
+
+ m_exeName.set( exeName );
+
+ auto result = InternalParseResult::ok( ParseState( ParseResultType::NoMatch, tokens ) );
+ while( result.value().remainingTokens() ) {
+ bool tokenParsed = false;
+
+ for( size_t i = 0; i < totalParsers; ++i ) {
+ auto& parseInfo = parseInfos[i];
+ if( parseInfo.parser->cardinality() == 0 || parseInfo.count < parseInfo.parser->cardinality() ) {
+ result = parseInfo.parser->parse(exeName, result.value().remainingTokens());
+ if (!result)
+ return result;
+ if (result.value().type() != ParseResultType::NoMatch) {
+ tokenParsed = true;
+ ++parseInfo.count;
+ break;
+ }
+ }
+ }
+
+ if( result.value().type() == ParseResultType::ShortCircuitAll )
+ return result;
+ if( !tokenParsed )
+ return InternalParseResult::runtimeError( "Unrecognised token: " + result.value().remainingTokens()->token );
+ }
+ // !TBD Check missing required options
+ return result;
+ }
+ };
+
+ template<typename DerivedT>
+ template<typename T>
+ auto ComposableParserImpl<DerivedT>::operator|( T const &other ) const -> Parser {
+ return Parser() | static_cast<DerivedT const &>( *this ) | other;
+ }
+} // namespace detail
+
+// A Combined parser
+using detail::Parser;
+
+// A parser for options
+using detail::Opt;
+
+// A parser for arguments
+using detail::Arg;
+
+// Wrapper for argc, argv from main()
+using detail::Args;
+
+// Specifies the name of the executable
+using detail::ExeName;
+
+// Convenience wrapper for option parser that specifies the help option
+using detail::Help;
+
+// enum of result types from a parse
+using detail::ParseResultType;
+
+// Result type for parser operation
+using detail::ParserResult;
+
+}} // namespace Catch::clara
+
+// end clara.hpp
+#ifdef __clang__
+#pragma clang diagnostic pop
+#endif
+
+// Restore Clara's value for console width, if present
+#ifdef CATCH_TEMP_CLARA_CONFIG_CONSOLE_WIDTH
+#define CATCH_CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH CATCH_TEMP_CLARA_CONFIG_CONSOLE_WIDTH
+#undef CATCH_TEMP_CLARA_CONFIG_CONSOLE_WIDTH
+#endif
+
+// end catch_clara.h
+namespace Catch {
+
+ clara::Parser makeCommandLineParser( ConfigData& config );
+
+} // end namespace Catch
+
+// end catch_commandline.h
+#include <fstream>
+#include <ctime>
+
+namespace Catch {
+
+ clara::Parser makeCommandLineParser( ConfigData& config ) {
+
+ using namespace clara;
+
+ auto const setWarning = [&]( std::string const& warning ) {
+ auto warningSet = [&]() {
+ if( warning == "NoAssertions" )
+ return WarnAbout::NoAssertions;
+
+ if ( warning == "NoTests" )
+ return WarnAbout::NoTests;
+
+ return WarnAbout::Nothing;
+ }();
+
+ if (warningSet == WarnAbout::Nothing)
+ return ParserResult::runtimeError( "Unrecognised warning: '" + warning + "'" );
+ config.warnings = static_cast<WarnAbout::What>( config.warnings | warningSet );
+ return ParserResult::ok( ParseResultType::Matched );
+ };
+ auto const loadTestNamesFromFile = [&]( std::string const& filename ) {
+ std::ifstream f( filename.c_str() );
+ if( !f.is_open() )
+ return ParserResult::runtimeError( "Unable to load input file: '" + filename + "'" );
+
+ std::string line;
+ while( std::getline( f, line ) ) {
+ line = trim(line);
+ if( !line.empty() && !startsWith( line, '#' ) ) {
+ if( !startsWith( line, '"' ) )
+ line = '"' + line + '"';
+ config.testsOrTags.push_back( line );
+ config.testsOrTags.emplace_back( "," );
+ }
+ }
+ //Remove comma in the end
+ if(!config.testsOrTags.empty())
+ config.testsOrTags.erase( config.testsOrTags.end()-1 );
+
+ return ParserResult::ok( ParseResultType::Matched );
+ };
+ auto const setTestOrder = [&]( std::string const& order ) {
+ if( startsWith( "declared", order ) )
+ config.runOrder = RunTests::InDeclarationOrder;
+ else if( startsWith( "lexical", order ) )
+ config.runOrder = RunTests::InLexicographicalOrder;
+ else if( startsWith( "random", order ) )
+ config.runOrder = RunTests::InRandomOrder;
+ else
+ return clara::ParserResult::runtimeError( "Unrecognised ordering: '" + order + "'" );
+ return ParserResult::ok( ParseResultType::Matched );
+ };
+ auto const setRngSeed = [&]( std::string const& seed ) {
+ if( seed != "time" )
+ return clara::detail::convertInto( seed, config.rngSeed );
+ config.rngSeed = static_cast<unsigned int>( std::time(nullptr) );
+ return ParserResult::ok( ParseResultType::Matched );
+ };
+ auto const setColourUsage = [&]( std::string const& useColour ) {
+ auto mode = toLower( useColour );
+
+ if( mode == "yes" )
+ config.useColour = UseColour::Yes;
+ else if( mode == "no" )
+ config.useColour = UseColour::No;
+ else if( mode == "auto" )
+ config.useColour = UseColour::Auto;
+ else
+ return ParserResult::runtimeError( "colour mode must be one of: auto, yes or no. '" + useColour + "' not recognised" );
+ return ParserResult::ok( ParseResultType::Matched );
+ };
+ auto const setWaitForKeypress = [&]( std::string const& keypress ) {
+ auto keypressLc = toLower( keypress );
+ if (keypressLc == "never")
+ config.waitForKeypress = WaitForKeypress::Never;
+ else if( keypressLc == "start" )
+ config.waitForKeypress = WaitForKeypress::BeforeStart;
+ else if( keypressLc == "exit" )
+ config.waitForKeypress = WaitForKeypress::BeforeExit;
+ else if( keypressLc == "both" )
+ config.waitForKeypress = WaitForKeypress::BeforeStartAndExit;
+ else
+ return ParserResult::runtimeError( "keypress argument must be one of: never, start, exit or both. '" + keypress + "' not recognised" );
+ return ParserResult::ok( ParseResultType::Matched );
+ };
+ auto const setVerbosity = [&]( std::string const& verbosity ) {
+ auto lcVerbosity = toLower( verbosity );
+ if( lcVerbosity == "quiet" )
+ config.verbosity = Verbosity::Quiet;
+ else if( lcVerbosity == "normal" )
+ config.verbosity = Verbosity::Normal;
+ else if( lcVerbosity == "high" )
+ config.verbosity = Verbosity::High;
+ else
+ return ParserResult::runtimeError( "Unrecognised verbosity, '" + verbosity + "'" );
+ return ParserResult::ok( ParseResultType::Matched );
+ };
+ auto const setReporter = [&]( std::string const& reporter ) {
+ IReporterRegistry::FactoryMap const& factories = getRegistryHub().getReporterRegistry().getFactories();
+
+ auto lcReporter = toLower( reporter );
+ auto result = factories.find( lcReporter );
+
+ if( factories.end() != result )
+ config.reporterName = lcReporter;
+ else
+ return ParserResult::runtimeError( "Unrecognized reporter, '" + reporter + "'. Check available with --list-reporters" );
+ return ParserResult::ok( ParseResultType::Matched );
+ };
+
+ auto cli
+ = ExeName( config.processName )
+ | Help( config.showHelp )
+ | Opt( config.listTests )
+ ["-l"]["--list-tests"]
+ ( "list all/matching test cases" )
+ | Opt( config.listTags )
+ ["-t"]["--list-tags"]
+ ( "list all/matching tags" )
+ | Opt( config.showSuccessfulTests )
+ ["-s"]["--success"]
+ ( "include successful tests in output" )
+ | Opt( config.shouldDebugBreak )
+ ["-b"]["--break"]
+ ( "break into debugger on failure" )
+ | Opt( config.noThrow )
+ ["-e"]["--nothrow"]
+ ( "skip exception tests" )
+ | Opt( config.showInvisibles )
+ ["-i"]["--invisibles"]
+ ( "show invisibles (tabs, newlines)" )
+ | Opt( config.outputFilename, "filename" )
+ ["-o"]["--out"]
+ ( "output filename" )
+ | Opt( setReporter, "name" )
+ ["-r"]["--reporter"]
+ ( "reporter to use (defaults to console)" )
+ | Opt( config.name, "name" )
+ ["-n"]["--name"]
+ ( "suite name" )
+ | Opt( [&]( bool ){ config.abortAfter = 1; } )
+ ["-a"]["--abort"]
+ ( "abort at first failure" )
+ | Opt( [&]( int x ){ config.abortAfter = x; }, "no. failures" )
+ ["-x"]["--abortx"]
+ ( "abort after x failures" )
+ | Opt( setWarning, "warning name" )
+ ["-w"]["--warn"]
+ ( "enable warnings" )
+ | Opt( [&]( bool flag ) { config.showDurations = flag ? ShowDurations::Always : ShowDurations::Never; }, "yes|no" )
+ ["-d"]["--durations"]
+ ( "show test durations" )
+ | Opt( config.minDuration, "seconds" )
+ ["-D"]["--min-duration"]
+ ( "show test durations for tests taking at least the given number of seconds" )
+ | Opt( loadTestNamesFromFile, "filename" )
+ ["-f"]["--input-file"]
+ ( "load test names to run from a file" )
+ | Opt( config.filenamesAsTags )
+ ["-#"]["--filenames-as-tags"]
+ ( "adds a tag for the filename" )
+ | Opt( config.sectionsToRun, "section name" )
+ ["-c"]["--section"]
+ ( "specify section to run" )
+ | Opt( setVerbosity, "quiet|normal|high" )
+ ["-v"]["--verbosity"]
+ ( "set output verbosity" )
+ | Opt( config.listTestNamesOnly )
+ ["--list-test-names-only"]
+ ( "list all/matching test cases names only" )
+ | Opt( config.listReporters )
+ ["--list-reporters"]
+ ( "list all reporters" )
+ | Opt( setTestOrder, "decl|lex|rand" )
+ ["--order"]
+ ( "test case order (defaults to decl)" )
+ | Opt( setRngSeed, "'time'|number" )
+ ["--rng-seed"]
+ ( "set a specific seed for random numbers" )
+ | Opt( setColourUsage, "yes|no" )
+ ["--use-colour"]
+ ( "should output be colourised" )
+ | Opt( config.libIdentify )
+ ["--libidentify"]
+ ( "report name and version according to libidentify standard" )
+ | Opt( setWaitForKeypress, "never|start|exit|both" )
+ ["--wait-for-keypress"]
+ ( "waits for a keypress before exiting" )
+ | Opt( config.benchmarkSamples, "samples" )
+ ["--benchmark-samples"]
+ ( "number of samples to collect (default: 100)" )
+ | Opt( config.benchmarkResamples, "resamples" )
+ ["--benchmark-resamples"]
+ ( "number of resamples for the bootstrap (default: 100000)" )
+ | Opt( config.benchmarkConfidenceInterval, "confidence interval" )
+ ["--benchmark-confidence-interval"]
+ ( "confidence interval for the bootstrap (between 0 and 1, default: 0.95)" )
+ | Opt( config.benchmarkNoAnalysis )
+ ["--benchmark-no-analysis"]
+ ( "perform only measurements; do not perform any analysis" )
+ | Opt( config.benchmarkWarmupTime, "benchmarkWarmupTime" )
+ ["--benchmark-warmup-time"]
+ ( "amount of time in milliseconds spent on warming up each test (default: 100)" )
+ | Arg( config.testsOrTags, "test name|pattern|tags" )
+ ( "which test or tests to use" );
+
+ return cli;
+ }
+
+} // end namespace Catch
+// end catch_commandline.cpp
+// start catch_common.cpp
+
+#include <cstring>
+#include <ostream>
+
+namespace Catch {
+
+ bool SourceLineInfo::operator == ( SourceLineInfo const& other ) const noexcept {
+ return line == other.line && (file == other.file || std::strcmp(file, other.file) == 0);
+ }
+ bool SourceLineInfo::operator < ( SourceLineInfo const& other ) const noexcept {
+ // We can assume that the same file will usually have the same pointer.
+ // Thus, if the pointers are the same, there is no point in calling the strcmp
+ return line < other.line || ( line == other.line && file != other.file && (std::strcmp(file, other.file) < 0));
+ }
+
+ std::ostream& operator << ( std::ostream& os, SourceLineInfo const& info ) {
+#ifndef __GNUG__
+ os << info.file << '(' << info.line << ')';
+#else
+ os << info.file << ':' << info.line;
+#endif
+ return os;
+ }
+
+ std::string StreamEndStop::operator+() const {
+ return std::string();
+ }
+
+ NonCopyable::NonCopyable() = default;
+ NonCopyable::~NonCopyable() = default;
+
+}
+// end catch_common.cpp
+// start catch_config.cpp
+
+namespace Catch {
+
+ Config::Config( ConfigData const& data )
+ : m_data( data ),
+ m_stream( openStream() )
+ {
+ // We need to trim filter specs to avoid trouble with superfluous
+ // whitespace (esp. important for bdd macros, as those are manually
+ // aligned with whitespace).
+
+ for (auto& elem : m_data.testsOrTags) {
+ elem = trim(elem);
+ }
+ for (auto& elem : m_data.sectionsToRun) {
+ elem = trim(elem);
+ }
+
+ TestSpecParser parser(ITagAliasRegistry::get());
+ if (!m_data.testsOrTags.empty()) {
+ m_hasTestFilters = true;
+ for (auto const& testOrTags : m_data.testsOrTags) {
+ parser.parse(testOrTags);
+ }
+ }
+ m_testSpec = parser.testSpec();
+ }
+
+ std::string const& Config::getFilename() const {
+ return m_data.outputFilename ;
+ }
+
+ bool Config::listTests() const { return m_data.listTests; }
+ bool Config::listTestNamesOnly() const { return m_data.listTestNamesOnly; }
+ bool Config::listTags() const { return m_data.listTags; }
+ bool Config::listReporters() const { return m_data.listReporters; }
+
+ std::string Config::getProcessName() const { return m_data.processName; }
+ std::string const& Config::getReporterName() const { return m_data.reporterName; }
+
+ std::vector<std::string> const& Config::getTestsOrTags() const { return m_data.testsOrTags; }
+ std::vector<std::string> const& Config::getSectionsToRun() const { return m_data.sectionsToRun; }
+
+ TestSpec const& Config::testSpec() const { return m_testSpec; }
+ bool Config::hasTestFilters() const { return m_hasTestFilters; }
+
+ bool Config::showHelp() const { return m_data.showHelp; }
+
+ // IConfig interface
+ bool Config::allowThrows() const { return !m_data.noThrow; }
+ std::ostream& Config::stream() const { return m_stream->stream(); }
+ std::string Config::name() const { return m_data.name.empty() ? m_data.processName : m_data.name; }
+ bool Config::includeSuccessfulResults() const { return m_data.showSuccessfulTests; }
+ bool Config::warnAboutMissingAssertions() const { return !!(m_data.warnings & WarnAbout::NoAssertions); }
+ bool Config::warnAboutNoTests() const { return !!(m_data.warnings & WarnAbout::NoTests); }
+ ShowDurations::OrNot Config::showDurations() const { return m_data.showDurations; }
+ double Config::minDuration() const { return m_data.minDuration; }
+ RunTests::InWhatOrder Config::runOrder() const { return m_data.runOrder; }
+ unsigned int Config::rngSeed() const { return m_data.rngSeed; }
+ UseColour::YesOrNo Config::useColour() const { return m_data.useColour; }
+ bool Config::shouldDebugBreak() const { return m_data.shouldDebugBreak; }
+ int Config::abortAfter() const { return m_data.abortAfter; }
+ bool Config::showInvisibles() const { return m_data.showInvisibles; }
+ Verbosity Config::verbosity() const { return m_data.verbosity; }
+
+ bool Config::benchmarkNoAnalysis() const { return m_data.benchmarkNoAnalysis; }
+ int Config::benchmarkSamples() const { return m_data.benchmarkSamples; }
+ double Config::benchmarkConfidenceInterval() const { return m_data.benchmarkConfidenceInterval; }
+ unsigned int Config::benchmarkResamples() const { return m_data.benchmarkResamples; }
+ std::chrono::milliseconds Config::benchmarkWarmupTime() const { return std::chrono::milliseconds(m_data.benchmarkWarmupTime); }
+
+ IStream const* Config::openStream() {
+ return Catch::makeStream(m_data.outputFilename);
+ }
+
+} // end namespace Catch
+// end catch_config.cpp
+// start catch_console_colour.cpp
+
+#if defined(__clang__)
+# pragma clang diagnostic push
+# pragma clang diagnostic ignored "-Wexit-time-destructors"
+#endif
+
+// start catch_errno_guard.h
+
+namespace Catch {
+
+ class ErrnoGuard {
+ public:
+ ErrnoGuard();
+ ~ErrnoGuard();
+ private:
+ int m_oldErrno;
+ };
+
+}
+
+// end catch_errno_guard.h
+// start catch_windows_h_proxy.h
+
+
+#if defined(CATCH_PLATFORM_WINDOWS)
+
+#if !defined(NOMINMAX) && !defined(CATCH_CONFIG_NO_NOMINMAX)
+# define CATCH_DEFINED_NOMINMAX
+# define NOMINMAX
+#endif
+#if !defined(WIN32_LEAN_AND_MEAN) && !defined(CATCH_CONFIG_NO_WIN32_LEAN_AND_MEAN)
+# define CATCH_DEFINED_WIN32_LEAN_AND_MEAN
+# define WIN32_LEAN_AND_MEAN
+#endif
+
+#ifdef __AFXDLL
+#include <AfxWin.h>
+#else
+#include <windows.h>
+#endif
+
+#ifdef CATCH_DEFINED_NOMINMAX
+# undef NOMINMAX
+#endif
+#ifdef CATCH_DEFINED_WIN32_LEAN_AND_MEAN
+# undef WIN32_LEAN_AND_MEAN
+#endif
+
+#endif // defined(CATCH_PLATFORM_WINDOWS)
+
+// end catch_windows_h_proxy.h
+#include <sstream>
+
+namespace Catch {
+ namespace {
+
+ struct IColourImpl {
+ virtual ~IColourImpl() = default;
+ virtual void use( Colour::Code _colourCode ) = 0;
+ };
+
+ struct NoColourImpl : IColourImpl {
+ void use( Colour::Code ) override {}
+
+ static IColourImpl* instance() {
+ static NoColourImpl s_instance;
+ return &s_instance;
+ }
+ };
+
+ } // anon namespace
+} // namespace Catch
+
+#if !defined( CATCH_CONFIG_COLOUR_NONE ) && !defined( CATCH_CONFIG_COLOUR_WINDOWS ) && !defined( CATCH_CONFIG_COLOUR_ANSI )
+# ifdef CATCH_PLATFORM_WINDOWS
+# define CATCH_CONFIG_COLOUR_WINDOWS
+# else
+# define CATCH_CONFIG_COLOUR_ANSI
+# endif
+#endif
+
+#if defined ( CATCH_CONFIG_COLOUR_WINDOWS ) /////////////////////////////////////////
+
+namespace Catch {
+namespace {
+
+ class Win32ColourImpl : public IColourImpl {
+ public:
+ Win32ColourImpl() : stdoutHandle( GetStdHandle(STD_OUTPUT_HANDLE) )
+ {
+ CONSOLE_SCREEN_BUFFER_INFO csbiInfo;
+ GetConsoleScreenBufferInfo( stdoutHandle, &csbiInfo );
+ originalForegroundAttributes = csbiInfo.wAttributes & ~( BACKGROUND_GREEN | BACKGROUND_RED | BACKGROUND_BLUE | BACKGROUND_INTENSITY );
+ originalBackgroundAttributes = csbiInfo.wAttributes & ~( FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE | FOREGROUND_INTENSITY );
+ }
+
+ void use( Colour::Code _colourCode ) override {
+ switch( _colourCode ) {
+ case Colour::None: return setTextAttribute( originalForegroundAttributes );
+ case Colour::White: return setTextAttribute( FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE );
+ case Colour::Red: return setTextAttribute( FOREGROUND_RED );
+ case Colour::Green: return setTextAttribute( FOREGROUND_GREEN );
+ case Colour::Blue: return setTextAttribute( FOREGROUND_BLUE );
+ case Colour::Cyan: return setTextAttribute( FOREGROUND_BLUE | FOREGROUND_GREEN );
+ case Colour::Yellow: return setTextAttribute( FOREGROUND_RED | FOREGROUND_GREEN );
+ case Colour::Grey: return setTextAttribute( 0 );
+
+ case Colour::LightGrey: return setTextAttribute( FOREGROUND_INTENSITY );
+ case Colour::BrightRed: return setTextAttribute( FOREGROUND_INTENSITY | FOREGROUND_RED );
+ case Colour::BrightGreen: return setTextAttribute( FOREGROUND_INTENSITY | FOREGROUND_GREEN );
+ case Colour::BrightWhite: return setTextAttribute( FOREGROUND_INTENSITY | FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE );
+ case Colour::BrightYellow: return setTextAttribute( FOREGROUND_INTENSITY | FOREGROUND_RED | FOREGROUND_GREEN );
+
+ case Colour::Bright: CATCH_INTERNAL_ERROR( "not a colour" );
+
+ default:
+ CATCH_ERROR( "Unknown colour requested" );
+ }
+ }
+
+ private:
+ void setTextAttribute( WORD _textAttribute ) {
+ SetConsoleTextAttribute( stdoutHandle, _textAttribute | originalBackgroundAttributes );
+ }
+ HANDLE stdoutHandle;
+ WORD originalForegroundAttributes;
+ WORD originalBackgroundAttributes;
+ };
+
+ IColourImpl* platformColourInstance() {
+ static Win32ColourImpl s_instance;
+
+ IConfigPtr config = getCurrentContext().getConfig();
+ UseColour::YesOrNo colourMode = config
+ ? config->useColour()
+ : UseColour::Auto;
+ if( colourMode == UseColour::Auto )
+ colourMode = UseColour::Yes;
+ return colourMode == UseColour::Yes
+ ? &s_instance
+ : NoColourImpl::instance();
+ }
+
+} // end anon namespace
+} // end namespace Catch
+
+#elif defined( CATCH_CONFIG_COLOUR_ANSI ) //////////////////////////////////////
+
+#include <unistd.h>
+
+namespace Catch {
+namespace {
+
+ // use POSIX/ ANSI console terminal codes
+ // Thanks to Adam Strzelecki for original contribution
+ // (http://github.com/nanoant)
+ // https://github.com/philsquared/Catch/pull/131
+ class PosixColourImpl : public IColourImpl {
+ public:
+ void use( Colour::Code _colourCode ) override {
+ switch( _colourCode ) {
+ case Colour::None:
+ case Colour::White: return setColour( "[0m" );
+ case Colour::Red: return setColour( "[0;31m" );
+ case Colour::Green: return setColour( "[0;32m" );
+ case Colour::Blue: return setColour( "[0;34m" );
+ case Colour::Cyan: return setColour( "[0;36m" );
+ case Colour::Yellow: return setColour( "[0;33m" );
+ case Colour::Grey: return setColour( "[1;30m" );
+
+ case Colour::LightGrey: return setColour( "[0;37m" );
+ case Colour::BrightRed: return setColour( "[1;31m" );
+ case Colour::BrightGreen: return setColour( "[1;32m" );
+ case Colour::BrightWhite: return setColour( "[1;37m" );
+ case Colour::BrightYellow: return setColour( "[1;33m" );
+
+ case Colour::Bright: CATCH_INTERNAL_ERROR( "not a colour" );
+ default: CATCH_INTERNAL_ERROR( "Unknown colour requested" );
+ }
+ }
+ static IColourImpl* instance() {
+ static PosixColourImpl s_instance;
+ return &s_instance;
+ }
+
+ private:
+ void setColour( const char* _escapeCode ) {
+ getCurrentContext().getConfig()->stream()
+ << '\033' << _escapeCode;
+ }
+ };
+
+ bool useColourOnPlatform() {
+ return
+#if defined(CATCH_PLATFORM_MAC) || defined(CATCH_PLATFORM_IPHONE)
+ !isDebuggerActive() &&
+#endif
+#if !(defined(__DJGPP__) && defined(__STRICT_ANSI__))
+ isatty(STDOUT_FILENO)
+#else
+ false
+#endif
+ ;
+ }
+ IColourImpl* platformColourInstance() {
+ ErrnoGuard guard;
+ IConfigPtr config = getCurrentContext().getConfig();
+ UseColour::YesOrNo colourMode = config
+ ? config->useColour()
+ : UseColour::Auto;
+ if( colourMode == UseColour::Auto )
+ colourMode = useColourOnPlatform()
+ ? UseColour::Yes
+ : UseColour::No;
+ return colourMode == UseColour::Yes
+ ? PosixColourImpl::instance()
+ : NoColourImpl::instance();
+ }
+
+} // end anon namespace
+} // end namespace Catch
+
+#else // not Windows or ANSI ///////////////////////////////////////////////
+
+namespace Catch {
+
+ static IColourImpl* platformColourInstance() { return NoColourImpl::instance(); }
+
+} // end namespace Catch
+
+#endif // Windows/ ANSI/ None
+
+namespace Catch {
+
+ Colour::Colour( Code _colourCode ) { use( _colourCode ); }
+ Colour::Colour( Colour&& other ) noexcept {
+ m_moved = other.m_moved;
+ other.m_moved = true;
+ }
+ Colour& Colour::operator=( Colour&& other ) noexcept {
+ m_moved = other.m_moved;
+ other.m_moved = true;
+ return *this;
+ }
+
+ Colour::~Colour(){ if( !m_moved ) use( None ); }
+
+ void Colour::use( Code _colourCode ) {
+ static IColourImpl* impl = platformColourInstance();
+ // Strictly speaking, this cannot possibly happen.
+ // However, under some conditions it does happen (see #1626),
+ // and this change is small enough that we can let practicality
+ // triumph over purity in this case.
+ if (impl != nullptr) {
+ impl->use( _colourCode );
+ }
+ }
+
+ std::ostream& operator << ( std::ostream& os, Colour const& ) {
+ return os;
+ }
+
+} // end namespace Catch
+
+#if defined(__clang__)
+# pragma clang diagnostic pop
+#endif
+
+// end catch_console_colour.cpp
+// start catch_context.cpp
+
+namespace Catch {
+
+ class Context : public IMutableContext, NonCopyable {
+
+ public: // IContext
+ IResultCapture* getResultCapture() override {
+ return m_resultCapture;
+ }
+ IRunner* getRunner() override {
+ return m_runner;
+ }
+
+ IConfigPtr const& getConfig() const override {
+ return m_config;
+ }
+
+ ~Context() override;
+
+ public: // IMutableContext
+ void setResultCapture( IResultCapture* resultCapture ) override {
+ m_resultCapture = resultCapture;
+ }
+ void setRunner( IRunner* runner ) override {
+ m_runner = runner;
+ }
+ void setConfig( IConfigPtr const& config ) override {
+ m_config = config;
+ }
+
+ friend IMutableContext& getCurrentMutableContext();
+
+ private:
+ IConfigPtr m_config;
+ IRunner* m_runner = nullptr;
+ IResultCapture* m_resultCapture = nullptr;
+ };
+
+ IMutableContext *IMutableContext::currentContext = nullptr;
+
+ void IMutableContext::createContext()
+ {
+ currentContext = new Context();
+ }
+
+ void cleanUpContext() {
+ delete IMutableContext::currentContext;
+ IMutableContext::currentContext = nullptr;
+ }
+ IContext::~IContext() = default;
+ IMutableContext::~IMutableContext() = default;
+ Context::~Context() = default;
+
+ SimplePcg32& rng() {
+ static SimplePcg32 s_rng;
+ return s_rng;
+ }
+
+}
+// end catch_context.cpp
+// start catch_debug_console.cpp
+
+// start catch_debug_console.h
+
+#include <string>
+
+namespace Catch {
+ void writeToDebugConsole( std::string const& text );
+}
+
+// end catch_debug_console.h
+#if defined(CATCH_CONFIG_ANDROID_LOGWRITE)
+#include <android/log.h>
+
+ namespace Catch {
+ void writeToDebugConsole( std::string const& text ) {
+ __android_log_write( ANDROID_LOG_DEBUG, "Catch", text.c_str() );
+ }
+ }
+
+#elif defined(CATCH_PLATFORM_WINDOWS)
+
+ namespace Catch {
+ void writeToDebugConsole( std::string const& text ) {
+ ::OutputDebugStringA( text.c_str() );
+ }
+ }
+
+#else
+
+ namespace Catch {
+ void writeToDebugConsole( std::string const& text ) {
+ // !TBD: Need a version for Mac/ XCode and other IDEs
+ Catch::cout() << text;
+ }
+ }
+
+#endif // Platform
+// end catch_debug_console.cpp
+// start catch_debugger.cpp
+
+#if defined(CATCH_PLATFORM_MAC) || defined(CATCH_PLATFORM_IPHONE)
+
+# include <cassert>
+# include <sys/types.h>
+# include <unistd.h>
+# include <cstddef>
+# include <ostream>
+
+#ifdef __apple_build_version__
+ // These headers will only compile with AppleClang (XCode)
+ // For other compilers (Clang, GCC, ... ) we need to exclude them
+# include <sys/sysctl.h>
+#endif
+
+ namespace Catch {
+ #ifdef __apple_build_version__
+ // The following function is taken directly from the following technical note:
+ // https://developer.apple.com/library/archive/qa/qa1361/_index.html
+
+ // Returns true if the current process is being debugged (either
+ // running under the debugger or has a debugger attached post facto).
+ bool isDebuggerActive(){
+ int mib[4];
+ struct kinfo_proc info;
+ std::size_t size;
+
+ // Initialize the flags so that, if sysctl fails for some bizarre
+ // reason, we get a predictable result.
+
+ info.kp_proc.p_flag = 0;
+
+ // Initialize mib, which tells sysctl the info we want, in this case
+ // we're looking for information about a specific process ID.
+
+ mib[0] = CTL_KERN;
+ mib[1] = KERN_PROC;
+ mib[2] = KERN_PROC_PID;
+ mib[3] = getpid();
+
+ // Call sysctl.
+
+ size = sizeof(info);
+ if( sysctl(mib, sizeof(mib) / sizeof(*mib), &info, &size, nullptr, 0) != 0 ) {
+ Catch::cerr() << "\n** Call to sysctl failed - unable to determine if debugger is active **\n" << std::endl;
+ return false;
+ }
+
+ // We're being debugged if the P_TRACED flag is set.
+
+ return ( (info.kp_proc.p_flag & P_TRACED) != 0 );
+ }
+ #else
+ bool isDebuggerActive() {
+ // We need to find another way to determine this for non-appleclang compilers on macOS
+ return false;
+ }
+ #endif
+ } // namespace Catch
+
+#elif defined(CATCH_PLATFORM_LINUX)
+ #include <fstream>
+ #include <string>
+
+ namespace Catch{
+ // The standard POSIX way of detecting a debugger is to attempt to
+ // ptrace() the process, but this needs to be done from a child and not
+ // this process itself to still allow attaching to this process later
+ // if wanted, so is rather heavy. Under Linux we have the PID of the
+ // "debugger" (which doesn't need to be gdb, of course, it could also
+ // be strace, for example) in /proc/$PID/status, so just get it from
+ // there instead.
+ bool isDebuggerActive(){
+ // Libstdc++ has a bug, where std::ifstream sets errno to 0
+ // This way our users can properly assert over errno values
+ ErrnoGuard guard;
+ std::ifstream in("/proc/self/status");
+ for( std::string line; std::getline(in, line); ) {
+ static const int PREFIX_LEN = 11;
+ if( line.compare(0, PREFIX_LEN, "TracerPid:\t") == 0 ) {
+ // We're traced if the PID is not 0 and no other PID starts
+ // with 0 digit, so it's enough to check for just a single
+ // character.
+ return line.length() > PREFIX_LEN && line[PREFIX_LEN] != '0';
+ }
+ }
+
+ return false;
+ }
+ } // namespace Catch
+#elif defined(_MSC_VER)
+ extern "C" __declspec(dllimport) int __stdcall IsDebuggerPresent();
+ namespace Catch {
+ bool isDebuggerActive() {
+ return IsDebuggerPresent() != 0;
+ }
+ }
+#elif defined(__MINGW32__)
+ extern "C" __declspec(dllimport) int __stdcall IsDebuggerPresent();
+ namespace Catch {
+ bool isDebuggerActive() {
+ return IsDebuggerPresent() != 0;
+ }
+ }
+#else
+ namespace Catch {
+ bool isDebuggerActive() { return false; }
+ }
+#endif // Platform
+// end catch_debugger.cpp
+// start catch_decomposer.cpp
+
+namespace Catch {
+
+ ITransientExpression::~ITransientExpression() = default;
+
+ void formatReconstructedExpression( std::ostream &os, std::string const& lhs, StringRef op, std::string const& rhs ) {
+ if( lhs.size() + rhs.size() < 40 &&
+ lhs.find('\n') == std::string::npos &&
+ rhs.find('\n') == std::string::npos )
+ os << lhs << " " << op << " " << rhs;
+ else
+ os << lhs << "\n" << op << "\n" << rhs;
+ }
+}
+// end catch_decomposer.cpp
+// start catch_enforce.cpp
+
+#include <stdexcept>
+
+namespace Catch {
+#if defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) && !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS_CUSTOM_HANDLER)
+ [[noreturn]]
+ void throw_exception(std::exception const& e) {
+ Catch::cerr() << "Catch will terminate because it needed to throw an exception.\n"
+ << "The message was: " << e.what() << '\n';
+ std::terminate();
+ }
+#endif
+
+ [[noreturn]]
+ void throw_logic_error(std::string const& msg) {
+ throw_exception(std::logic_error(msg));
+ }
+
+ [[noreturn]]
+ void throw_domain_error(std::string const& msg) {
+ throw_exception(std::domain_error(msg));
+ }
+
+ [[noreturn]]
+ void throw_runtime_error(std::string const& msg) {
+ throw_exception(std::runtime_error(msg));
+ }
+
+} // namespace Catch;
+// end catch_enforce.cpp
+// start catch_enum_values_registry.cpp
+// start catch_enum_values_registry.h
+
+#include <vector>
+#include <memory>
+
+namespace Catch {
+
+ namespace Detail {
+
+ std::unique_ptr<EnumInfo> makeEnumInfo( StringRef enumName, StringRef allValueNames, std::vector<int> const& values );
+
+ class EnumValuesRegistry : public IMutableEnumValuesRegistry {
+
+ std::vector<std::unique_ptr<EnumInfo>> m_enumInfos;
+
+ EnumInfo const& registerEnum( StringRef enumName, StringRef allEnums, std::vector<int> const& values) override;
+ };
+
+ std::vector<StringRef> parseEnums( StringRef enums );
+
+ } // Detail
+
+} // Catch
+
+// end catch_enum_values_registry.h
+
+#include <map>
+#include <cassert>
+
+namespace Catch {
+
+ IMutableEnumValuesRegistry::~IMutableEnumValuesRegistry() {}
+
+ namespace Detail {
+
+ namespace {
+ // Extracts the actual name part of an enum instance
+ // In other words, it returns the Blue part of Bikeshed::Colour::Blue
+ StringRef extractInstanceName(StringRef enumInstance) {
+ // Find last occurrence of ":"
+ size_t name_start = enumInstance.size();
+ while (name_start > 0 && enumInstance[name_start - 1] != ':') {
+ --name_start;
+ }
+ return enumInstance.substr(name_start, enumInstance.size() - name_start);
+ }
+ }
+
+ std::vector<StringRef> parseEnums( StringRef enums ) {
+ auto enumValues = splitStringRef( enums, ',' );
+ std::vector<StringRef> parsed;
+ parsed.reserve( enumValues.size() );
+ for( auto const& enumValue : enumValues ) {
+ parsed.push_back(trim(extractInstanceName(enumValue)));
+ }
+ return parsed;
+ }
+
+ EnumInfo::~EnumInfo() {}
+
+ StringRef EnumInfo::lookup( int value ) const {
+ for( auto const& valueToName : m_values ) {
+ if( valueToName.first == value )
+ return valueToName.second;
+ }
+ return "{** unexpected enum value **}"_sr;
+ }
+
+ std::unique_ptr<EnumInfo> makeEnumInfo( StringRef enumName, StringRef allValueNames, std::vector<int> const& values ) {
+ std::unique_ptr<EnumInfo> enumInfo( new EnumInfo );
+ enumInfo->m_name = enumName;
+ enumInfo->m_values.reserve( values.size() );
+
+ const auto valueNames = Catch::Detail::parseEnums( allValueNames );
+ assert( valueNames.size() == values.size() );
+ std::size_t i = 0;
+ for( auto value : values )
+ enumInfo->m_values.emplace_back(value, valueNames[i++]);
+
+ return enumInfo;
+ }
+
+ EnumInfo const& EnumValuesRegistry::registerEnum( StringRef enumName, StringRef allValueNames, std::vector<int> const& values ) {
+ m_enumInfos.push_back(makeEnumInfo(enumName, allValueNames, values));
+ return *m_enumInfos.back();
+ }
+
+ } // Detail
+} // Catch
+
+// end catch_enum_values_registry.cpp
+// start catch_errno_guard.cpp
+
+#include <cerrno>
+
+namespace Catch {
+ ErrnoGuard::ErrnoGuard():m_oldErrno(errno){}
+ ErrnoGuard::~ErrnoGuard() { errno = m_oldErrno; }
+}
+// end catch_errno_guard.cpp
+// start catch_exception_translator_registry.cpp
+
+// start catch_exception_translator_registry.h
+
+#include <vector>
+#include <string>
+#include <memory>
+
+namespace Catch {
+
+ class ExceptionTranslatorRegistry : public IExceptionTranslatorRegistry {
+ public:
+ ~ExceptionTranslatorRegistry();
+ virtual void registerTranslator( const IExceptionTranslator* translator );
+ std::string translateActiveException() const override;
+ std::string tryTranslators() const;
+
+ private:
+ std::vector<std::unique_ptr<IExceptionTranslator const>> m_translators;
+ };
+}
+
+// end catch_exception_translator_registry.h
+#ifdef __OBJC__
+#import "Foundation/Foundation.h"
+#endif
+
+namespace Catch {
+
+ ExceptionTranslatorRegistry::~ExceptionTranslatorRegistry() {
+ }
+
+ void ExceptionTranslatorRegistry::registerTranslator( const IExceptionTranslator* translator ) {
+ m_translators.push_back( std::unique_ptr<const IExceptionTranslator>( translator ) );
+ }
+
+#if !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS)
+ std::string ExceptionTranslatorRegistry::translateActiveException() const {
+ try {
+#ifdef __OBJC__
+ // In Objective-C try objective-c exceptions first
+ @try {
+ return tryTranslators();
+ }
+ @catch (NSException *exception) {
+ return Catch::Detail::stringify( [exception description] );
+ }
+#else
+ // Compiling a mixed mode project with MSVC means that CLR
+ // exceptions will be caught in (...) as well. However, these
+ // do not fill-in std::current_exception and thus lead to crash
+ // when attempting rethrow.
+ // /EHa switch also causes structured exceptions to be caught
+ // here, but they fill-in current_exception properly, so
+ // at worst the output should be a little weird, instead of
+ // causing a crash.
+ if (std::current_exception() == nullptr) {
+ return "Non C++ exception. Possibly a CLR exception.";
+ }
+ return tryTranslators();
+#endif
+ }
+ catch( TestFailureException& ) {
+ std::rethrow_exception(std::current_exception());
+ }
+ catch( std::exception& ex ) {
+ return ex.what();
+ }
+ catch( std::string& msg ) {
+ return msg;
+ }
+ catch( const char* msg ) {
+ return msg;
+ }
+ catch(...) {
+ return "Unknown exception";
+ }
+ }
+
+ std::string ExceptionTranslatorRegistry::tryTranslators() const {
+ if (m_translators.empty()) {
+ std::rethrow_exception(std::current_exception());
+ } else {
+ return m_translators[0]->translate(m_translators.begin() + 1, m_translators.end());
+ }
+ }
+
+#else // ^^ Exceptions are enabled // Exceptions are disabled vv
+ std::string ExceptionTranslatorRegistry::translateActiveException() const {
+ CATCH_INTERNAL_ERROR("Attempted to translate active exception under CATCH_CONFIG_DISABLE_EXCEPTIONS!");
+ }
+
+ std::string ExceptionTranslatorRegistry::tryTranslators() const {
+ CATCH_INTERNAL_ERROR("Attempted to use exception translators under CATCH_CONFIG_DISABLE_EXCEPTIONS!");
+ }
+#endif
+
+}
+// end catch_exception_translator_registry.cpp
+// start catch_fatal_condition.cpp
+
+#include <algorithm>
+
+#if !defined( CATCH_CONFIG_WINDOWS_SEH ) && !defined( CATCH_CONFIG_POSIX_SIGNALS )
+
+namespace Catch {
+
+ // If neither SEH nor signal handling is required, the handler impls
+ // do not have to do anything, and can be empty.
+ void FatalConditionHandler::engage_platform() {}
+ void FatalConditionHandler::disengage_platform() {}
+ FatalConditionHandler::FatalConditionHandler() = default;
+ FatalConditionHandler::~FatalConditionHandler() = default;
+
+} // end namespace Catch
+
+#endif // !CATCH_CONFIG_WINDOWS_SEH && !CATCH_CONFIG_POSIX_SIGNALS
+
+#if defined( CATCH_CONFIG_WINDOWS_SEH ) && defined( CATCH_CONFIG_POSIX_SIGNALS )
+#error "Inconsistent configuration: Windows' SEH handling and POSIX signals cannot be enabled at the same time"
+#endif // CATCH_CONFIG_WINDOWS_SEH && CATCH_CONFIG_POSIX_SIGNALS
+
+#if defined( CATCH_CONFIG_WINDOWS_SEH ) || defined( CATCH_CONFIG_POSIX_SIGNALS )
+
+namespace {
+ //! Signals fatal error message to the run context
+ void reportFatal( char const * const message ) {
+ Catch::getCurrentContext().getResultCapture()->handleFatalErrorCondition( message );
+ }
+
+ //! Minimal size Catch2 needs for its own fatal error handling.
+ //! Picked anecdotally, so it might not be sufficient on all
+ //! platforms, and for all configurations.
+ constexpr std::size_t minStackSizeForErrors = 32 * 1024;
+} // end unnamed namespace
+
+#endif // CATCH_CONFIG_WINDOWS_SEH || CATCH_CONFIG_POSIX_SIGNALS
+
+#if defined( CATCH_CONFIG_WINDOWS_SEH )
+
+namespace Catch {
+
+ struct SignalDefs { DWORD id; const char* name; };
+
+ // There is no 1-1 mapping between signals and windows exceptions.
+ // Windows can easily distinguish between SO and SigSegV,
+ // but SigInt, SigTerm, etc are handled differently.
+ static SignalDefs signalDefs[] = {
+ { static_cast<DWORD>(EXCEPTION_ILLEGAL_INSTRUCTION), "SIGILL - Illegal instruction signal" },
+ { static_cast<DWORD>(EXCEPTION_STACK_OVERFLOW), "SIGSEGV - Stack overflow" },
+ { static_cast<DWORD>(EXCEPTION_ACCESS_VIOLATION), "SIGSEGV - Segmentation violation signal" },
+ { static_cast<DWORD>(EXCEPTION_INT_DIVIDE_BY_ZERO), "Divide by zero error" },
+ };
+
+ static LONG CALLBACK handleVectoredException(PEXCEPTION_POINTERS ExceptionInfo) {
+ for (auto const& def : signalDefs) {
+ if (ExceptionInfo->ExceptionRecord->ExceptionCode == def.id) {
+ reportFatal(def.name);
+ }
+ }
+ // If its not an exception we care about, pass it along.
+ // This stops us from eating debugger breaks etc.
+ return EXCEPTION_CONTINUE_SEARCH;
+ }
+
+ // Since we do not support multiple instantiations, we put these
+ // into global variables and rely on cleaning them up in outlined
+ // constructors/destructors
+ static PVOID exceptionHandlerHandle = nullptr;
+
+ // For MSVC, we reserve part of the stack memory for handling
+ // memory overflow structured exception.
+ FatalConditionHandler::FatalConditionHandler() {
+ ULONG guaranteeSize = static_cast<ULONG>(minStackSizeForErrors);
+ if (!SetThreadStackGuarantee(&guaranteeSize)) {
+ // We do not want to fully error out, because needing
+ // the stack reserve should be rare enough anyway.
+ Catch::cerr()
+ << "Failed to reserve piece of stack."
+ << " Stack overflows will not be reported successfully.";
+ }
+ }
+
+ // We do not attempt to unset the stack guarantee, because
+ // Windows does not support lowering the stack size guarantee.
+ FatalConditionHandler::~FatalConditionHandler() = default;
+
+ void FatalConditionHandler::engage_platform() {
+ // Register as first handler in current chain
+ exceptionHandlerHandle = AddVectoredExceptionHandler(1, handleVectoredException);
+ if (!exceptionHandlerHandle) {
+ CATCH_RUNTIME_ERROR("Could not register vectored exception handler");
+ }
+ }
+
+ void FatalConditionHandler::disengage_platform() {
+ if (!RemoveVectoredExceptionHandler(exceptionHandlerHandle)) {
+ CATCH_RUNTIME_ERROR("Could not unregister vectored exception handler");
+ }
+ exceptionHandlerHandle = nullptr;
+ }
+
+} // end namespace Catch
+
+#endif // CATCH_CONFIG_WINDOWS_SEH
+
+#if defined( CATCH_CONFIG_POSIX_SIGNALS )
+
+#include <signal.h>
+
+namespace Catch {
+
+ struct SignalDefs {
+ int id;
+ const char* name;
+ };
+
+ static SignalDefs signalDefs[] = {
+ { SIGINT, "SIGINT - Terminal interrupt signal" },
+ { SIGILL, "SIGILL - Illegal instruction signal" },
+ { SIGFPE, "SIGFPE - Floating point error signal" },
+ { SIGSEGV, "SIGSEGV - Segmentation violation signal" },
+ { SIGTERM, "SIGTERM - Termination request signal" },
+ { SIGABRT, "SIGABRT - Abort (abnormal termination) signal" }
+ };
+
+// Older GCCs trigger -Wmissing-field-initializers for T foo = {}
+// which is zero initialization, but not explicit. We want to avoid
+// that.
+#if defined(__GNUC__)
+# pragma GCC diagnostic push
+# pragma GCC diagnostic ignored "-Wmissing-field-initializers"
+#endif
+
+ static char* altStackMem = nullptr;
+ static std::size_t altStackSize = 0;
+ static stack_t oldSigStack{};
+ static struct sigaction oldSigActions[sizeof(signalDefs) / sizeof(SignalDefs)]{};
+
+ static void restorePreviousSignalHandlers() {
+ // We set signal handlers back to the previous ones. Hopefully
+ // nobody overwrote them in the meantime, and doesn't expect
+ // their signal handlers to live past ours given that they
+ // installed them after ours..
+ for (std::size_t i = 0; i < sizeof(signalDefs) / sizeof(SignalDefs); ++i) {
+ sigaction(signalDefs[i].id, &oldSigActions[i], nullptr);
+ }
+ // Return the old stack
+ sigaltstack(&oldSigStack, nullptr);
+ }
+
+ static void handleSignal( int sig ) {
+ char const * name = "<unknown signal>";
+ for (auto const& def : signalDefs) {
+ if (sig == def.id) {
+ name = def.name;
+ break;
+ }
+ }
+ // We need to restore previous signal handlers and let them do
+ // their thing, so that the users can have the debugger break
+ // when a signal is raised, and so on.
+ restorePreviousSignalHandlers();
+ reportFatal( name );
+ raise( sig );
+ }
+
+ FatalConditionHandler::FatalConditionHandler() {
+ assert(!altStackMem && "Cannot initialize POSIX signal handler when one already exists");
+ if (altStackSize == 0) {
+ altStackSize = std::max(static_cast<size_t>(SIGSTKSZ), minStackSizeForErrors);
+ }
+ altStackMem = new char[altStackSize]();
+ }
+
+ FatalConditionHandler::~FatalConditionHandler() {
+ delete[] altStackMem;
+ // We signal that another instance can be constructed by zeroing
+ // out the pointer.
+ altStackMem = nullptr;
+ }
+
+ void FatalConditionHandler::engage_platform() {
+ stack_t sigStack;
+ sigStack.ss_sp = altStackMem;
+ sigStack.ss_size = altStackSize;
+ sigStack.ss_flags = 0;
+ sigaltstack(&sigStack, &oldSigStack);
+ struct sigaction sa = { };
+
+ sa.sa_handler = handleSignal;
+ sa.sa_flags = SA_ONSTACK;
+ for (std::size_t i = 0; i < sizeof(signalDefs)/sizeof(SignalDefs); ++i) {
+ sigaction(signalDefs[i].id, &sa, &oldSigActions[i]);
+ }
+ }
+
+#if defined(__GNUC__)
+# pragma GCC diagnostic pop
+#endif
+
+ void FatalConditionHandler::disengage_platform() {
+ restorePreviousSignalHandlers();
+ }
+
+} // end namespace Catch
+
+#endif // CATCH_CONFIG_POSIX_SIGNALS
+// end catch_fatal_condition.cpp
+// start catch_generators.cpp
+
+#include <limits>
+#include <set>
+
+namespace Catch {
+
+IGeneratorTracker::~IGeneratorTracker() {}
+
+const char* GeneratorException::what() const noexcept {
+ return m_msg;
+}
+
+namespace Generators {
+
+ GeneratorUntypedBase::~GeneratorUntypedBase() {}
+
+ auto acquireGeneratorTracker( StringRef generatorName, SourceLineInfo const& lineInfo ) -> IGeneratorTracker& {
+ return getResultCapture().acquireGeneratorTracker( generatorName, lineInfo );
+ }
+
+} // namespace Generators
+} // namespace Catch
+// end catch_generators.cpp
+// start catch_interfaces_capture.cpp
+
+namespace Catch {
+ IResultCapture::~IResultCapture() = default;
+}
+// end catch_interfaces_capture.cpp
+// start catch_interfaces_config.cpp
+
+namespace Catch {
+ IConfig::~IConfig() = default;
+}
+// end catch_interfaces_config.cpp
+// start catch_interfaces_exception.cpp
+
+namespace Catch {
+ IExceptionTranslator::~IExceptionTranslator() = default;
+ IExceptionTranslatorRegistry::~IExceptionTranslatorRegistry() = default;
+}
+// end catch_interfaces_exception.cpp
+// start catch_interfaces_registry_hub.cpp
+
+namespace Catch {
+ IRegistryHub::~IRegistryHub() = default;
+ IMutableRegistryHub::~IMutableRegistryHub() = default;
+}
+// end catch_interfaces_registry_hub.cpp
+// start catch_interfaces_reporter.cpp
+
+// start catch_reporter_listening.h
+
+namespace Catch {
+
+ class ListeningReporter : public IStreamingReporter {
+ using Reporters = std::vector<IStreamingReporterPtr>;
+ Reporters m_listeners;
+ IStreamingReporterPtr m_reporter = nullptr;
+ ReporterPreferences m_preferences;
+
+ public:
+ ListeningReporter();
+
+ void addListener( IStreamingReporterPtr&& listener );
+ void addReporter( IStreamingReporterPtr&& reporter );
+
+ public: // IStreamingReporter
+
+ ReporterPreferences getPreferences() const override;
+
+ void noMatchingTestCases( std::string const& spec ) override;
+
+ void reportInvalidArguments(std::string const&arg) override;
+
+ static std::set<Verbosity> getSupportedVerbosities();
+
+#if defined(CATCH_CONFIG_ENABLE_BENCHMARKING)
+ void benchmarkPreparing(std::string const& name) override;
+ void benchmarkStarting( BenchmarkInfo const& benchmarkInfo ) override;
+ void benchmarkEnded( BenchmarkStats<> const& benchmarkStats ) override;
+ void benchmarkFailed(std::string const&) override;
+#endif // CATCH_CONFIG_ENABLE_BENCHMARKING
+
+ void testRunStarting( TestRunInfo const& testRunInfo ) override;
+ void testGroupStarting( GroupInfo const& groupInfo ) override;
+ void testCaseStarting( TestCaseInfo const& testInfo ) override;
+ void sectionStarting( SectionInfo const& sectionInfo ) override;
+ void assertionStarting( AssertionInfo const& assertionInfo ) override;
+
+ // The return value indicates if the messages buffer should be cleared:
+ bool assertionEnded( AssertionStats const& assertionStats ) override;
+ void sectionEnded( SectionStats const& sectionStats ) override;
+ void testCaseEnded( TestCaseStats const& testCaseStats ) override;
+ void testGroupEnded( TestGroupStats const& testGroupStats ) override;
+ void testRunEnded( TestRunStats const& testRunStats ) override;
+
+ void skipTest( TestCaseInfo const& testInfo ) override;
+ bool isMulti() const override;
+
+ };
+
+} // end namespace Catch
+
+// end catch_reporter_listening.h
+namespace Catch {
+
+ ReporterConfig::ReporterConfig( IConfigPtr const& _fullConfig )
+ : m_stream( &_fullConfig->stream() ), m_fullConfig( _fullConfig ) {}
+
+ ReporterConfig::ReporterConfig( IConfigPtr const& _fullConfig, std::ostream& _stream )
+ : m_stream( &_stream ), m_fullConfig( _fullConfig ) {}
+
+ std::ostream& ReporterConfig::stream() const { return *m_stream; }
+ IConfigPtr ReporterConfig::fullConfig() const { return m_fullConfig; }
+
+ TestRunInfo::TestRunInfo( std::string const& _name ) : name( _name ) {}
+
+ GroupInfo::GroupInfo( std::string const& _name,
+ std::size_t _groupIndex,
+ std::size_t _groupsCount )
+ : name( _name ),
+ groupIndex( _groupIndex ),
+ groupsCounts( _groupsCount )
+ {}
+
+ AssertionStats::AssertionStats( AssertionResult const& _assertionResult,
+ std::vector<MessageInfo> const& _infoMessages,
+ Totals const& _totals )
+ : assertionResult( _assertionResult ),
+ infoMessages( _infoMessages ),
+ totals( _totals )
+ {
+ assertionResult.m_resultData.lazyExpression.m_transientExpression = _assertionResult.m_resultData.lazyExpression.m_transientExpression;
+
+ if( assertionResult.hasMessage() ) {
+ // Copy message into messages list.
+ // !TBD This should have been done earlier, somewhere
+ MessageBuilder builder( assertionResult.getTestMacroName(), assertionResult.getSourceInfo(), assertionResult.getResultType() );
+ builder << assertionResult.getMessage();
+ builder.m_info.message = builder.m_stream.str();
+
+ infoMessages.push_back( builder.m_info );
+ }
+ }
+
+ AssertionStats::~AssertionStats() = default;
+
+ SectionStats::SectionStats( SectionInfo const& _sectionInfo,
+ Counts const& _assertions,
+ double _durationInSeconds,
+ bool _missingAssertions )
+ : sectionInfo( _sectionInfo ),
+ assertions( _assertions ),
+ durationInSeconds( _durationInSeconds ),
+ missingAssertions( _missingAssertions )
+ {}
+
+ SectionStats::~SectionStats() = default;
+
+ TestCaseStats::TestCaseStats( TestCaseInfo const& _testInfo,
+ Totals const& _totals,
+ std::string const& _stdOut,
+ std::string const& _stdErr,
+ bool _aborting )
+ : testInfo( _testInfo ),
+ totals( _totals ),
+ stdOut( _stdOut ),
+ stdErr( _stdErr ),
+ aborting( _aborting )
+ {}
+
+ TestCaseStats::~TestCaseStats() = default;
+
+ TestGroupStats::TestGroupStats( GroupInfo const& _groupInfo,
+ Totals const& _totals,
+ bool _aborting )
+ : groupInfo( _groupInfo ),
+ totals( _totals ),
+ aborting( _aborting )
+ {}
+
+ TestGroupStats::TestGroupStats( GroupInfo const& _groupInfo )
+ : groupInfo( _groupInfo ),
+ aborting( false )
+ {}
+
+ TestGroupStats::~TestGroupStats() = default;
+
+ TestRunStats::TestRunStats( TestRunInfo const& _runInfo,
+ Totals const& _totals,
+ bool _aborting )
+ : runInfo( _runInfo ),
+ totals( _totals ),
+ aborting( _aborting )
+ {}
+
+ TestRunStats::~TestRunStats() = default;
+
+ void IStreamingReporter::fatalErrorEncountered( StringRef ) {}
+ bool IStreamingReporter::isMulti() const { return false; }
+
+ IReporterFactory::~IReporterFactory() = default;
+ IReporterRegistry::~IReporterRegistry() = default;
+
+} // end namespace Catch
+// end catch_interfaces_reporter.cpp
+// start catch_interfaces_runner.cpp
+
+namespace Catch {
+ IRunner::~IRunner() = default;
+}
+// end catch_interfaces_runner.cpp
+// start catch_interfaces_testcase.cpp
+
+namespace Catch {
+ ITestInvoker::~ITestInvoker() = default;
+ ITestCaseRegistry::~ITestCaseRegistry() = default;
+}
+// end catch_interfaces_testcase.cpp
+// start catch_leak_detector.cpp
+
+#ifdef CATCH_CONFIG_WINDOWS_CRTDBG
+#include <crtdbg.h>
+
+namespace Catch {
+
+ LeakDetector::LeakDetector() {
+ int flag = _CrtSetDbgFlag(_CRTDBG_REPORT_FLAG);
+ flag |= _CRTDBG_LEAK_CHECK_DF;
+ flag |= _CRTDBG_ALLOC_MEM_DF;
+ _CrtSetDbgFlag(flag);
+ _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE | _CRTDBG_MODE_DEBUG);
+ _CrtSetReportFile(_CRT_WARN, _CRTDBG_FILE_STDERR);
+ // Change this to leaking allocation's number to break there
+ _CrtSetBreakAlloc(-1);
+ }
+}
+
+#else
+
+ Catch::LeakDetector::LeakDetector() {}
+
+#endif
+
+Catch::LeakDetector::~LeakDetector() {
+ Catch::cleanUp();
+}
+// end catch_leak_detector.cpp
+// start catch_list.cpp
+
+// start catch_list.h
+
+#include <set>
+
+namespace Catch {
+
+ std::size_t listTests( Config const& config );
+
+ std::size_t listTestsNamesOnly( Config const& config );
+
+ struct TagInfo {
+ void add( std::string const& spelling );
+ std::string all() const;
+
+ std::set<std::string> spellings;
+ std::size_t count = 0;
+ };
+
+ std::size_t listTags( Config const& config );
+
+ std::size_t listReporters();
+
+ Option<std::size_t> list( std::shared_ptr<Config> const& config );
+
+} // end namespace Catch
+
+// end catch_list.h
+// start catch_text.h
+
+namespace Catch {
+ using namespace clara::TextFlow;
+}
+
+// end catch_text.h
+#include <limits>
+#include <algorithm>
+#include <iomanip>
+
+namespace Catch {
+
+ std::size_t listTests( Config const& config ) {
+ TestSpec const& testSpec = config.testSpec();
+ if( config.hasTestFilters() )
+ Catch::cout() << "Matching test cases:\n";
+ else {
+ Catch::cout() << "All available test cases:\n";
+ }
+
+ auto matchedTestCases = filterTests( getAllTestCasesSorted( config ), testSpec, config );
+ for( auto const& testCaseInfo : matchedTestCases ) {
+ Colour::Code colour = testCaseInfo.isHidden()
+ ? Colour::SecondaryText
+ : Colour::None;
+ Colour colourGuard( colour );
+
+ Catch::cout() << Column( testCaseInfo.name ).initialIndent( 2 ).indent( 4 ) << "\n";
+ if( config.verbosity() >= Verbosity::High ) {
+ Catch::cout() << Column( Catch::Detail::stringify( testCaseInfo.lineInfo ) ).indent(4) << std::endl;
+ std::string description = testCaseInfo.description;
+ if( description.empty() )
+ description = "(NO DESCRIPTION)";
+ Catch::cout() << Column( description ).indent(4) << std::endl;
+ }
+ if( !testCaseInfo.tags.empty() )
+ Catch::cout() << Column( testCaseInfo.tagsAsString() ).indent( 6 ) << "\n";
+ }
+
+ if( !config.hasTestFilters() )
+ Catch::cout() << pluralise( matchedTestCases.size(), "test case" ) << '\n' << std::endl;
+ else
+ Catch::cout() << pluralise( matchedTestCases.size(), "matching test case" ) << '\n' << std::endl;
+ return matchedTestCases.size();
+ }
+
+ std::size_t listTestsNamesOnly( Config const& config ) {
+ TestSpec const& testSpec = config.testSpec();
+ std::size_t matchedTests = 0;
+ std::vector<TestCase> matchedTestCases = filterTests( getAllTestCasesSorted( config ), testSpec, config );
+ for( auto const& testCaseInfo : matchedTestCases ) {
+ matchedTests++;
+ if( startsWith( testCaseInfo.name, '#' ) )
+ Catch::cout() << '"' << testCaseInfo.name << '"';
+ else
+ Catch::cout() << testCaseInfo.name;
+ if ( config.verbosity() >= Verbosity::High )
+ Catch::cout() << "\t@" << testCaseInfo.lineInfo;
+ Catch::cout() << std::endl;
+ }
+ return matchedTests;
+ }
+
+ void TagInfo::add( std::string const& spelling ) {
+ ++count;
+ spellings.insert( spelling );
+ }
+
+ std::string TagInfo::all() const {
+ size_t size = 0;
+ for (auto const& spelling : spellings) {
+ // Add 2 for the brackes
+ size += spelling.size() + 2;
+ }
+
+ std::string out; out.reserve(size);
+ for (auto const& spelling : spellings) {
+ out += '[';
+ out += spelling;
+ out += ']';
+ }
+ return out;
+ }
+
+ std::size_t listTags( Config const& config ) {
+ TestSpec const& testSpec = config.testSpec();
+ if( config.hasTestFilters() )
+ Catch::cout() << "Tags for matching test cases:\n";
+ else {
+ Catch::cout() << "All available tags:\n";
+ }
+
+ std::map<std::string, TagInfo> tagCounts;
+
+ std::vector<TestCase> matchedTestCases = filterTests( getAllTestCasesSorted( config ), testSpec, config );
+ for( auto const& testCase : matchedTestCases ) {
+ for( auto const& tagName : testCase.getTestCaseInfo().tags ) {
+ std::string lcaseTagName = toLower( tagName );
+ auto countIt = tagCounts.find( lcaseTagName );
+ if( countIt == tagCounts.end() )
+ countIt = tagCounts.insert( std::make_pair( lcaseTagName, TagInfo() ) ).first;
+ countIt->second.add( tagName );
+ }
+ }
+
+ for( auto const& tagCount : tagCounts ) {
+ ReusableStringStream rss;
+ rss << " " << std::setw(2) << tagCount.second.count << " ";
+ auto str = rss.str();
+ auto wrapper = Column( tagCount.second.all() )
+ .initialIndent( 0 )
+ .indent( str.size() )
+ .width( CATCH_CONFIG_CONSOLE_WIDTH-10 );
+ Catch::cout() << str << wrapper << '\n';
+ }
+ Catch::cout() << pluralise( tagCounts.size(), "tag" ) << '\n' << std::endl;
+ return tagCounts.size();
+ }
+
+ std::size_t listReporters() {
+ Catch::cout() << "Available reporters:\n";
+ IReporterRegistry::FactoryMap const& factories = getRegistryHub().getReporterRegistry().getFactories();
+ std::size_t maxNameLen = 0;
+ for( auto const& factoryKvp : factories )
+ maxNameLen = (std::max)( maxNameLen, factoryKvp.first.size() );
+
+ for( auto const& factoryKvp : factories ) {
+ Catch::cout()
+ << Column( factoryKvp.first + ":" )
+ .indent(2)
+ .width( 5+maxNameLen )
+ + Column( factoryKvp.second->getDescription() )
+ .initialIndent(0)
+ .indent(2)
+ .width( CATCH_CONFIG_CONSOLE_WIDTH - maxNameLen-8 )
+ << "\n";
+ }
+ Catch::cout() << std::endl;
+ return factories.size();
+ }
+
+ Option<std::size_t> list( std::shared_ptr<Config> const& config ) {
+ Option<std::size_t> listedCount;
+ getCurrentMutableContext().setConfig( config );
+ if( config->listTests() )
+ listedCount = listedCount.valueOr(0) + listTests( *config );
+ if( config->listTestNamesOnly() )
+ listedCount = listedCount.valueOr(0) + listTestsNamesOnly( *config );
+ if( config->listTags() )
+ listedCount = listedCount.valueOr(0) + listTags( *config );
+ if( config->listReporters() )
+ listedCount = listedCount.valueOr(0) + listReporters();
+ return listedCount;
+ }
+
+} // end namespace Catch
+// end catch_list.cpp
+// start catch_matchers.cpp
+
+namespace Catch {
+namespace Matchers {
+ namespace Impl {
+
+ std::string MatcherUntypedBase::toString() const {
+ if( m_cachedToString.empty() )
+ m_cachedToString = describe();
+ return m_cachedToString;
+ }
+
+ MatcherUntypedBase::~MatcherUntypedBase() = default;
+
+ } // namespace Impl
+} // namespace Matchers
+
+using namespace Matchers;
+using Matchers::Impl::MatcherBase;
+
+} // namespace Catch
+// end catch_matchers.cpp
+// start catch_matchers_exception.cpp
+
+namespace Catch {
+namespace Matchers {
+namespace Exception {
+
+bool ExceptionMessageMatcher::match(std::exception const& ex) const {
+ return ex.what() == m_message;
+}
+
+std::string ExceptionMessageMatcher::describe() const {
+ return "exception message matches \"" + m_message + "\"";
+}
+
+}
+Exception::ExceptionMessageMatcher Message(std::string const& message) {
+ return Exception::ExceptionMessageMatcher(message);
+}
+
+// namespace Exception
+} // namespace Matchers
+} // namespace Catch
+// end catch_matchers_exception.cpp
+// start catch_matchers_floating.cpp
+
+// start catch_polyfills.hpp
+
+namespace Catch {
+ bool isnan(float f);
+ bool isnan(double d);
+}
+
+// end catch_polyfills.hpp
+// start catch_to_string.hpp
+
+#include <string>
+
+namespace Catch {
+ template <typename T>
+ std::string to_string(T const& t) {
+#if defined(CATCH_CONFIG_CPP11_TO_STRING)
+ return std::to_string(t);
+#else
+ ReusableStringStream rss;
+ rss << t;
+ return rss.str();
+#endif
+ }
+} // end namespace Catch
+
+// end catch_to_string.hpp
+#include <algorithm>
+#include <cmath>
+#include <cstdlib>
+#include <cstdint>
+#include <cstring>
+#include <sstream>
+#include <type_traits>
+#include <iomanip>
+#include <limits>
+
+namespace Catch {
+namespace {
+
+ int32_t convert(float f) {
+ static_assert(sizeof(float) == sizeof(int32_t), "Important ULP matcher assumption violated");
+ int32_t i;
+ std::memcpy(&i, &f, sizeof(f));
+ return i;
+ }
+
+ int64_t convert(double d) {
+ static_assert(sizeof(double) == sizeof(int64_t), "Important ULP matcher assumption violated");
+ int64_t i;
+ std::memcpy(&i, &d, sizeof(d));
+ return i;
+ }
+
+ template <typename FP>
+ bool almostEqualUlps(FP lhs, FP rhs, uint64_t maxUlpDiff) {
+ // Comparison with NaN should always be false.
+ // This way we can rule it out before getting into the ugly details
+ if (Catch::isnan(lhs) || Catch::isnan(rhs)) {
+ return false;
+ }
+
+ auto lc = convert(lhs);
+ auto rc = convert(rhs);
+
+ if ((lc < 0) != (rc < 0)) {
+ // Potentially we can have +0 and -0
+ return lhs == rhs;
+ }
+
+ // static cast as a workaround for IBM XLC
+ auto ulpDiff = std::abs(static_cast<FP>(lc - rc));
+ return static_cast<uint64_t>(ulpDiff) <= maxUlpDiff;
+ }
+
+#if defined(CATCH_CONFIG_GLOBAL_NEXTAFTER)
+
+ float nextafter(float x, float y) {
+ return ::nextafterf(x, y);
+ }
+
+ double nextafter(double x, double y) {
+ return ::nextafter(x, y);
+ }
+
+#endif // ^^^ CATCH_CONFIG_GLOBAL_NEXTAFTER ^^^
+
+template <typename FP>
+FP step(FP start, FP direction, uint64_t steps) {
+ for (uint64_t i = 0; i < steps; ++i) {
+#if defined(CATCH_CONFIG_GLOBAL_NEXTAFTER)
+ start = Catch::nextafter(start, direction);
+#else
+ start = std::nextafter(start, direction);
+#endif
+ }
+ return start;
+}
+
+// Performs equivalent check of std::fabs(lhs - rhs) <= margin
+// But without the subtraction to allow for INFINITY in comparison
+bool marginComparison(double lhs, double rhs, double margin) {
+ return (lhs + margin >= rhs) && (rhs + margin >= lhs);
+}
+
+template <typename FloatingPoint>
+void write(std::ostream& out, FloatingPoint num) {
+ out << std::scientific
+ << std::setprecision(std::numeric_limits<FloatingPoint>::max_digits10 - 1)
+ << num;
+}
+
+} // end anonymous namespace
+
+namespace Matchers {
+namespace Floating {
+
+ enum class FloatingPointKind : uint8_t {
+ Float,
+ Double
+ };
+
+ WithinAbsMatcher::WithinAbsMatcher(double target, double margin)
+ :m_target{ target }, m_margin{ margin } {
+ CATCH_ENFORCE(margin >= 0, "Invalid margin: " << margin << '.'
+ << " Margin has to be non-negative.");
+ }
+
+ // Performs equivalent check of std::fabs(lhs - rhs) <= margin
+ // But without the subtraction to allow for INFINITY in comparison
+ bool WithinAbsMatcher::match(double const& matchee) const {
+ return (matchee + m_margin >= m_target) && (m_target + m_margin >= matchee);
+ }
+
+ std::string WithinAbsMatcher::describe() const {
+ return "is within " + ::Catch::Detail::stringify(m_margin) + " of " + ::Catch::Detail::stringify(m_target);
+ }
+
+ WithinUlpsMatcher::WithinUlpsMatcher(double target, uint64_t ulps, FloatingPointKind baseType)
+ :m_target{ target }, m_ulps{ ulps }, m_type{ baseType } {
+ CATCH_ENFORCE(m_type == FloatingPointKind::Double
+ || m_ulps < (std::numeric_limits<uint32_t>::max)(),
+ "Provided ULP is impossibly large for a float comparison.");
+ }
+
+#if defined(__clang__)
+#pragma clang diagnostic push
+// Clang <3.5 reports on the default branch in the switch below
+#pragma clang diagnostic ignored "-Wunreachable-code"
+#endif
+
+ bool WithinUlpsMatcher::match(double const& matchee) const {
+ switch (m_type) {
+ case FloatingPointKind::Float:
+ return almostEqualUlps<float>(static_cast<float>(matchee), static_cast<float>(m_target), m_ulps);
+ case FloatingPointKind::Double:
+ return almostEqualUlps<double>(matchee, m_target, m_ulps);
+ default:
+ CATCH_INTERNAL_ERROR( "Unknown FloatingPointKind value" );
+ }
+ }
+
+#if defined(__clang__)
+#pragma clang diagnostic pop
+#endif
+
+ std::string WithinUlpsMatcher::describe() const {
+ std::stringstream ret;
+
+ ret << "is within " << m_ulps << " ULPs of ";
+
+ if (m_type == FloatingPointKind::Float) {
+ write(ret, static_cast<float>(m_target));
+ ret << 'f';
+ } else {
+ write(ret, m_target);
+ }
+
+ ret << " ([";
+ if (m_type == FloatingPointKind::Double) {
+ write(ret, step(m_target, static_cast<double>(-INFINITY), m_ulps));
+ ret << ", ";
+ write(ret, step(m_target, static_cast<double>( INFINITY), m_ulps));
+ } else {
+ // We have to cast INFINITY to float because of MinGW, see #1782
+ write(ret, step(static_cast<float>(m_target), static_cast<float>(-INFINITY), m_ulps));
+ ret << ", ";
+ write(ret, step(static_cast<float>(m_target), static_cast<float>( INFINITY), m_ulps));
+ }
+ ret << "])";
+
+ return ret.str();
+ }
+
+ WithinRelMatcher::WithinRelMatcher(double target, double epsilon):
+ m_target(target),
+ m_epsilon(epsilon){
+ CATCH_ENFORCE(m_epsilon >= 0., "Relative comparison with epsilon < 0 does not make sense.");
+ CATCH_ENFORCE(m_epsilon < 1., "Relative comparison with epsilon >= 1 does not make sense.");
+ }
+
+ bool WithinRelMatcher::match(double const& matchee) const {
+ const auto relMargin = m_epsilon * (std::max)(std::fabs(matchee), std::fabs(m_target));
+ return marginComparison(matchee, m_target,
+ std::isinf(relMargin)? 0 : relMargin);
+ }
+
+ std::string WithinRelMatcher::describe() const {
+ Catch::ReusableStringStream sstr;
+ sstr << "and " << m_target << " are within " << m_epsilon * 100. << "% of each other";
+ return sstr.str();
+ }
+
+}// namespace Floating
+
+Floating::WithinUlpsMatcher WithinULP(double target, uint64_t maxUlpDiff) {
+ return Floating::WithinUlpsMatcher(target, maxUlpDiff, Floating::FloatingPointKind::Double);
+}
+
+Floating::WithinUlpsMatcher WithinULP(float target, uint64_t maxUlpDiff) {
+ return Floating::WithinUlpsMatcher(target, maxUlpDiff, Floating::FloatingPointKind::Float);
+}
+
+Floating::WithinAbsMatcher WithinAbs(double target, double margin) {
+ return Floating::WithinAbsMatcher(target, margin);
+}
+
+Floating::WithinRelMatcher WithinRel(double target, double eps) {
+ return Floating::WithinRelMatcher(target, eps);
+}
+
+Floating::WithinRelMatcher WithinRel(double target) {
+ return Floating::WithinRelMatcher(target, std::numeric_limits<double>::epsilon() * 100);
+}
+
+Floating::WithinRelMatcher WithinRel(float target, float eps) {
+ return Floating::WithinRelMatcher(target, eps);
+}
+
+Floating::WithinRelMatcher WithinRel(float target) {
+ return Floating::WithinRelMatcher(target, std::numeric_limits<float>::epsilon() * 100);
+}
+
+} // namespace Matchers
+} // namespace Catch
+// end catch_matchers_floating.cpp
+// start catch_matchers_generic.cpp
+
+std::string Catch::Matchers::Generic::Detail::finalizeDescription(const std::string& desc) {
+ if (desc.empty()) {
+ return "matches undescribed predicate";
+ } else {
+ return "matches predicate: \"" + desc + '"';
+ }
+}
+// end catch_matchers_generic.cpp
+// start catch_matchers_string.cpp
+
+#include <regex>
+
+namespace Catch {
+namespace Matchers {
+
+ namespace StdString {
+
+ CasedString::CasedString( std::string const& str, CaseSensitive::Choice caseSensitivity )
+ : m_caseSensitivity( caseSensitivity ),
+ m_str( adjustString( str ) )
+ {}
+ std::string CasedString::adjustString( std::string const& str ) const {
+ return m_caseSensitivity == CaseSensitive::No
+ ? toLower( str )
+ : str;
+ }
+ std::string CasedString::caseSensitivitySuffix() const {
+ return m_caseSensitivity == CaseSensitive::No
+ ? " (case insensitive)"
+ : std::string();
+ }
+
+ StringMatcherBase::StringMatcherBase( std::string const& operation, CasedString const& comparator )
+ : m_comparator( comparator ),
+ m_operation( operation ) {
+ }
+
+ std::string StringMatcherBase::describe() const {
+ std::string description;
+ description.reserve(5 + m_operation.size() + m_comparator.m_str.size() +
+ m_comparator.caseSensitivitySuffix().size());
+ description += m_operation;
+ description += ": \"";
+ description += m_comparator.m_str;
+ description += "\"";
+ description += m_comparator.caseSensitivitySuffix();
+ return description;
+ }
+
+ EqualsMatcher::EqualsMatcher( CasedString const& comparator ) : StringMatcherBase( "equals", comparator ) {}
+
+ bool EqualsMatcher::match( std::string const& source ) const {
+ return m_comparator.adjustString( source ) == m_comparator.m_str;
+ }
+
+ ContainsMatcher::ContainsMatcher( CasedString const& comparator ) : StringMatcherBase( "contains", comparator ) {}
+
+ bool ContainsMatcher::match( std::string const& source ) const {
+ return contains( m_comparator.adjustString( source ), m_comparator.m_str );
+ }
+
+ StartsWithMatcher::StartsWithMatcher( CasedString const& comparator ) : StringMatcherBase( "starts with", comparator ) {}
+
+ bool StartsWithMatcher::match( std::string const& source ) const {
+ return startsWith( m_comparator.adjustString( source ), m_comparator.m_str );
+ }
+
+ EndsWithMatcher::EndsWithMatcher( CasedString const& comparator ) : StringMatcherBase( "ends with", comparator ) {}
+
+ bool EndsWithMatcher::match( std::string const& source ) const {
+ return endsWith( m_comparator.adjustString( source ), m_comparator.m_str );
+ }
+
+ RegexMatcher::RegexMatcher(std::string regex, CaseSensitive::Choice caseSensitivity): m_regex(std::move(regex)), m_caseSensitivity(caseSensitivity) {}
+
+ bool RegexMatcher::match(std::string const& matchee) const {
+ auto flags = std::regex::ECMAScript; // ECMAScript is the default syntax option anyway
+ if (m_caseSensitivity == CaseSensitive::Choice::No) {
+ flags |= std::regex::icase;
+ }
+ auto reg = std::regex(m_regex, flags);
+ return std::regex_match(matchee, reg);
+ }
+
+ std::string RegexMatcher::describe() const {
+ return "matches " + ::Catch::Detail::stringify(m_regex) + ((m_caseSensitivity == CaseSensitive::Choice::Yes)? " case sensitively" : " case insensitively");
+ }
+
+ } // namespace StdString
+
+ StdString::EqualsMatcher Equals( std::string const& str, CaseSensitive::Choice caseSensitivity ) {
+ return StdString::EqualsMatcher( StdString::CasedString( str, caseSensitivity) );
+ }
+ StdString::ContainsMatcher Contains( std::string const& str, CaseSensitive::Choice caseSensitivity ) {
+ return StdString::ContainsMatcher( StdString::CasedString( str, caseSensitivity) );
+ }
+ StdString::EndsWithMatcher EndsWith( std::string const& str, CaseSensitive::Choice caseSensitivity ) {
+ return StdString::EndsWithMatcher( StdString::CasedString( str, caseSensitivity) );
+ }
+ StdString::StartsWithMatcher StartsWith( std::string const& str, CaseSensitive::Choice caseSensitivity ) {
+ return StdString::StartsWithMatcher( StdString::CasedString( str, caseSensitivity) );
+ }
+
+ StdString::RegexMatcher Matches(std::string const& regex, CaseSensitive::Choice caseSensitivity) {
+ return StdString::RegexMatcher(regex, caseSensitivity);
+ }
+
+} // namespace Matchers
+} // namespace Catch
+// end catch_matchers_string.cpp
+// start catch_message.cpp
+
+// start catch_uncaught_exceptions.h
+
+namespace Catch {
+ bool uncaught_exceptions();
+} // end namespace Catch
+
+// end catch_uncaught_exceptions.h
+#include <cassert>
+#include <stack>
+
+namespace Catch {
+
+ MessageInfo::MessageInfo( StringRef const& _macroName,
+ SourceLineInfo const& _lineInfo,
+ ResultWas::OfType _type )
+ : macroName( _macroName ),
+ lineInfo( _lineInfo ),
+ type( _type ),
+ sequence( ++globalCount )
+ {}
+
+ bool MessageInfo::operator==( MessageInfo const& other ) const {
+ return sequence == other.sequence;
+ }
+
+ bool MessageInfo::operator<( MessageInfo const& other ) const {
+ return sequence < other.sequence;
+ }
+
+ // This may need protecting if threading support is added
+ unsigned int MessageInfo::globalCount = 0;
+
+ ////////////////////////////////////////////////////////////////////////////
+
+ Catch::MessageBuilder::MessageBuilder( StringRef const& macroName,
+ SourceLineInfo const& lineInfo,
+ ResultWas::OfType type )
+ :m_info(macroName, lineInfo, type) {}
+
+ ////////////////////////////////////////////////////////////////////////////
+
+ ScopedMessage::ScopedMessage( MessageBuilder const& builder )
+ : m_info( builder.m_info ), m_moved()
+ {
+ m_info.message = builder.m_stream.str();
+ getResultCapture().pushScopedMessage( m_info );
+ }
+
+ ScopedMessage::ScopedMessage( ScopedMessage&& old )
+ : m_info( old.m_info ), m_moved()
+ {
+ old.m_moved = true;
+ }
+
+ ScopedMessage::~ScopedMessage() {
+ if ( !uncaught_exceptions() && !m_moved ){
+ getResultCapture().popScopedMessage(m_info);
+ }
+ }
+
+ Capturer::Capturer( StringRef macroName, SourceLineInfo const& lineInfo, ResultWas::OfType resultType, StringRef names ) {
+ auto trimmed = [&] (size_t start, size_t end) {
+ while (names[start] == ',' || isspace(static_cast<unsigned char>(names[start]))) {
+ ++start;
+ }
+ while (names[end] == ',' || isspace(static_cast<unsigned char>(names[end]))) {
+ --end;
+ }
+ return names.substr(start, end - start + 1);
+ };
+ auto skipq = [&] (size_t start, char quote) {
+ for (auto i = start + 1; i < names.size() ; ++i) {
+ if (names[i] == quote)
+ return i;
+ if (names[i] == '\\')
+ ++i;
+ }
+ CATCH_INTERNAL_ERROR("CAPTURE parsing encountered unmatched quote");
+ };
+
+ size_t start = 0;
+ std::stack<char> openings;
+ for (size_t pos = 0; pos < names.size(); ++pos) {
+ char c = names[pos];
+ switch (c) {
+ case '[':
+ case '{':
+ case '(':
+ // It is basically impossible to disambiguate between
+ // comparison and start of template args in this context
+// case '<':
+ openings.push(c);
+ break;
+ case ']':
+ case '}':
+ case ')':
+// case '>':
+ openings.pop();
+ break;
+ case '"':
+ case '\'':
+ pos = skipq(pos, c);
+ break;
+ case ',':
+ if (start != pos && openings.empty()) {
+ m_messages.emplace_back(macroName, lineInfo, resultType);
+ m_messages.back().message = static_cast<std::string>(trimmed(start, pos));
+ m_messages.back().message += " := ";
+ start = pos;
+ }
+ }
+ }
+ assert(openings.empty() && "Mismatched openings");
+ m_messages.emplace_back(macroName, lineInfo, resultType);
+ m_messages.back().message = static_cast<std::string>(trimmed(start, names.size() - 1));
+ m_messages.back().message += " := ";
+ }
+ Capturer::~Capturer() {
+ if ( !uncaught_exceptions() ){
+ assert( m_captured == m_messages.size() );
+ for( size_t i = 0; i < m_captured; ++i )
+ m_resultCapture.popScopedMessage( m_messages[i] );
+ }
+ }
+
+ void Capturer::captureValue( size_t index, std::string const& value ) {
+ assert( index < m_messages.size() );
+ m_messages[index].message += value;
+ m_resultCapture.pushScopedMessage( m_messages[index] );
+ m_captured++;
+ }
+
+} // end namespace Catch
+// end catch_message.cpp
+// start catch_output_redirect.cpp
+
+// start catch_output_redirect.h
+#ifndef TWOBLUECUBES_CATCH_OUTPUT_REDIRECT_H
+#define TWOBLUECUBES_CATCH_OUTPUT_REDIRECT_H
+
+#include <cstdio>
+#include <iosfwd>
+#include <string>
+
+namespace Catch {
+
+ class RedirectedStream {
+ std::ostream& m_originalStream;
+ std::ostream& m_redirectionStream;
+ std::streambuf* m_prevBuf;
+
+ public:
+ RedirectedStream( std::ostream& originalStream, std::ostream& redirectionStream );
+ ~RedirectedStream();
+ };
+
+ class RedirectedStdOut {
+ ReusableStringStream m_rss;
+ RedirectedStream m_cout;
+ public:
+ RedirectedStdOut();
+ auto str() const -> std::string;
+ };
+
+ // StdErr has two constituent streams in C++, std::cerr and std::clog
+ // This means that we need to redirect 2 streams into 1 to keep proper
+ // order of writes
+ class RedirectedStdErr {
+ ReusableStringStream m_rss;
+ RedirectedStream m_cerr;
+ RedirectedStream m_clog;
+ public:
+ RedirectedStdErr();
+ auto str() const -> std::string;
+ };
+
+ class RedirectedStreams {
+ public:
+ RedirectedStreams(RedirectedStreams const&) = delete;
+ RedirectedStreams& operator=(RedirectedStreams const&) = delete;
+ RedirectedStreams(RedirectedStreams&&) = delete;
+ RedirectedStreams& operator=(RedirectedStreams&&) = delete;
+
+ RedirectedStreams(std::string& redirectedCout, std::string& redirectedCerr);
+ ~RedirectedStreams();
+ private:
+ std::string& m_redirectedCout;
+ std::string& m_redirectedCerr;
+ RedirectedStdOut m_redirectedStdOut;
+ RedirectedStdErr m_redirectedStdErr;
+ };
+
+#if defined(CATCH_CONFIG_NEW_CAPTURE)
+
+ // Windows's implementation of std::tmpfile is terrible (it tries
+ // to create a file inside system folder, thus requiring elevated
+ // privileges for the binary), so we have to use tmpnam(_s) and
+ // create the file ourselves there.
+ class TempFile {
+ public:
+ TempFile(TempFile const&) = delete;
+ TempFile& operator=(TempFile const&) = delete;
+ TempFile(TempFile&&) = delete;
+ TempFile& operator=(TempFile&&) = delete;
+
+ TempFile();
+ ~TempFile();
+
+ std::FILE* getFile();
+ std::string getContents();
+
+ private:
+ std::FILE* m_file = nullptr;
+ #if defined(_MSC_VER)
+ char m_buffer[L_tmpnam] = { 0 };
+ #endif
+ };
+
+ class OutputRedirect {
+ public:
+ OutputRedirect(OutputRedirect const&) = delete;
+ OutputRedirect& operator=(OutputRedirect const&) = delete;
+ OutputRedirect(OutputRedirect&&) = delete;
+ OutputRedirect& operator=(OutputRedirect&&) = delete;
+
+ OutputRedirect(std::string& stdout_dest, std::string& stderr_dest);
+ ~OutputRedirect();
+
+ private:
+ int m_originalStdout = -1;
+ int m_originalStderr = -1;
+ TempFile m_stdoutFile;
+ TempFile m_stderrFile;
+ std::string& m_stdoutDest;
+ std::string& m_stderrDest;
+ };
+
+#endif
+
+} // end namespace Catch
+
+#endif // TWOBLUECUBES_CATCH_OUTPUT_REDIRECT_H
+// end catch_output_redirect.h
+#include <cstdio>
+#include <cstring>
+#include <fstream>
+#include <sstream>
+#include <stdexcept>
+
+#if defined(CATCH_CONFIG_NEW_CAPTURE)
+ #if defined(_MSC_VER)
+ #include <io.h> //_dup and _dup2
+ #define dup _dup
+ #define dup2 _dup2
+ #define fileno _fileno
+ #else
+ #include <unistd.h> // dup and dup2
+ #endif
+#endif
+
+namespace Catch {
+
+ RedirectedStream::RedirectedStream( std::ostream& originalStream, std::ostream& redirectionStream )
+ : m_originalStream( originalStream ),
+ m_redirectionStream( redirectionStream ),
+ m_prevBuf( m_originalStream.rdbuf() )
+ {
+ m_originalStream.rdbuf( m_redirectionStream.rdbuf() );
+ }
+
+ RedirectedStream::~RedirectedStream() {
+ m_originalStream.rdbuf( m_prevBuf );
+ }
+
+ RedirectedStdOut::RedirectedStdOut() : m_cout( Catch::cout(), m_rss.get() ) {}
+ auto RedirectedStdOut::str() const -> std::string { return m_rss.str(); }
+
+ RedirectedStdErr::RedirectedStdErr()
+ : m_cerr( Catch::cerr(), m_rss.get() ),
+ m_clog( Catch::clog(), m_rss.get() )
+ {}
+ auto RedirectedStdErr::str() const -> std::string { return m_rss.str(); }
+
+ RedirectedStreams::RedirectedStreams(std::string& redirectedCout, std::string& redirectedCerr)
+ : m_redirectedCout(redirectedCout),
+ m_redirectedCerr(redirectedCerr)
+ {}
+
+ RedirectedStreams::~RedirectedStreams() {
+ m_redirectedCout += m_redirectedStdOut.str();
+ m_redirectedCerr += m_redirectedStdErr.str();
+ }
+
+#if defined(CATCH_CONFIG_NEW_CAPTURE)
+
+#if defined(_MSC_VER)
+ TempFile::TempFile() {
+ if (tmpnam_s(m_buffer)) {
+ CATCH_RUNTIME_ERROR("Could not get a temp filename");
+ }
+ if (fopen_s(&m_file, m_buffer, "w+")) {
+ char buffer[100];
+ if (strerror_s(buffer, errno)) {
+ CATCH_RUNTIME_ERROR("Could not translate errno to a string");
+ }
+ CATCH_RUNTIME_ERROR("Could not open the temp file: '" << m_buffer << "' because: " << buffer);
+ }
+ }
+#else
+ TempFile::TempFile() {
+ m_file = std::tmpfile();
+ if (!m_file) {
+ CATCH_RUNTIME_ERROR("Could not create a temp file.");
+ }
+ }
+
+#endif
+
+ TempFile::~TempFile() {
+ // TBD: What to do about errors here?
+ std::fclose(m_file);
+ // We manually create the file on Windows only, on Linux
+ // it will be autodeleted
+#if defined(_MSC_VER)
+ std::remove(m_buffer);
+#endif
+ }
+
+ FILE* TempFile::getFile() {
+ return m_file;
+ }
+
+ std::string TempFile::getContents() {
+ std::stringstream sstr;
+ char buffer[100] = {};
+ std::rewind(m_file);
+ while (std::fgets(buffer, sizeof(buffer), m_file)) {
+ sstr << buffer;
+ }
+ return sstr.str();
+ }
+
+ OutputRedirect::OutputRedirect(std::string& stdout_dest, std::string& stderr_dest) :
+ m_originalStdout(dup(1)),
+ m_originalStderr(dup(2)),
+ m_stdoutDest(stdout_dest),
+ m_stderrDest(stderr_dest) {
+ dup2(fileno(m_stdoutFile.getFile()), 1);
+ dup2(fileno(m_stderrFile.getFile()), 2);
+ }
+
+ OutputRedirect::~OutputRedirect() {
+ Catch::cout() << std::flush;
+ fflush(stdout);
+ // Since we support overriding these streams, we flush cerr
+ // even though std::cerr is unbuffered
+ Catch::cerr() << std::flush;
+ Catch::clog() << std::flush;
+ fflush(stderr);
+
+ dup2(m_originalStdout, 1);
+ dup2(m_originalStderr, 2);
+
+ m_stdoutDest += m_stdoutFile.getContents();
+ m_stderrDest += m_stderrFile.getContents();
+ }
+
+#endif // CATCH_CONFIG_NEW_CAPTURE
+
+} // namespace Catch
+
+#if defined(CATCH_CONFIG_NEW_CAPTURE)
+ #if defined(_MSC_VER)
+ #undef dup
+ #undef dup2
+ #undef fileno
+ #endif
+#endif
+// end catch_output_redirect.cpp
+// start catch_polyfills.cpp
+
+#include <cmath>
+
+namespace Catch {
+
+#if !defined(CATCH_CONFIG_POLYFILL_ISNAN)
+ bool isnan(float f) {
+ return std::isnan(f);
+ }
+ bool isnan(double d) {
+ return std::isnan(d);
+ }
+#else
+ // For now we only use this for embarcadero
+ bool isnan(float f) {
+ return std::_isnan(f);
+ }
+ bool isnan(double d) {
+ return std::_isnan(d);
+ }
+#endif
+
+} // end namespace Catch
+// end catch_polyfills.cpp
+// start catch_random_number_generator.cpp
+
+namespace Catch {
+
+namespace {
+
+#if defined(_MSC_VER)
+#pragma warning(push)
+#pragma warning(disable:4146) // we negate uint32 during the rotate
+#endif
+ // Safe rotr implementation thanks to John Regehr
+ uint32_t rotate_right(uint32_t val, uint32_t count) {
+ const uint32_t mask = 31;
+ count &= mask;
+ return (val >> count) | (val << (-count & mask));
+ }
+
+#if defined(_MSC_VER)
+#pragma warning(pop)
+#endif
+
+}
+
+ SimplePcg32::SimplePcg32(result_type seed_) {
+ seed(seed_);
+ }
+
+ void SimplePcg32::seed(result_type seed_) {
+ m_state = 0;
+ (*this)();
+ m_state += seed_;
+ (*this)();
+ }
+
+ void SimplePcg32::discard(uint64_t skip) {
+ // We could implement this to run in O(log n) steps, but this
+ // should suffice for our use case.
+ for (uint64_t s = 0; s < skip; ++s) {
+ static_cast<void>((*this)());
+ }
+ }
+
+ SimplePcg32::result_type SimplePcg32::operator()() {
+ // prepare the output value
+ const uint32_t xorshifted = static_cast<uint32_t>(((m_state >> 18u) ^ m_state) >> 27u);
+ const auto output = rotate_right(xorshifted, m_state >> 59u);
+
+ // advance state
+ m_state = m_state * 6364136223846793005ULL + s_inc;
+
+ return output;
+ }
+
+ bool operator==(SimplePcg32 const& lhs, SimplePcg32 const& rhs) {
+ return lhs.m_state == rhs.m_state;
+ }
+
+ bool operator!=(SimplePcg32 const& lhs, SimplePcg32 const& rhs) {
+ return lhs.m_state != rhs.m_state;
+ }
+}
+// end catch_random_number_generator.cpp
+// start catch_registry_hub.cpp
+
+// start catch_test_case_registry_impl.h
+
+#include <vector>
+#include <set>
+#include <algorithm>
+#include <ios>
+
+namespace Catch {
+
+ class TestCase;
+ struct IConfig;
+
+ std::vector<TestCase> sortTests( IConfig const& config, std::vector<TestCase> const& unsortedTestCases );
+
+ bool isThrowSafe( TestCase const& testCase, IConfig const& config );
+ bool matchTest( TestCase const& testCase, TestSpec const& testSpec, IConfig const& config );
+
+ void enforceNoDuplicateTestCases( std::vector<TestCase> const& functions );
+
+ std::vector<TestCase> filterTests( std::vector<TestCase> const& testCases, TestSpec const& testSpec, IConfig const& config );
+ std::vector<TestCase> const& getAllTestCasesSorted( IConfig const& config );
+
+ class TestRegistry : public ITestCaseRegistry {
+ public:
+ virtual ~TestRegistry() = default;
+
+ virtual void registerTest( TestCase const& testCase );
+
+ std::vector<TestCase> const& getAllTests() const override;
+ std::vector<TestCase> const& getAllTestsSorted( IConfig const& config ) const override;
+
+ private:
+ std::vector<TestCase> m_functions;
+ mutable RunTests::InWhatOrder m_currentSortOrder = RunTests::InDeclarationOrder;
+ mutable std::vector<TestCase> m_sortedFunctions;
+ std::size_t m_unnamedCount = 0;
+ std::ios_base::Init m_ostreamInit; // Forces cout/ cerr to be initialised
+ };
+
+ ///////////////////////////////////////////////////////////////////////////
+
+ class TestInvokerAsFunction : public ITestInvoker {
+ void(*m_testAsFunction)();
+ public:
+ TestInvokerAsFunction( void(*testAsFunction)() ) noexcept;
+
+ void invoke() const override;
+ };
+
+ std::string extractClassName( StringRef const& classOrQualifiedMethodName );
+
+ ///////////////////////////////////////////////////////////////////////////
+
+} // end namespace Catch
+
+// end catch_test_case_registry_impl.h
+// start catch_reporter_registry.h
+
+#include <map>
+
+namespace Catch {
+
+ class ReporterRegistry : public IReporterRegistry {
+
+ public:
+
+ ~ReporterRegistry() override;
+
+ IStreamingReporterPtr create( std::string const& name, IConfigPtr const& config ) const override;
+
+ void registerReporter( std::string const& name, IReporterFactoryPtr const& factory );
+ void registerListener( IReporterFactoryPtr const& factory );
+
+ FactoryMap const& getFactories() const override;
+ Listeners const& getListeners() const override;
+
+ private:
+ FactoryMap m_factories;
+ Listeners m_listeners;
+ };
+}
+
+// end catch_reporter_registry.h
+// start catch_tag_alias_registry.h
+
+// start catch_tag_alias.h
+
+#include <string>
+
+namespace Catch {
+
+ struct TagAlias {
+ TagAlias(std::string const& _tag, SourceLineInfo _lineInfo);
+
+ std::string tag;
+ SourceLineInfo lineInfo;
+ };
+
+} // end namespace Catch
+
+// end catch_tag_alias.h
+#include <map>
+
+namespace Catch {
+
+ class TagAliasRegistry : public ITagAliasRegistry {
+ public:
+ ~TagAliasRegistry() override;
+ TagAlias const* find( std::string const& alias ) const override;
+ std::string expandAliases( std::string const& unexpandedTestSpec ) const override;
+ void add( std::string const& alias, std::string const& tag, SourceLineInfo const& lineInfo );
+
+ private:
+ std::map<std::string, TagAlias> m_registry;
+ };
+
+} // end namespace Catch
+
+// end catch_tag_alias_registry.h
+// start catch_startup_exception_registry.h
+
+#include <vector>
+#include <exception>
+
+namespace Catch {
+
+ class StartupExceptionRegistry {
+#if !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS)
+ public:
+ void add(std::exception_ptr const& exception) noexcept;
+ std::vector<std::exception_ptr> const& getExceptions() const noexcept;
+ private:
+ std::vector<std::exception_ptr> m_exceptions;
+#endif
+ };
+
+} // end namespace Catch
+
+// end catch_startup_exception_registry.h
+// start catch_singletons.hpp
+
+namespace Catch {
+
+ struct ISingleton {
+ virtual ~ISingleton();
+ };
+
+ void addSingleton( ISingleton* singleton );
+ void cleanupSingletons();
+
+ template<typename SingletonImplT, typename InterfaceT = SingletonImplT, typename MutableInterfaceT = InterfaceT>
+ class Singleton : SingletonImplT, public ISingleton {
+
+ static auto getInternal() -> Singleton* {
+ static Singleton* s_instance = nullptr;
+ if( !s_instance ) {
+ s_instance = new Singleton;
+ addSingleton( s_instance );
+ }
+ return s_instance;
+ }
+
+ public:
+ static auto get() -> InterfaceT const& {
+ return *getInternal();
+ }
+ static auto getMutable() -> MutableInterfaceT& {
+ return *getInternal();
+ }
+ };
+
+} // namespace Catch
+
+// end catch_singletons.hpp
+namespace Catch {
+
+ namespace {
+
+ class RegistryHub : public IRegistryHub, public IMutableRegistryHub,
+ private NonCopyable {
+
+ public: // IRegistryHub
+ RegistryHub() = default;
+ IReporterRegistry const& getReporterRegistry() const override {
+ return m_reporterRegistry;
+ }
+ ITestCaseRegistry const& getTestCaseRegistry() const override {
+ return m_testCaseRegistry;
+ }
+ IExceptionTranslatorRegistry const& getExceptionTranslatorRegistry() const override {
+ return m_exceptionTranslatorRegistry;
+ }
+ ITagAliasRegistry const& getTagAliasRegistry() const override {
+ return m_tagAliasRegistry;
+ }
+ StartupExceptionRegistry const& getStartupExceptionRegistry() const override {
+ return m_exceptionRegistry;
+ }
+
+ public: // IMutableRegistryHub
+ void registerReporter( std::string const& name, IReporterFactoryPtr const& factory ) override {
+ m_reporterRegistry.registerReporter( name, factory );
+ }
+ void registerListener( IReporterFactoryPtr const& factory ) override {
+ m_reporterRegistry.registerListener( factory );
+ }
+ void registerTest( TestCase const& testInfo ) override {
+ m_testCaseRegistry.registerTest( testInfo );
+ }
+ void registerTranslator( const IExceptionTranslator* translator ) override {
+ m_exceptionTranslatorRegistry.registerTranslator( translator );
+ }
+ void registerTagAlias( std::string const& alias, std::string const& tag, SourceLineInfo const& lineInfo ) override {
+ m_tagAliasRegistry.add( alias, tag, lineInfo );
+ }
+ void registerStartupException() noexcept override {
+#if !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS)
+ m_exceptionRegistry.add(std::current_exception());
+#else
+ CATCH_INTERNAL_ERROR("Attempted to register active exception under CATCH_CONFIG_DISABLE_EXCEPTIONS!");
+#endif
+ }
+ IMutableEnumValuesRegistry& getMutableEnumValuesRegistry() override {
+ return m_enumValuesRegistry;
+ }
+
+ private:
+ TestRegistry m_testCaseRegistry;
+ ReporterRegistry m_reporterRegistry;
+ ExceptionTranslatorRegistry m_exceptionTranslatorRegistry;
+ TagAliasRegistry m_tagAliasRegistry;
+ StartupExceptionRegistry m_exceptionRegistry;
+ Detail::EnumValuesRegistry m_enumValuesRegistry;
+ };
+ }
+
+ using RegistryHubSingleton = Singleton<RegistryHub, IRegistryHub, IMutableRegistryHub>;
+
+ IRegistryHub const& getRegistryHub() {
+ return RegistryHubSingleton::get();
+ }
+ IMutableRegistryHub& getMutableRegistryHub() {
+ return RegistryHubSingleton::getMutable();
+ }
+ void cleanUp() {
+ cleanupSingletons();
+ cleanUpContext();
+ }
+ std::string translateActiveException() {
+ return getRegistryHub().getExceptionTranslatorRegistry().translateActiveException();
+ }
+
+} // end namespace Catch
+// end catch_registry_hub.cpp
+// start catch_reporter_registry.cpp
+
+namespace Catch {
+
+ ReporterRegistry::~ReporterRegistry() = default;
+
+ IStreamingReporterPtr ReporterRegistry::create( std::string const& name, IConfigPtr const& config ) const {
+ auto it = m_factories.find( name );
+ if( it == m_factories.end() )
+ return nullptr;
+ return it->second->create( ReporterConfig( config ) );
+ }
+
+ void ReporterRegistry::registerReporter( std::string const& name, IReporterFactoryPtr const& factory ) {
+ m_factories.emplace(name, factory);
+ }
+ void ReporterRegistry::registerListener( IReporterFactoryPtr const& factory ) {
+ m_listeners.push_back( factory );
+ }
+
+ IReporterRegistry::FactoryMap const& ReporterRegistry::getFactories() const {
+ return m_factories;
+ }
+ IReporterRegistry::Listeners const& ReporterRegistry::getListeners() const {
+ return m_listeners;
+ }
+
+}
+// end catch_reporter_registry.cpp
+// start catch_result_type.cpp
+
+namespace Catch {
+
+ bool isOk( ResultWas::OfType resultType ) {
+ return ( resultType & ResultWas::FailureBit ) == 0;
+ }
+ bool isJustInfo( int flags ) {
+ return flags == ResultWas::Info;
+ }
+
+ ResultDisposition::Flags operator | ( ResultDisposition::Flags lhs, ResultDisposition::Flags rhs ) {
+ return static_cast<ResultDisposition::Flags>( static_cast<int>( lhs ) | static_cast<int>( rhs ) );
+ }
+
+ bool shouldContinueOnFailure( int flags ) { return ( flags & ResultDisposition::ContinueOnFailure ) != 0; }
+ bool shouldSuppressFailure( int flags ) { return ( flags & ResultDisposition::SuppressFail ) != 0; }
+
+} // end namespace Catch
+// end catch_result_type.cpp
+// start catch_run_context.cpp
+
+#include <cassert>
+#include <algorithm>
+#include <sstream>
+
+namespace Catch {
+
+ namespace Generators {
+ struct GeneratorTracker : TestCaseTracking::TrackerBase, IGeneratorTracker {
+ GeneratorBasePtr m_generator;
+
+ GeneratorTracker( TestCaseTracking::NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent )
+ : TrackerBase( nameAndLocation, ctx, parent )
+ {}
+ ~GeneratorTracker();
+
+ static GeneratorTracker& acquire( TrackerContext& ctx, TestCaseTracking::NameAndLocation const& nameAndLocation ) {
+ std::shared_ptr<GeneratorTracker> tracker;
+
+ ITracker& currentTracker = ctx.currentTracker();
+ // Under specific circumstances, the generator we want
+ // to acquire is also the current tracker. If this is
+ // the case, we have to avoid looking through current
+ // tracker's children, and instead return the current
+ // tracker.
+ // A case where this check is important is e.g.
+ // for (int i = 0; i < 5; ++i) {
+ // int n = GENERATE(1, 2);
+ // }
+ //
+ // without it, the code above creates 5 nested generators.
+ if (currentTracker.nameAndLocation() == nameAndLocation) {
+ auto thisTracker = currentTracker.parent().findChild(nameAndLocation);
+ assert(thisTracker);
+ assert(thisTracker->isGeneratorTracker());
+ tracker = std::static_pointer_cast<GeneratorTracker>(thisTracker);
+ } else if ( TestCaseTracking::ITrackerPtr childTracker = currentTracker.findChild( nameAndLocation ) ) {
+ assert( childTracker );
+ assert( childTracker->isGeneratorTracker() );
+ tracker = std::static_pointer_cast<GeneratorTracker>( childTracker );
+ } else {
+ tracker = std::make_shared<GeneratorTracker>( nameAndLocation, ctx, &currentTracker );
+ currentTracker.addChild( tracker );
+ }
+
+ if( !tracker->isComplete() ) {
+ tracker->open();
+ }
+
+ return *tracker;
+ }
+
+ // TrackerBase interface
+ bool isGeneratorTracker() const override { return true; }
+ auto hasGenerator() const -> bool override {
+ return !!m_generator;
+ }
+ void close() override {
+ TrackerBase::close();
+ // If a generator has a child (it is followed by a section)
+ // and none of its children have started, then we must wait
+ // until later to start consuming its values.
+ // This catches cases where `GENERATE` is placed between two
+ // `SECTION`s.
+ // **The check for m_children.empty cannot be removed**.
+ // doing so would break `GENERATE` _not_ followed by `SECTION`s.
+ const bool should_wait_for_child = [&]() {
+ // No children -> nobody to wait for
+ if ( m_children.empty() ) {
+ return false;
+ }
+ // If at least one child started executing, don't wait
+ if ( std::find_if(
+ m_children.begin(),
+ m_children.end(),
+ []( TestCaseTracking::ITrackerPtr tracker ) {
+ return tracker->hasStarted();
+ } ) != m_children.end() ) {
+ return false;
+ }
+
+ // No children have started. We need to check if they _can_
+ // start, and thus we should wait for them, or they cannot
+ // start (due to filters), and we shouldn't wait for them
+ auto* parent = m_parent;
+ // This is safe: there is always at least one section
+ // tracker in a test case tracking tree
+ while ( !parent->isSectionTracker() ) {
+ parent = &( parent->parent() );
+ }
+ assert( parent &&
+ "Missing root (test case) level section" );
+
+ auto const& parentSection =
+ static_cast<SectionTracker&>( *parent );
+ auto const& filters = parentSection.getFilters();
+ // No filters -> no restrictions on running sections
+ if ( filters.empty() ) {
+ return true;
+ }
+
+ for ( auto const& child : m_children ) {
+ if ( child->isSectionTracker() &&
+ std::find( filters.begin(),
+ filters.end(),
+ static_cast<SectionTracker&>( *child )
+ .trimmedName() ) !=
+ filters.end() ) {
+ return true;
+ }
+ }
+ return false;
+ }();
+
+ // This check is a bit tricky, because m_generator->next()
+ // has a side-effect, where it consumes generator's current
+ // value, but we do not want to invoke the side-effect if
+ // this generator is still waiting for any child to start.
+ if ( should_wait_for_child ||
+ ( m_runState == CompletedSuccessfully &&
+ m_generator->next() ) ) {
+ m_children.clear();
+ m_runState = Executing;
+ }
+ }
+
+ // IGeneratorTracker interface
+ auto getGenerator() const -> GeneratorBasePtr const& override {
+ return m_generator;
+ }
+ void setGenerator( GeneratorBasePtr&& generator ) override {
+ m_generator = std::move( generator );
+ }
+ };
+ GeneratorTracker::~GeneratorTracker() {}
+ }
+
+ RunContext::RunContext(IConfigPtr const& _config, IStreamingReporterPtr&& reporter)
+ : m_runInfo(_config->name()),
+ m_context(getCurrentMutableContext()),
+ m_config(_config),
+ m_reporter(std::move(reporter)),
+ m_lastAssertionInfo{ StringRef(), SourceLineInfo("",0), StringRef(), ResultDisposition::Normal },
+ m_includeSuccessfulResults( m_config->includeSuccessfulResults() || m_reporter->getPreferences().shouldReportAllAssertions )
+ {
+ m_context.setRunner(this);
+ m_context.setConfig(m_config);
+ m_context.setResultCapture(this);
+ m_reporter->testRunStarting(m_runInfo);
+ }
+
+ RunContext::~RunContext() {
+ m_reporter->testRunEnded(TestRunStats(m_runInfo, m_totals, aborting()));
+ }
+
+ void RunContext::testGroupStarting(std::string const& testSpec, std::size_t groupIndex, std::size_t groupsCount) {
+ m_reporter->testGroupStarting(GroupInfo(testSpec, groupIndex, groupsCount));
+ }
+
+ void RunContext::testGroupEnded(std::string const& testSpec, Totals const& totals, std::size_t groupIndex, std::size_t groupsCount) {
+ m_reporter->testGroupEnded(TestGroupStats(GroupInfo(testSpec, groupIndex, groupsCount), totals, aborting()));
+ }
+
+ Totals RunContext::runTest(TestCase const& testCase) {
+ Totals prevTotals = m_totals;
+
+ std::string redirectedCout;
+ std::string redirectedCerr;
+
+ auto const& testInfo = testCase.getTestCaseInfo();
+
+ m_reporter->testCaseStarting(testInfo);
+
+ m_activeTestCase = &testCase;
+
+ ITracker& rootTracker = m_trackerContext.startRun();
+ assert(rootTracker.isSectionTracker());
+ static_cast<SectionTracker&>(rootTracker).addInitialFilters(m_config->getSectionsToRun());
+ do {
+ m_trackerContext.startCycle();
+ m_testCaseTracker = &SectionTracker::acquire(m_trackerContext, TestCaseTracking::NameAndLocation(testInfo.name, testInfo.lineInfo));
+ runCurrentTest(redirectedCout, redirectedCerr);
+ } while (!m_testCaseTracker->isSuccessfullyCompleted() && !aborting());
+
+ Totals deltaTotals = m_totals.delta(prevTotals);
+ if (testInfo.expectedToFail() && deltaTotals.testCases.passed > 0) {
+ deltaTotals.assertions.failed++;
+ deltaTotals.testCases.passed--;
+ deltaTotals.testCases.failed++;
+ }
+ m_totals.testCases += deltaTotals.testCases;
+ m_reporter->testCaseEnded(TestCaseStats(testInfo,
+ deltaTotals,
+ redirectedCout,
+ redirectedCerr,
+ aborting()));
+
+ m_activeTestCase = nullptr;
+ m_testCaseTracker = nullptr;
+
+ return deltaTotals;
+ }
+
+ IConfigPtr RunContext::config() const {
+ return m_config;
+ }
+
+ IStreamingReporter& RunContext::reporter() const {
+ return *m_reporter;
+ }
+
+ void RunContext::assertionEnded(AssertionResult const & result) {
+ if (result.getResultType() == ResultWas::Ok) {
+ m_totals.assertions.passed++;
+ m_lastAssertionPassed = true;
+ } else if (!result.isOk()) {
+ m_lastAssertionPassed = false;
+ if( m_activeTestCase->getTestCaseInfo().okToFail() )
+ m_totals.assertions.failedButOk++;
+ else
+ m_totals.assertions.failed++;
+ }
+ else {
+ m_lastAssertionPassed = true;
+ }
+
+ // We have no use for the return value (whether messages should be cleared), because messages were made scoped
+ // and should be let to clear themselves out.
+ static_cast<void>(m_reporter->assertionEnded(AssertionStats(result, m_messages, m_totals)));
+
+ if (result.getResultType() != ResultWas::Warning)
+ m_messageScopes.clear();
+
+ // Reset working state
+ resetAssertionInfo();
+ m_lastResult = result;
+ }
+ void RunContext::resetAssertionInfo() {
+ m_lastAssertionInfo.macroName = StringRef();
+ m_lastAssertionInfo.capturedExpression = "{Unknown expression after the reported line}"_sr;
+ }
+
+ bool RunContext::sectionStarted(SectionInfo const & sectionInfo, Counts & assertions) {
+ ITracker& sectionTracker = SectionTracker::acquire(m_trackerContext, TestCaseTracking::NameAndLocation(sectionInfo.name, sectionInfo.lineInfo));
+ if (!sectionTracker.isOpen())
+ return false;
+ m_activeSections.push_back(&sectionTracker);
+
+ m_lastAssertionInfo.lineInfo = sectionInfo.lineInfo;
+
+ m_reporter->sectionStarting(sectionInfo);
+
+ assertions = m_totals.assertions;
+
+ return true;
+ }
+ auto RunContext::acquireGeneratorTracker( StringRef generatorName, SourceLineInfo const& lineInfo ) -> IGeneratorTracker& {
+ using namespace Generators;
+ GeneratorTracker& tracker = GeneratorTracker::acquire(m_trackerContext,
+ TestCaseTracking::NameAndLocation( static_cast<std::string>(generatorName), lineInfo ) );
+ m_lastAssertionInfo.lineInfo = lineInfo;
+ return tracker;
+ }
+
+ bool RunContext::testForMissingAssertions(Counts& assertions) {
+ if (assertions.total() != 0)
+ return false;
+ if (!m_config->warnAboutMissingAssertions())
+ return false;
+ if (m_trackerContext.currentTracker().hasChildren())
+ return false;
+ m_totals.assertions.failed++;
+ assertions.failed++;
+ return true;
+ }
+
+ void RunContext::sectionEnded(SectionEndInfo const & endInfo) {
+ Counts assertions = m_totals.assertions - endInfo.prevAssertions;
+ bool missingAssertions = testForMissingAssertions(assertions);
+
+ if (!m_activeSections.empty()) {
+ m_activeSections.back()->close();
+ m_activeSections.pop_back();
+ }
+
+ m_reporter->sectionEnded(SectionStats(endInfo.sectionInfo, assertions, endInfo.durationInSeconds, missingAssertions));
+ m_messages.clear();
+ m_messageScopes.clear();
+ }
+
+ void RunContext::sectionEndedEarly(SectionEndInfo const & endInfo) {
+ if (m_unfinishedSections.empty())
+ m_activeSections.back()->fail();
+ else
+ m_activeSections.back()->close();
+ m_activeSections.pop_back();
+
+ m_unfinishedSections.push_back(endInfo);
+ }
+
+#if defined(CATCH_CONFIG_ENABLE_BENCHMARKING)
+ void RunContext::benchmarkPreparing(std::string const& name) {
+ m_reporter->benchmarkPreparing(name);
+ }
+ void RunContext::benchmarkStarting( BenchmarkInfo const& info ) {
+ m_reporter->benchmarkStarting( info );
+ }
+ void RunContext::benchmarkEnded( BenchmarkStats<> const& stats ) {
+ m_reporter->benchmarkEnded( stats );
+ }
+ void RunContext::benchmarkFailed(std::string const & error) {
+ m_reporter->benchmarkFailed(error);
+ }
+#endif // CATCH_CONFIG_ENABLE_BENCHMARKING
+
+ void RunContext::pushScopedMessage(MessageInfo const & message) {
+ m_messages.push_back(message);
+ }
+
+ void RunContext::popScopedMessage(MessageInfo const & message) {
+ m_messages.erase(std::remove(m_messages.begin(), m_messages.end(), message), m_messages.end());
+ }
+
+ void RunContext::emplaceUnscopedMessage( MessageBuilder const& builder ) {
+ m_messageScopes.emplace_back( builder );
+ }
+
+ std::string RunContext::getCurrentTestName() const {
+ return m_activeTestCase
+ ? m_activeTestCase->getTestCaseInfo().name
+ : std::string();
+ }
+
+ const AssertionResult * RunContext::getLastResult() const {
+ return &(*m_lastResult);
+ }
+
+ void RunContext::exceptionEarlyReported() {
+ m_shouldReportUnexpected = false;
+ }
+
+ void RunContext::handleFatalErrorCondition( StringRef message ) {
+ // First notify reporter that bad things happened
+ m_reporter->fatalErrorEncountered(message);
+
+ // Don't rebuild the result -- the stringification itself can cause more fatal errors
+ // Instead, fake a result data.
+ AssertionResultData tempResult( ResultWas::FatalErrorCondition, { false } );
+ tempResult.message = static_cast<std::string>(message);
+ AssertionResult result(m_lastAssertionInfo, tempResult);
+
+ assertionEnded(result);
+
+ handleUnfinishedSections();
+
+ // Recreate section for test case (as we will lose the one that was in scope)
+ auto const& testCaseInfo = m_activeTestCase->getTestCaseInfo();
+ SectionInfo testCaseSection(testCaseInfo.lineInfo, testCaseInfo.name);
+
+ Counts assertions;
+ assertions.failed = 1;
+ SectionStats testCaseSectionStats(testCaseSection, assertions, 0, false);
+ m_reporter->sectionEnded(testCaseSectionStats);
+
+ auto const& testInfo = m_activeTestCase->getTestCaseInfo();
+
+ Totals deltaTotals;
+ deltaTotals.testCases.failed = 1;
+ deltaTotals.assertions.failed = 1;
+ m_reporter->testCaseEnded(TestCaseStats(testInfo,
+ deltaTotals,
+ std::string(),
+ std::string(),
+ false));
+ m_totals.testCases.failed++;
+ testGroupEnded(std::string(), m_totals, 1, 1);
+ m_reporter->testRunEnded(TestRunStats(m_runInfo, m_totals, false));
+ }
+
+ bool RunContext::lastAssertionPassed() {
+ return m_lastAssertionPassed;
+ }
+
+ void RunContext::assertionPassed() {
+ m_lastAssertionPassed = true;
+ ++m_totals.assertions.passed;
+ resetAssertionInfo();
+ m_messageScopes.clear();
+ }
+
+ bool RunContext::aborting() const {
+ return m_totals.assertions.failed >= static_cast<std::size_t>(m_config->abortAfter());
+ }
+
+ void RunContext::runCurrentTest(std::string & redirectedCout, std::string & redirectedCerr) {
+ auto const& testCaseInfo = m_activeTestCase->getTestCaseInfo();
+ SectionInfo testCaseSection(testCaseInfo.lineInfo, testCaseInfo.name);
+ m_reporter->sectionStarting(testCaseSection);
+ Counts prevAssertions = m_totals.assertions;
+ double duration = 0;
+ m_shouldReportUnexpected = true;
+ m_lastAssertionInfo = { "TEST_CASE"_sr, testCaseInfo.lineInfo, StringRef(), ResultDisposition::Normal };
+
+ seedRng(*m_config);
+
+ Timer timer;
+ CATCH_TRY {
+ if (m_reporter->getPreferences().shouldRedirectStdOut) {
+#if !defined(CATCH_CONFIG_EXPERIMENTAL_REDIRECT)
+ RedirectedStreams redirectedStreams(redirectedCout, redirectedCerr);
+
+ timer.start();
+ invokeActiveTestCase();
+#else
+ OutputRedirect r(redirectedCout, redirectedCerr);
+ timer.start();
+ invokeActiveTestCase();
+#endif
+ } else {
+ timer.start();
+ invokeActiveTestCase();
+ }
+ duration = timer.getElapsedSeconds();
+ } CATCH_CATCH_ANON (TestFailureException&) {
+ // This just means the test was aborted due to failure
+ } CATCH_CATCH_ALL {
+ // Under CATCH_CONFIG_FAST_COMPILE, unexpected exceptions under REQUIRE assertions
+ // are reported without translation at the point of origin.
+ if( m_shouldReportUnexpected ) {
+ AssertionReaction dummyReaction;
+ handleUnexpectedInflightException( m_lastAssertionInfo, translateActiveException(), dummyReaction );
+ }
+ }
+ Counts assertions = m_totals.assertions - prevAssertions;
+ bool missingAssertions = testForMissingAssertions(assertions);
+
+ m_testCaseTracker->close();
+ handleUnfinishedSections();
+ m_messages.clear();
+ m_messageScopes.clear();
+
+ SectionStats testCaseSectionStats(testCaseSection, assertions, duration, missingAssertions);
+ m_reporter->sectionEnded(testCaseSectionStats);
+ }
+
+ void RunContext::invokeActiveTestCase() {
+ FatalConditionHandlerGuard _(&m_fatalConditionhandler);
+ m_activeTestCase->invoke();
+ }
+
+ void RunContext::handleUnfinishedSections() {
+ // If sections ended prematurely due to an exception we stored their
+ // infos here so we can tear them down outside the unwind process.
+ for (auto it = m_unfinishedSections.rbegin(),
+ itEnd = m_unfinishedSections.rend();
+ it != itEnd;
+ ++it)
+ sectionEnded(*it);
+ m_unfinishedSections.clear();
+ }
+
+ void RunContext::handleExpr(
+ AssertionInfo const& info,
+ ITransientExpression const& expr,
+ AssertionReaction& reaction
+ ) {
+ m_reporter->assertionStarting( info );
+
+ bool negated = isFalseTest( info.resultDisposition );
+ bool result = expr.getResult() != negated;
+
+ if( result ) {
+ if (!m_includeSuccessfulResults) {
+ assertionPassed();
+ }
+ else {
+ reportExpr(info, ResultWas::Ok, &expr, negated);
+ }
+ }
+ else {
+ reportExpr(info, ResultWas::ExpressionFailed, &expr, negated );
+ populateReaction( reaction );
+ }
+ }
+ void RunContext::reportExpr(
+ AssertionInfo const &info,
+ ResultWas::OfType resultType,
+ ITransientExpression const *expr,
+ bool negated ) {
+
+ m_lastAssertionInfo = info;
+ AssertionResultData data( resultType, LazyExpression( negated ) );
+
+ AssertionResult assertionResult{ info, data };
+ assertionResult.m_resultData.lazyExpression.m_transientExpression = expr;
+
+ assertionEnded( assertionResult );
+ }
+
+ void RunContext::handleMessage(
+ AssertionInfo const& info,
+ ResultWas::OfType resultType,
+ StringRef const& message,
+ AssertionReaction& reaction
+ ) {
+ m_reporter->assertionStarting( info );
+
+ m_lastAssertionInfo = info;
+
+ AssertionResultData data( resultType, LazyExpression( false ) );
+ data.message = static_cast<std::string>(message);
+ AssertionResult assertionResult{ m_lastAssertionInfo, data };
+ assertionEnded( assertionResult );
+ if( !assertionResult.isOk() )
+ populateReaction( reaction );
+ }
+ void RunContext::handleUnexpectedExceptionNotThrown(
+ AssertionInfo const& info,
+ AssertionReaction& reaction
+ ) {
+ handleNonExpr(info, Catch::ResultWas::DidntThrowException, reaction);
+ }
+
+ void RunContext::handleUnexpectedInflightException(
+ AssertionInfo const& info,
+ std::string const& message,
+ AssertionReaction& reaction
+ ) {
+ m_lastAssertionInfo = info;
+
+ AssertionResultData data( ResultWas::ThrewException, LazyExpression( false ) );
+ data.message = message;
+ AssertionResult assertionResult{ info, data };
+ assertionEnded( assertionResult );
+ populateReaction( reaction );
+ }
+
+ void RunContext::populateReaction( AssertionReaction& reaction ) {
+ reaction.shouldDebugBreak = m_config->shouldDebugBreak();
+ reaction.shouldThrow = aborting() || (m_lastAssertionInfo.resultDisposition & ResultDisposition::Normal);
+ }
+
+ void RunContext::handleIncomplete(
+ AssertionInfo const& info
+ ) {
+ m_lastAssertionInfo = info;
+
+ AssertionResultData data( ResultWas::ThrewException, LazyExpression( false ) );
+ data.message = "Exception translation was disabled by CATCH_CONFIG_FAST_COMPILE";
+ AssertionResult assertionResult{ info, data };
+ assertionEnded( assertionResult );
+ }
+ void RunContext::handleNonExpr(
+ AssertionInfo const &info,
+ ResultWas::OfType resultType,
+ AssertionReaction &reaction
+ ) {
+ m_lastAssertionInfo = info;
+
+ AssertionResultData data( resultType, LazyExpression( false ) );
+ AssertionResult assertionResult{ info, data };
+ assertionEnded( assertionResult );
+
+ if( !assertionResult.isOk() )
+ populateReaction( reaction );
+ }
+
+ IResultCapture& getResultCapture() {
+ if (auto* capture = getCurrentContext().getResultCapture())
+ return *capture;
+ else
+ CATCH_INTERNAL_ERROR("No result capture instance");
+ }
+
+ void seedRng(IConfig const& config) {
+ if (config.rngSeed() != 0) {
+ std::srand(config.rngSeed());
+ rng().seed(config.rngSeed());
+ }
+ }
+
+ unsigned int rngSeed() {
+ return getCurrentContext().getConfig()->rngSeed();
+ }
+
+}
+// end catch_run_context.cpp
+// start catch_section.cpp
+
+namespace Catch {
+
+ Section::Section( SectionInfo const& info )
+ : m_info( info ),
+ m_sectionIncluded( getResultCapture().sectionStarted( m_info, m_assertions ) )
+ {
+ m_timer.start();
+ }
+
+ Section::~Section() {
+ if( m_sectionIncluded ) {
+ SectionEndInfo endInfo{ m_info, m_assertions, m_timer.getElapsedSeconds() };
+ if( uncaught_exceptions() )
+ getResultCapture().sectionEndedEarly( endInfo );
+ else
+ getResultCapture().sectionEnded( endInfo );
+ }
+ }
+
+ // This indicates whether the section should be executed or not
+ Section::operator bool() const {
+ return m_sectionIncluded;
+ }
+
+} // end namespace Catch
+// end catch_section.cpp
+// start catch_section_info.cpp
+
+namespace Catch {
+
+ SectionInfo::SectionInfo
+ ( SourceLineInfo const& _lineInfo,
+ std::string const& _name )
+ : name( _name ),
+ lineInfo( _lineInfo )
+ {}
+
+} // end namespace Catch
+// end catch_section_info.cpp
+// start catch_session.cpp
+
+// start catch_session.h
+
+#include <memory>
+
+namespace Catch {
+
+ class Session : NonCopyable {
+ public:
+
+ Session();
+ ~Session() override;
+
+ void showHelp() const;
+ void libIdentify();
+
+ int applyCommandLine( int argc, char const * const * argv );
+ #if defined(CATCH_CONFIG_WCHAR) && defined(_WIN32) && defined(UNICODE)
+ int applyCommandLine( int argc, wchar_t const * const * argv );
+ #endif
+
+ void useConfigData( ConfigData const& configData );
+
+ template<typename CharT>
+ int run(int argc, CharT const * const argv[]) {
+ if (m_startupExceptions)
+ return 1;
+ int returnCode = applyCommandLine(argc, argv);
+ if (returnCode == 0)
+ returnCode = run();
+ return returnCode;
+ }
+
+ int run();
+
+ clara::Parser const& cli() const;
+ void cli( clara::Parser const& newParser );
+ ConfigData& configData();
+ Config& config();
+ private:
+ int runInternal();
+
+ clara::Parser m_cli;
+ ConfigData m_configData;
+ std::shared_ptr<Config> m_config;
+ bool m_startupExceptions = false;
+ };
+
+} // end namespace Catch
+
+// end catch_session.h
+// start catch_version.h
+
+#include <iosfwd>
+
+namespace Catch {
+
+ // Versioning information
+ struct Version {
+ Version( Version const& ) = delete;
+ Version& operator=( Version const& ) = delete;
+ Version( unsigned int _majorVersion,
+ unsigned int _minorVersion,
+ unsigned int _patchNumber,
+ char const * const _branchName,
+ unsigned int _buildNumber );
+
+ unsigned int const majorVersion;
+ unsigned int const minorVersion;
+ unsigned int const patchNumber;
+
+ // buildNumber is only used if branchName is not null
+ char const * const branchName;
+ unsigned int const buildNumber;
+
+ friend std::ostream& operator << ( std::ostream& os, Version const& version );
+ };
+
+ Version const& libraryVersion();
+}
+
+// end catch_version.h
+#include <cstdlib>
+#include <iomanip>
+#include <set>
+#include <iterator>
+
+namespace Catch {
+
+ namespace {
+ const int MaxExitCode = 255;
+
+ IStreamingReporterPtr createReporter(std::string const& reporterName, IConfigPtr const& config) {
+ auto reporter = Catch::getRegistryHub().getReporterRegistry().create(reporterName, config);
+ CATCH_ENFORCE(reporter, "No reporter registered with name: '" << reporterName << "'");
+
+ return reporter;
+ }
+
+ IStreamingReporterPtr makeReporter(std::shared_ptr<Config> const& config) {
+ if (Catch::getRegistryHub().getReporterRegistry().getListeners().empty()) {
+ return createReporter(config->getReporterName(), config);
+ }
+
+ // On older platforms, returning std::unique_ptr<ListeningReporter>
+ // when the return type is std::unique_ptr<IStreamingReporter>
+ // doesn't compile without a std::move call. However, this causes
+ // a warning on newer platforms. Thus, we have to work around
+ // it a bit and downcast the pointer manually.
+ auto ret = std::unique_ptr<IStreamingReporter>(new ListeningReporter);
+ auto& multi = static_cast<ListeningReporter&>(*ret);
+ auto const& listeners = Catch::getRegistryHub().getReporterRegistry().getListeners();
+ for (auto const& listener : listeners) {
+ multi.addListener(listener->create(Catch::ReporterConfig(config)));
+ }
+ multi.addReporter(createReporter(config->getReporterName(), config));
+ return ret;
+ }
+
+ class TestGroup {
+ public:
+ explicit TestGroup(std::shared_ptr<Config> const& config)
+ : m_config{config}
+ , m_context{config, makeReporter(config)}
+ {
+ auto const& allTestCases = getAllTestCasesSorted(*m_config);
+ m_matches = m_config->testSpec().matchesByFilter(allTestCases, *m_config);
+ auto const& invalidArgs = m_config->testSpec().getInvalidArgs();
+
+ if (m_matches.empty() && invalidArgs.empty()) {
+ for (auto const& test : allTestCases)
+ if (!test.isHidden())
+ m_tests.emplace(&test);
+ } else {
+ for (auto const& match : m_matches)
+ m_tests.insert(match.tests.begin(), match.tests.end());
+ }
+ }
+
+ Totals execute() {
+ auto const& invalidArgs = m_config->testSpec().getInvalidArgs();
+ Totals totals;
+ m_context.testGroupStarting(m_config->name(), 1, 1);
+ for (auto const& testCase : m_tests) {
+ if (!m_context.aborting())
+ totals += m_context.runTest(*testCase);
+ else
+ m_context.reporter().skipTest(*testCase);
+ }
+
+ for (auto const& match : m_matches) {
+ if (match.tests.empty()) {
+ m_context.reporter().noMatchingTestCases(match.name);
+ totals.error = -1;
+ }
+ }
+
+ if (!invalidArgs.empty()) {
+ for (auto const& invalidArg: invalidArgs)
+ m_context.reporter().reportInvalidArguments(invalidArg);
+ }
+
+ m_context.testGroupEnded(m_config->name(), totals, 1, 1);
+ return totals;
+ }
+
+ private:
+ using Tests = std::set<TestCase const*>;
+
+ std::shared_ptr<Config> m_config;
+ RunContext m_context;
+ Tests m_tests;
+ TestSpec::Matches m_matches;
+ };
+
+ void applyFilenamesAsTags(Catch::IConfig const& config) {
+ auto& tests = const_cast<std::vector<TestCase>&>(getAllTestCasesSorted(config));
+ for (auto& testCase : tests) {
+ auto tags = testCase.tags;
+
+ std::string filename = testCase.lineInfo.file;
+ auto lastSlash = filename.find_last_of("\\/");
+ if (lastSlash != std::string::npos) {
+ filename.erase(0, lastSlash);
+ filename[0] = '#';
+ }
+ else
+ {
+ filename.insert(0, "#");
+ }
+
+ auto lastDot = filename.find_last_of('.');
+ if (lastDot != std::string::npos) {
+ filename.erase(lastDot);
+ }
+
+ tags.push_back(std::move(filename));
+ setTags(testCase, tags);
+ }
+ }
+
+ } // anon namespace
+
+ Session::Session() {
+ static bool alreadyInstantiated = false;
+ if( alreadyInstantiated ) {
+ CATCH_TRY { CATCH_INTERNAL_ERROR( "Only one instance of Catch::Session can ever be used" ); }
+ CATCH_CATCH_ALL { getMutableRegistryHub().registerStartupException(); }
+ }
+
+ // There cannot be exceptions at startup in no-exception mode.
+#if !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS)
+ const auto& exceptions = getRegistryHub().getStartupExceptionRegistry().getExceptions();
+ if ( !exceptions.empty() ) {
+ config();
+ getCurrentMutableContext().setConfig(m_config);
+
+ m_startupExceptions = true;
+ Colour colourGuard( Colour::Red );
+ Catch::cerr() << "Errors occurred during startup!" << '\n';
+ // iterate over all exceptions and notify user
+ for ( const auto& ex_ptr : exceptions ) {
+ try {
+ std::rethrow_exception(ex_ptr);
+ } catch ( std::exception const& ex ) {
+ Catch::cerr() << Column( ex.what() ).indent(2) << '\n';
+ }
+ }
+ }
+#endif
+
+ alreadyInstantiated = true;
+ m_cli = makeCommandLineParser( m_configData );
+ }
+ Session::~Session() {
+ Catch::cleanUp();
+ }
+
+ void Session::showHelp() const {
+ Catch::cout()
+ << "\nCatch v" << libraryVersion() << "\n"
+ << m_cli << std::endl
+ << "For more detailed usage please see the project docs\n" << std::endl;
+ }
+ void Session::libIdentify() {
+ Catch::cout()
+ << std::left << std::setw(16) << "description: " << "A Catch2 test executable\n"
+ << std::left << std::setw(16) << "category: " << "testframework\n"
+ << std::left << std::setw(16) << "framework: " << "Catch Test\n"
+ << std::left << std::setw(16) << "version: " << libraryVersion() << std::endl;
+ }
+
+ int Session::applyCommandLine( int argc, char const * const * argv ) {
+ if( m_startupExceptions )
+ return 1;
+
+ auto result = m_cli.parse( clara::Args( argc, argv ) );
+ if( !result ) {
+ config();
+ getCurrentMutableContext().setConfig(m_config);
+ Catch::cerr()
+ << Colour( Colour::Red )
+ << "\nError(s) in input:\n"
+ << Column( result.errorMessage() ).indent( 2 )
+ << "\n\n";
+ Catch::cerr() << "Run with -? for usage\n" << std::endl;
+ return MaxExitCode;
+ }
+
+ if( m_configData.showHelp )
+ showHelp();
+ if( m_configData.libIdentify )
+ libIdentify();
+ m_config.reset();
+ return 0;
+ }
+
+#if defined(CATCH_CONFIG_WCHAR) && defined(_WIN32) && defined(UNICODE)
+ int Session::applyCommandLine( int argc, wchar_t const * const * argv ) {
+
+ char **utf8Argv = new char *[ argc ];
+
+ for ( int i = 0; i < argc; ++i ) {
+ int bufSize = WideCharToMultiByte( CP_UTF8, 0, argv[i], -1, nullptr, 0, nullptr, nullptr );
+
+ utf8Argv[ i ] = new char[ bufSize ];
+
+ WideCharToMultiByte( CP_UTF8, 0, argv[i], -1, utf8Argv[i], bufSize, nullptr, nullptr );
+ }
+
+ int returnCode = applyCommandLine( argc, utf8Argv );
+
+ for ( int i = 0; i < argc; ++i )
+ delete [] utf8Argv[ i ];
+
+ delete [] utf8Argv;
+
+ return returnCode;
+ }
+#endif
+
+ void Session::useConfigData( ConfigData const& configData ) {
+ m_configData = configData;
+ m_config.reset();
+ }
+
+ int Session::run() {
+ if( ( m_configData.waitForKeypress & WaitForKeypress::BeforeStart ) != 0 ) {
+ Catch::cout() << "...waiting for enter/ return before starting" << std::endl;
+ static_cast<void>(std::getchar());
+ }
+ int exitCode = runInternal();
+ if( ( m_configData.waitForKeypress & WaitForKeypress::BeforeExit ) != 0 ) {
+ Catch::cout() << "...waiting for enter/ return before exiting, with code: " << exitCode << std::endl;
+ static_cast<void>(std::getchar());
+ }
+ return exitCode;
+ }
+
+ clara::Parser const& Session::cli() const {
+ return m_cli;
+ }
+ void Session::cli( clara::Parser const& newParser ) {
+ m_cli = newParser;
+ }
+ ConfigData& Session::configData() {
+ return m_configData;
+ }
+ Config& Session::config() {
+ if( !m_config )
+ m_config = std::make_shared<Config>( m_configData );
+ return *m_config;
+ }
+
+ int Session::runInternal() {
+ if( m_startupExceptions )
+ return 1;
+
+ if (m_configData.showHelp || m_configData.libIdentify) {
+ return 0;
+ }
+
+ CATCH_TRY {
+ config(); // Force config to be constructed
+
+ seedRng( *m_config );
+
+ if( m_configData.filenamesAsTags )
+ applyFilenamesAsTags( *m_config );
+
+ // Handle list request
+ if( Option<std::size_t> listed = list( m_config ) )
+ return (std::min) (MaxExitCode, static_cast<int>(*listed));
+
+ TestGroup tests { m_config };
+ auto const totals = tests.execute();
+
+ if( m_config->warnAboutNoTests() && totals.error == -1 )
+ return 2;
+
+ // Note that on unices only the lower 8 bits are usually used, clamping
+ // the return value to 255 prevents false negative when some multiple
+ // of 256 tests has failed
+ return (std::min) (MaxExitCode, (std::max) (totals.error, static_cast<int>(totals.assertions.failed)));
+ }
+#if !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS)
+ catch( std::exception& ex ) {
+ Catch::cerr() << ex.what() << std::endl;
+ return MaxExitCode;
+ }
+#endif
+ }
+
+} // end namespace Catch
+// end catch_session.cpp
+// start catch_singletons.cpp
+
+#include <vector>
+
+namespace Catch {
+
+ namespace {
+ static auto getSingletons() -> std::vector<ISingleton*>*& {
+ static std::vector<ISingleton*>* g_singletons = nullptr;
+ if( !g_singletons )
+ g_singletons = new std::vector<ISingleton*>();
+ return g_singletons;
+ }
+ }
+
+ ISingleton::~ISingleton() {}
+
+ void addSingleton(ISingleton* singleton ) {
+ getSingletons()->push_back( singleton );
+ }
+ void cleanupSingletons() {
+ auto& singletons = getSingletons();
+ for( auto singleton : *singletons )
+ delete singleton;
+ delete singletons;
+ singletons = nullptr;
+ }
+
+} // namespace Catch
+// end catch_singletons.cpp
+// start catch_startup_exception_registry.cpp
+
+#if !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS)
+namespace Catch {
+void StartupExceptionRegistry::add( std::exception_ptr const& exception ) noexcept {
+ CATCH_TRY {
+ m_exceptions.push_back(exception);
+ } CATCH_CATCH_ALL {
+ // If we run out of memory during start-up there's really not a lot more we can do about it
+ std::terminate();
+ }
+ }
+
+ std::vector<std::exception_ptr> const& StartupExceptionRegistry::getExceptions() const noexcept {
+ return m_exceptions;
+ }
+
+} // end namespace Catch
+#endif
+// end catch_startup_exception_registry.cpp
+// start catch_stream.cpp
+
+#include <cstdio>
+#include <iostream>
+#include <fstream>
+#include <sstream>
+#include <vector>
+#include <memory>
+
+namespace Catch {
+
+ Catch::IStream::~IStream() = default;
+
+ namespace Detail { namespace {
+ template<typename WriterF, std::size_t bufferSize=256>
+ class StreamBufImpl : public std::streambuf {
+ char data[bufferSize];
+ WriterF m_writer;
+
+ public:
+ StreamBufImpl() {
+ setp( data, data + sizeof(data) );
+ }
+
+ ~StreamBufImpl() noexcept {
+ StreamBufImpl::sync();
+ }
+
+ private:
+ int overflow( int c ) override {
+ sync();
+
+ if( c != EOF ) {
+ if( pbase() == epptr() )
+ m_writer( std::string( 1, static_cast<char>( c ) ) );
+ else
+ sputc( static_cast<char>( c ) );
+ }
+ return 0;
+ }
+
+ int sync() override {
+ if( pbase() != pptr() ) {
+ m_writer( std::string( pbase(), static_cast<std::string::size_type>( pptr() - pbase() ) ) );
+ setp( pbase(), epptr() );
+ }
+ return 0;
+ }
+ };
+
+ ///////////////////////////////////////////////////////////////////////////
+
+ struct OutputDebugWriter {
+
+ void operator()( std::string const&str ) {
+ writeToDebugConsole( str );
+ }
+ };
+
+ ///////////////////////////////////////////////////////////////////////////
+
+ class FileStream : public IStream {
+ mutable std::ofstream m_ofs;
+ public:
+ FileStream( StringRef filename ) {
+ m_ofs.open( filename.c_str() );
+ CATCH_ENFORCE( !m_ofs.fail(), "Unable to open file: '" << filename << "'" );
+ }
+ ~FileStream() override = default;
+ public: // IStream
+ std::ostream& stream() const override {
+ return m_ofs;
+ }
+ };
+
+ ///////////////////////////////////////////////////////////////////////////
+
+ class CoutStream : public IStream {
+ mutable std::ostream m_os;
+ public:
+ // Store the streambuf from cout up-front because
+ // cout may get redirected when running tests
+ CoutStream() : m_os( Catch::cout().rdbuf() ) {}
+ ~CoutStream() override = default;
+
+ public: // IStream
+ std::ostream& stream() const override { return m_os; }
+ };
+
+ ///////////////////////////////////////////////////////////////////////////
+
+ class DebugOutStream : public IStream {
+ std::unique_ptr<StreamBufImpl<OutputDebugWriter>> m_streamBuf;
+ mutable std::ostream m_os;
+ public:
+ DebugOutStream()
+ : m_streamBuf( new StreamBufImpl<OutputDebugWriter>() ),
+ m_os( m_streamBuf.get() )
+ {}
+
+ ~DebugOutStream() override = default;
+
+ public: // IStream
+ std::ostream& stream() const override { return m_os; }
+ };
+
+ }} // namespace anon::detail
+
+ ///////////////////////////////////////////////////////////////////////////
+
+ auto makeStream( StringRef const &filename ) -> IStream const* {
+ if( filename.empty() )
+ return new Detail::CoutStream();
+ else if( filename[0] == '%' ) {
+ if( filename == "%debug" )
+ return new Detail::DebugOutStream();
+ else
+ CATCH_ERROR( "Unrecognised stream: '" << filename << "'" );
+ }
+ else
+ return new Detail::FileStream( filename );
+ }
+
+ // This class encapsulates the idea of a pool of ostringstreams that can be reused.
+ struct StringStreams {
+ std::vector<std::unique_ptr<std::ostringstream>> m_streams;
+ std::vector<std::size_t> m_unused;
+ std::ostringstream m_referenceStream; // Used for copy state/ flags from
+
+ auto add() -> std::size_t {
+ if( m_unused.empty() ) {
+ m_streams.push_back( std::unique_ptr<std::ostringstream>( new std::ostringstream ) );
+ return m_streams.size()-1;
+ }
+ else {
+ auto index = m_unused.back();
+ m_unused.pop_back();
+ return index;
+ }
+ }
+
+ void release( std::size_t index ) {
+ m_streams[index]->copyfmt( m_referenceStream ); // Restore initial flags and other state
+ m_unused.push_back(index);
+ }
+ };
+
+ ReusableStringStream::ReusableStringStream()
+ : m_index( Singleton<StringStreams>::getMutable().add() ),
+ m_oss( Singleton<StringStreams>::getMutable().m_streams[m_index].get() )
+ {}
+
+ ReusableStringStream::~ReusableStringStream() {
+ static_cast<std::ostringstream*>( m_oss )->str("");
+ m_oss->clear();
+ Singleton<StringStreams>::getMutable().release( m_index );
+ }
+
+ auto ReusableStringStream::str() const -> std::string {
+ return static_cast<std::ostringstream*>( m_oss )->str();
+ }
+
+ ///////////////////////////////////////////////////////////////////////////
+
+#ifndef CATCH_CONFIG_NOSTDOUT // If you #define this you must implement these functions
+ std::ostream& cout() { return std::cout; }
+ std::ostream& cerr() { return std::cerr; }
+ std::ostream& clog() { return std::clog; }
+#endif
+}
+// end catch_stream.cpp
+// start catch_string_manip.cpp
+
+#include <algorithm>
+#include <ostream>
+#include <cstring>
+#include <cctype>
+#include <vector>
+
+namespace Catch {
+
+ namespace {
+ char toLowerCh(char c) {
+ return static_cast<char>( std::tolower( static_cast<unsigned char>(c) ) );
+ }
+ }
+
+ bool startsWith( std::string const& s, std::string const& prefix ) {
+ return s.size() >= prefix.size() && std::equal(prefix.begin(), prefix.end(), s.begin());
+ }
+ bool startsWith( std::string const& s, char prefix ) {
+ return !s.empty() && s[0] == prefix;
+ }
+ bool endsWith( std::string const& s, std::string const& suffix ) {
+ return s.size() >= suffix.size() && std::equal(suffix.rbegin(), suffix.rend(), s.rbegin());
+ }
+ bool endsWith( std::string const& s, char suffix ) {
+ return !s.empty() && s[s.size()-1] == suffix;
+ }
+ bool contains( std::string const& s, std::string const& infix ) {
+ return s.find( infix ) != std::string::npos;
+ }
+ void toLowerInPlace( std::string& s ) {
+ std::transform( s.begin(), s.end(), s.begin(), toLowerCh );
+ }
+ std::string toLower( std::string const& s ) {
+ std::string lc = s;
+ toLowerInPlace( lc );
+ return lc;
+ }
+ std::string trim( std::string const& str ) {
+ static char const* whitespaceChars = "\n\r\t ";
+ std::string::size_type start = str.find_first_not_of( whitespaceChars );
+ std::string::size_type end = str.find_last_not_of( whitespaceChars );
+
+ return start != std::string::npos ? str.substr( start, 1+end-start ) : std::string();
+ }
+
+ StringRef trim(StringRef ref) {
+ const auto is_ws = [](char c) {
+ return c == ' ' || c == '\t' || c == '\n' || c == '\r';
+ };
+ size_t real_begin = 0;
+ while (real_begin < ref.size() && is_ws(ref[real_begin])) { ++real_begin; }
+ size_t real_end = ref.size();
+ while (real_end > real_begin && is_ws(ref[real_end - 1])) { --real_end; }
+
+ return ref.substr(real_begin, real_end - real_begin);
+ }
+
+ bool replaceInPlace( std::string& str, std::string const& replaceThis, std::string const& withThis ) {
+ bool replaced = false;
+ std::size_t i = str.find( replaceThis );
+ while( i != std::string::npos ) {
+ replaced = true;
+ str = str.substr( 0, i ) + withThis + str.substr( i+replaceThis.size() );
+ if( i < str.size()-withThis.size() )
+ i = str.find( replaceThis, i+withThis.size() );
+ else
+ i = std::string::npos;
+ }
+ return replaced;
+ }
+
+ std::vector<StringRef> splitStringRef( StringRef str, char delimiter ) {
+ std::vector<StringRef> subStrings;
+ std::size_t start = 0;
+ for(std::size_t pos = 0; pos < str.size(); ++pos ) {
+ if( str[pos] == delimiter ) {
+ if( pos - start > 1 )
+ subStrings.push_back( str.substr( start, pos-start ) );
+ start = pos+1;
+ }
+ }
+ if( start < str.size() )
+ subStrings.push_back( str.substr( start, str.size()-start ) );
+ return subStrings;
+ }
+
+ pluralise::pluralise( std::size_t count, std::string const& label )
+ : m_count( count ),
+ m_label( label )
+ {}
+
+ std::ostream& operator << ( std::ostream& os, pluralise const& pluraliser ) {
+ os << pluraliser.m_count << ' ' << pluraliser.m_label;
+ if( pluraliser.m_count != 1 )
+ os << 's';
+ return os;
+ }
+
+}
+// end catch_string_manip.cpp
+// start catch_stringref.cpp
+
+#include <algorithm>
+#include <ostream>
+#include <cstring>
+#include <cstdint>
+
+namespace Catch {
+ StringRef::StringRef( char const* rawChars ) noexcept
+ : StringRef( rawChars, static_cast<StringRef::size_type>(std::strlen(rawChars) ) )
+ {}
+
+ auto StringRef::c_str() const -> char const* {
+ CATCH_ENFORCE(isNullTerminated(), "Called StringRef::c_str() on a non-null-terminated instance");
+ return m_start;
+ }
+ auto StringRef::data() const noexcept -> char const* {
+ return m_start;
+ }
+
+ auto StringRef::substr( size_type start, size_type size ) const noexcept -> StringRef {
+ if (start < m_size) {
+ return StringRef(m_start + start, (std::min)(m_size - start, size));
+ } else {
+ return StringRef();
+ }
+ }
+ auto StringRef::operator == ( StringRef const& other ) const noexcept -> bool {
+ return m_size == other.m_size
+ && (std::memcmp( m_start, other.m_start, m_size ) == 0);
+ }
+
+ auto operator << ( std::ostream& os, StringRef const& str ) -> std::ostream& {
+ return os.write(str.data(), str.size());
+ }
+
+ auto operator+=( std::string& lhs, StringRef const& rhs ) -> std::string& {
+ lhs.append(rhs.data(), rhs.size());
+ return lhs;
+ }
+
+} // namespace Catch
+// end catch_stringref.cpp
+// start catch_tag_alias.cpp
+
+namespace Catch {
+ TagAlias::TagAlias(std::string const & _tag, SourceLineInfo _lineInfo): tag(_tag), lineInfo(_lineInfo) {}
+}
+// end catch_tag_alias.cpp
+// start catch_tag_alias_autoregistrar.cpp
+
+namespace Catch {
+
+ RegistrarForTagAliases::RegistrarForTagAliases(char const* alias, char const* tag, SourceLineInfo const& lineInfo) {
+ CATCH_TRY {
+ getMutableRegistryHub().registerTagAlias(alias, tag, lineInfo);
+ } CATCH_CATCH_ALL {
+ // Do not throw when constructing global objects, instead register the exception to be processed later
+ getMutableRegistryHub().registerStartupException();
+ }
+ }
+
+}
+// end catch_tag_alias_autoregistrar.cpp
+// start catch_tag_alias_registry.cpp
+
+#include <sstream>
+
+namespace Catch {
+
+ TagAliasRegistry::~TagAliasRegistry() {}
+
+ TagAlias const* TagAliasRegistry::find( std::string const& alias ) const {
+ auto it = m_registry.find( alias );
+ if( it != m_registry.end() )
+ return &(it->second);
+ else
+ return nullptr;
+ }
+
+ std::string TagAliasRegistry::expandAliases( std::string const& unexpandedTestSpec ) const {
+ std::string expandedTestSpec = unexpandedTestSpec;
+ for( auto const& registryKvp : m_registry ) {
+ std::size_t pos = expandedTestSpec.find( registryKvp.first );
+ if( pos != std::string::npos ) {
+ expandedTestSpec = expandedTestSpec.substr( 0, pos ) +
+ registryKvp.second.tag +
+ expandedTestSpec.substr( pos + registryKvp.first.size() );
+ }
+ }
+ return expandedTestSpec;
+ }
+
+ void TagAliasRegistry::add( std::string const& alias, std::string const& tag, SourceLineInfo const& lineInfo ) {
+ CATCH_ENFORCE( startsWith(alias, "[@") && endsWith(alias, ']'),
+ "error: tag alias, '" << alias << "' is not of the form [@alias name].\n" << lineInfo );
+
+ CATCH_ENFORCE( m_registry.insert(std::make_pair(alias, TagAlias(tag, lineInfo))).second,
+ "error: tag alias, '" << alias << "' already registered.\n"
+ << "\tFirst seen at: " << find(alias)->lineInfo << "\n"
+ << "\tRedefined at: " << lineInfo );
+ }
+
+ ITagAliasRegistry::~ITagAliasRegistry() {}
+
+ ITagAliasRegistry const& ITagAliasRegistry::get() {
+ return getRegistryHub().getTagAliasRegistry();
+ }
+
+} // end namespace Catch
+// end catch_tag_alias_registry.cpp
+// start catch_test_case_info.cpp
+
+#include <cctype>
+#include <exception>
+#include <algorithm>
+#include <sstream>
+
+namespace Catch {
+
+ namespace {
+ TestCaseInfo::SpecialProperties parseSpecialTag( std::string const& tag ) {
+ if( startsWith( tag, '.' ) ||
+ tag == "!hide" )
+ return TestCaseInfo::IsHidden;
+ else if( tag == "!throws" )
+ return TestCaseInfo::Throws;
+ else if( tag == "!shouldfail" )
+ return TestCaseInfo::ShouldFail;
+ else if( tag == "!mayfail" )
+ return TestCaseInfo::MayFail;
+ else if( tag == "!nonportable" )
+ return TestCaseInfo::NonPortable;
+ else if( tag == "!benchmark" )
+ return static_cast<TestCaseInfo::SpecialProperties>( TestCaseInfo::Benchmark | TestCaseInfo::IsHidden );
+ else
+ return TestCaseInfo::None;
+ }
+ bool isReservedTag( std::string const& tag ) {
+ return parseSpecialTag( tag ) == TestCaseInfo::None && tag.size() > 0 && !std::isalnum( static_cast<unsigned char>(tag[0]) );
+ }
+ void enforceNotReservedTag( std::string const& tag, SourceLineInfo const& _lineInfo ) {
+ CATCH_ENFORCE( !isReservedTag(tag),
+ "Tag name: [" << tag << "] is not allowed.\n"
+ << "Tag names starting with non alphanumeric characters are reserved\n"
+ << _lineInfo );
+ }
+ }
+
+ TestCase makeTestCase( ITestInvoker* _testCase,
+ std::string const& _className,
+ NameAndTags const& nameAndTags,
+ SourceLineInfo const& _lineInfo )
+ {
+ bool isHidden = false;
+
+ // Parse out tags
+ std::vector<std::string> tags;
+ std::string desc, tag;
+ bool inTag = false;
+ for (char c : nameAndTags.tags) {
+ if( !inTag ) {
+ if( c == '[' )
+ inTag = true;
+ else
+ desc += c;
+ }
+ else {
+ if( c == ']' ) {
+ TestCaseInfo::SpecialProperties prop = parseSpecialTag( tag );
+ if( ( prop & TestCaseInfo::IsHidden ) != 0 )
+ isHidden = true;
+ else if( prop == TestCaseInfo::None )
+ enforceNotReservedTag( tag, _lineInfo );
+
+ // Merged hide tags like `[.approvals]` should be added as
+ // `[.][approvals]`. The `[.]` is added at later point, so
+ // we only strip the prefix
+ if (startsWith(tag, '.') && tag.size() > 1) {
+ tag.erase(0, 1);
+ }
+ tags.push_back( tag );
+ tag.clear();
+ inTag = false;
+ }
+ else
+ tag += c;
+ }
+ }
+ if( isHidden ) {
+ // Add all "hidden" tags to make them behave identically
+ tags.insert( tags.end(), { ".", "!hide" } );
+ }
+
+ TestCaseInfo info( static_cast<std::string>(nameAndTags.name), _className, desc, tags, _lineInfo );
+ return TestCase( _testCase, std::move(info) );
+ }
+
+ void setTags( TestCaseInfo& testCaseInfo, std::vector<std::string> tags ) {
+ std::sort(begin(tags), end(tags));
+ tags.erase(std::unique(begin(tags), end(tags)), end(tags));
+ testCaseInfo.lcaseTags.clear();
+
+ for( auto const& tag : tags ) {
+ std::string lcaseTag = toLower( tag );
+ testCaseInfo.properties = static_cast<TestCaseInfo::SpecialProperties>( testCaseInfo.properties | parseSpecialTag( lcaseTag ) );
+ testCaseInfo.lcaseTags.push_back( lcaseTag );
+ }
+ testCaseInfo.tags = std::move(tags);
+ }
+
+ TestCaseInfo::TestCaseInfo( std::string const& _name,
+ std::string const& _className,
+ std::string const& _description,
+ std::vector<std::string> const& _tags,
+ SourceLineInfo const& _lineInfo )
+ : name( _name ),
+ className( _className ),
+ description( _description ),
+ lineInfo( _lineInfo ),
+ properties( None )
+ {
+ setTags( *this, _tags );
+ }
+
+ bool TestCaseInfo::isHidden() const {
+ return ( properties & IsHidden ) != 0;
+ }
+ bool TestCaseInfo::throws() const {
+ return ( properties & Throws ) != 0;
+ }
+ bool TestCaseInfo::okToFail() const {
+ return ( properties & (ShouldFail | MayFail ) ) != 0;
+ }
+ bool TestCaseInfo::expectedToFail() const {
+ return ( properties & (ShouldFail ) ) != 0;
+ }
+
+ std::string TestCaseInfo::tagsAsString() const {
+ std::string ret;
+ // '[' and ']' per tag
+ std::size_t full_size = 2 * tags.size();
+ for (const auto& tag : tags) {
+ full_size += tag.size();
+ }
+ ret.reserve(full_size);
+ for (const auto& tag : tags) {
+ ret.push_back('[');
+ ret.append(tag);
+ ret.push_back(']');
+ }
+
+ return ret;
+ }
+
+ TestCase::TestCase( ITestInvoker* testCase, TestCaseInfo&& info ) : TestCaseInfo( std::move(info) ), test( testCase ) {}
+
+ TestCase TestCase::withName( std::string const& _newName ) const {
+ TestCase other( *this );
+ other.name = _newName;
+ return other;
+ }
+
+ void TestCase::invoke() const {
+ test->invoke();
+ }
+
+ bool TestCase::operator == ( TestCase const& other ) const {
+ return test.get() == other.test.get() &&
+ name == other.name &&
+ className == other.className;
+ }
+
+ bool TestCase::operator < ( TestCase const& other ) const {
+ return name < other.name;
+ }
+
+ TestCaseInfo const& TestCase::getTestCaseInfo() const
+ {
+ return *this;
+ }
+
+} // end namespace Catch
+// end catch_test_case_info.cpp
+// start catch_test_case_registry_impl.cpp
+
+#include <algorithm>
+#include <sstream>
+
+namespace Catch {
+
+ namespace {
+ struct TestHasher {
+ using hash_t = uint64_t;
+
+ explicit TestHasher( hash_t hashSuffix ):
+ m_hashSuffix{ hashSuffix } {}
+
+ uint32_t operator()( TestCase const& t ) const {
+ // FNV-1a hash with multiplication fold.
+ const hash_t prime = 1099511628211u;
+ hash_t hash = 14695981039346656037u;
+ for ( const char c : t.name ) {
+ hash ^= c;
+ hash *= prime;
+ }
+ hash ^= m_hashSuffix;
+ hash *= prime;
+ const uint32_t low{ static_cast<uint32_t>( hash ) };
+ const uint32_t high{ static_cast<uint32_t>( hash >> 32 ) };
+ return low * high;
+ }
+
+ private:
+ hash_t m_hashSuffix;
+ };
+ } // end unnamed namespace
+
+ std::vector<TestCase> sortTests( IConfig const& config, std::vector<TestCase> const& unsortedTestCases ) {
+ switch( config.runOrder() ) {
+ case RunTests::InDeclarationOrder:
+ // already in declaration order
+ break;
+
+ case RunTests::InLexicographicalOrder: {
+ std::vector<TestCase> sorted = unsortedTestCases;
+ std::sort( sorted.begin(), sorted.end() );
+ return sorted;
+ }
+
+ case RunTests::InRandomOrder: {
+ seedRng( config );
+ TestHasher h{ config.rngSeed() };
+
+ using hashedTest = std::pair<TestHasher::hash_t, TestCase const*>;
+ std::vector<hashedTest> indexed_tests;
+ indexed_tests.reserve( unsortedTestCases.size() );
+
+ for (auto const& testCase : unsortedTestCases) {
+ indexed_tests.emplace_back(h(testCase), &testCase);
+ }
+
+ std::sort(indexed_tests.begin(), indexed_tests.end(),
+ [](hashedTest const& lhs, hashedTest const& rhs) {
+ if (lhs.first == rhs.first) {
+ return lhs.second->name < rhs.second->name;
+ }
+ return lhs.first < rhs.first;
+ });
+
+ std::vector<TestCase> sorted;
+ sorted.reserve( indexed_tests.size() );
+
+ for (auto const& hashed : indexed_tests) {
+ sorted.emplace_back(*hashed.second);
+ }
+
+ return sorted;
+ }
+ }
+ return unsortedTestCases;
+ }
+
+ bool isThrowSafe( TestCase const& testCase, IConfig const& config ) {
+ return !testCase.throws() || config.allowThrows();
+ }
+
+ bool matchTest( TestCase const& testCase, TestSpec const& testSpec, IConfig const& config ) {
+ return testSpec.matches( testCase ) && isThrowSafe( testCase, config );
+ }
+
+ void enforceNoDuplicateTestCases( std::vector<TestCase> const& functions ) {
+ std::set<TestCase> seenFunctions;
+ for( auto const& function : functions ) {
+ auto prev = seenFunctions.insert( function );
+ CATCH_ENFORCE( prev.second,
+ "error: TEST_CASE( \"" << function.name << "\" ) already defined.\n"
+ << "\tFirst seen at " << prev.first->getTestCaseInfo().lineInfo << "\n"
+ << "\tRedefined at " << function.getTestCaseInfo().lineInfo );
+ }
+ }
+
+ std::vector<TestCase> filterTests( std::vector<TestCase> const& testCases, TestSpec const& testSpec, IConfig const& config ) {
+ std::vector<TestCase> filtered;
+ filtered.reserve( testCases.size() );
+ for (auto const& testCase : testCases) {
+ if ((!testSpec.hasFilters() && !testCase.isHidden()) ||
+ (testSpec.hasFilters() && matchTest(testCase, testSpec, config))) {
+ filtered.push_back(testCase);
+ }
+ }
+ return filtered;
+ }
+ std::vector<TestCase> const& getAllTestCasesSorted( IConfig const& config ) {
+ return getRegistryHub().getTestCaseRegistry().getAllTestsSorted( config );
+ }
+
+ void TestRegistry::registerTest( TestCase const& testCase ) {
+ std::string name = testCase.getTestCaseInfo().name;
+ if( name.empty() ) {
+ ReusableStringStream rss;
+ rss << "Anonymous test case " << ++m_unnamedCount;
+ return registerTest( testCase.withName( rss.str() ) );
+ }
+ m_functions.push_back( testCase );
+ }
+
+ std::vector<TestCase> const& TestRegistry::getAllTests() const {
+ return m_functions;
+ }
+ std::vector<TestCase> const& TestRegistry::getAllTestsSorted( IConfig const& config ) const {
+ if( m_sortedFunctions.empty() )
+ enforceNoDuplicateTestCases( m_functions );
+
+ if( m_currentSortOrder != config.runOrder() || m_sortedFunctions.empty() ) {
+ m_sortedFunctions = sortTests( config, m_functions );
+ m_currentSortOrder = config.runOrder();
+ }
+ return m_sortedFunctions;
+ }
+
+ ///////////////////////////////////////////////////////////////////////////
+ TestInvokerAsFunction::TestInvokerAsFunction( void(*testAsFunction)() ) noexcept : m_testAsFunction( testAsFunction ) {}
+
+ void TestInvokerAsFunction::invoke() const {
+ m_testAsFunction();
+ }
+
+ std::string extractClassName( StringRef const& classOrQualifiedMethodName ) {
+ std::string className(classOrQualifiedMethodName);
+ if( startsWith( className, '&' ) )
+ {
+ std::size_t lastColons = className.rfind( "::" );
+ std::size_t penultimateColons = className.rfind( "::", lastColons-1 );
+ if( penultimateColons == std::string::npos )
+ penultimateColons = 1;
+ className = className.substr( penultimateColons, lastColons-penultimateColons );
+ }
+ return className;
+ }
+
+} // end namespace Catch
+// end catch_test_case_registry_impl.cpp
+// start catch_test_case_tracker.cpp
+
+#include <algorithm>
+#include <cassert>
+#include <stdexcept>
+#include <memory>
+#include <sstream>
+
+#if defined(__clang__)
+# pragma clang diagnostic push
+# pragma clang diagnostic ignored "-Wexit-time-destructors"
+#endif
+
+namespace Catch {
+namespace TestCaseTracking {
+
+ NameAndLocation::NameAndLocation( std::string const& _name, SourceLineInfo const& _location )
+ : name( _name ),
+ location( _location )
+ {}
+
+ ITracker::~ITracker() = default;
+
+ ITracker& TrackerContext::startRun() {
+ m_rootTracker = std::make_shared<SectionTracker>( NameAndLocation( "{root}", CATCH_INTERNAL_LINEINFO ), *this, nullptr );
+ m_currentTracker = nullptr;
+ m_runState = Executing;
+ return *m_rootTracker;
+ }
+
+ void TrackerContext::endRun() {
+ m_rootTracker.reset();
+ m_currentTracker = nullptr;
+ m_runState = NotStarted;
+ }
+
+ void TrackerContext::startCycle() {
+ m_currentTracker = m_rootTracker.get();
+ m_runState = Executing;
+ }
+ void TrackerContext::completeCycle() {
+ m_runState = CompletedCycle;
+ }
+
+ bool TrackerContext::completedCycle() const {
+ return m_runState == CompletedCycle;
+ }
+ ITracker& TrackerContext::currentTracker() {
+ return *m_currentTracker;
+ }
+ void TrackerContext::setCurrentTracker( ITracker* tracker ) {
+ m_currentTracker = tracker;
+ }
+
+ TrackerBase::TrackerBase( NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent ):
+ ITracker(nameAndLocation),
+ m_ctx( ctx ),
+ m_parent( parent )
+ {}
+
+ bool TrackerBase::isComplete() const {
+ return m_runState == CompletedSuccessfully || m_runState == Failed;
+ }
+ bool TrackerBase::isSuccessfullyCompleted() const {
+ return m_runState == CompletedSuccessfully;
+ }
+ bool TrackerBase::isOpen() const {
+ return m_runState != NotStarted && !isComplete();
+ }
+ bool TrackerBase::hasChildren() const {
+ return !m_children.empty();
+ }
+
+ void TrackerBase::addChild( ITrackerPtr const& child ) {
+ m_children.push_back( child );
+ }
+
+ ITrackerPtr TrackerBase::findChild( NameAndLocation const& nameAndLocation ) {
+ auto it = std::find_if( m_children.begin(), m_children.end(),
+ [&nameAndLocation]( ITrackerPtr const& tracker ){
+ return
+ tracker->nameAndLocation().location == nameAndLocation.location &&
+ tracker->nameAndLocation().name == nameAndLocation.name;
+ } );
+ return( it != m_children.end() )
+ ? *it
+ : nullptr;
+ }
+ ITracker& TrackerBase::parent() {
+ assert( m_parent ); // Should always be non-null except for root
+ return *m_parent;
+ }
+
+ void TrackerBase::openChild() {
+ if( m_runState != ExecutingChildren ) {
+ m_runState = ExecutingChildren;
+ if( m_parent )
+ m_parent->openChild();
+ }
+ }
+
+ bool TrackerBase::isSectionTracker() const { return false; }
+ bool TrackerBase::isGeneratorTracker() const { return false; }
+
+ void TrackerBase::open() {
+ m_runState = Executing;
+ moveToThis();
+ if( m_parent )
+ m_parent->openChild();
+ }
+
+ void TrackerBase::close() {
+
+ // Close any still open children (e.g. generators)
+ while( &m_ctx.currentTracker() != this )
+ m_ctx.currentTracker().close();
+
+ switch( m_runState ) {
+ case NeedsAnotherRun:
+ break;
+
+ case Executing:
+ m_runState = CompletedSuccessfully;
+ break;
+ case ExecutingChildren:
+ if( std::all_of(m_children.begin(), m_children.end(), [](ITrackerPtr const& t){ return t->isComplete(); }) )
+ m_runState = CompletedSuccessfully;
+ break;
+
+ case NotStarted:
+ case CompletedSuccessfully:
+ case Failed:
+ CATCH_INTERNAL_ERROR( "Illogical state: " << m_runState );
+
+ default:
+ CATCH_INTERNAL_ERROR( "Unknown state: " << m_runState );
+ }
+ moveToParent();
+ m_ctx.completeCycle();
+ }
+ void TrackerBase::fail() {
+ m_runState = Failed;
+ if( m_parent )
+ m_parent->markAsNeedingAnotherRun();
+ moveToParent();
+ m_ctx.completeCycle();
+ }
+ void TrackerBase::markAsNeedingAnotherRun() {
+ m_runState = NeedsAnotherRun;
+ }
+
+ void TrackerBase::moveToParent() {
+ assert( m_parent );
+ m_ctx.setCurrentTracker( m_parent );
+ }
+ void TrackerBase::moveToThis() {
+ m_ctx.setCurrentTracker( this );
+ }
+
+ SectionTracker::SectionTracker( NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent )
+ : TrackerBase( nameAndLocation, ctx, parent ),
+ m_trimmed_name(trim(nameAndLocation.name))
+ {
+ if( parent ) {
+ while( !parent->isSectionTracker() )
+ parent = &parent->parent();
+
+ SectionTracker& parentSection = static_cast<SectionTracker&>( *parent );
+ addNextFilters( parentSection.m_filters );
+ }
+ }
+
+ bool SectionTracker::isComplete() const {
+ bool complete = true;
+
+ if (m_filters.empty()
+ || m_filters[0] == ""
+ || std::find(m_filters.begin(), m_filters.end(), m_trimmed_name) != m_filters.end()) {
+ complete = TrackerBase::isComplete();
+ }
+ return complete;
+ }
+
+ bool SectionTracker::isSectionTracker() const { return true; }
+
+ SectionTracker& SectionTracker::acquire( TrackerContext& ctx, NameAndLocation const& nameAndLocation ) {
+ std::shared_ptr<SectionTracker> section;
+
+ ITracker& currentTracker = ctx.currentTracker();
+ if( ITrackerPtr childTracker = currentTracker.findChild( nameAndLocation ) ) {
+ assert( childTracker );
+ assert( childTracker->isSectionTracker() );
+ section = std::static_pointer_cast<SectionTracker>( childTracker );
+ }
+ else {
+ section = std::make_shared<SectionTracker>( nameAndLocation, ctx, &currentTracker );
+ currentTracker.addChild( section );
+ }
+ if( !ctx.completedCycle() )
+ section->tryOpen();
+ return *section;
+ }
+
+ void SectionTracker::tryOpen() {
+ if( !isComplete() )
+ open();
+ }
+
+ void SectionTracker::addInitialFilters( std::vector<std::string> const& filters ) {
+ if( !filters.empty() ) {
+ m_filters.reserve( m_filters.size() + filters.size() + 2 );
+ m_filters.emplace_back(""); // Root - should never be consulted
+ m_filters.emplace_back(""); // Test Case - not a section filter
+ m_filters.insert( m_filters.end(), filters.begin(), filters.end() );
+ }
+ }
+ void SectionTracker::addNextFilters( std::vector<std::string> const& filters ) {
+ if( filters.size() > 1 )
+ m_filters.insert( m_filters.end(), filters.begin()+1, filters.end() );
+ }
+
+ std::vector<std::string> const& SectionTracker::getFilters() const {
+ return m_filters;
+ }
+
+ std::string const& SectionTracker::trimmedName() const {
+ return m_trimmed_name;
+ }
+
+} // namespace TestCaseTracking
+
+using TestCaseTracking::ITracker;
+using TestCaseTracking::TrackerContext;
+using TestCaseTracking::SectionTracker;
+
+} // namespace Catch
+
+#if defined(__clang__)
+# pragma clang diagnostic pop
+#endif
+// end catch_test_case_tracker.cpp
+// start catch_test_registry.cpp
+
+namespace Catch {
+
+ auto makeTestInvoker( void(*testAsFunction)() ) noexcept -> ITestInvoker* {
+ return new(std::nothrow) TestInvokerAsFunction( testAsFunction );
+ }
+
+ NameAndTags::NameAndTags( StringRef const& name_ , StringRef const& tags_ ) noexcept : name( name_ ), tags( tags_ ) {}
+
+ AutoReg::AutoReg( ITestInvoker* invoker, SourceLineInfo const& lineInfo, StringRef const& classOrMethod, NameAndTags const& nameAndTags ) noexcept {
+ CATCH_TRY {
+ getMutableRegistryHub()
+ .registerTest(
+ makeTestCase(
+ invoker,
+ extractClassName( classOrMethod ),
+ nameAndTags,
+ lineInfo));
+ } CATCH_CATCH_ALL {
+ // Do not throw when constructing global objects, instead register the exception to be processed later
+ getMutableRegistryHub().registerStartupException();
+ }
+ }
+
+ AutoReg::~AutoReg() = default;
+}
+// end catch_test_registry.cpp
+// start catch_test_spec.cpp
+
+#include <algorithm>
+#include <string>
+#include <vector>
+#include <memory>
+
+namespace Catch {
+
+ TestSpec::Pattern::Pattern( std::string const& name )
+ : m_name( name )
+ {}
+
+ TestSpec::Pattern::~Pattern() = default;
+
+ std::string const& TestSpec::Pattern::name() const {
+ return m_name;
+ }
+
+ TestSpec::NamePattern::NamePattern( std::string const& name, std::string const& filterString )
+ : Pattern( filterString )
+ , m_wildcardPattern( toLower( name ), CaseSensitive::No )
+ {}
+
+ bool TestSpec::NamePattern::matches( TestCaseInfo const& testCase ) const {
+ return m_wildcardPattern.matches( testCase.name );
+ }
+
+ TestSpec::TagPattern::TagPattern( std::string const& tag, std::string const& filterString )
+ : Pattern( filterString )
+ , m_tag( toLower( tag ) )
+ {}
+
+ bool TestSpec::TagPattern::matches( TestCaseInfo const& testCase ) const {
+ return std::find(begin(testCase.lcaseTags),
+ end(testCase.lcaseTags),
+ m_tag) != end(testCase.lcaseTags);
+ }
+
+ TestSpec::ExcludedPattern::ExcludedPattern( PatternPtr const& underlyingPattern )
+ : Pattern( underlyingPattern->name() )
+ , m_underlyingPattern( underlyingPattern )
+ {}
+
+ bool TestSpec::ExcludedPattern::matches( TestCaseInfo const& testCase ) const {
+ return !m_underlyingPattern->matches( testCase );
+ }
+
+ bool TestSpec::Filter::matches( TestCaseInfo const& testCase ) const {
+ return std::all_of( m_patterns.begin(), m_patterns.end(), [&]( PatternPtr const& p ){ return p->matches( testCase ); } );
+ }
+
+ std::string TestSpec::Filter::name() const {
+ std::string name;
+ for( auto const& p : m_patterns )
+ name += p->name();
+ return name;
+ }
+
+ bool TestSpec::hasFilters() const {
+ return !m_filters.empty();
+ }
+
+ bool TestSpec::matches( TestCaseInfo const& testCase ) const {
+ return std::any_of( m_filters.begin(), m_filters.end(), [&]( Filter const& f ){ return f.matches( testCase ); } );
+ }
+
+ TestSpec::Matches TestSpec::matchesByFilter( std::vector<TestCase> const& testCases, IConfig const& config ) const
+ {
+ Matches matches( m_filters.size() );
+ std::transform( m_filters.begin(), m_filters.end(), matches.begin(), [&]( Filter const& filter ){
+ std::vector<TestCase const*> currentMatches;
+ for( auto const& test : testCases )
+ if( isThrowSafe( test, config ) && filter.matches( test ) )
+ currentMatches.emplace_back( &test );
+ return FilterMatch{ filter.name(), currentMatches };
+ } );
+ return matches;
+ }
+
+ const TestSpec::vectorStrings& TestSpec::getInvalidArgs() const{
+ return (m_invalidArgs);
+ }
+
+}
+// end catch_test_spec.cpp
+// start catch_test_spec_parser.cpp
+
+namespace Catch {
+
+ TestSpecParser::TestSpecParser( ITagAliasRegistry const& tagAliases ) : m_tagAliases( &tagAliases ) {}
+
+ TestSpecParser& TestSpecParser::parse( std::string const& arg ) {
+ m_mode = None;
+ m_exclusion = false;
+ m_arg = m_tagAliases->expandAliases( arg );
+ m_escapeChars.clear();
+ m_substring.reserve(m_arg.size());
+ m_patternName.reserve(m_arg.size());
+ m_realPatternPos = 0;
+
+ for( m_pos = 0; m_pos < m_arg.size(); ++m_pos )
+ //if visitChar fails
+ if( !visitChar( m_arg[m_pos] ) ){
+ m_testSpec.m_invalidArgs.push_back(arg);
+ break;
+ }
+ endMode();
+ return *this;
+ }
+ TestSpec TestSpecParser::testSpec() {
+ addFilter();
+ return m_testSpec;
+ }
+ bool TestSpecParser::visitChar( char c ) {
+ if( (m_mode != EscapedName) && (c == '\\') ) {
+ escape();
+ addCharToPattern(c);
+ return true;
+ }else if((m_mode != EscapedName) && (c == ',') ) {
+ return separate();
+ }
+
+ switch( m_mode ) {
+ case None:
+ if( processNoneChar( c ) )
+ return true;
+ break;
+ case Name:
+ processNameChar( c );
+ break;
+ case EscapedName:
+ endMode();
+ addCharToPattern(c);
+ return true;
+ default:
+ case Tag:
+ case QuotedName:
+ if( processOtherChar( c ) )
+ return true;
+ break;
+ }
+
+ m_substring += c;
+ if( !isControlChar( c ) ) {
+ m_patternName += c;
+ m_realPatternPos++;
+ }
+ return true;
+ }
+ // Two of the processing methods return true to signal the caller to return
+ // without adding the given character to the current pattern strings
+ bool TestSpecParser::processNoneChar( char c ) {
+ switch( c ) {
+ case ' ':
+ return true;
+ case '~':
+ m_exclusion = true;
+ return false;
+ case '[':
+ startNewMode( Tag );
+ return false;
+ case '"':
+ startNewMode( QuotedName );
+ return false;
+ default:
+ startNewMode( Name );
+ return false;
+ }
+ }
+ void TestSpecParser::processNameChar( char c ) {
+ if( c == '[' ) {
+ if( m_substring == "exclude:" )
+ m_exclusion = true;
+ else
+ endMode();
+ startNewMode( Tag );
+ }
+ }
+ bool TestSpecParser::processOtherChar( char c ) {
+ if( !isControlChar( c ) )
+ return false;
+ m_substring += c;
+ endMode();
+ return true;
+ }
+ void TestSpecParser::startNewMode( Mode mode ) {
+ m_mode = mode;
+ }
+ void TestSpecParser::endMode() {
+ switch( m_mode ) {
+ case Name:
+ case QuotedName:
+ return addNamePattern();
+ case Tag:
+ return addTagPattern();
+ case EscapedName:
+ revertBackToLastMode();
+ return;
+ case None:
+ default:
+ return startNewMode( None );
+ }
+ }
+ void TestSpecParser::escape() {
+ saveLastMode();
+ m_mode = EscapedName;
+ m_escapeChars.push_back(m_realPatternPos);
+ }
+ bool TestSpecParser::isControlChar( char c ) const {
+ switch( m_mode ) {
+ default:
+ return false;
+ case None:
+ return c == '~';
+ case Name:
+ return c == '[';
+ case EscapedName:
+ return true;
+ case QuotedName:
+ return c == '"';
+ case Tag:
+ return c == '[' || c == ']';
+ }
+ }
+
+ void TestSpecParser::addFilter() {
+ if( !m_currentFilter.m_patterns.empty() ) {
+ m_testSpec.m_filters.push_back( m_currentFilter );
+ m_currentFilter = TestSpec::Filter();
+ }
+ }
+
+ void TestSpecParser::saveLastMode() {
+ lastMode = m_mode;
+ }
+
+ void TestSpecParser::revertBackToLastMode() {
+ m_mode = lastMode;
+ }
+
+ bool TestSpecParser::separate() {
+ if( (m_mode==QuotedName) || (m_mode==Tag) ){
+ //invalid argument, signal failure to previous scope.
+ m_mode = None;
+ m_pos = m_arg.size();
+ m_substring.clear();
+ m_patternName.clear();
+ m_realPatternPos = 0;
+ return false;
+ }
+ endMode();
+ addFilter();
+ return true; //success
+ }
+
+ std::string TestSpecParser::preprocessPattern() {
+ std::string token = m_patternName;
+ for (std::size_t i = 0; i < m_escapeChars.size(); ++i)
+ token = token.substr(0, m_escapeChars[i] - i) + token.substr(m_escapeChars[i] - i + 1);
+ m_escapeChars.clear();
+ if (startsWith(token, "exclude:")) {
+ m_exclusion = true;
+ token = token.substr(8);
+ }
+
+ m_patternName.clear();
+ m_realPatternPos = 0;
+
+ return token;
+ }
+
+ void TestSpecParser::addNamePattern() {
+ auto token = preprocessPattern();
+
+ if (!token.empty()) {
+ TestSpec::PatternPtr pattern = std::make_shared<TestSpec::NamePattern>(token, m_substring);
+ if (m_exclusion)
+ pattern = std::make_shared<TestSpec::ExcludedPattern>(pattern);
+ m_currentFilter.m_patterns.push_back(pattern);
+ }
+ m_substring.clear();
+ m_exclusion = false;
+ m_mode = None;
+ }
+
+ void TestSpecParser::addTagPattern() {
+ auto token = preprocessPattern();
+
+ if (!token.empty()) {
+ // If the tag pattern is the "hide and tag" shorthand (e.g. [.foo])
+ // we have to create a separate hide tag and shorten the real one
+ if (token.size() > 1 && token[0] == '.') {
+ token.erase(token.begin());
+ TestSpec::PatternPtr pattern = std::make_shared<TestSpec::TagPattern>(".", m_substring);
+ if (m_exclusion) {
+ pattern = std::make_shared<TestSpec::ExcludedPattern>(pattern);
+ }
+ m_currentFilter.m_patterns.push_back(pattern);
+ }
+
+ TestSpec::PatternPtr pattern = std::make_shared<TestSpec::TagPattern>(token, m_substring);
+
+ if (m_exclusion) {
+ pattern = std::make_shared<TestSpec::ExcludedPattern>(pattern);
+ }
+ m_currentFilter.m_patterns.push_back(pattern);
+ }
+ m_substring.clear();
+ m_exclusion = false;
+ m_mode = None;
+ }
+
+ TestSpec parseTestSpec( std::string const& arg ) {
+ return TestSpecParser( ITagAliasRegistry::get() ).parse( arg ).testSpec();
+ }
+
+} // namespace Catch
+// end catch_test_spec_parser.cpp
+// start catch_timer.cpp
+
+#include <chrono>
+
+static const uint64_t nanosecondsInSecond = 1000000000;
+
+namespace Catch {
+
+ auto getCurrentNanosecondsSinceEpoch() -> uint64_t {
+ return std::chrono::duration_cast<std::chrono::nanoseconds>( std::chrono::high_resolution_clock::now().time_since_epoch() ).count();
+ }
+
+ namespace {
+ auto estimateClockResolution() -> uint64_t {
+ uint64_t sum = 0;
+ static const uint64_t iterations = 1000000;
+
+ auto startTime = getCurrentNanosecondsSinceEpoch();
+
+ for( std::size_t i = 0; i < iterations; ++i ) {
+
+ uint64_t ticks;
+ uint64_t baseTicks = getCurrentNanosecondsSinceEpoch();
+ do {
+ ticks = getCurrentNanosecondsSinceEpoch();
+ } while( ticks == baseTicks );
+
+ auto delta = ticks - baseTicks;
+ sum += delta;
+
+ // If we have been calibrating for over 3 seconds -- the clock
+ // is terrible and we should move on.
+ // TBD: How to signal that the measured resolution is probably wrong?
+ if (ticks > startTime + 3 * nanosecondsInSecond) {
+ return sum / ( i + 1u );
+ }
+ }
+
+ // We're just taking the mean, here. To do better we could take the std. dev and exclude outliers
+ // - and potentially do more iterations if there's a high variance.
+ return sum/iterations;
+ }
+ }
+ auto getEstimatedClockResolution() -> uint64_t {
+ static auto s_resolution = estimateClockResolution();
+ return s_resolution;
+ }
+
+ void Timer::start() {
+ m_nanoseconds = getCurrentNanosecondsSinceEpoch();
+ }
+ auto Timer::getElapsedNanoseconds() const -> uint64_t {
+ return getCurrentNanosecondsSinceEpoch() - m_nanoseconds;
+ }
+ auto Timer::getElapsedMicroseconds() const -> uint64_t {
+ return getElapsedNanoseconds()/1000;
+ }
+ auto Timer::getElapsedMilliseconds() const -> unsigned int {
+ return static_cast<unsigned int>(getElapsedMicroseconds()/1000);
+ }
+ auto Timer::getElapsedSeconds() const -> double {
+ return getElapsedMicroseconds()/1000000.0;
+ }
+
+} // namespace Catch
+// end catch_timer.cpp
+// start catch_tostring.cpp
+
+#if defined(__clang__)
+# pragma clang diagnostic push
+# pragma clang diagnostic ignored "-Wexit-time-destructors"
+# pragma clang diagnostic ignored "-Wglobal-constructors"
+#endif
+
+// Enable specific decls locally
+#if !defined(CATCH_CONFIG_ENABLE_CHRONO_STRINGMAKER)
+#define CATCH_CONFIG_ENABLE_CHRONO_STRINGMAKER
+#endif
+
+#include <cmath>
+#include <iomanip>
+
+namespace Catch {
+
+namespace Detail {
+
+ const std::string unprintableString = "{?}";
+
+ namespace {
+ const int hexThreshold = 255;
+
+ struct Endianness {
+ enum Arch { Big, Little };
+
+ static Arch which() {
+ int one = 1;
+ // If the lowest byte we read is non-zero, we can assume
+ // that little endian format is used.
+ auto value = *reinterpret_cast<char*>(&one);
+ return value ? Little : Big;
+ }
+ };
+ }
+
+ std::string rawMemoryToString( const void *object, std::size_t size ) {
+ // Reverse order for little endian architectures
+ int i = 0, end = static_cast<int>( size ), inc = 1;
+ if( Endianness::which() == Endianness::Little ) {
+ i = end-1;
+ end = inc = -1;
+ }
+
+ unsigned char const *bytes = static_cast<unsigned char const *>(object);
+ ReusableStringStream rss;
+ rss << "0x" << std::setfill('0') << std::hex;
+ for( ; i != end; i += inc )
+ rss << std::setw(2) << static_cast<unsigned>(bytes[i]);
+ return rss.str();
+ }
+}
+
+template<typename T>
+std::string fpToString( T value, int precision ) {
+ if (Catch::isnan(value)) {
+ return "nan";
+ }
+
+ ReusableStringStream rss;
+ rss << std::setprecision( precision )
+ << std::fixed
+ << value;
+ std::string d = rss.str();
+ std::size_t i = d.find_last_not_of( '0' );
+ if( i != std::string::npos && i != d.size()-1 ) {
+ if( d[i] == '.' )
+ i++;
+ d = d.substr( 0, i+1 );
+ }
+ return d;
+}
+
+//// ======================================================= ////
+//
+// Out-of-line defs for full specialization of StringMaker
+//
+//// ======================================================= ////
+
+std::string StringMaker<std::string>::convert(const std::string& str) {
+ if (!getCurrentContext().getConfig()->showInvisibles()) {
+ return '"' + str + '"';
+ }
+
+ std::string s("\"");
+ for (char c : str) {
+ switch (c) {
+ case '\n':
+ s.append("\\n");
+ break;
+ case '\t':
+ s.append("\\t");
+ break;
+ default:
+ s.push_back(c);
+ break;
+ }
+ }
+ s.append("\"");
+ return s;
+}
+
+#ifdef CATCH_CONFIG_CPP17_STRING_VIEW
+std::string StringMaker<std::string_view>::convert(std::string_view str) {
+ return ::Catch::Detail::stringify(std::string{ str });
+}
+#endif
+
+std::string StringMaker<char const*>::convert(char const* str) {
+ if (str) {
+ return ::Catch::Detail::stringify(std::string{ str });
+ } else {
+ return{ "{null string}" };
+ }
+}
+std::string StringMaker<char*>::convert(char* str) {
+ if (str) {
+ return ::Catch::Detail::stringify(std::string{ str });
+ } else {
+ return{ "{null string}" };
+ }
+}
+
+#ifdef CATCH_CONFIG_WCHAR
+std::string StringMaker<std::wstring>::convert(const std::wstring& wstr) {
+ std::string s;
+ s.reserve(wstr.size());
+ for (auto c : wstr) {
+ s += (c <= 0xff) ? static_cast<char>(c) : '?';
+ }
+ return ::Catch::Detail::stringify(s);
+}
+
+# ifdef CATCH_CONFIG_CPP17_STRING_VIEW
+std::string StringMaker<std::wstring_view>::convert(std::wstring_view str) {
+ return StringMaker<std::wstring>::convert(std::wstring(str));
+}
+# endif
+
+std::string StringMaker<wchar_t const*>::convert(wchar_t const * str) {
+ if (str) {
+ return ::Catch::Detail::stringify(std::wstring{ str });
+ } else {
+ return{ "{null string}" };
+ }
+}
+std::string StringMaker<wchar_t *>::convert(wchar_t * str) {
+ if (str) {
+ return ::Catch::Detail::stringify(std::wstring{ str });
+ } else {
+ return{ "{null string}" };
+ }
+}
+#endif
+
+#if defined(CATCH_CONFIG_CPP17_BYTE)
+#include <cstddef>
+std::string StringMaker<std::byte>::convert(std::byte value) {
+ return ::Catch::Detail::stringify(std::to_integer<unsigned long long>(value));
+}
+#endif // defined(CATCH_CONFIG_CPP17_BYTE)
+
+std::string StringMaker<int>::convert(int value) {
+ return ::Catch::Detail::stringify(static_cast<long long>(value));
+}
+std::string StringMaker<long>::convert(long value) {
+ return ::Catch::Detail::stringify(static_cast<long long>(value));
+}
+std::string StringMaker<long long>::convert(long long value) {
+ ReusableStringStream rss;
+ rss << value;
+ if (value > Detail::hexThreshold) {
+ rss << " (0x" << std::hex << value << ')';
+ }
+ return rss.str();
+}
+
+std::string StringMaker<unsigned int>::convert(unsigned int value) {
+ return ::Catch::Detail::stringify(static_cast<unsigned long long>(value));
+}
+std::string StringMaker<unsigned long>::convert(unsigned long value) {
+ return ::Catch::Detail::stringify(static_cast<unsigned long long>(value));
+}
+std::string StringMaker<unsigned long long>::convert(unsigned long long value) {
+ ReusableStringStream rss;
+ rss << value;
+ if (value > Detail::hexThreshold) {
+ rss << " (0x" << std::hex << value << ')';
+ }
+ return rss.str();
+}
+
+std::string StringMaker<bool>::convert(bool b) {
+ return b ? "true" : "false";
+}
+
+std::string StringMaker<signed char>::convert(signed char value) {
+ if (value == '\r') {
+ return "'\\r'";
+ } else if (value == '\f') {
+ return "'\\f'";
+ } else if (value == '\n') {
+ return "'\\n'";
+ } else if (value == '\t') {
+ return "'\\t'";
+ } else if ('\0' <= value && value < ' ') {
+ return ::Catch::Detail::stringify(static_cast<unsigned int>(value));
+ } else {
+ char chstr[] = "' '";
+ chstr[1] = value;
+ return chstr;
+ }
+}
+std::string StringMaker<char>::convert(char c) {
+ return ::Catch::Detail::stringify(static_cast<signed char>(c));
+}
+std::string StringMaker<unsigned char>::convert(unsigned char c) {
+ return ::Catch::Detail::stringify(static_cast<char>(c));
+}
+
+std::string StringMaker<std::nullptr_t>::convert(std::nullptr_t) {
+ return "nullptr";
+}
+
+int StringMaker<float>::precision = 5;
+
+std::string StringMaker<float>::convert(float value) {
+ return fpToString(value, precision) + 'f';
+}
+
+int StringMaker<double>::precision = 10;
+
+std::string StringMaker<double>::convert(double value) {
+ return fpToString(value, precision);
+}
+
+std::string ratio_string<std::atto>::symbol() { return "a"; }
+std::string ratio_string<std::femto>::symbol() { return "f"; }
+std::string ratio_string<std::pico>::symbol() { return "p"; }
+std::string ratio_string<std::nano>::symbol() { return "n"; }
+std::string ratio_string<std::micro>::symbol() { return "u"; }
+std::string ratio_string<std::milli>::symbol() { return "m"; }
+
+} // end namespace Catch
+
+#if defined(__clang__)
+# pragma clang diagnostic pop
+#endif
+
+// end catch_tostring.cpp
+// start catch_totals.cpp
+
+namespace Catch {
+
+ Counts Counts::operator - ( Counts const& other ) const {
+ Counts diff;
+ diff.passed = passed - other.passed;
+ diff.failed = failed - other.failed;
+ diff.failedButOk = failedButOk - other.failedButOk;
+ return diff;
+ }
+
+ Counts& Counts::operator += ( Counts const& other ) {
+ passed += other.passed;
+ failed += other.failed;
+ failedButOk += other.failedButOk;
+ return *this;
+ }
+
+ std::size_t Counts::total() const {
+ return passed + failed + failedButOk;
+ }
+ bool Counts::allPassed() const {
+ return failed == 0 && failedButOk == 0;
+ }
+ bool Counts::allOk() const {
+ return failed == 0;
+ }
+
+ Totals Totals::operator - ( Totals const& other ) const {
+ Totals diff;
+ diff.assertions = assertions - other.assertions;
+ diff.testCases = testCases - other.testCases;
+ return diff;
+ }
+
+ Totals& Totals::operator += ( Totals const& other ) {
+ assertions += other.assertions;
+ testCases += other.testCases;
+ return *this;
+ }
+
+ Totals Totals::delta( Totals const& prevTotals ) const {
+ Totals diff = *this - prevTotals;
+ if( diff.assertions.failed > 0 )
+ ++diff.testCases.failed;
+ else if( diff.assertions.failedButOk > 0 )
+ ++diff.testCases.failedButOk;
+ else
+ ++diff.testCases.passed;
+ return diff;
+ }
+
+}
+// end catch_totals.cpp
+// start catch_uncaught_exceptions.cpp
+
+// start catch_config_uncaught_exceptions.hpp
+
+// Copyright Catch2 Authors
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at
+// https://www.boost.org/LICENSE_1_0.txt)
+
+// SPDX-License-Identifier: BSL-1.0
+
+#ifndef CATCH_CONFIG_UNCAUGHT_EXCEPTIONS_HPP
+#define CATCH_CONFIG_UNCAUGHT_EXCEPTIONS_HPP
+
+#if defined(_MSC_VER)
+# if _MSC_VER >= 1900 // Visual Studio 2015 or newer
+# define CATCH_INTERNAL_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS
+# endif
+#endif
+
+#include <exception>
+
+#if defined(__cpp_lib_uncaught_exceptions) \
+ && !defined(CATCH_INTERNAL_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS)
+
+# define CATCH_INTERNAL_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS
+#endif // __cpp_lib_uncaught_exceptions
+
+#if defined(CATCH_INTERNAL_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS) \
+ && !defined(CATCH_CONFIG_NO_CPP17_UNCAUGHT_EXCEPTIONS) \
+ && !defined(CATCH_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS)
+
+# define CATCH_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS
+#endif
+
+#endif // CATCH_CONFIG_UNCAUGHT_EXCEPTIONS_HPP
+// end catch_config_uncaught_exceptions.hpp
+#include <exception>
+
+namespace Catch {
+ bool uncaught_exceptions() {
+#if defined(CATCH_CONFIG_DISABLE_EXCEPTIONS)
+ return false;
+#elif defined(CATCH_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS)
+ return std::uncaught_exceptions() > 0;
+#else
+ return std::uncaught_exception();
+#endif
+ }
+} // end namespace Catch
+// end catch_uncaught_exceptions.cpp
+// start catch_version.cpp
+
+#include <ostream>
+
+namespace Catch {
+
+ Version::Version
+ ( unsigned int _majorVersion,
+ unsigned int _minorVersion,
+ unsigned int _patchNumber,
+ char const * const _branchName,
+ unsigned int _buildNumber )
+ : majorVersion( _majorVersion ),
+ minorVersion( _minorVersion ),
+ patchNumber( _patchNumber ),
+ branchName( _branchName ),
+ buildNumber( _buildNumber )
+ {}
+
+ std::ostream& operator << ( std::ostream& os, Version const& version ) {
+ os << version.majorVersion << '.'
+ << version.minorVersion << '.'
+ << version.patchNumber;
+ // branchName is never null -> 0th char is \0 if it is empty
+ if (version.branchName[0]) {
+ os << '-' << version.branchName
+ << '.' << version.buildNumber;
+ }
+ return os;
+ }
+
+ Version const& libraryVersion() {
+ static Version version( 2, 13, 10, "", 0 );
+ return version;
+ }
+
+}
+// end catch_version.cpp
+// start catch_wildcard_pattern.cpp
+
+namespace Catch {
+
+ WildcardPattern::WildcardPattern( std::string const& pattern,
+ CaseSensitive::Choice caseSensitivity )
+ : m_caseSensitivity( caseSensitivity ),
+ m_pattern( normaliseString( pattern ) )
+ {
+ if( startsWith( m_pattern, '*' ) ) {
+ m_pattern = m_pattern.substr( 1 );
+ m_wildcard = WildcardAtStart;
+ }
+ if( endsWith( m_pattern, '*' ) ) {
+ m_pattern = m_pattern.substr( 0, m_pattern.size()-1 );
+ m_wildcard = static_cast<WildcardPosition>( m_wildcard | WildcardAtEnd );
+ }
+ }
+
+ bool WildcardPattern::matches( std::string const& str ) const {
+ switch( m_wildcard ) {
+ case NoWildcard:
+ return m_pattern == normaliseString( str );
+ case WildcardAtStart:
+ return endsWith( normaliseString( str ), m_pattern );
+ case WildcardAtEnd:
+ return startsWith( normaliseString( str ), m_pattern );
+ case WildcardAtBothEnds:
+ return contains( normaliseString( str ), m_pattern );
+ default:
+ CATCH_INTERNAL_ERROR( "Unknown enum" );
+ }
+ }
+
+ std::string WildcardPattern::normaliseString( std::string const& str ) const {
+ return trim( m_caseSensitivity == CaseSensitive::No ? toLower( str ) : str );
+ }
+}
+// end catch_wildcard_pattern.cpp
+// start catch_xmlwriter.cpp
+
+#include <iomanip>
+#include <type_traits>
+
+namespace Catch {
+
+namespace {
+
+ size_t trailingBytes(unsigned char c) {
+ if ((c & 0xE0) == 0xC0) {
+ return 2;
+ }
+ if ((c & 0xF0) == 0xE0) {
+ return 3;
+ }
+ if ((c & 0xF8) == 0xF0) {
+ return 4;
+ }
+ CATCH_INTERNAL_ERROR("Invalid multibyte utf-8 start byte encountered");
+ }
+
+ uint32_t headerValue(unsigned char c) {
+ if ((c & 0xE0) == 0xC0) {
+ return c & 0x1F;
+ }
+ if ((c & 0xF0) == 0xE0) {
+ return c & 0x0F;
+ }
+ if ((c & 0xF8) == 0xF0) {
+ return c & 0x07;
+ }
+ CATCH_INTERNAL_ERROR("Invalid multibyte utf-8 start byte encountered");
+ }
+
+ void hexEscapeChar(std::ostream& os, unsigned char c) {
+ std::ios_base::fmtflags f(os.flags());
+ os << "\\x"
+ << std::uppercase << std::hex << std::setfill('0') << std::setw(2)
+ << static_cast<int>(c);
+ os.flags(f);
+ }
+
+ bool shouldNewline(XmlFormatting fmt) {
+ return !!(static_cast<std::underlying_type<XmlFormatting>::type>(fmt & XmlFormatting::Newline));
+ }
+
+ bool shouldIndent(XmlFormatting fmt) {
+ return !!(static_cast<std::underlying_type<XmlFormatting>::type>(fmt & XmlFormatting::Indent));
+ }
+
+} // anonymous namespace
+
+ XmlFormatting operator | (XmlFormatting lhs, XmlFormatting rhs) {
+ return static_cast<XmlFormatting>(
+ static_cast<std::underlying_type<XmlFormatting>::type>(lhs) |
+ static_cast<std::underlying_type<XmlFormatting>::type>(rhs)
+ );
+ }
+
+ XmlFormatting operator & (XmlFormatting lhs, XmlFormatting rhs) {
+ return static_cast<XmlFormatting>(
+ static_cast<std::underlying_type<XmlFormatting>::type>(lhs) &
+ static_cast<std::underlying_type<XmlFormatting>::type>(rhs)
+ );
+ }
+
+ XmlEncode::XmlEncode( std::string const& str, ForWhat forWhat )
+ : m_str( str ),
+ m_forWhat( forWhat )
+ {}
+
+ void XmlEncode::encodeTo( std::ostream& os ) const {
+ // Apostrophe escaping not necessary if we always use " to write attributes
+ // (see: http://www.w3.org/TR/xml/#syntax)
+
+ for( std::size_t idx = 0; idx < m_str.size(); ++ idx ) {
+ unsigned char c = m_str[idx];
+ switch (c) {
+ case '<': os << "&lt;"; break;
+ case '&': os << "&amp;"; break;
+
+ case '>':
+ // See: http://www.w3.org/TR/xml/#syntax
+ if (idx > 2 && m_str[idx - 1] == ']' && m_str[idx - 2] == ']')
+ os << "&gt;";
+ else
+ os << c;
+ break;
+
+ case '\"':
+ if (m_forWhat == ForAttributes)
+ os << "&quot;";
+ else
+ os << c;
+ break;
+
+ default:
+ // Check for control characters and invalid utf-8
+
+ // Escape control characters in standard ascii
+ // see http://stackoverflow.com/questions/404107/why-are-control-characters-illegal-in-xml-1-0
+ if (c < 0x09 || (c > 0x0D && c < 0x20) || c == 0x7F) {
+ hexEscapeChar(os, c);
+ break;
+ }
+
+ // Plain ASCII: Write it to stream
+ if (c < 0x7F) {
+ os << c;
+ break;
+ }
+
+ // UTF-8 territory
+ // Check if the encoding is valid and if it is not, hex escape bytes.
+ // Important: We do not check the exact decoded values for validity, only the encoding format
+ // First check that this bytes is a valid lead byte:
+ // This means that it is not encoded as 1111 1XXX
+ // Or as 10XX XXXX
+ if (c < 0xC0 ||
+ c >= 0xF8) {
+ hexEscapeChar(os, c);
+ break;
+ }
+
+ auto encBytes = trailingBytes(c);
+ // Are there enough bytes left to avoid accessing out-of-bounds memory?
+ if (idx + encBytes - 1 >= m_str.size()) {
+ hexEscapeChar(os, c);
+ break;
+ }
+ // The header is valid, check data
+ // The next encBytes bytes must together be a valid utf-8
+ // This means: bitpattern 10XX XXXX and the extracted value is sane (ish)
+ bool valid = true;
+ uint32_t value = headerValue(c);
+ for (std::size_t n = 1; n < encBytes; ++n) {
+ unsigned char nc = m_str[idx + n];
+ valid &= ((nc & 0xC0) == 0x80);
+ value = (value << 6) | (nc & 0x3F);
+ }
+
+ if (
+ // Wrong bit pattern of following bytes
+ (!valid) ||
+ // Overlong encodings
+ (value < 0x80) ||
+ (0x80 <= value && value < 0x800 && encBytes > 2) ||
+ (0x800 < value && value < 0x10000 && encBytes > 3) ||
+ // Encoded value out of range
+ (value >= 0x110000)
+ ) {
+ hexEscapeChar(os, c);
+ break;
+ }
+
+ // If we got here, this is in fact a valid(ish) utf-8 sequence
+ for (std::size_t n = 0; n < encBytes; ++n) {
+ os << m_str[idx + n];
+ }
+ idx += encBytes - 1;
+ break;
+ }
+ }
+ }
+
+ std::ostream& operator << ( std::ostream& os, XmlEncode const& xmlEncode ) {
+ xmlEncode.encodeTo( os );
+ return os;
+ }
+
+ XmlWriter::ScopedElement::ScopedElement( XmlWriter* writer, XmlFormatting fmt )
+ : m_writer( writer ),
+ m_fmt(fmt)
+ {}
+
+ XmlWriter::ScopedElement::ScopedElement( ScopedElement&& other ) noexcept
+ : m_writer( other.m_writer ),
+ m_fmt(other.m_fmt)
+ {
+ other.m_writer = nullptr;
+ other.m_fmt = XmlFormatting::None;
+ }
+ XmlWriter::ScopedElement& XmlWriter::ScopedElement::operator=( ScopedElement&& other ) noexcept {
+ if ( m_writer ) {
+ m_writer->endElement();
+ }
+ m_writer = other.m_writer;
+ other.m_writer = nullptr;
+ m_fmt = other.m_fmt;
+ other.m_fmt = XmlFormatting::None;
+ return *this;
+ }
+
+ XmlWriter::ScopedElement::~ScopedElement() {
+ if (m_writer) {
+ m_writer->endElement(m_fmt);
+ }
+ }
+
+ XmlWriter::ScopedElement& XmlWriter::ScopedElement::writeText( std::string const& text, XmlFormatting fmt ) {
+ m_writer->writeText( text, fmt );
+ return *this;
+ }
+
+ XmlWriter::XmlWriter( std::ostream& os ) : m_os( os )
+ {
+ writeDeclaration();
+ }
+
+ XmlWriter::~XmlWriter() {
+ while (!m_tags.empty()) {
+ endElement();
+ }
+ newlineIfNecessary();
+ }
+
+ XmlWriter& XmlWriter::startElement( std::string const& name, XmlFormatting fmt ) {
+ ensureTagClosed();
+ newlineIfNecessary();
+ if (shouldIndent(fmt)) {
+ m_os << m_indent;
+ m_indent += " ";
+ }
+ m_os << '<' << name;
+ m_tags.push_back( name );
+ m_tagIsOpen = true;
+ applyFormatting(fmt);
+ return *this;
+ }
+
+ XmlWriter::ScopedElement XmlWriter::scopedElement( std::string const& name, XmlFormatting fmt ) {
+ ScopedElement scoped( this, fmt );
+ startElement( name, fmt );
+ return scoped;
+ }
+
+ XmlWriter& XmlWriter::endElement(XmlFormatting fmt) {
+ m_indent = m_indent.substr(0, m_indent.size() - 2);
+
+ if( m_tagIsOpen ) {
+ m_os << "/>";
+ m_tagIsOpen = false;
+ } else {
+ newlineIfNecessary();
+ if (shouldIndent(fmt)) {
+ m_os << m_indent;
+ }
+ m_os << "</" << m_tags.back() << ">";
+ }
+ m_os << std::flush;
+ applyFormatting(fmt);
+ m_tags.pop_back();
+ return *this;
+ }
+
+ XmlWriter& XmlWriter::writeAttribute( std::string const& name, std::string const& attribute ) {
+ if( !name.empty() && !attribute.empty() )
+ m_os << ' ' << name << "=\"" << XmlEncode( attribute, XmlEncode::ForAttributes ) << '"';
+ return *this;
+ }
+
+ XmlWriter& XmlWriter::writeAttribute( std::string const& name, bool attribute ) {
+ m_os << ' ' << name << "=\"" << ( attribute ? "true" : "false" ) << '"';
+ return *this;
+ }
+
+ XmlWriter& XmlWriter::writeText( std::string const& text, XmlFormatting fmt) {
+ if( !text.empty() ){
+ bool tagWasOpen = m_tagIsOpen;
+ ensureTagClosed();
+ if (tagWasOpen && shouldIndent(fmt)) {
+ m_os << m_indent;
+ }
+ m_os << XmlEncode( text );
+ applyFormatting(fmt);
+ }
+ return *this;
+ }
+
+ XmlWriter& XmlWriter::writeComment( std::string const& text, XmlFormatting fmt) {
+ ensureTagClosed();
+ if (shouldIndent(fmt)) {
+ m_os << m_indent;
+ }
+ m_os << "<!--" << text << "-->";
+ applyFormatting(fmt);
+ return *this;
+ }
+
+ void XmlWriter::writeStylesheetRef( std::string const& url ) {
+ m_os << "<?xml-stylesheet type=\"text/xsl\" href=\"" << url << "\"?>\n";
+ }
+
+ XmlWriter& XmlWriter::writeBlankLine() {
+ ensureTagClosed();
+ m_os << '\n';
+ return *this;
+ }
+
+ void XmlWriter::ensureTagClosed() {
+ if( m_tagIsOpen ) {
+ m_os << '>' << std::flush;
+ newlineIfNecessary();
+ m_tagIsOpen = false;
+ }
+ }
+
+ void XmlWriter::applyFormatting(XmlFormatting fmt) {
+ m_needsNewline = shouldNewline(fmt);
+ }
+
+ void XmlWriter::writeDeclaration() {
+ m_os << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
+ }
+
+ void XmlWriter::newlineIfNecessary() {
+ if( m_needsNewline ) {
+ m_os << std::endl;
+ m_needsNewline = false;
+ }
+ }
+}
+// end catch_xmlwriter.cpp
+// start catch_reporter_bases.cpp
+
+#include <cstring>
+#include <cfloat>
+#include <cstdio>
+#include <cassert>
+#include <memory>
+
+namespace Catch {
+ void prepareExpandedExpression(AssertionResult& result) {
+ result.getExpandedExpression();
+ }
+
+ // Because formatting using c++ streams is stateful, drop down to C is required
+ // Alternatively we could use stringstream, but its performance is... not good.
+ std::string getFormattedDuration( double duration ) {
+ // Max exponent + 1 is required to represent the whole part
+ // + 1 for decimal point
+ // + 3 for the 3 decimal places
+ // + 1 for null terminator
+ const std::size_t maxDoubleSize = DBL_MAX_10_EXP + 1 + 1 + 3 + 1;
+ char buffer[maxDoubleSize];
+
+ // Save previous errno, to prevent sprintf from overwriting it
+ ErrnoGuard guard;
+#ifdef _MSC_VER
+ sprintf_s(buffer, "%.3f", duration);
+#else
+ std::sprintf(buffer, "%.3f", duration);
+#endif
+ return std::string(buffer);
+ }
+
+ bool shouldShowDuration( IConfig const& config, double duration ) {
+ if ( config.showDurations() == ShowDurations::Always ) {
+ return true;
+ }
+ if ( config.showDurations() == ShowDurations::Never ) {
+ return false;
+ }
+ const double min = config.minDuration();
+ return min >= 0 && duration >= min;
+ }
+
+ std::string serializeFilters( std::vector<std::string> const& container ) {
+ ReusableStringStream oss;
+ bool first = true;
+ for (auto&& filter : container)
+ {
+ if (!first)
+ oss << ' ';
+ else
+ first = false;
+
+ oss << filter;
+ }
+ return oss.str();
+ }
+
+ TestEventListenerBase::TestEventListenerBase(ReporterConfig const & _config)
+ :StreamingReporterBase(_config) {}
+
+ std::set<Verbosity> TestEventListenerBase::getSupportedVerbosities() {
+ return { Verbosity::Quiet, Verbosity::Normal, Verbosity::High };
+ }
+
+ void TestEventListenerBase::assertionStarting(AssertionInfo const &) {}
+
+ bool TestEventListenerBase::assertionEnded(AssertionStats const &) {
+ return false;
+ }
+
+} // end namespace Catch
+// end catch_reporter_bases.cpp
+// start catch_reporter_compact.cpp
+
+namespace {
+
+#ifdef CATCH_PLATFORM_MAC
+ const char* failedString() { return "FAILED"; }
+ const char* passedString() { return "PASSED"; }
+#else
+ const char* failedString() { return "failed"; }
+ const char* passedString() { return "passed"; }
+#endif
+
+ // Colour::LightGrey
+ Catch::Colour::Code dimColour() { return Catch::Colour::FileName; }
+
+ std::string bothOrAll( std::size_t count ) {
+ return count == 1 ? std::string() :
+ count == 2 ? "both " : "all " ;
+ }
+
+} // anon namespace
+
+namespace Catch {
+namespace {
+// Colour, message variants:
+// - white: No tests ran.
+// - red: Failed [both/all] N test cases, failed [both/all] M assertions.
+// - white: Passed [both/all] N test cases (no assertions).
+// - red: Failed N tests cases, failed M assertions.
+// - green: Passed [both/all] N tests cases with M assertions.
+void printTotals(std::ostream& out, const Totals& totals) {
+ if (totals.testCases.total() == 0) {
+ out << "No tests ran.";
+ } else if (totals.testCases.failed == totals.testCases.total()) {
+ Colour colour(Colour::ResultError);
+ const std::string qualify_assertions_failed =
+ totals.assertions.failed == totals.assertions.total() ?
+ bothOrAll(totals.assertions.failed) : std::string();
+ out <<
+ "Failed " << bothOrAll(totals.testCases.failed)
+ << pluralise(totals.testCases.failed, "test case") << ", "
+ "failed " << qualify_assertions_failed <<
+ pluralise(totals.assertions.failed, "assertion") << '.';
+ } else if (totals.assertions.total() == 0) {
+ out <<
+ "Passed " << bothOrAll(totals.testCases.total())
+ << pluralise(totals.testCases.total(), "test case")
+ << " (no assertions).";
+ } else if (totals.assertions.failed) {
+ Colour colour(Colour::ResultError);
+ out <<
+ "Failed " << pluralise(totals.testCases.failed, "test case") << ", "
+ "failed " << pluralise(totals.assertions.failed, "assertion") << '.';
+ } else {
+ Colour colour(Colour::ResultSuccess);
+ out <<
+ "Passed " << bothOrAll(totals.testCases.passed)
+ << pluralise(totals.testCases.passed, "test case") <<
+ " with " << pluralise(totals.assertions.passed, "assertion") << '.';
+ }
+}
+
+// Implementation of CompactReporter formatting
+class AssertionPrinter {
+public:
+ AssertionPrinter& operator= (AssertionPrinter const&) = delete;
+ AssertionPrinter(AssertionPrinter const&) = delete;
+ AssertionPrinter(std::ostream& _stream, AssertionStats const& _stats, bool _printInfoMessages)
+ : stream(_stream)
+ , result(_stats.assertionResult)
+ , messages(_stats.infoMessages)
+ , itMessage(_stats.infoMessages.begin())
+ , printInfoMessages(_printInfoMessages) {}
+
+ void print() {
+ printSourceInfo();
+
+ itMessage = messages.begin();
+
+ switch (result.getResultType()) {
+ case ResultWas::Ok:
+ printResultType(Colour::ResultSuccess, passedString());
+ printOriginalExpression();
+ printReconstructedExpression();
+ if (!result.hasExpression())
+ printRemainingMessages(Colour::None);
+ else
+ printRemainingMessages();
+ break;
+ case ResultWas::ExpressionFailed:
+ if (result.isOk())
+ printResultType(Colour::ResultSuccess, failedString() + std::string(" - but was ok"));
+ else
+ printResultType(Colour::Error, failedString());
+ printOriginalExpression();
+ printReconstructedExpression();
+ printRemainingMessages();
+ break;
+ case ResultWas::ThrewException:
+ printResultType(Colour::Error, failedString());
+ printIssue("unexpected exception with message:");
+ printMessage();
+ printExpressionWas();
+ printRemainingMessages();
+ break;
+ case ResultWas::FatalErrorCondition:
+ printResultType(Colour::Error, failedString());
+ printIssue("fatal error condition with message:");
+ printMessage();
+ printExpressionWas();
+ printRemainingMessages();
+ break;
+ case ResultWas::DidntThrowException:
+ printResultType(Colour::Error, failedString());
+ printIssue("expected exception, got none");
+ printExpressionWas();
+ printRemainingMessages();
+ break;
+ case ResultWas::Info:
+ printResultType(Colour::None, "info");
+ printMessage();
+ printRemainingMessages();
+ break;
+ case ResultWas::Warning:
+ printResultType(Colour::None, "warning");
+ printMessage();
+ printRemainingMessages();
+ break;
+ case ResultWas::ExplicitFailure:
+ printResultType(Colour::Error, failedString());
+ printIssue("explicitly");
+ printRemainingMessages(Colour::None);
+ break;
+ // These cases are here to prevent compiler warnings
+ case ResultWas::Unknown:
+ case ResultWas::FailureBit:
+ case ResultWas::Exception:
+ printResultType(Colour::Error, "** internal error **");
+ break;
+ }
+ }
+
+private:
+ void printSourceInfo() const {
+ Colour colourGuard(Colour::FileName);
+ stream << result.getSourceInfo() << ':';
+ }
+
+ void printResultType(Colour::Code colour, std::string const& passOrFail) const {
+ if (!passOrFail.empty()) {
+ {
+ Colour colourGuard(colour);
+ stream << ' ' << passOrFail;
+ }
+ stream << ':';
+ }
+ }
+
+ void printIssue(std::string const& issue) const {
+ stream << ' ' << issue;
+ }
+
+ void printExpressionWas() {
+ if (result.hasExpression()) {
+ stream << ';';
+ {
+ Colour colour(dimColour());
+ stream << " expression was:";
+ }
+ printOriginalExpression();
+ }
+ }
+
+ void printOriginalExpression() const {
+ if (result.hasExpression()) {
+ stream << ' ' << result.getExpression();
+ }
+ }
+
+ void printReconstructedExpression() const {
+ if (result.hasExpandedExpression()) {
+ {
+ Colour colour(dimColour());
+ stream << " for: ";
+ }
+ stream << result.getExpandedExpression();
+ }
+ }
+
+ void printMessage() {
+ if (itMessage != messages.end()) {
+ stream << " '" << itMessage->message << '\'';
+ ++itMessage;
+ }
+ }
+
+ void printRemainingMessages(Colour::Code colour = dimColour()) {
+ if (itMessage == messages.end())
+ return;
+
+ const auto itEnd = messages.cend();
+ const auto N = static_cast<std::size_t>(std::distance(itMessage, itEnd));
+
+ {
+ Colour colourGuard(colour);
+ stream << " with " << pluralise(N, "message") << ':';
+ }
+
+ while (itMessage != itEnd) {
+ // If this assertion is a warning ignore any INFO messages
+ if (printInfoMessages || itMessage->type != ResultWas::Info) {
+ printMessage();
+ if (itMessage != itEnd) {
+ Colour colourGuard(dimColour());
+ stream << " and";
+ }
+ continue;
+ }
+ ++itMessage;
+ }
+ }
+
+private:
+ std::ostream& stream;
+ AssertionResult const& result;
+ std::vector<MessageInfo> messages;
+ std::vector<MessageInfo>::const_iterator itMessage;
+ bool printInfoMessages;
+};
+
+} // anon namespace
+
+ std::string CompactReporter::getDescription() {
+ return "Reports test results on a single line, suitable for IDEs";
+ }
+
+ void CompactReporter::noMatchingTestCases( std::string const& spec ) {
+ stream << "No test cases matched '" << spec << '\'' << std::endl;
+ }
+
+ void CompactReporter::assertionStarting( AssertionInfo const& ) {}
+
+ bool CompactReporter::assertionEnded( AssertionStats const& _assertionStats ) {
+ AssertionResult const& result = _assertionStats.assertionResult;
+
+ bool printInfoMessages = true;
+
+ // Drop out if result was successful and we're not printing those
+ if( !m_config->includeSuccessfulResults() && result.isOk() ) {
+ if( result.getResultType() != ResultWas::Warning )
+ return false;
+ printInfoMessages = false;
+ }
+
+ AssertionPrinter printer( stream, _assertionStats, printInfoMessages );
+ printer.print();
+
+ stream << std::endl;
+ return true;
+ }
+
+ void CompactReporter::sectionEnded(SectionStats const& _sectionStats) {
+ double dur = _sectionStats.durationInSeconds;
+ if ( shouldShowDuration( *m_config, dur ) ) {
+ stream << getFormattedDuration( dur ) << " s: " << _sectionStats.sectionInfo.name << std::endl;
+ }
+ }
+
+ void CompactReporter::testRunEnded( TestRunStats const& _testRunStats ) {
+ printTotals( stream, _testRunStats.totals );
+ stream << '\n' << std::endl;
+ StreamingReporterBase::testRunEnded( _testRunStats );
+ }
+
+ CompactReporter::~CompactReporter() {}
+
+ CATCH_REGISTER_REPORTER( "compact", CompactReporter )
+
+} // end namespace Catch
+// end catch_reporter_compact.cpp
+// start catch_reporter_console.cpp
+
+#include <cfloat>
+#include <cstdio>
+
+#if defined(_MSC_VER)
+#pragma warning(push)
+#pragma warning(disable:4061) // Not all labels are EXPLICITLY handled in switch
+ // Note that 4062 (not all labels are handled and default is missing) is enabled
+#endif
+
+#if defined(__clang__)
+# pragma clang diagnostic push
+// For simplicity, benchmarking-only helpers are always enabled
+# pragma clang diagnostic ignored "-Wunused-function"
+#endif
+
+namespace Catch {
+
+namespace {
+
+// Formatter impl for ConsoleReporter
+class ConsoleAssertionPrinter {
+public:
+ ConsoleAssertionPrinter& operator= (ConsoleAssertionPrinter const&) = delete;
+ ConsoleAssertionPrinter(ConsoleAssertionPrinter const&) = delete;
+ ConsoleAssertionPrinter(std::ostream& _stream, AssertionStats const& _stats, bool _printInfoMessages)
+ : stream(_stream),
+ stats(_stats),
+ result(_stats.assertionResult),
+ colour(Colour::None),
+ message(result.getMessage()),
+ messages(_stats.infoMessages),
+ printInfoMessages(_printInfoMessages) {
+ switch (result.getResultType()) {
+ case ResultWas::Ok:
+ colour = Colour::Success;
+ passOrFail = "PASSED";
+ //if( result.hasMessage() )
+ if (_stats.infoMessages.size() == 1)
+ messageLabel = "with message";
+ if (_stats.infoMessages.size() > 1)
+ messageLabel = "with messages";
+ break;
+ case ResultWas::ExpressionFailed:
+ if (result.isOk()) {
+ colour = Colour::Success;
+ passOrFail = "FAILED - but was ok";
+ } else {
+ colour = Colour::Error;
+ passOrFail = "FAILED";
+ }
+ if (_stats.infoMessages.size() == 1)
+ messageLabel = "with message";
+ if (_stats.infoMessages.size() > 1)
+ messageLabel = "with messages";
+ break;
+ case ResultWas::ThrewException:
+ colour = Colour::Error;
+ passOrFail = "FAILED";
+ messageLabel = "due to unexpected exception with ";
+ if (_stats.infoMessages.size() == 1)
+ messageLabel += "message";
+ if (_stats.infoMessages.size() > 1)
+ messageLabel += "messages";
+ break;
+ case ResultWas::FatalErrorCondition:
+ colour = Colour::Error;
+ passOrFail = "FAILED";
+ messageLabel = "due to a fatal error condition";
+ break;
+ case ResultWas::DidntThrowException:
+ colour = Colour::Error;
+ passOrFail = "FAILED";
+ messageLabel = "because no exception was thrown where one was expected";
+ break;
+ case ResultWas::Info:
+ messageLabel = "info";
+ break;
+ case ResultWas::Warning:
+ messageLabel = "warning";
+ break;
+ case ResultWas::ExplicitFailure:
+ passOrFail = "FAILED";
+ colour = Colour::Error;
+ if (_stats.infoMessages.size() == 1)
+ messageLabel = "explicitly with message";
+ if (_stats.infoMessages.size() > 1)
+ messageLabel = "explicitly with messages";
+ break;
+ // These cases are here to prevent compiler warnings
+ case ResultWas::Unknown:
+ case ResultWas::FailureBit:
+ case ResultWas::Exception:
+ passOrFail = "** internal error **";
+ colour = Colour::Error;
+ break;
+ }
+ }
+
+ void print() const {
+ printSourceInfo();
+ if (stats.totals.assertions.total() > 0) {
+ printResultType();
+ printOriginalExpression();
+ printReconstructedExpression();
+ } else {
+ stream << '\n';
+ }
+ printMessage();
+ }
+
+private:
+ void printResultType() const {
+ if (!passOrFail.empty()) {
+ Colour colourGuard(colour);
+ stream << passOrFail << ":\n";
+ }
+ }
+ void printOriginalExpression() const {
+ if (result.hasExpression()) {
+ Colour colourGuard(Colour::OriginalExpression);
+ stream << " ";
+ stream << result.getExpressionInMacro();
+ stream << '\n';
+ }
+ }
+ void printReconstructedExpression() const {
+ if (result.hasExpandedExpression()) {
+ stream << "with expansion:\n";
+ Colour colourGuard(Colour::ReconstructedExpression);
+ stream << Column(result.getExpandedExpression()).indent(2) << '\n';
+ }
+ }
+ void printMessage() const {
+ if (!messageLabel.empty())
+ stream << messageLabel << ':' << '\n';
+ for (auto const& msg : messages) {
+ // If this assertion is a warning ignore any INFO messages
+ if (printInfoMessages || msg.type != ResultWas::Info)
+ stream << Column(msg.message).indent(2) << '\n';
+ }
+ }
+ void printSourceInfo() const {
+ Colour colourGuard(Colour::FileName);
+ stream << result.getSourceInfo() << ": ";
+ }
+
+ std::ostream& stream;
+ AssertionStats const& stats;
+ AssertionResult const& result;
+ Colour::Code colour;
+ std::string passOrFail;
+ std::string messageLabel;
+ std::string message;
+ std::vector<MessageInfo> messages;
+ bool printInfoMessages;
+};
+
+std::size_t makeRatio(std::size_t number, std::size_t total) {
+ std::size_t ratio = total > 0 ? CATCH_CONFIG_CONSOLE_WIDTH * number / total : 0;
+ return (ratio == 0 && number > 0) ? 1 : ratio;
+}
+
+std::size_t& findMax(std::size_t& i, std::size_t& j, std::size_t& k) {
+ if (i > j && i > k)
+ return i;
+ else if (j > k)
+ return j;
+ else
+ return k;
+}
+
+struct ColumnInfo {
+ enum Justification { Left, Right };
+ std::string name;
+ int width;
+ Justification justification;
+};
+struct ColumnBreak {};
+struct RowBreak {};
+
+class Duration {
+ enum class Unit {
+ Auto,
+ Nanoseconds,
+ Microseconds,
+ Milliseconds,
+ Seconds,
+ Minutes
+ };
+ static const uint64_t s_nanosecondsInAMicrosecond = 1000;
+ static const uint64_t s_nanosecondsInAMillisecond = 1000 * s_nanosecondsInAMicrosecond;
+ static const uint64_t s_nanosecondsInASecond = 1000 * s_nanosecondsInAMillisecond;
+ static const uint64_t s_nanosecondsInAMinute = 60 * s_nanosecondsInASecond;
+
+ double m_inNanoseconds;
+ Unit m_units;
+
+public:
+ explicit Duration(double inNanoseconds, Unit units = Unit::Auto)
+ : m_inNanoseconds(inNanoseconds),
+ m_units(units) {
+ if (m_units == Unit::Auto) {
+ if (m_inNanoseconds < s_nanosecondsInAMicrosecond)
+ m_units = Unit::Nanoseconds;
+ else if (m_inNanoseconds < s_nanosecondsInAMillisecond)
+ m_units = Unit::Microseconds;
+ else if (m_inNanoseconds < s_nanosecondsInASecond)
+ m_units = Unit::Milliseconds;
+ else if (m_inNanoseconds < s_nanosecondsInAMinute)
+ m_units = Unit::Seconds;
+ else
+ m_units = Unit::Minutes;
+ }
+
+ }
+
+ auto value() const -> double {
+ switch (m_units) {
+ case Unit::Microseconds:
+ return m_inNanoseconds / static_cast<double>(s_nanosecondsInAMicrosecond);
+ case Unit::Milliseconds:
+ return m_inNanoseconds / static_cast<double>(s_nanosecondsInAMillisecond);
+ case Unit::Seconds:
+ return m_inNanoseconds / static_cast<double>(s_nanosecondsInASecond);
+ case Unit::Minutes:
+ return m_inNanoseconds / static_cast<double>(s_nanosecondsInAMinute);
+ default:
+ return m_inNanoseconds;
+ }
+ }
+ auto unitsAsString() const -> std::string {
+ switch (m_units) {
+ case Unit::Nanoseconds:
+ return "ns";
+ case Unit::Microseconds:
+ return "us";
+ case Unit::Milliseconds:
+ return "ms";
+ case Unit::Seconds:
+ return "s";
+ case Unit::Minutes:
+ return "m";
+ default:
+ return "** internal error **";
+ }
+
+ }
+ friend auto operator << (std::ostream& os, Duration const& duration) -> std::ostream& {
+ return os << duration.value() << ' ' << duration.unitsAsString();
+ }
+};
+} // end anon namespace
+
+class TablePrinter {
+ std::ostream& m_os;
+ std::vector<ColumnInfo> m_columnInfos;
+ std::ostringstream m_oss;
+ int m_currentColumn = -1;
+ bool m_isOpen = false;
+
+public:
+ TablePrinter( std::ostream& os, std::vector<ColumnInfo> columnInfos )
+ : m_os( os ),
+ m_columnInfos( std::move( columnInfos ) ) {}
+
+ auto columnInfos() const -> std::vector<ColumnInfo> const& {
+ return m_columnInfos;
+ }
+
+ void open() {
+ if (!m_isOpen) {
+ m_isOpen = true;
+ *this << RowBreak();
+
+ Columns headerCols;
+ Spacer spacer(2);
+ for (auto const& info : m_columnInfos) {
+ headerCols += Column(info.name).width(static_cast<std::size_t>(info.width - 2));
+ headerCols += spacer;
+ }
+ m_os << headerCols << '\n';
+
+ m_os << Catch::getLineOfChars<'-'>() << '\n';
+ }
+ }
+ void close() {
+ if (m_isOpen) {
+ *this << RowBreak();
+ m_os << std::endl;
+ m_isOpen = false;
+ }
+ }
+
+ template<typename T>
+ friend TablePrinter& operator << (TablePrinter& tp, T const& value) {
+ tp.m_oss << value;
+ return tp;
+ }
+
+ friend TablePrinter& operator << (TablePrinter& tp, ColumnBreak) {
+ auto colStr = tp.m_oss.str();
+ const auto strSize = colStr.size();
+ tp.m_oss.str("");
+ tp.open();
+ if (tp.m_currentColumn == static_cast<int>(tp.m_columnInfos.size() - 1)) {
+ tp.m_currentColumn = -1;
+ tp.m_os << '\n';
+ }
+ tp.m_currentColumn++;
+
+ auto colInfo = tp.m_columnInfos[tp.m_currentColumn];
+ auto padding = (strSize + 1 < static_cast<std::size_t>(colInfo.width))
+ ? std::string(colInfo.width - (strSize + 1), ' ')
+ : std::string();
+ if (colInfo.justification == ColumnInfo::Left)
+ tp.m_os << colStr << padding << ' ';
+ else
+ tp.m_os << padding << colStr << ' ';
+ return tp;
+ }
+
+ friend TablePrinter& operator << (TablePrinter& tp, RowBreak) {
+ if (tp.m_currentColumn > 0) {
+ tp.m_os << '\n';
+ tp.m_currentColumn = -1;
+ }
+ return tp;
+ }
+};
+
+ConsoleReporter::ConsoleReporter(ReporterConfig const& config)
+ : StreamingReporterBase(config),
+ m_tablePrinter(new TablePrinter(config.stream(),
+ [&config]() -> std::vector<ColumnInfo> {
+ if (config.fullConfig()->benchmarkNoAnalysis())
+ {
+ return{
+ { "benchmark name", CATCH_CONFIG_CONSOLE_WIDTH - 43, ColumnInfo::Left },
+ { " samples", 14, ColumnInfo::Right },
+ { " iterations", 14, ColumnInfo::Right },
+ { " mean", 14, ColumnInfo::Right }
+ };
+ }
+ else
+ {
+ return{
+ { "benchmark name", CATCH_CONFIG_CONSOLE_WIDTH - 43, ColumnInfo::Left },
+ { "samples mean std dev", 14, ColumnInfo::Right },
+ { "iterations low mean low std dev", 14, ColumnInfo::Right },
+ { "estimated high mean high std dev", 14, ColumnInfo::Right }
+ };
+ }
+ }())) {}
+ConsoleReporter::~ConsoleReporter() = default;
+
+std::string ConsoleReporter::getDescription() {
+ return "Reports test results as plain lines of text";
+}
+
+void ConsoleReporter::noMatchingTestCases(std::string const& spec) {
+ stream << "No test cases matched '" << spec << '\'' << std::endl;
+}
+
+void ConsoleReporter::reportInvalidArguments(std::string const&arg){
+ stream << "Invalid Filter: " << arg << std::endl;
+}
+
+void ConsoleReporter::assertionStarting(AssertionInfo const&) {}
+
+bool ConsoleReporter::assertionEnded(AssertionStats const& _assertionStats) {
+ AssertionResult const& result = _assertionStats.assertionResult;
+
+ bool includeResults = m_config->includeSuccessfulResults() || !result.isOk();
+
+ // Drop out if result was successful but we're not printing them.
+ if (!includeResults && result.getResultType() != ResultWas::Warning)
+ return false;
+
+ lazyPrint();
+
+ ConsoleAssertionPrinter printer(stream, _assertionStats, includeResults);
+ printer.print();
+ stream << std::endl;
+ return true;
+}
+
+void ConsoleReporter::sectionStarting(SectionInfo const& _sectionInfo) {
+ m_tablePrinter->close();
+ m_headerPrinted = false;
+ StreamingReporterBase::sectionStarting(_sectionInfo);
+}
+void ConsoleReporter::sectionEnded(SectionStats const& _sectionStats) {
+ m_tablePrinter->close();
+ if (_sectionStats.missingAssertions) {
+ lazyPrint();
+ Colour colour(Colour::ResultError);
+ if (m_sectionStack.size() > 1)
+ stream << "\nNo assertions in section";
+ else
+ stream << "\nNo assertions in test case";
+ stream << " '" << _sectionStats.sectionInfo.name << "'\n" << std::endl;
+ }
+ double dur = _sectionStats.durationInSeconds;
+ if (shouldShowDuration(*m_config, dur)) {
+ stream << getFormattedDuration(dur) << " s: " << _sectionStats.sectionInfo.name << std::endl;
+ }
+ if (m_headerPrinted) {
+ m_headerPrinted = false;
+ }
+ StreamingReporterBase::sectionEnded(_sectionStats);
+}
+
+#if defined(CATCH_CONFIG_ENABLE_BENCHMARKING)
+void ConsoleReporter::benchmarkPreparing(std::string const& name) {
+ lazyPrintWithoutClosingBenchmarkTable();
+
+ auto nameCol = Column(name).width(static_cast<std::size_t>(m_tablePrinter->columnInfos()[0].width - 2));
+
+ bool firstLine = true;
+ for (auto line : nameCol) {
+ if (!firstLine)
+ (*m_tablePrinter) << ColumnBreak() << ColumnBreak() << ColumnBreak();
+ else
+ firstLine = false;
+
+ (*m_tablePrinter) << line << ColumnBreak();
+ }
+}
+
+void ConsoleReporter::benchmarkStarting(BenchmarkInfo const& info) {
+ (*m_tablePrinter) << info.samples << ColumnBreak()
+ << info.iterations << ColumnBreak();
+ if (!m_config->benchmarkNoAnalysis())
+ (*m_tablePrinter) << Duration(info.estimatedDuration) << ColumnBreak();
+}
+void ConsoleReporter::benchmarkEnded(BenchmarkStats<> const& stats) {
+ if (m_config->benchmarkNoAnalysis())
+ {
+ (*m_tablePrinter) << Duration(stats.mean.point.count()) << ColumnBreak();
+ }
+ else
+ {
+ (*m_tablePrinter) << ColumnBreak()
+ << Duration(stats.mean.point.count()) << ColumnBreak()
+ << Duration(stats.mean.lower_bound.count()) << ColumnBreak()
+ << Duration(stats.mean.upper_bound.count()) << ColumnBreak() << ColumnBreak()
+ << Duration(stats.standardDeviation.point.count()) << ColumnBreak()
+ << Duration(stats.standardDeviation.lower_bound.count()) << ColumnBreak()
+ << Duration(stats.standardDeviation.upper_bound.count()) << ColumnBreak() << ColumnBreak() << ColumnBreak() << ColumnBreak() << ColumnBreak();
+ }
+}
+
+void ConsoleReporter::benchmarkFailed(std::string const& error) {
+ Colour colour(Colour::Red);
+ (*m_tablePrinter)
+ << "Benchmark failed (" << error << ')'
+ << ColumnBreak() << RowBreak();
+}
+#endif // CATCH_CONFIG_ENABLE_BENCHMARKING
+
+void ConsoleReporter::testCaseEnded(TestCaseStats const& _testCaseStats) {
+ m_tablePrinter->close();
+ StreamingReporterBase::testCaseEnded(_testCaseStats);
+ m_headerPrinted = false;
+}
+void ConsoleReporter::testGroupEnded(TestGroupStats const& _testGroupStats) {
+ if (currentGroupInfo.used) {
+ printSummaryDivider();
+ stream << "Summary for group '" << _testGroupStats.groupInfo.name << "':\n";
+ printTotals(_testGroupStats.totals);
+ stream << '\n' << std::endl;
+ }
+ StreamingReporterBase::testGroupEnded(_testGroupStats);
+}
+void ConsoleReporter::testRunEnded(TestRunStats const& _testRunStats) {
+ printTotalsDivider(_testRunStats.totals);
+ printTotals(_testRunStats.totals);
+ stream << std::endl;
+ StreamingReporterBase::testRunEnded(_testRunStats);
+}
+void ConsoleReporter::testRunStarting(TestRunInfo const& _testInfo) {
+ StreamingReporterBase::testRunStarting(_testInfo);
+ printTestFilters();
+}
+
+void ConsoleReporter::lazyPrint() {
+
+ m_tablePrinter->close();
+ lazyPrintWithoutClosingBenchmarkTable();
+}
+
+void ConsoleReporter::lazyPrintWithoutClosingBenchmarkTable() {
+
+ if (!currentTestRunInfo.used)
+ lazyPrintRunInfo();
+ if (!currentGroupInfo.used)
+ lazyPrintGroupInfo();
+
+ if (!m_headerPrinted) {
+ printTestCaseAndSectionHeader();
+ m_headerPrinted = true;
+ }
+}
+void ConsoleReporter::lazyPrintRunInfo() {
+ stream << '\n' << getLineOfChars<'~'>() << '\n';
+ Colour colour(Colour::SecondaryText);
+ stream << currentTestRunInfo->name
+ << " is a Catch v" << libraryVersion() << " host application.\n"
+ << "Run with -? for options\n\n";
+
+ if (m_config->rngSeed() != 0)
+ stream << "Randomness seeded to: " << m_config->rngSeed() << "\n\n";
+
+ currentTestRunInfo.used = true;
+}
+void ConsoleReporter::lazyPrintGroupInfo() {
+ if (!currentGroupInfo->name.empty() && currentGroupInfo->groupsCounts > 1) {
+ printClosedHeader("Group: " + currentGroupInfo->name);
+ currentGroupInfo.used = true;
+ }
+}
+void ConsoleReporter::printTestCaseAndSectionHeader() {
+ assert(!m_sectionStack.empty());
+ printOpenHeader(currentTestCaseInfo->name);
+
+ if (m_sectionStack.size() > 1) {
+ Colour colourGuard(Colour::Headers);
+
+ auto
+ it = m_sectionStack.begin() + 1, // Skip first section (test case)
+ itEnd = m_sectionStack.end();
+ for (; it != itEnd; ++it)
+ printHeaderString(it->name, 2);
+ }
+
+ SourceLineInfo lineInfo = m_sectionStack.back().lineInfo;
+
+ stream << getLineOfChars<'-'>() << '\n';
+ Colour colourGuard(Colour::FileName);
+ stream << lineInfo << '\n';
+ stream << getLineOfChars<'.'>() << '\n' << std::endl;
+}
+
+void ConsoleReporter::printClosedHeader(std::string const& _name) {
+ printOpenHeader(_name);
+ stream << getLineOfChars<'.'>() << '\n';
+}
+void ConsoleReporter::printOpenHeader(std::string const& _name) {
+ stream << getLineOfChars<'-'>() << '\n';
+ {
+ Colour colourGuard(Colour::Headers);
+ printHeaderString(_name);
+ }
+}
+
+// if string has a : in first line will set indent to follow it on
+// subsequent lines
+void ConsoleReporter::printHeaderString(std::string const& _string, std::size_t indent) {
+ std::size_t i = _string.find(": ");
+ if (i != std::string::npos)
+ i += 2;
+ else
+ i = 0;
+ stream << Column(_string).indent(indent + i).initialIndent(indent) << '\n';
+}
+
+struct SummaryColumn {
+
+ SummaryColumn( std::string _label, Colour::Code _colour )
+ : label( std::move( _label ) ),
+ colour( _colour ) {}
+ SummaryColumn addRow( std::size_t count ) {
+ ReusableStringStream rss;
+ rss << count;
+ std::string row = rss.str();
+ for (auto& oldRow : rows) {
+ while (oldRow.size() < row.size())
+ oldRow = ' ' + oldRow;
+ while (oldRow.size() > row.size())
+ row = ' ' + row;
+ }
+ rows.push_back(row);
+ return *this;
+ }
+
+ std::string label;
+ Colour::Code colour;
+ std::vector<std::string> rows;
+
+};
+
+void ConsoleReporter::printTotals( Totals const& totals ) {
+ if (totals.testCases.total() == 0) {
+ stream << Colour(Colour::Warning) << "No tests ran\n";
+ } else if (totals.assertions.total() > 0 && totals.testCases.allPassed()) {
+ stream << Colour(Colour::ResultSuccess) << "All tests passed";
+ stream << " ("
+ << pluralise(totals.assertions.passed, "assertion") << " in "
+ << pluralise(totals.testCases.passed, "test case") << ')'
+ << '\n';
+ } else {
+
+ std::vector<SummaryColumn> columns;
+ columns.push_back(SummaryColumn("", Colour::None)
+ .addRow(totals.testCases.total())
+ .addRow(totals.assertions.total()));
+ columns.push_back(SummaryColumn("passed", Colour::Success)
+ .addRow(totals.testCases.passed)
+ .addRow(totals.assertions.passed));
+ columns.push_back(SummaryColumn("failed", Colour::ResultError)
+ .addRow(totals.testCases.failed)
+ .addRow(totals.assertions.failed));
+ columns.push_back(SummaryColumn("failed as expected", Colour::ResultExpectedFailure)
+ .addRow(totals.testCases.failedButOk)
+ .addRow(totals.assertions.failedButOk));
+
+ printSummaryRow("test cases", columns, 0);
+ printSummaryRow("assertions", columns, 1);
+ }
+}
+void ConsoleReporter::printSummaryRow(std::string const& label, std::vector<SummaryColumn> const& cols, std::size_t row) {
+ for (auto col : cols) {
+ std::string value = col.rows[row];
+ if (col.label.empty()) {
+ stream << label << ": ";
+ if (value != "0")
+ stream << value;
+ else
+ stream << Colour(Colour::Warning) << "- none -";
+ } else if (value != "0") {
+ stream << Colour(Colour::LightGrey) << " | ";
+ stream << Colour(col.colour)
+ << value << ' ' << col.label;
+ }
+ }
+ stream << '\n';
+}
+
+void ConsoleReporter::printTotalsDivider(Totals const& totals) {
+ if (totals.testCases.total() > 0) {
+ std::size_t failedRatio = makeRatio(totals.testCases.failed, totals.testCases.total());
+ std::size_t failedButOkRatio = makeRatio(totals.testCases.failedButOk, totals.testCases.total());
+ std::size_t passedRatio = makeRatio(totals.testCases.passed, totals.testCases.total());
+ while (failedRatio + failedButOkRatio + passedRatio < CATCH_CONFIG_CONSOLE_WIDTH - 1)
+ findMax(failedRatio, failedButOkRatio, passedRatio)++;
+ while (failedRatio + failedButOkRatio + passedRatio > CATCH_CONFIG_CONSOLE_WIDTH - 1)
+ findMax(failedRatio, failedButOkRatio, passedRatio)--;
+
+ stream << Colour(Colour::Error) << std::string(failedRatio, '=');
+ stream << Colour(Colour::ResultExpectedFailure) << std::string(failedButOkRatio, '=');
+ if (totals.testCases.allPassed())
+ stream << Colour(Colour::ResultSuccess) << std::string(passedRatio, '=');
+ else
+ stream << Colour(Colour::Success) << std::string(passedRatio, '=');
+ } else {
+ stream << Colour(Colour::Warning) << std::string(CATCH_CONFIG_CONSOLE_WIDTH - 1, '=');
+ }
+ stream << '\n';
+}
+void ConsoleReporter::printSummaryDivider() {
+ stream << getLineOfChars<'-'>() << '\n';
+}
+
+void ConsoleReporter::printTestFilters() {
+ if (m_config->testSpec().hasFilters()) {
+ Colour guard(Colour::BrightYellow);
+ stream << "Filters: " << serializeFilters(m_config->getTestsOrTags()) << '\n';
+ }
+}
+
+CATCH_REGISTER_REPORTER("console", ConsoleReporter)
+
+} // end namespace Catch
+
+#if defined(_MSC_VER)
+#pragma warning(pop)
+#endif
+
+#if defined(__clang__)
+# pragma clang diagnostic pop
+#endif
+// end catch_reporter_console.cpp
+// start catch_reporter_junit.cpp
+
+#include <cassert>
+#include <sstream>
+#include <ctime>
+#include <algorithm>
+#include <iomanip>
+
+namespace Catch {
+
+ namespace {
+ std::string getCurrentTimestamp() {
+ // Beware, this is not reentrant because of backward compatibility issues
+ // Also, UTC only, again because of backward compatibility (%z is C++11)
+ time_t rawtime;
+ std::time(&rawtime);
+ auto const timeStampSize = sizeof("2017-01-16T17:06:45Z");
+
+#ifdef _MSC_VER
+ std::tm timeInfo = {};
+ gmtime_s(&timeInfo, &rawtime);
+#else
+ std::tm* timeInfo;
+ timeInfo = std::gmtime(&rawtime);
+#endif
+
+ char timeStamp[timeStampSize];
+ const char * const fmt = "%Y-%m-%dT%H:%M:%SZ";
+
+#ifdef _MSC_VER
+ std::strftime(timeStamp, timeStampSize, fmt, &timeInfo);
+#else
+ std::strftime(timeStamp, timeStampSize, fmt, timeInfo);
+#endif
+ return std::string(timeStamp, timeStampSize-1);
+ }
+
+ std::string fileNameTag(const std::vector<std::string> &tags) {
+ auto it = std::find_if(begin(tags),
+ end(tags),
+ [] (std::string const& tag) {return tag.front() == '#'; });
+ if (it != tags.end())
+ return it->substr(1);
+ return std::string();
+ }
+
+ // Formats the duration in seconds to 3 decimal places.
+ // This is done because some genius defined Maven Surefire schema
+ // in a way that only accepts 3 decimal places, and tools like
+ // Jenkins use that schema for validation JUnit reporter output.
+ std::string formatDuration( double seconds ) {
+ ReusableStringStream rss;
+ rss << std::fixed << std::setprecision( 3 ) << seconds;
+ return rss.str();
+ }
+
+ } // anonymous namespace
+
+ JunitReporter::JunitReporter( ReporterConfig const& _config )
+ : CumulativeReporterBase( _config ),
+ xml( _config.stream() )
+ {
+ m_reporterPrefs.shouldRedirectStdOut = true;
+ m_reporterPrefs.shouldReportAllAssertions = true;
+ }
+
+ JunitReporter::~JunitReporter() {}
+
+ std::string JunitReporter::getDescription() {
+ return "Reports test results in an XML format that looks like Ant's junitreport target";
+ }
+
+ void JunitReporter::noMatchingTestCases( std::string const& /*spec*/ ) {}
+
+ void JunitReporter::testRunStarting( TestRunInfo const& runInfo ) {
+ CumulativeReporterBase::testRunStarting( runInfo );
+ xml.startElement( "testsuites" );
+ }
+
+ void JunitReporter::testGroupStarting( GroupInfo const& groupInfo ) {
+ suiteTimer.start();
+ stdOutForSuite.clear();
+ stdErrForSuite.clear();
+ unexpectedExceptions = 0;
+ CumulativeReporterBase::testGroupStarting( groupInfo );
+ }
+
+ void JunitReporter::testCaseStarting( TestCaseInfo const& testCaseInfo ) {
+ m_okToFail = testCaseInfo.okToFail();
+ }
+
+ bool JunitReporter::assertionEnded( AssertionStats const& assertionStats ) {
+ if( assertionStats.assertionResult.getResultType() == ResultWas::ThrewException && !m_okToFail )
+ unexpectedExceptions++;
+ return CumulativeReporterBase::assertionEnded( assertionStats );
+ }
+
+ void JunitReporter::testCaseEnded( TestCaseStats const& testCaseStats ) {
+ stdOutForSuite += testCaseStats.stdOut;
+ stdErrForSuite += testCaseStats.stdErr;
+ CumulativeReporterBase::testCaseEnded( testCaseStats );
+ }
+
+ void JunitReporter::testGroupEnded( TestGroupStats const& testGroupStats ) {
+ double suiteTime = suiteTimer.getElapsedSeconds();
+ CumulativeReporterBase::testGroupEnded( testGroupStats );
+ writeGroup( *m_testGroups.back(), suiteTime );
+ }
+
+ void JunitReporter::testRunEndedCumulative() {
+ xml.endElement();
+ }
+
+ void JunitReporter::writeGroup( TestGroupNode const& groupNode, double suiteTime ) {
+ XmlWriter::ScopedElement e = xml.scopedElement( "testsuite" );
+
+ TestGroupStats const& stats = groupNode.value;
+ xml.writeAttribute( "name", stats.groupInfo.name );
+ xml.writeAttribute( "errors", unexpectedExceptions );
+ xml.writeAttribute( "failures", stats.totals.assertions.failed-unexpectedExceptions );
+ xml.writeAttribute( "tests", stats.totals.assertions.total() );
+ xml.writeAttribute( "hostname", "tbd" ); // !TBD
+ if( m_config->showDurations() == ShowDurations::Never )
+ xml.writeAttribute( "time", "" );
+ else
+ xml.writeAttribute( "time", formatDuration( suiteTime ) );
+ xml.writeAttribute( "timestamp", getCurrentTimestamp() );
+
+ // Write properties if there are any
+ if (m_config->hasTestFilters() || m_config->rngSeed() != 0) {
+ auto properties = xml.scopedElement("properties");
+ if (m_config->hasTestFilters()) {
+ xml.scopedElement("property")
+ .writeAttribute("name", "filters")
+ .writeAttribute("value", serializeFilters(m_config->getTestsOrTags()));
+ }
+ if (m_config->rngSeed() != 0) {
+ xml.scopedElement("property")
+ .writeAttribute("name", "random-seed")
+ .writeAttribute("value", m_config->rngSeed());
+ }
+ }
+
+ // Write test cases
+ for( auto const& child : groupNode.children )
+ writeTestCase( *child );
+
+ xml.scopedElement( "system-out" ).writeText( trim( stdOutForSuite ), XmlFormatting::Newline );
+ xml.scopedElement( "system-err" ).writeText( trim( stdErrForSuite ), XmlFormatting::Newline );
+ }
+
+ void JunitReporter::writeTestCase( TestCaseNode const& testCaseNode ) {
+ TestCaseStats const& stats = testCaseNode.value;
+
+ // All test cases have exactly one section - which represents the
+ // test case itself. That section may have 0-n nested sections
+ assert( testCaseNode.children.size() == 1 );
+ SectionNode const& rootSection = *testCaseNode.children.front();
+
+ std::string className = stats.testInfo.className;
+
+ if( className.empty() ) {
+ className = fileNameTag(stats.testInfo.tags);
+ if ( className.empty() )
+ className = "global";
+ }
+
+ if ( !m_config->name().empty() )
+ className = m_config->name() + "." + className;
+
+ writeSection( className, "", rootSection, stats.testInfo.okToFail() );
+ }
+
+ void JunitReporter::writeSection( std::string const& className,
+ std::string const& rootName,
+ SectionNode const& sectionNode,
+ bool testOkToFail) {
+ std::string name = trim( sectionNode.stats.sectionInfo.name );
+ if( !rootName.empty() )
+ name = rootName + '/' + name;
+
+ if( !sectionNode.assertions.empty() ||
+ !sectionNode.stdOut.empty() ||
+ !sectionNode.stdErr.empty() ) {
+ XmlWriter::ScopedElement e = xml.scopedElement( "testcase" );
+ if( className.empty() ) {
+ xml.writeAttribute( "classname", name );
+ xml.writeAttribute( "name", "root" );
+ }
+ else {
+ xml.writeAttribute( "classname", className );
+ xml.writeAttribute( "name", name );
+ }
+ xml.writeAttribute( "time", formatDuration( sectionNode.stats.durationInSeconds ) );
+ // This is not ideal, but it should be enough to mimic gtest's
+ // junit output.
+ // Ideally the JUnit reporter would also handle `skipTest`
+ // events and write those out appropriately.
+ xml.writeAttribute( "status", "run" );
+
+ if (sectionNode.stats.assertions.failedButOk) {
+ xml.scopedElement("skipped")
+ .writeAttribute("message", "TEST_CASE tagged with !mayfail");
+ }
+
+ writeAssertions( sectionNode );
+
+ if( !sectionNode.stdOut.empty() )
+ xml.scopedElement( "system-out" ).writeText( trim( sectionNode.stdOut ), XmlFormatting::Newline );
+ if( !sectionNode.stdErr.empty() )
+ xml.scopedElement( "system-err" ).writeText( trim( sectionNode.stdErr ), XmlFormatting::Newline );
+ }
+ for( auto const& childNode : sectionNode.childSections )
+ if( className.empty() )
+ writeSection( name, "", *childNode, testOkToFail );
+ else
+ writeSection( className, name, *childNode, testOkToFail );
+ }
+
+ void JunitReporter::writeAssertions( SectionNode const& sectionNode ) {
+ for( auto const& assertion : sectionNode.assertions )
+ writeAssertion( assertion );
+ }
+
+ void JunitReporter::writeAssertion( AssertionStats const& stats ) {
+ AssertionResult const& result = stats.assertionResult;
+ if( !result.isOk() ) {
+ std::string elementName;
+ switch( result.getResultType() ) {
+ case ResultWas::ThrewException:
+ case ResultWas::FatalErrorCondition:
+ elementName = "error";
+ break;
+ case ResultWas::ExplicitFailure:
+ case ResultWas::ExpressionFailed:
+ case ResultWas::DidntThrowException:
+ elementName = "failure";
+ break;
+
+ // We should never see these here:
+ case ResultWas::Info:
+ case ResultWas::Warning:
+ case ResultWas::Ok:
+ case ResultWas::Unknown:
+ case ResultWas::FailureBit:
+ case ResultWas::Exception:
+ elementName = "internalError";
+ break;
+ }
+
+ XmlWriter::ScopedElement e = xml.scopedElement( elementName );
+
+ xml.writeAttribute( "message", result.getExpression() );
+ xml.writeAttribute( "type", result.getTestMacroName() );
+
+ ReusableStringStream rss;
+ if (stats.totals.assertions.total() > 0) {
+ rss << "FAILED" << ":\n";
+ if (result.hasExpression()) {
+ rss << " ";
+ rss << result.getExpressionInMacro();
+ rss << '\n';
+ }
+ if (result.hasExpandedExpression()) {
+ rss << "with expansion:\n";
+ rss << Column(result.getExpandedExpression()).indent(2) << '\n';
+ }
+ } else {
+ rss << '\n';
+ }
+
+ if( !result.getMessage().empty() )
+ rss << result.getMessage() << '\n';
+ for( auto const& msg : stats.infoMessages )
+ if( msg.type == ResultWas::Info )
+ rss << msg.message << '\n';
+
+ rss << "at " << result.getSourceInfo();
+ xml.writeText( rss.str(), XmlFormatting::Newline );
+ }
+ }
+
+ CATCH_REGISTER_REPORTER( "junit", JunitReporter )
+
+} // end namespace Catch
+// end catch_reporter_junit.cpp
+// start catch_reporter_listening.cpp
+
+#include <cassert>
+
+namespace Catch {
+
+ ListeningReporter::ListeningReporter() {
+ // We will assume that listeners will always want all assertions
+ m_preferences.shouldReportAllAssertions = true;
+ }
+
+ void ListeningReporter::addListener( IStreamingReporterPtr&& listener ) {
+ m_listeners.push_back( std::move( listener ) );
+ }
+
+ void ListeningReporter::addReporter(IStreamingReporterPtr&& reporter) {
+ assert(!m_reporter && "Listening reporter can wrap only 1 real reporter");
+ m_reporter = std::move( reporter );
+ m_preferences.shouldRedirectStdOut = m_reporter->getPreferences().shouldRedirectStdOut;
+ }
+
+ ReporterPreferences ListeningReporter::getPreferences() const {
+ return m_preferences;
+ }
+
+ std::set<Verbosity> ListeningReporter::getSupportedVerbosities() {
+ return std::set<Verbosity>{ };
+ }
+
+ void ListeningReporter::noMatchingTestCases( std::string const& spec ) {
+ for ( auto const& listener : m_listeners ) {
+ listener->noMatchingTestCases( spec );
+ }
+ m_reporter->noMatchingTestCases( spec );
+ }
+
+ void ListeningReporter::reportInvalidArguments(std::string const&arg){
+ for ( auto const& listener : m_listeners ) {
+ listener->reportInvalidArguments( arg );
+ }
+ m_reporter->reportInvalidArguments( arg );
+ }
+
+#if defined(CATCH_CONFIG_ENABLE_BENCHMARKING)
+ void ListeningReporter::benchmarkPreparing( std::string const& name ) {
+ for (auto const& listener : m_listeners) {
+ listener->benchmarkPreparing(name);
+ }
+ m_reporter->benchmarkPreparing(name);
+ }
+ void ListeningReporter::benchmarkStarting( BenchmarkInfo const& benchmarkInfo ) {
+ for ( auto const& listener : m_listeners ) {
+ listener->benchmarkStarting( benchmarkInfo );
+ }
+ m_reporter->benchmarkStarting( benchmarkInfo );
+ }
+ void ListeningReporter::benchmarkEnded( BenchmarkStats<> const& benchmarkStats ) {
+ for ( auto const& listener : m_listeners ) {
+ listener->benchmarkEnded( benchmarkStats );
+ }
+ m_reporter->benchmarkEnded( benchmarkStats );
+ }
+
+ void ListeningReporter::benchmarkFailed( std::string const& error ) {
+ for (auto const& listener : m_listeners) {
+ listener->benchmarkFailed(error);
+ }
+ m_reporter->benchmarkFailed(error);
+ }
+#endif // CATCH_CONFIG_ENABLE_BENCHMARKING
+
+ void ListeningReporter::testRunStarting( TestRunInfo const& testRunInfo ) {
+ for ( auto const& listener : m_listeners ) {
+ listener->testRunStarting( testRunInfo );
+ }
+ m_reporter->testRunStarting( testRunInfo );
+ }
+
+ void ListeningReporter::testGroupStarting( GroupInfo const& groupInfo ) {
+ for ( auto const& listener : m_listeners ) {
+ listener->testGroupStarting( groupInfo );
+ }
+ m_reporter->testGroupStarting( groupInfo );
+ }
+
+ void ListeningReporter::testCaseStarting( TestCaseInfo const& testInfo ) {
+ for ( auto const& listener : m_listeners ) {
+ listener->testCaseStarting( testInfo );
+ }
+ m_reporter->testCaseStarting( testInfo );
+ }
+
+ void ListeningReporter::sectionStarting( SectionInfo const& sectionInfo ) {
+ for ( auto const& listener : m_listeners ) {
+ listener->sectionStarting( sectionInfo );
+ }
+ m_reporter->sectionStarting( sectionInfo );
+ }
+
+ void ListeningReporter::assertionStarting( AssertionInfo const& assertionInfo ) {
+ for ( auto const& listener : m_listeners ) {
+ listener->assertionStarting( assertionInfo );
+ }
+ m_reporter->assertionStarting( assertionInfo );
+ }
+
+ // The return value indicates if the messages buffer should be cleared:
+ bool ListeningReporter::assertionEnded( AssertionStats const& assertionStats ) {
+ for( auto const& listener : m_listeners ) {
+ static_cast<void>( listener->assertionEnded( assertionStats ) );
+ }
+ return m_reporter->assertionEnded( assertionStats );
+ }
+
+ void ListeningReporter::sectionEnded( SectionStats const& sectionStats ) {
+ for ( auto const& listener : m_listeners ) {
+ listener->sectionEnded( sectionStats );
+ }
+ m_reporter->sectionEnded( sectionStats );
+ }
+
+ void ListeningReporter::testCaseEnded( TestCaseStats const& testCaseStats ) {
+ for ( auto const& listener : m_listeners ) {
+ listener->testCaseEnded( testCaseStats );
+ }
+ m_reporter->testCaseEnded( testCaseStats );
+ }
+
+ void ListeningReporter::testGroupEnded( TestGroupStats const& testGroupStats ) {
+ for ( auto const& listener : m_listeners ) {
+ listener->testGroupEnded( testGroupStats );
+ }
+ m_reporter->testGroupEnded( testGroupStats );
+ }
+
+ void ListeningReporter::testRunEnded( TestRunStats const& testRunStats ) {
+ for ( auto const& listener : m_listeners ) {
+ listener->testRunEnded( testRunStats );
+ }
+ m_reporter->testRunEnded( testRunStats );
+ }
+
+ void ListeningReporter::skipTest( TestCaseInfo const& testInfo ) {
+ for ( auto const& listener : m_listeners ) {
+ listener->skipTest( testInfo );
+ }
+ m_reporter->skipTest( testInfo );
+ }
+
+ bool ListeningReporter::isMulti() const {
+ return true;
+ }
+
+} // end namespace Catch
+// end catch_reporter_listening.cpp
+// start catch_reporter_xml.cpp
+
+#if defined(_MSC_VER)
+#pragma warning(push)
+#pragma warning(disable:4061) // Not all labels are EXPLICITLY handled in switch
+ // Note that 4062 (not all labels are handled
+ // and default is missing) is enabled
+#endif
+
+namespace Catch {
+ XmlReporter::XmlReporter( ReporterConfig const& _config )
+ : StreamingReporterBase( _config ),
+ m_xml(_config.stream())
+ {
+ m_reporterPrefs.shouldRedirectStdOut = true;
+ m_reporterPrefs.shouldReportAllAssertions = true;
+ }
+
+ XmlReporter::~XmlReporter() = default;
+
+ std::string XmlReporter::getDescription() {
+ return "Reports test results as an XML document";
+ }
+
+ std::string XmlReporter::getStylesheetRef() const {
+ return std::string();
+ }
+
+ void XmlReporter::writeSourceInfo( SourceLineInfo const& sourceInfo ) {
+ m_xml
+ .writeAttribute( "filename", sourceInfo.file )
+ .writeAttribute( "line", sourceInfo.line );
+ }
+
+ void XmlReporter::noMatchingTestCases( std::string const& s ) {
+ StreamingReporterBase::noMatchingTestCases( s );
+ }
+
+ void XmlReporter::testRunStarting( TestRunInfo const& testInfo ) {
+ StreamingReporterBase::testRunStarting( testInfo );
+ std::string stylesheetRef = getStylesheetRef();
+ if( !stylesheetRef.empty() )
+ m_xml.writeStylesheetRef( stylesheetRef );
+ m_xml.startElement( "Catch" );
+ if( !m_config->name().empty() )
+ m_xml.writeAttribute( "name", m_config->name() );
+ if (m_config->testSpec().hasFilters())
+ m_xml.writeAttribute( "filters", serializeFilters( m_config->getTestsOrTags() ) );
+ if( m_config->rngSeed() != 0 )
+ m_xml.scopedElement( "Randomness" )
+ .writeAttribute( "seed", m_config->rngSeed() );
+ }
+
+ void XmlReporter::testGroupStarting( GroupInfo const& groupInfo ) {
+ StreamingReporterBase::testGroupStarting( groupInfo );
+ m_xml.startElement( "Group" )
+ .writeAttribute( "name", groupInfo.name );
+ }
+
+ void XmlReporter::testCaseStarting( TestCaseInfo const& testInfo ) {
+ StreamingReporterBase::testCaseStarting(testInfo);
+ m_xml.startElement( "TestCase" )
+ .writeAttribute( "name", trim( testInfo.name ) )
+ .writeAttribute( "description", testInfo.description )
+ .writeAttribute( "tags", testInfo.tagsAsString() );
+
+ writeSourceInfo( testInfo.lineInfo );
+
+ if ( m_config->showDurations() == ShowDurations::Always )
+ m_testCaseTimer.start();
+ m_xml.ensureTagClosed();
+ }
+
+ void XmlReporter::sectionStarting( SectionInfo const& sectionInfo ) {
+ StreamingReporterBase::sectionStarting( sectionInfo );
+ if( m_sectionDepth++ > 0 ) {
+ m_xml.startElement( "Section" )
+ .writeAttribute( "name", trim( sectionInfo.name ) );
+ writeSourceInfo( sectionInfo.lineInfo );
+ m_xml.ensureTagClosed();
+ }
+ }
+
+ void XmlReporter::assertionStarting( AssertionInfo const& ) { }
+
+ bool XmlReporter::assertionEnded( AssertionStats const& assertionStats ) {
+
+ AssertionResult const& result = assertionStats.assertionResult;
+
+ bool includeResults = m_config->includeSuccessfulResults() || !result.isOk();
+
+ if( includeResults || result.getResultType() == ResultWas::Warning ) {
+ // Print any info messages in <Info> tags.
+ for( auto const& msg : assertionStats.infoMessages ) {
+ if( msg.type == ResultWas::Info && includeResults ) {
+ m_xml.scopedElement( "Info" )
+ .writeText( msg.message );
+ } else if ( msg.type == ResultWas::Warning ) {
+ m_xml.scopedElement( "Warning" )
+ .writeText( msg.message );
+ }
+ }
+ }
+
+ // Drop out if result was successful but we're not printing them.
+ if( !includeResults && result.getResultType() != ResultWas::Warning )
+ return true;
+
+ // Print the expression if there is one.
+ if( result.hasExpression() ) {
+ m_xml.startElement( "Expression" )
+ .writeAttribute( "success", result.succeeded() )
+ .writeAttribute( "type", result.getTestMacroName() );
+
+ writeSourceInfo( result.getSourceInfo() );
+
+ m_xml.scopedElement( "Original" )
+ .writeText( result.getExpression() );
+ m_xml.scopedElement( "Expanded" )
+ .writeText( result.getExpandedExpression() );
+ }
+
+ // And... Print a result applicable to each result type.
+ switch( result.getResultType() ) {
+ case ResultWas::ThrewException:
+ m_xml.startElement( "Exception" );
+ writeSourceInfo( result.getSourceInfo() );
+ m_xml.writeText( result.getMessage() );
+ m_xml.endElement();
+ break;
+ case ResultWas::FatalErrorCondition:
+ m_xml.startElement( "FatalErrorCondition" );
+ writeSourceInfo( result.getSourceInfo() );
+ m_xml.writeText( result.getMessage() );
+ m_xml.endElement();
+ break;
+ case ResultWas::Info:
+ m_xml.scopedElement( "Info" )
+ .writeText( result.getMessage() );
+ break;
+ case ResultWas::Warning:
+ // Warning will already have been written
+ break;
+ case ResultWas::ExplicitFailure:
+ m_xml.startElement( "Failure" );
+ writeSourceInfo( result.getSourceInfo() );
+ m_xml.writeText( result.getMessage() );
+ m_xml.endElement();
+ break;
+ default:
+ break;
+ }
+
+ if( result.hasExpression() )
+ m_xml.endElement();
+
+ return true;
+ }
+
+ void XmlReporter::sectionEnded( SectionStats const& sectionStats ) {
+ StreamingReporterBase::sectionEnded( sectionStats );
+ if( --m_sectionDepth > 0 ) {
+ XmlWriter::ScopedElement e = m_xml.scopedElement( "OverallResults" );
+ e.writeAttribute( "successes", sectionStats.assertions.passed );
+ e.writeAttribute( "failures", sectionStats.assertions.failed );
+ e.writeAttribute( "expectedFailures", sectionStats.assertions.failedButOk );
+
+ if ( m_config->showDurations() == ShowDurations::Always )
+ e.writeAttribute( "durationInSeconds", sectionStats.durationInSeconds );
+
+ m_xml.endElement();
+ }
+ }
+
+ void XmlReporter::testCaseEnded( TestCaseStats const& testCaseStats ) {
+ StreamingReporterBase::testCaseEnded( testCaseStats );
+ XmlWriter::ScopedElement e = m_xml.scopedElement( "OverallResult" );
+ e.writeAttribute( "success", testCaseStats.totals.assertions.allOk() );
+
+ if ( m_config->showDurations() == ShowDurations::Always )
+ e.writeAttribute( "durationInSeconds", m_testCaseTimer.getElapsedSeconds() );
+
+ if( !testCaseStats.stdOut.empty() )
+ m_xml.scopedElement( "StdOut" ).writeText( trim( testCaseStats.stdOut ), XmlFormatting::Newline );
+ if( !testCaseStats.stdErr.empty() )
+ m_xml.scopedElement( "StdErr" ).writeText( trim( testCaseStats.stdErr ), XmlFormatting::Newline );
+
+ m_xml.endElement();
+ }
+
+ void XmlReporter::testGroupEnded( TestGroupStats const& testGroupStats ) {
+ StreamingReporterBase::testGroupEnded( testGroupStats );
+ // TODO: Check testGroupStats.aborting and act accordingly.
+ m_xml.scopedElement( "OverallResults" )
+ .writeAttribute( "successes", testGroupStats.totals.assertions.passed )
+ .writeAttribute( "failures", testGroupStats.totals.assertions.failed )
+ .writeAttribute( "expectedFailures", testGroupStats.totals.assertions.failedButOk );
+ m_xml.scopedElement( "OverallResultsCases")
+ .writeAttribute( "successes", testGroupStats.totals.testCases.passed )
+ .writeAttribute( "failures", testGroupStats.totals.testCases.failed )
+ .writeAttribute( "expectedFailures", testGroupStats.totals.testCases.failedButOk );
+ m_xml.endElement();
+ }
+
+ void XmlReporter::testRunEnded( TestRunStats const& testRunStats ) {
+ StreamingReporterBase::testRunEnded( testRunStats );
+ m_xml.scopedElement( "OverallResults" )
+ .writeAttribute( "successes", testRunStats.totals.assertions.passed )
+ .writeAttribute( "failures", testRunStats.totals.assertions.failed )
+ .writeAttribute( "expectedFailures", testRunStats.totals.assertions.failedButOk );
+ m_xml.scopedElement( "OverallResultsCases")
+ .writeAttribute( "successes", testRunStats.totals.testCases.passed )
+ .writeAttribute( "failures", testRunStats.totals.testCases.failed )
+ .writeAttribute( "expectedFailures", testRunStats.totals.testCases.failedButOk );
+ m_xml.endElement();
+ }
+
+#if defined(CATCH_CONFIG_ENABLE_BENCHMARKING)
+ void XmlReporter::benchmarkPreparing(std::string const& name) {
+ m_xml.startElement("BenchmarkResults")
+ .writeAttribute("name", name);
+ }
+
+ void XmlReporter::benchmarkStarting(BenchmarkInfo const &info) {
+ m_xml.writeAttribute("samples", info.samples)
+ .writeAttribute("resamples", info.resamples)
+ .writeAttribute("iterations", info.iterations)
+ .writeAttribute("clockResolution", info.clockResolution)
+ .writeAttribute("estimatedDuration", info.estimatedDuration)
+ .writeComment("All values in nano seconds");
+ }
+
+ void XmlReporter::benchmarkEnded(BenchmarkStats<> const& benchmarkStats) {
+ m_xml.startElement("mean")
+ .writeAttribute("value", benchmarkStats.mean.point.count())
+ .writeAttribute("lowerBound", benchmarkStats.mean.lower_bound.count())
+ .writeAttribute("upperBound", benchmarkStats.mean.upper_bound.count())
+ .writeAttribute("ci", benchmarkStats.mean.confidence_interval);
+ m_xml.endElement();
+ m_xml.startElement("standardDeviation")
+ .writeAttribute("value", benchmarkStats.standardDeviation.point.count())
+ .writeAttribute("lowerBound", benchmarkStats.standardDeviation.lower_bound.count())
+ .writeAttribute("upperBound", benchmarkStats.standardDeviation.upper_bound.count())
+ .writeAttribute("ci", benchmarkStats.standardDeviation.confidence_interval);
+ m_xml.endElement();
+ m_xml.startElement("outliers")
+ .writeAttribute("variance", benchmarkStats.outlierVariance)
+ .writeAttribute("lowMild", benchmarkStats.outliers.low_mild)
+ .writeAttribute("lowSevere", benchmarkStats.outliers.low_severe)
+ .writeAttribute("highMild", benchmarkStats.outliers.high_mild)
+ .writeAttribute("highSevere", benchmarkStats.outliers.high_severe);
+ m_xml.endElement();
+ m_xml.endElement();
+ }
+
+ void XmlReporter::benchmarkFailed(std::string const &error) {
+ m_xml.scopedElement("failed").
+ writeAttribute("message", error);
+ m_xml.endElement();
+ }
+#endif // CATCH_CONFIG_ENABLE_BENCHMARKING
+
+ CATCH_REGISTER_REPORTER( "xml", XmlReporter )
+
+} // end namespace Catch
+
+#if defined(_MSC_VER)
+#pragma warning(pop)
+#endif
+// end catch_reporter_xml.cpp
+
+namespace Catch {
+ LeakDetector leakDetector;
+}
+
+#ifdef __clang__
+#pragma clang diagnostic pop
+#endif
+
+// end catch_impl.hpp
+#endif
+
+#ifdef CATCH_CONFIG_MAIN
+// start catch_default_main.hpp
+
+#ifndef __OBJC__
+
+#ifndef CATCH_INTERNAL_CDECL
+#ifdef _MSC_VER
+#define CATCH_INTERNAL_CDECL __cdecl
+#else
+#define CATCH_INTERNAL_CDECL
+#endif
+#endif
+
+#if defined(CATCH_CONFIG_WCHAR) && defined(CATCH_PLATFORM_WINDOWS) && defined(_UNICODE) && !defined(DO_NOT_USE_WMAIN)
+// Standard C/C++ Win32 Unicode wmain entry point
+extern "C" int CATCH_INTERNAL_CDECL wmain (int argc, wchar_t * argv[], wchar_t * []) {
+#else
+// Standard C/C++ main entry point
+int CATCH_INTERNAL_CDECL main (int argc, char * argv[]) {
+#endif
+
+ return Catch::Session().run( argc, argv );
+}
+
+#else // __OBJC__
+
+// Objective-C entry point
+int main (int argc, char * const argv[]) {
+#if !CATCH_ARC_ENABLED
+ NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
+#endif
+
+ Catch::registerTestMethods();
+ int result = Catch::Session().run( argc, (char**)argv );
+
+#if !CATCH_ARC_ENABLED
+ [pool drain];
+#endif
+
+ return result;
+}
+
+#endif // __OBJC__
+
+// end catch_default_main.hpp
+#endif
+
+#if !defined(CATCH_CONFIG_IMPL_ONLY)
+
+#ifdef CLARA_CONFIG_MAIN_NOT_DEFINED
+# undef CLARA_CONFIG_MAIN
+#endif
+
+#if !defined(CATCH_CONFIG_DISABLE)
+//////
+// If this config identifier is defined then all CATCH macros are prefixed with CATCH_
+#ifdef CATCH_CONFIG_PREFIX_ALL
+
+#define CATCH_REQUIRE( ... ) INTERNAL_CATCH_TEST( "CATCH_REQUIRE", Catch::ResultDisposition::Normal, __VA_ARGS__ )
+#define CATCH_REQUIRE_FALSE( ... ) INTERNAL_CATCH_TEST( "CATCH_REQUIRE_FALSE", Catch::ResultDisposition::Normal | Catch::ResultDisposition::FalseTest, __VA_ARGS__ )
+
+#define CATCH_REQUIRE_THROWS( ... ) INTERNAL_CATCH_THROWS( "CATCH_REQUIRE_THROWS", Catch::ResultDisposition::Normal, __VA_ARGS__ )
+#define CATCH_REQUIRE_THROWS_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( "CATCH_REQUIRE_THROWS_AS", exceptionType, Catch::ResultDisposition::Normal, expr )
+#define CATCH_REQUIRE_THROWS_WITH( expr, matcher ) INTERNAL_CATCH_THROWS_STR_MATCHES( "CATCH_REQUIRE_THROWS_WITH", Catch::ResultDisposition::Normal, matcher, expr )
+#if !defined(CATCH_CONFIG_DISABLE_MATCHERS)
+#define CATCH_REQUIRE_THROWS_MATCHES( expr, exceptionType, matcher ) INTERNAL_CATCH_THROWS_MATCHES( "CATCH_REQUIRE_THROWS_MATCHES", exceptionType, Catch::ResultDisposition::Normal, matcher, expr )
+#endif// CATCH_CONFIG_DISABLE_MATCHERS
+#define CATCH_REQUIRE_NOTHROW( ... ) INTERNAL_CATCH_NO_THROW( "CATCH_REQUIRE_NOTHROW", Catch::ResultDisposition::Normal, __VA_ARGS__ )
+
+#define CATCH_CHECK( ... ) INTERNAL_CATCH_TEST( "CATCH_CHECK", Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ )
+#define CATCH_CHECK_FALSE( ... ) INTERNAL_CATCH_TEST( "CATCH_CHECK_FALSE", Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::FalseTest, __VA_ARGS__ )
+#define CATCH_CHECKED_IF( ... ) INTERNAL_CATCH_IF( "CATCH_CHECKED_IF", Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ )
+#define CATCH_CHECKED_ELSE( ... ) INTERNAL_CATCH_ELSE( "CATCH_CHECKED_ELSE", Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ )
+#define CATCH_CHECK_NOFAIL( ... ) INTERNAL_CATCH_TEST( "CATCH_CHECK_NOFAIL", Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::SuppressFail, __VA_ARGS__ )
+
+#define CATCH_CHECK_THROWS( ... ) INTERNAL_CATCH_THROWS( "CATCH_CHECK_THROWS", Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ )
+#define CATCH_CHECK_THROWS_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( "CATCH_CHECK_THROWS_AS", exceptionType, Catch::ResultDisposition::ContinueOnFailure, expr )
+#define CATCH_CHECK_THROWS_WITH( expr, matcher ) INTERNAL_CATCH_THROWS_STR_MATCHES( "CATCH_CHECK_THROWS_WITH", Catch::ResultDisposition::ContinueOnFailure, matcher, expr )
+#if !defined(CATCH_CONFIG_DISABLE_MATCHERS)
+#define CATCH_CHECK_THROWS_MATCHES( expr, exceptionType, matcher ) INTERNAL_CATCH_THROWS_MATCHES( "CATCH_CHECK_THROWS_MATCHES", exceptionType, Catch::ResultDisposition::ContinueOnFailure, matcher, expr )
+#endif // CATCH_CONFIG_DISABLE_MATCHERS
+#define CATCH_CHECK_NOTHROW( ... ) INTERNAL_CATCH_NO_THROW( "CATCH_CHECK_NOTHROW", Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ )
+
+#if !defined(CATCH_CONFIG_DISABLE_MATCHERS)
+#define CATCH_CHECK_THAT( arg, matcher ) INTERNAL_CHECK_THAT( "CATCH_CHECK_THAT", matcher, Catch::ResultDisposition::ContinueOnFailure, arg )
+
+#define CATCH_REQUIRE_THAT( arg, matcher ) INTERNAL_CHECK_THAT( "CATCH_REQUIRE_THAT", matcher, Catch::ResultDisposition::Normal, arg )
+#endif // CATCH_CONFIG_DISABLE_MATCHERS
+
+#define CATCH_INFO( msg ) INTERNAL_CATCH_INFO( "CATCH_INFO", msg )
+#define CATCH_UNSCOPED_INFO( msg ) INTERNAL_CATCH_UNSCOPED_INFO( "CATCH_UNSCOPED_INFO", msg )
+#define CATCH_WARN( msg ) INTERNAL_CATCH_MSG( "CATCH_WARN", Catch::ResultWas::Warning, Catch::ResultDisposition::ContinueOnFailure, msg )
+#define CATCH_CAPTURE( ... ) INTERNAL_CATCH_CAPTURE( INTERNAL_CATCH_UNIQUE_NAME(capturer), "CATCH_CAPTURE",__VA_ARGS__ )
+
+#define CATCH_TEST_CASE( ... ) INTERNAL_CATCH_TESTCASE( __VA_ARGS__ )
+#define CATCH_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEST_CASE_METHOD( className, __VA_ARGS__ )
+#define CATCH_METHOD_AS_TEST_CASE( method, ... ) INTERNAL_CATCH_METHOD_AS_TEST_CASE( method, __VA_ARGS__ )
+#define CATCH_REGISTER_TEST_CASE( Function, ... ) INTERNAL_CATCH_REGISTER_TESTCASE( Function, __VA_ARGS__ )
+#define CATCH_SECTION( ... ) INTERNAL_CATCH_SECTION( __VA_ARGS__ )
+#define CATCH_DYNAMIC_SECTION( ... ) INTERNAL_CATCH_DYNAMIC_SECTION( __VA_ARGS__ )
+#define CATCH_FAIL( ... ) INTERNAL_CATCH_MSG( "CATCH_FAIL", Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::Normal, __VA_ARGS__ )
+#define CATCH_FAIL_CHECK( ... ) INTERNAL_CATCH_MSG( "CATCH_FAIL_CHECK", Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ )
+#define CATCH_SUCCEED( ... ) INTERNAL_CATCH_MSG( "CATCH_SUCCEED", Catch::ResultWas::Ok, Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ )
+
+#define CATCH_ANON_TEST_CASE() INTERNAL_CATCH_TESTCASE()
+
+#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
+#define CATCH_TEMPLATE_TEST_CASE( ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE( __VA_ARGS__ )
+#define CATCH_TEMPLATE_TEST_CASE_SIG( ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE_SIG( __VA_ARGS__ )
+#define CATCH_TEMPLATE_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD( className, __VA_ARGS__ )
+#define CATCH_TEMPLATE_TEST_CASE_METHOD_SIG( className, ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_SIG( className, __VA_ARGS__ )
+#define CATCH_TEMPLATE_PRODUCT_TEST_CASE( ... ) INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE( __VA_ARGS__ )
+#define CATCH_TEMPLATE_PRODUCT_TEST_CASE_SIG( ... ) INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_SIG( __VA_ARGS__ )
+#define CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD( className, __VA_ARGS__ )
+#define CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG( className, ... ) INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG( className, __VA_ARGS__ )
+#else
+#define CATCH_TEMPLATE_TEST_CASE( ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE( __VA_ARGS__ ) )
+#define CATCH_TEMPLATE_TEST_CASE_SIG( ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_SIG( __VA_ARGS__ ) )
+#define CATCH_TEMPLATE_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD( className, __VA_ARGS__ ) )
+#define CATCH_TEMPLATE_TEST_CASE_METHOD_SIG( className, ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_SIG( className, __VA_ARGS__ ) )
+#define CATCH_TEMPLATE_PRODUCT_TEST_CASE( ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE( __VA_ARGS__ ) )
+#define CATCH_TEMPLATE_PRODUCT_TEST_CASE_SIG( ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_SIG( __VA_ARGS__ ) )
+#define CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD( className, __VA_ARGS__ ) )
+#define CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG( className, ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG( className, __VA_ARGS__ ) )
+#endif
+
+#if !defined(CATCH_CONFIG_RUNTIME_STATIC_REQUIRE)
+#define CATCH_STATIC_REQUIRE( ... ) static_assert( __VA_ARGS__ , #__VA_ARGS__ ); CATCH_SUCCEED( #__VA_ARGS__ )
+#define CATCH_STATIC_REQUIRE_FALSE( ... ) static_assert( !(__VA_ARGS__), "!(" #__VA_ARGS__ ")" ); CATCH_SUCCEED( #__VA_ARGS__ )
+#else
+#define CATCH_STATIC_REQUIRE( ... ) CATCH_REQUIRE( __VA_ARGS__ )
+#define CATCH_STATIC_REQUIRE_FALSE( ... ) CATCH_REQUIRE_FALSE( __VA_ARGS__ )
+#endif
+
+// "BDD-style" convenience wrappers
+#define CATCH_SCENARIO( ... ) CATCH_TEST_CASE( "Scenario: " __VA_ARGS__ )
+#define CATCH_SCENARIO_METHOD( className, ... ) INTERNAL_CATCH_TEST_CASE_METHOD( className, "Scenario: " __VA_ARGS__ )
+#define CATCH_GIVEN( desc ) INTERNAL_CATCH_DYNAMIC_SECTION( " Given: " << desc )
+#define CATCH_AND_GIVEN( desc ) INTERNAL_CATCH_DYNAMIC_SECTION( "And given: " << desc )
+#define CATCH_WHEN( desc ) INTERNAL_CATCH_DYNAMIC_SECTION( " When: " << desc )
+#define CATCH_AND_WHEN( desc ) INTERNAL_CATCH_DYNAMIC_SECTION( " And when: " << desc )
+#define CATCH_THEN( desc ) INTERNAL_CATCH_DYNAMIC_SECTION( " Then: " << desc )
+#define CATCH_AND_THEN( desc ) INTERNAL_CATCH_DYNAMIC_SECTION( " And: " << desc )
+
+#if defined(CATCH_CONFIG_ENABLE_BENCHMARKING)
+#define CATCH_BENCHMARK(...) \
+ INTERNAL_CATCH_BENCHMARK(INTERNAL_CATCH_UNIQUE_NAME(C_A_T_C_H_B_E_N_C_H_), INTERNAL_CATCH_GET_1_ARG(__VA_ARGS__,,), INTERNAL_CATCH_GET_2_ARG(__VA_ARGS__,,))
+#define CATCH_BENCHMARK_ADVANCED(name) \
+ INTERNAL_CATCH_BENCHMARK_ADVANCED(INTERNAL_CATCH_UNIQUE_NAME(C_A_T_C_H_B_E_N_C_H_), name)
+#endif // CATCH_CONFIG_ENABLE_BENCHMARKING
+
+// If CATCH_CONFIG_PREFIX_ALL is not defined then the CATCH_ prefix is not required
+#else
+
+#define REQUIRE( ... ) INTERNAL_CATCH_TEST( "REQUIRE", Catch::ResultDisposition::Normal, __VA_ARGS__ )
+#define REQUIRE_FALSE( ... ) INTERNAL_CATCH_TEST( "REQUIRE_FALSE", Catch::ResultDisposition::Normal | Catch::ResultDisposition::FalseTest, __VA_ARGS__ )
+
+#define REQUIRE_THROWS( ... ) INTERNAL_CATCH_THROWS( "REQUIRE_THROWS", Catch::ResultDisposition::Normal, __VA_ARGS__ )
+#define REQUIRE_THROWS_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( "REQUIRE_THROWS_AS", exceptionType, Catch::ResultDisposition::Normal, expr )
+#define REQUIRE_THROWS_WITH( expr, matcher ) INTERNAL_CATCH_THROWS_STR_MATCHES( "REQUIRE_THROWS_WITH", Catch::ResultDisposition::Normal, matcher, expr )
+#if !defined(CATCH_CONFIG_DISABLE_MATCHERS)
+#define REQUIRE_THROWS_MATCHES( expr, exceptionType, matcher ) INTERNAL_CATCH_THROWS_MATCHES( "REQUIRE_THROWS_MATCHES", exceptionType, Catch::ResultDisposition::Normal, matcher, expr )
+#endif // CATCH_CONFIG_DISABLE_MATCHERS
+#define REQUIRE_NOTHROW( ... ) INTERNAL_CATCH_NO_THROW( "REQUIRE_NOTHROW", Catch::ResultDisposition::Normal, __VA_ARGS__ )
+
+#define CHECK( ... ) INTERNAL_CATCH_TEST( "CHECK", Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ )
+#define CHECK_FALSE( ... ) INTERNAL_CATCH_TEST( "CHECK_FALSE", Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::FalseTest, __VA_ARGS__ )
+#define CHECKED_IF( ... ) INTERNAL_CATCH_IF( "CHECKED_IF", Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ )
+#define CHECKED_ELSE( ... ) INTERNAL_CATCH_ELSE( "CHECKED_ELSE", Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ )
+#define CHECK_NOFAIL( ... ) INTERNAL_CATCH_TEST( "CHECK_NOFAIL", Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::SuppressFail, __VA_ARGS__ )
+
+#define CHECK_THROWS( ... ) INTERNAL_CATCH_THROWS( "CHECK_THROWS", Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ )
+#define CHECK_THROWS_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( "CHECK_THROWS_AS", exceptionType, Catch::ResultDisposition::ContinueOnFailure, expr )
+#define CHECK_THROWS_WITH( expr, matcher ) INTERNAL_CATCH_THROWS_STR_MATCHES( "CHECK_THROWS_WITH", Catch::ResultDisposition::ContinueOnFailure, matcher, expr )
+#if !defined(CATCH_CONFIG_DISABLE_MATCHERS)
+#define CHECK_THROWS_MATCHES( expr, exceptionType, matcher ) INTERNAL_CATCH_THROWS_MATCHES( "CHECK_THROWS_MATCHES", exceptionType, Catch::ResultDisposition::ContinueOnFailure, matcher, expr )
+#endif // CATCH_CONFIG_DISABLE_MATCHERS
+#define CHECK_NOTHROW( ... ) INTERNAL_CATCH_NO_THROW( "CHECK_NOTHROW", Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ )
+
+#if !defined(CATCH_CONFIG_DISABLE_MATCHERS)
+#define CHECK_THAT( arg, matcher ) INTERNAL_CHECK_THAT( "CHECK_THAT", matcher, Catch::ResultDisposition::ContinueOnFailure, arg )
+
+#define REQUIRE_THAT( arg, matcher ) INTERNAL_CHECK_THAT( "REQUIRE_THAT", matcher, Catch::ResultDisposition::Normal, arg )
+#endif // CATCH_CONFIG_DISABLE_MATCHERS
+
+#define INFO( msg ) INTERNAL_CATCH_INFO( "INFO", msg )
+#define UNSCOPED_INFO( msg ) INTERNAL_CATCH_UNSCOPED_INFO( "UNSCOPED_INFO", msg )
+#define WARN( msg ) INTERNAL_CATCH_MSG( "WARN", Catch::ResultWas::Warning, Catch::ResultDisposition::ContinueOnFailure, msg )
+#define CAPTURE( ... ) INTERNAL_CATCH_CAPTURE( INTERNAL_CATCH_UNIQUE_NAME(capturer), "CAPTURE",__VA_ARGS__ )
+
+#define TEST_CASE( ... ) INTERNAL_CATCH_TESTCASE( __VA_ARGS__ )
+#define TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEST_CASE_METHOD( className, __VA_ARGS__ )
+#define METHOD_AS_TEST_CASE( method, ... ) INTERNAL_CATCH_METHOD_AS_TEST_CASE( method, __VA_ARGS__ )
+#define REGISTER_TEST_CASE( Function, ... ) INTERNAL_CATCH_REGISTER_TESTCASE( Function, __VA_ARGS__ )
+#define SECTION( ... ) INTERNAL_CATCH_SECTION( __VA_ARGS__ )
+#define DYNAMIC_SECTION( ... ) INTERNAL_CATCH_DYNAMIC_SECTION( __VA_ARGS__ )
+#define FAIL( ... ) INTERNAL_CATCH_MSG( "FAIL", Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::Normal, __VA_ARGS__ )
+#define FAIL_CHECK( ... ) INTERNAL_CATCH_MSG( "FAIL_CHECK", Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ )
+#define SUCCEED( ... ) INTERNAL_CATCH_MSG( "SUCCEED", Catch::ResultWas::Ok, Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ )
+#define ANON_TEST_CASE() INTERNAL_CATCH_TESTCASE()
+
+#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
+#define TEMPLATE_TEST_CASE( ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE( __VA_ARGS__ )
+#define TEMPLATE_TEST_CASE_SIG( ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE_SIG( __VA_ARGS__ )
+#define TEMPLATE_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD( className, __VA_ARGS__ )
+#define TEMPLATE_TEST_CASE_METHOD_SIG( className, ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_SIG( className, __VA_ARGS__ )
+#define TEMPLATE_PRODUCT_TEST_CASE( ... ) INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE( __VA_ARGS__ )
+#define TEMPLATE_PRODUCT_TEST_CASE_SIG( ... ) INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_SIG( __VA_ARGS__ )
+#define TEMPLATE_PRODUCT_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD( className, __VA_ARGS__ )
+#define TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG( className, ... ) INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG( className, __VA_ARGS__ )
+#define TEMPLATE_LIST_TEST_CASE( ... ) INTERNAL_CATCH_TEMPLATE_LIST_TEST_CASE(__VA_ARGS__)
+#define TEMPLATE_LIST_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEMPLATE_LIST_TEST_CASE_METHOD( className, __VA_ARGS__ )
+#else
+#define TEMPLATE_TEST_CASE( ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE( __VA_ARGS__ ) )
+#define TEMPLATE_TEST_CASE_SIG( ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_SIG( __VA_ARGS__ ) )
+#define TEMPLATE_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD( className, __VA_ARGS__ ) )
+#define TEMPLATE_TEST_CASE_METHOD_SIG( className, ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_SIG( className, __VA_ARGS__ ) )
+#define TEMPLATE_PRODUCT_TEST_CASE( ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE( __VA_ARGS__ ) )
+#define TEMPLATE_PRODUCT_TEST_CASE_SIG( ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_SIG( __VA_ARGS__ ) )
+#define TEMPLATE_PRODUCT_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD( className, __VA_ARGS__ ) )
+#define TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG( className, ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG( className, __VA_ARGS__ ) )
+#define TEMPLATE_LIST_TEST_CASE( ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_LIST_TEST_CASE( __VA_ARGS__ ) )
+#define TEMPLATE_LIST_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_LIST_TEST_CASE_METHOD( className, __VA_ARGS__ ) )
+#endif
+
+#if !defined(CATCH_CONFIG_RUNTIME_STATIC_REQUIRE)
+#define STATIC_REQUIRE( ... ) static_assert( __VA_ARGS__, #__VA_ARGS__ ); SUCCEED( #__VA_ARGS__ )
+#define STATIC_REQUIRE_FALSE( ... ) static_assert( !(__VA_ARGS__), "!(" #__VA_ARGS__ ")" ); SUCCEED( "!(" #__VA_ARGS__ ")" )
+#else
+#define STATIC_REQUIRE( ... ) REQUIRE( __VA_ARGS__ )
+#define STATIC_REQUIRE_FALSE( ... ) REQUIRE_FALSE( __VA_ARGS__ )
+#endif
+
+#endif
+
+#define CATCH_TRANSLATE_EXCEPTION( signature ) INTERNAL_CATCH_TRANSLATE_EXCEPTION( signature )
+
+// "BDD-style" convenience wrappers
+#define SCENARIO( ... ) TEST_CASE( "Scenario: " __VA_ARGS__ )
+#define SCENARIO_METHOD( className, ... ) INTERNAL_CATCH_TEST_CASE_METHOD( className, "Scenario: " __VA_ARGS__ )
+
+#define GIVEN( desc ) INTERNAL_CATCH_DYNAMIC_SECTION( " Given: " << desc )
+#define AND_GIVEN( desc ) INTERNAL_CATCH_DYNAMIC_SECTION( "And given: " << desc )
+#define WHEN( desc ) INTERNAL_CATCH_DYNAMIC_SECTION( " When: " << desc )
+#define AND_WHEN( desc ) INTERNAL_CATCH_DYNAMIC_SECTION( " And when: " << desc )
+#define THEN( desc ) INTERNAL_CATCH_DYNAMIC_SECTION( " Then: " << desc )
+#define AND_THEN( desc ) INTERNAL_CATCH_DYNAMIC_SECTION( " And: " << desc )
+
+#if defined(CATCH_CONFIG_ENABLE_BENCHMARKING)
+#define BENCHMARK(...) \
+ INTERNAL_CATCH_BENCHMARK(INTERNAL_CATCH_UNIQUE_NAME(C_A_T_C_H_B_E_N_C_H_), INTERNAL_CATCH_GET_1_ARG(__VA_ARGS__,,), INTERNAL_CATCH_GET_2_ARG(__VA_ARGS__,,))
+#define BENCHMARK_ADVANCED(name) \
+ INTERNAL_CATCH_BENCHMARK_ADVANCED(INTERNAL_CATCH_UNIQUE_NAME(C_A_T_C_H_B_E_N_C_H_), name)
+#endif // CATCH_CONFIG_ENABLE_BENCHMARKING
+
+using Catch::Detail::Approx;
+
+#else // CATCH_CONFIG_DISABLE
+
+//////
+// If this config identifier is defined then all CATCH macros are prefixed with CATCH_
+#ifdef CATCH_CONFIG_PREFIX_ALL
+
+#define CATCH_REQUIRE( ... ) (void)(0)
+#define CATCH_REQUIRE_FALSE( ... ) (void)(0)
+
+#define CATCH_REQUIRE_THROWS( ... ) (void)(0)
+#define CATCH_REQUIRE_THROWS_AS( expr, exceptionType ) (void)(0)
+#define CATCH_REQUIRE_THROWS_WITH( expr, matcher ) (void)(0)
+#if !defined(CATCH_CONFIG_DISABLE_MATCHERS)
+#define CATCH_REQUIRE_THROWS_MATCHES( expr, exceptionType, matcher ) (void)(0)
+#endif// CATCH_CONFIG_DISABLE_MATCHERS
+#define CATCH_REQUIRE_NOTHROW( ... ) (void)(0)
+
+#define CATCH_CHECK( ... ) (void)(0)
+#define CATCH_CHECK_FALSE( ... ) (void)(0)
+#define CATCH_CHECKED_IF( ... ) if (__VA_ARGS__)
+#define CATCH_CHECKED_ELSE( ... ) if (!(__VA_ARGS__))
+#define CATCH_CHECK_NOFAIL( ... ) (void)(0)
+
+#define CATCH_CHECK_THROWS( ... ) (void)(0)
+#define CATCH_CHECK_THROWS_AS( expr, exceptionType ) (void)(0)
+#define CATCH_CHECK_THROWS_WITH( expr, matcher ) (void)(0)
+#if !defined(CATCH_CONFIG_DISABLE_MATCHERS)
+#define CATCH_CHECK_THROWS_MATCHES( expr, exceptionType, matcher ) (void)(0)
+#endif // CATCH_CONFIG_DISABLE_MATCHERS
+#define CATCH_CHECK_NOTHROW( ... ) (void)(0)
+
+#if !defined(CATCH_CONFIG_DISABLE_MATCHERS)
+#define CATCH_CHECK_THAT( arg, matcher ) (void)(0)
+
+#define CATCH_REQUIRE_THAT( arg, matcher ) (void)(0)
+#endif // CATCH_CONFIG_DISABLE_MATCHERS
+
+#define CATCH_INFO( msg ) (void)(0)
+#define CATCH_UNSCOPED_INFO( msg ) (void)(0)
+#define CATCH_WARN( msg ) (void)(0)
+#define CATCH_CAPTURE( msg ) (void)(0)
+
+#define CATCH_TEST_CASE( ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_S_T_ ))
+#define CATCH_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_S_T_ ))
+#define CATCH_METHOD_AS_TEST_CASE( method, ... )
+#define CATCH_REGISTER_TEST_CASE( Function, ... ) (void)(0)
+#define CATCH_SECTION( ... )
+#define CATCH_DYNAMIC_SECTION( ... )
+#define CATCH_FAIL( ... ) (void)(0)
+#define CATCH_FAIL_CHECK( ... ) (void)(0)
+#define CATCH_SUCCEED( ... ) (void)(0)
+
+#define CATCH_ANON_TEST_CASE() INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_S_T_ ))
+
+#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
+#define CATCH_TEMPLATE_TEST_CASE( ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION(__VA_ARGS__)
+#define CATCH_TEMPLATE_TEST_CASE_SIG( ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE_SIG_NO_REGISTRATION(__VA_ARGS__)
+#define CATCH_TEMPLATE_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION(className, __VA_ARGS__)
+#define CATCH_TEMPLATE_TEST_CASE_METHOD_SIG( className, ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_SIG_NO_REGISTRATION(className, __VA_ARGS__ )
+#define CATCH_TEMPLATE_PRODUCT_TEST_CASE( ... ) CATCH_TEMPLATE_TEST_CASE( __VA_ARGS__ )
+#define CATCH_TEMPLATE_PRODUCT_TEST_CASE_SIG( ... ) CATCH_TEMPLATE_TEST_CASE( __VA_ARGS__ )
+#define CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD( className, ... ) CATCH_TEMPLATE_TEST_CASE_METHOD( className, __VA_ARGS__ )
+#define CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG( className, ... ) CATCH_TEMPLATE_TEST_CASE_METHOD( className, __VA_ARGS__ )
+#else
+#define CATCH_TEMPLATE_TEST_CASE( ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION(__VA_ARGS__) )
+#define CATCH_TEMPLATE_TEST_CASE_SIG( ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_SIG_NO_REGISTRATION(__VA_ARGS__) )
+#define CATCH_TEMPLATE_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION(className, __VA_ARGS__ ) )
+#define CATCH_TEMPLATE_TEST_CASE_METHOD_SIG( className, ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_SIG_NO_REGISTRATION(className, __VA_ARGS__ ) )
+#define CATCH_TEMPLATE_PRODUCT_TEST_CASE( ... ) CATCH_TEMPLATE_TEST_CASE( __VA_ARGS__ )
+#define CATCH_TEMPLATE_PRODUCT_TEST_CASE_SIG( ... ) CATCH_TEMPLATE_TEST_CASE( __VA_ARGS__ )
+#define CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD( className, ... ) CATCH_TEMPLATE_TEST_CASE_METHOD( className, __VA_ARGS__ )
+#define CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG( className, ... ) CATCH_TEMPLATE_TEST_CASE_METHOD( className, __VA_ARGS__ )
+#endif
+
+// "BDD-style" convenience wrappers
+#define CATCH_SCENARIO( ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_S_T_ ))
+#define CATCH_SCENARIO_METHOD( className, ... ) INTERNAL_CATCH_TESTCASE_METHOD_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_S_T_ ), className )
+#define CATCH_GIVEN( desc )
+#define CATCH_AND_GIVEN( desc )
+#define CATCH_WHEN( desc )
+#define CATCH_AND_WHEN( desc )
+#define CATCH_THEN( desc )
+#define CATCH_AND_THEN( desc )
+
+#define CATCH_STATIC_REQUIRE( ... ) (void)(0)
+#define CATCH_STATIC_REQUIRE_FALSE( ... ) (void)(0)
+
+// If CATCH_CONFIG_PREFIX_ALL is not defined then the CATCH_ prefix is not required
+#else
+
+#define REQUIRE( ... ) (void)(0)
+#define REQUIRE_FALSE( ... ) (void)(0)
+
+#define REQUIRE_THROWS( ... ) (void)(0)
+#define REQUIRE_THROWS_AS( expr, exceptionType ) (void)(0)
+#define REQUIRE_THROWS_WITH( expr, matcher ) (void)(0)
+#if !defined(CATCH_CONFIG_DISABLE_MATCHERS)
+#define REQUIRE_THROWS_MATCHES( expr, exceptionType, matcher ) (void)(0)
+#endif // CATCH_CONFIG_DISABLE_MATCHERS
+#define REQUIRE_NOTHROW( ... ) (void)(0)
+
+#define CHECK( ... ) (void)(0)
+#define CHECK_FALSE( ... ) (void)(0)
+#define CHECKED_IF( ... ) if (__VA_ARGS__)
+#define CHECKED_ELSE( ... ) if (!(__VA_ARGS__))
+#define CHECK_NOFAIL( ... ) (void)(0)
+
+#define CHECK_THROWS( ... ) (void)(0)
+#define CHECK_THROWS_AS( expr, exceptionType ) (void)(0)
+#define CHECK_THROWS_WITH( expr, matcher ) (void)(0)
+#if !defined(CATCH_CONFIG_DISABLE_MATCHERS)
+#define CHECK_THROWS_MATCHES( expr, exceptionType, matcher ) (void)(0)
+#endif // CATCH_CONFIG_DISABLE_MATCHERS
+#define CHECK_NOTHROW( ... ) (void)(0)
+
+#if !defined(CATCH_CONFIG_DISABLE_MATCHERS)
+#define CHECK_THAT( arg, matcher ) (void)(0)
+
+#define REQUIRE_THAT( arg, matcher ) (void)(0)
+#endif // CATCH_CONFIG_DISABLE_MATCHERS
+
+#define INFO( msg ) (void)(0)
+#define UNSCOPED_INFO( msg ) (void)(0)
+#define WARN( msg ) (void)(0)
+#define CAPTURE( ... ) (void)(0)
+
+#define TEST_CASE( ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_S_T_ ))
+#define TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_S_T_ ))
+#define METHOD_AS_TEST_CASE( method, ... )
+#define REGISTER_TEST_CASE( Function, ... ) (void)(0)
+#define SECTION( ... )
+#define DYNAMIC_SECTION( ... )
+#define FAIL( ... ) (void)(0)
+#define FAIL_CHECK( ... ) (void)(0)
+#define SUCCEED( ... ) (void)(0)
+#define ANON_TEST_CASE() INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_S_T_ ))
+
+#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
+#define TEMPLATE_TEST_CASE( ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION(__VA_ARGS__)
+#define TEMPLATE_TEST_CASE_SIG( ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE_SIG_NO_REGISTRATION(__VA_ARGS__)
+#define TEMPLATE_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION(className, __VA_ARGS__)
+#define TEMPLATE_TEST_CASE_METHOD_SIG( className, ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_SIG_NO_REGISTRATION(className, __VA_ARGS__ )
+#define TEMPLATE_PRODUCT_TEST_CASE( ... ) TEMPLATE_TEST_CASE( __VA_ARGS__ )
+#define TEMPLATE_PRODUCT_TEST_CASE_SIG( ... ) TEMPLATE_TEST_CASE( __VA_ARGS__ )
+#define TEMPLATE_PRODUCT_TEST_CASE_METHOD( className, ... ) TEMPLATE_TEST_CASE_METHOD( className, __VA_ARGS__ )
+#define TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG( className, ... ) TEMPLATE_TEST_CASE_METHOD( className, __VA_ARGS__ )
+#else
+#define TEMPLATE_TEST_CASE( ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION(__VA_ARGS__) )
+#define TEMPLATE_TEST_CASE_SIG( ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_SIG_NO_REGISTRATION(__VA_ARGS__) )
+#define TEMPLATE_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION(className, __VA_ARGS__ ) )
+#define TEMPLATE_TEST_CASE_METHOD_SIG( className, ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_SIG_NO_REGISTRATION(className, __VA_ARGS__ ) )
+#define TEMPLATE_PRODUCT_TEST_CASE( ... ) TEMPLATE_TEST_CASE( __VA_ARGS__ )
+#define TEMPLATE_PRODUCT_TEST_CASE_SIG( ... ) TEMPLATE_TEST_CASE( __VA_ARGS__ )
+#define TEMPLATE_PRODUCT_TEST_CASE_METHOD( className, ... ) TEMPLATE_TEST_CASE_METHOD( className, __VA_ARGS__ )
+#define TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG( className, ... ) TEMPLATE_TEST_CASE_METHOD( className, __VA_ARGS__ )
+#endif
+
+#define STATIC_REQUIRE( ... ) (void)(0)
+#define STATIC_REQUIRE_FALSE( ... ) (void)(0)
+
+#endif
+
+#define CATCH_TRANSLATE_EXCEPTION( signature ) INTERNAL_CATCH_TRANSLATE_EXCEPTION_NO_REG( INTERNAL_CATCH_UNIQUE_NAME( catch_internal_ExceptionTranslator ), signature )
+
+// "BDD-style" convenience wrappers
+#define SCENARIO( ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_S_T_ ) )
+#define SCENARIO_METHOD( className, ... ) INTERNAL_CATCH_TESTCASE_METHOD_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_S_T_ ), className )
+
+#define GIVEN( desc )
+#define AND_GIVEN( desc )
+#define WHEN( desc )
+#define AND_WHEN( desc )
+#define THEN( desc )
+#define AND_THEN( desc )
+
+using Catch::Detail::Approx;
+
+#endif
+
+#endif // ! CATCH_CONFIG_IMPL_ONLY
+
+// start catch_reenable_warnings.h
+
+
+#ifdef __clang__
+# ifdef __ICC // icpc defines the __clang__ macro
+# pragma warning(pop)
+# else
+# pragma clang diagnostic pop
+# endif
+#elif defined __GNUC__
+# pragma GCC diagnostic pop
+#endif
+
+// end catch_reenable_warnings.h
+// end catch.hpp
+#endif // TWOBLUECUBES_SINGLE_INCLUDE_CATCH_HPP_INCLUDED
+
diff --git a/src/qdoc/catch/qt_attribution.json b/src/qdoc/catch/qt_attribution.json
new file mode 100644
index 000000000..55b8a3ff6
--- /dev/null
+++ b/src/qdoc/catch/qt_attribution.json
@@ -0,0 +1,20 @@
+[
+ {
+ "Id": "catch2",
+ "Name": "Catch2",
+ "QDocModule": "qdoc",
+ "QtUsage": "Used for testing of QDoc",
+ "QtParts": [
+ "tests"
+ ],
+ "Files": "include/catch/catch.hpp",
+
+ "Description": "Catch2 is a multi-paradigm test framework for C++.",
+ "Homepage": "https://github.com/catchorg/Catch2",
+ "Version": "2.13.10",
+ "License": "Boost Software License 1.0",
+ "LicenseId": "BSL-1.0",
+ "LicenseFile": "CATCH_LICENSE.txt",
+ "Copyright": "Copyright (c) 2022 Two Blue Cubes Ltd. All rights reserved."
+ }
+]
diff --git a/src/qdoc/catch_conversions/CMakeLists.txt b/src/qdoc/catch_conversions/CMakeLists.txt
new file mode 100644
index 000000000..48ddcc26f
--- /dev/null
+++ b/src/qdoc/catch_conversions/CMakeLists.txt
@@ -0,0 +1,12 @@
+qt_internal_add_module(QDocCatchConversionsPrivate
+ INTERNAL_MODULE
+ HEADER_MODULE
+ EXTERNAL_HEADERS_DIR src
+ NO_GENERATE_CPP_EXPORTS
+)
+
+qt_internal_extend_target(QDocCatchConversionsPrivate
+ PUBLIC_INCLUDE_DIRECTORIES
+ $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/src>
+ $<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}/QtQDocCatchConversions>
+)
diff --git a/src/qdoc/catch_conversions/src/catch_conversions/qdoc_catch_conversions.h b/src/qdoc/catch_conversions/src/catch_conversions/qdoc_catch_conversions.h
new file mode 100644
index 000000000..d1079561b
--- /dev/null
+++ b/src/qdoc/catch_conversions/src/catch_conversions/qdoc_catch_conversions.h
@@ -0,0 +1,24 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#pragma once
+
+#include "qt_catch_conversions.h"
+
+#include <qdoc/boundaries/filesystem/directorypath.h>
+#include <qdoc/boundaries/filesystem/filepath.h>
+#include <qdoc/boundaries/filesystem/resolvedfile.h>
+
+#include <ostream>
+
+inline std::ostream& operator<<(std::ostream& os, const DirectoryPath& dirpath) {
+ return os << dirpath.value().toStdString();
+}
+
+inline std::ostream& operator<<(std::ostream& os, const FilePath& filepath) {
+ return os << filepath.value().toStdString();
+}
+
+inline std::ostream& operator<<(std::ostream& os, const ResolvedFile& resolved_file) {
+ return os << "ResolvedFile{ query: " << resolved_file.get_query().toStdString() << ", " << "filepath: " << resolved_file.get_path() << " }";
+}
diff --git a/src/qdoc/catch_conversions/src/catch_conversions/qt_catch_conversions.h b/src/qdoc/catch_conversions/src/catch_conversions/qt_catch_conversions.h
new file mode 100644
index 000000000..68abf5fb0
--- /dev/null
+++ b/src/qdoc/catch_conversions/src/catch_conversions/qt_catch_conversions.h
@@ -0,0 +1,19 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#pragma once
+
+#include "std_catch_conversions.h"
+
+#include <ostream>
+
+#include <QChar>
+#include <QString>
+
+inline std::ostream& operator<<(std::ostream& os, const QChar& character) {
+ return os << QString{character}.toStdString();
+}
+
+inline std::ostream& operator<<(std::ostream& os, const QString& string) {
+ return os << string.toStdString();
+}
diff --git a/src/qdoc/catch_conversions/src/catch_conversions/std_catch_conversions.h b/src/qdoc/catch_conversions/src/catch_conversions/std_catch_conversions.h
new file mode 100644
index 000000000..be8fa65d7
--- /dev/null
+++ b/src/qdoc/catch_conversions/src/catch_conversions/std_catch_conversions.h
@@ -0,0 +1,16 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#pragma once
+
+#include <ostream>
+#include <optional>
+
+template<typename T>
+inline std::ostream& operator<<(std::ostream& os, const std::optional<T>& optional) {
+ os << "std::optional{\n\t";
+ if (optional) os << optional.value();
+ else os <<"nullopt";
+
+ return os << "\n};";
+}
diff --git a/src/qdoc/catch_generators/CMakeLists.txt b/src/qdoc/catch_generators/CMakeLists.txt
new file mode 100644
index 000000000..18f2e6928
--- /dev/null
+++ b/src/qdoc/catch_generators/CMakeLists.txt
@@ -0,0 +1,16 @@
+qt_internal_add_module(QDocCatchGeneratorsPrivate
+ INTERNAL_MODULE
+ HEADER_MODULE
+ EXTERNAL_HEADERS_DIR src
+ NO_GENERATE_CPP_EXPORTS
+)
+
+qt_internal_extend_target(QDocCatchGeneratorsPrivate
+ PUBLIC_INCLUDE_DIRECTORIES
+ $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/src>
+ $<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}/QtQDocCatchGenerators>
+)
+
+if(QT_BUILD_TESTS)
+ add_subdirectory(tests)
+endif()
diff --git a/src/qdoc/catch_generators/src/catch_generators/generators/combinators/cycle_generator.h b/src/qdoc/catch_generators/src/catch_generators/generators/combinators/cycle_generator.h
new file mode 100644
index 000000000..b60600747
--- /dev/null
+++ b/src/qdoc/catch_generators/src/catch_generators/generators/combinators/cycle_generator.h
@@ -0,0 +1,80 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#pragma once
+
+#include "../../namespaces.h"
+#include "../../utilities/semantics/generator_handler.h"
+
+#include <catch/catch.hpp>
+
+#include <vector>
+
+namespace QDOC_CATCH_GENERATORS_ROOT_NAMESPACE {
+ namespace QDOC_CATCH_GENERATORS_PRIVATE_NAMESPACE {
+
+ template<typename T>
+ class CycleGenerator : public Catch::Generators::IGenerator<T> {
+ public:
+ CycleGenerator(Catch::Generators::GeneratorWrapper<T>&& generator)
+ : generator{std::move(generator)},
+ cache{},
+ cache_index{0}
+ {
+ // REMARK: We generally handle extracting the first
+ // value by using an handler, to avoid code
+ // duplication and the possibility of an error.
+ // In this specific case, we turn to a more "manual"
+ // approach as it better models the cache-based
+ // implementation, removing the need to not increment
+ // cache_index the first time that next is called.
+ cache.emplace_back(this->generator.get());
+ }
+
+ T const& get() const override { return cache[cache_index]; }
+
+ bool next() override {
+ if (generator.next()) {
+ cache.emplace_back(generator.get());
+ ++cache_index;
+ } else {
+ cache_index = (cache_index + 1) % cache.size();
+ }
+
+ return true;
+ }
+
+ private:
+ Catch::Generators::GeneratorWrapper<T> generator;
+
+ std::vector<T> cache;
+ std::size_t cache_index;
+ };
+
+ } // end QDOC_CATCH_GENERATORS_PRIVATE_NAMESPACE
+
+ /*!
+ * Returns a generator that behaves like \a generator until \a
+ * generator is exhausted, repeating the same generation that \a
+ * generator produced, infinitely, afterwards.
+ *
+ * This is generally intended to produce infinite generators from
+ * finite ones.
+ *
+ * For example, consider a generator that produces values based on
+ * another generator that it owns.
+ * If the owning generator needs to produce more values that the
+ * owned generator can support, it might fail at some point.
+ * By cycling over the owned generator, we can extend the sequence
+ * of produced values so that enough are generated, in a controlled
+ * way.
+ *
+ * The type T should generally be copyable for this generator to
+ * work.
+ */
+ template<typename T>
+ inline Catch::Generators::GeneratorWrapper<T> cycle(Catch::Generators::GeneratorWrapper<T>&& generator) {
+ return Catch::Generators::GeneratorWrapper<T>(std::unique_ptr<Catch::Generators::IGenerator<T>>(new QDOC_CATCH_GENERATORS_PRIVATE_NAMESPACE::CycleGenerator(std::move(generator))));
+ }
+
+} // end QDOC_CATCH_GENERATORS_ROOT_NAMESPACE
diff --git a/src/qdoc/catch_generators/src/catch_generators/generators/combinators/oneof_generator.h b/src/qdoc/catch_generators/src/catch_generators/generators/combinators/oneof_generator.h
new file mode 100644
index 000000000..5de9dcb6c
--- /dev/null
+++ b/src/qdoc/catch_generators/src/catch_generators/generators/combinators/oneof_generator.h
@@ -0,0 +1,185 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#pragma once
+
+#include "../../namespaces.h"
+#include "../../utilities/statistics/percentages.h"
+#include "../../utilities/semantics/generator_handler.h"
+
+#include <catch/catch.hpp>
+
+#include <vector>
+#include <random>
+#include <algorithm>
+#include <numeric>
+
+namespace QDOC_CATCH_GENERATORS_ROOT_NAMESPACE {
+ namespace QDOC_CATCH_GENERATORS_PRIVATE_NAMESPACE {
+
+ template<typename T>
+ class OneOfGenerator : public Catch::Generators::IGenerator<T> {
+ public:
+ OneOfGenerator(
+ std::vector<Catch::Generators::GeneratorWrapper<T>>&& generators,
+ const std::vector<double>& weights
+ ) : generators{std::move(generators)},
+ random_engine{std::random_device{}()},
+ choice_distribution{weights.cbegin(), weights.cend()}
+ {
+ assert(weights.size() == this->generators.size());
+ assert(std::reduce(weights.cbegin(), weights.cend()) == Approx(100.0));
+
+ std::transform(
+ this->generators.begin(), this->generators.end(), this->generators.begin(),
+ [](auto& generator){ return QDOC_CATCH_GENERATORS_UTILITIES_ABSOLUTE_NAMESPACE::handler(std::move(generator)); }
+ );
+
+ static_cast<void>(next());
+ }
+
+ T const& get() const override { return current_value; }
+
+ bool next() override {
+ std::size_t generator_index{choice_distribution(random_engine)};
+
+ if (!generators[generator_index].next()) return false;
+ current_value = generators[generator_index].get();
+
+ return true;
+ }
+
+ private:
+ std::vector<Catch::Generators::GeneratorWrapper<T>> generators;
+
+ std::mt19937 random_engine;
+ std::discrete_distribution<std::size_t> choice_distribution;
+
+ T current_value;
+ };
+
+ } // end QDOC_CATCH_GENERATORS_PRIVATE_NAMESPACE
+
+ /*!
+ * Returns a generator whose set of elements is the union of the
+ * set of elements of the generators in \a generators.
+ *
+ * Each time the generator produces a value, a generator from \a
+ * generators is randomly chosen to produce the value.
+ *
+ * The distribution for the choice is given by \a weights.
+ * The \e {ith} element in \a weights represent the percentage
+ * probability of the \e {ith} element of \a generators to be
+ * chosen.
+ *
+ * It follows that the size of \a weights must be the same as the
+ * size of \a generators.
+ *
+ * Furthermore, the sum of elements in \a weights should be a
+ * hundred.
+ *
+ * The generator produces values until a generator that is chosen
+ * to produce a value is unable to do so.
+ * The first such generator to do so will stop the generation
+ * independently of the availability of the other generators.
+ *
+ * Similarly, values will be produced as long as the chosen
+ * generator can produce a value, independently of the other
+ * generators being exhausted already.
+ */
+ template<typename T>
+ inline Catch::Generators::GeneratorWrapper<T> oneof(
+ std::vector<Catch::Generators::GeneratorWrapper<T>>&& generators,
+ const std::vector<double>& weights
+ ) {
+ return Catch::Generators::GeneratorWrapper<T>(std::unique_ptr<Catch::Generators::IGenerator<T>>(new QDOC_CATCH_GENERATORS_PRIVATE_NAMESPACE::OneOfGenerator(std::move(generators), weights)));
+ }
+
+
+ /*!
+ * Returns a generator whose set of elements is the union of the
+ * set of elements of the generators in \a generators and in which
+ * the distribution of the generated elements is uniform over \a
+ * generators.
+ *
+ * Each time the generator produces a value, a generator from \a
+ * generators is randomly chosen to produce the value.
+ *
+ * Each generator from \a generators has the same chance of being
+ * chosen.
+ *
+ * Do note that the distribution over the set of values is not
+ * necessarily uniform.
+ *
+ * The generator produces values until a generator that is chosen
+ * to produce a value is unable to do so.
+ * The first such generator to do so will stop the generation
+ * independently of the availability of the other generators.
+ *
+ * Similarly, values will be produced as long as the chosen
+ * generator can produce a value, independently of the other
+ * generators being exhausted already.
+ */
+ template<typename T>
+ inline Catch::Generators::GeneratorWrapper<T> uniform_oneof(
+ std::vector<Catch::Generators::GeneratorWrapper<T>>&& generators
+ ) {
+ std::vector<double> weights(
+ generators.size(),
+ QDOC_CATCH_GENERATORS_UTILITIES_ABSOLUTE_NAMESPACE::uniform_probability(generators.size())
+ );
+ return oneof(std::move(generators), std::move(weights));
+ }
+
+ /*!
+ * Returns a generator whose set of elements is the union of the
+ * set of elements of the generators in \a generators and in which
+ * the distribution of the generated elements is uniform over the
+ * elements of \a generators.
+ *
+ * The generators in \a generator should have a uniform
+ * distribution and be finite.
+ * If the set of elements that the generators in \a generator is
+ * not disjoint, the distribution will be skewed towards repeated
+ * elements.
+ *
+ * Each time the generator produces a value, a generator from \a
+ * generators is randomly chosen to produce the value.
+ *
+ * Each generator from \a generators has a probability of being
+ * chosen based on the proportion of the cardinality of the subset
+ * it produces.
+ *
+ * The \e {ith} element of \a amounts should contain the
+ * cardinality of the set produced by the \e {ith} generator in \a
+ * generators.
+ *
+ * The generator produces values until a generator that is chosen
+ * to produce a value is unable to do so.
+ * The first such generator to do so will stop the generation
+ * independently of the availability of the other generators.
+ *
+ * Similarly, values will be produced as long as the chosen
+ * generator can produce a value, independently of the other
+ * generators being exhausted already.
+ */
+ template<typename T>
+ inline Catch::Generators::GeneratorWrapper<T> uniformly_valued_oneof(
+ std::vector<Catch::Generators::GeneratorWrapper<T>>&& generators,
+ const std::vector<std::size_t>& amounts
+ ) {
+ std::size_t total_amount{std::accumulate(amounts.cbegin(), amounts.cend(), std::size_t{0})};
+
+ std::vector<double> weights;
+ weights.reserve(amounts.size());
+
+ std::transform(
+ amounts.cbegin(), amounts.cend(),
+ std::back_inserter(weights),
+ [total_amount](auto element){ return QDOC_CATCH_GENERATORS_UTILITIES_ABSOLUTE_NAMESPACE::percent_of(static_cast<double>(element), static_cast<double>(total_amount)); }
+ );
+
+ return oneof(std::move(generators), std::move(weights));
+ }
+
+} // end QDOC_CATCH_GENERATORS_ROOT_NAMESPACE
diff --git a/src/qdoc/catch_generators/src/catch_generators/generators/k_partition_of_r_generator.h b/src/qdoc/catch_generators/src/catch_generators/generators/k_partition_of_r_generator.h
new file mode 100644
index 000000000..832ee2838
--- /dev/null
+++ b/src/qdoc/catch_generators/src/catch_generators/generators/k_partition_of_r_generator.h
@@ -0,0 +1,113 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#pragma once
+
+#include "../namespaces.h"
+
+#include <catch/catch.hpp>
+
+#include <random>
+#include <numeric>
+#include <algorithm>
+
+namespace QDOC_CATCH_GENERATORS_ROOT_NAMESPACE {
+ namespace QDOC_CATCH_GENERATORS_PRIVATE_NAMESPACE {
+
+ class KPartitionOfRGenerator : public Catch::Generators::IGenerator<std::vector<double>> {
+ public:
+ KPartitionOfRGenerator(double r, std::size_t k)
+ : random_engine{std::random_device{}()},
+ interval_distribution{0.0, r},
+ k{k},
+ r{r},
+ current_partition(k)
+ {
+ assert(r >= 0.0);
+ assert(k >= 1);
+
+ static_cast<void>(next());
+ }
+
+ std::vector<double> const& get() const override { return current_partition; }
+
+ bool next() override {
+ if (k == 1) current_partition[0] = r;
+ else {
+ // REMARK: The following wasn't formally proved
+ // but is based on intuition.
+ // It is probably erroneous but is expected to be
+ // good enough for our case.
+
+ // REMARK: We aim to provide a non skewed
+ // distribution for the elements of the partition.
+ //
+ // The reasoning for this is to ensure that our
+ // testing surface has a good chance of hitting
+ // many of the available elements between the many
+ // runs.
+ //
+ // To approximate this, a specific algorithm was chosen.
+ // The following code can be intuitively seen as doing the following:
+ //
+ // Consider an interval [0.0, r] on the real line, where r > 0.0.
+ //
+ // k - 1 > 0 elements of the interval are chosen,
+ // partitioning the interval into disjoint
+ // sub-intervals.
+ //
+ // ---------------------------------------------------------------------------------------------------------------------
+ // | | | | |
+ // 0 k_1 k_2 k_3 r
+ // | | | | |
+ // _______--------------------_______________________________________________________-----------------------------------
+ // k_1 - 0 k_2 - k_1 k_3 - k_2 r - k_3
+ // p1 p2 p3 p4
+ //
+ // The length of each sub interval is chosen as one of the elements of the partition.
+ //
+ // Trivially, the sum of the chosen elements is r.
+ //
+ // Furthermore, as long as the distribution used
+ // to choose the elements of the original interval
+ // is uniform, the probability of each partition
+ // being produced should tend to being uniform
+ // itself.
+ std::generate(current_partition.begin(), current_partition.end() - 1, [this](){ return interval_distribution(random_engine); });
+
+ current_partition.back() = r;
+
+ std::sort(current_partition.begin(), current_partition.end());
+ std::adjacent_difference(current_partition.begin(), current_partition.end(), current_partition.begin());
+ }
+
+ return true;
+ }
+
+ private:
+ std::mt19937 random_engine;
+ std::uniform_real_distribution<double> interval_distribution;
+
+ std::size_t k;
+ double r;
+
+ std::vector<double> current_partition;
+ };
+
+ } // end QDOC_CATCH_GENERATORS_PRIVATE_NAMESPACE
+
+ /*!
+ * Returns a generator that generates collections of \a k elements
+ * whose sum is \a r.
+ *
+ * \a r must be a real number greater or euqal to zero and \a k
+ * must be a natural number greater than zero.
+ *
+ * The generated partitions tends to be uniformely distributed
+ * over the set of partitions of r.
+ */
+ inline Catch::Generators::GeneratorWrapper<std::vector<double>> k_partition_of_r(double r, std::size_t k) {
+ return Catch::Generators::GeneratorWrapper<std::vector<double>>(std::unique_ptr<Catch::Generators::IGenerator<std::vector<double>>>(new QDOC_CATCH_GENERATORS_PRIVATE_NAMESPACE::KPartitionOfRGenerator(r, k)));
+ }
+
+} // end QDOC_CATCH_GENERATORS_ROOT_NAMESPACE
diff --git a/src/qdoc/catch_generators/src/catch_generators/generators/path_generator.h b/src/qdoc/catch_generators/src/catch_generators/generators/path_generator.h
new file mode 100644
index 000000000..875502e49
--- /dev/null
+++ b/src/qdoc/catch_generators/src/catch_generators/generators/path_generator.h
@@ -0,0 +1,853 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+// TODO: Change the include paths to implicitly consider
+// `catch_generators` a root directory and change the CMakeLists.txt
+// file to make this possible.
+
+#include "../namespaces.h"
+#include "qchar_generator.h"
+#include "qstring_generator.h"
+#include "../utilities/semantics/move_into_vector.h"
+#include "../utilities/semantics/generator_handler.h"
+
+#if defined(Q_OS_WINDOWS)
+
+ #include "combinators/cycle_generator.h"
+
+#endif
+
+#include <catch/catch.hpp>
+
+#include <random>
+
+#include <QChar>
+#include <QString>
+#include <QStringList>
+#include <QRegularExpression>
+
+#if defined(Q_OS_WINDOWS)
+
+ #include <QStorageInfo>
+
+#endif
+
+namespace QDOC_CATCH_GENERATORS_ROOT_NAMESPACE {
+
+
+ struct PathGeneratorConfiguration {
+ double multi_device_path_probability{0.5};
+ double absolute_path_probability{0.5};
+ double directory_path_probability{0.5};
+ double has_trailing_separator_probability{0.5};
+ std::size_t minimum_components_amount{1};
+ std::size_t maximum_components_amount{10};
+
+ PathGeneratorConfiguration& set_multi_device_path_probability(double amount) {
+ multi_device_path_probability = amount;
+ return *this;
+ }
+
+ PathGeneratorConfiguration& set_absolute_path_probability(double amount) {
+ absolute_path_probability = amount;
+ return *this;
+ }
+
+ PathGeneratorConfiguration& set_directory_path_probability(double amount) {
+ directory_path_probability = amount;
+ return *this;
+ }
+
+ PathGeneratorConfiguration& set_has_trailing_separator_probability(double amount) {
+ has_trailing_separator_probability = amount;
+ return *this;
+ }
+
+ PathGeneratorConfiguration& set_minimum_components_amount(std::size_t amount) {
+ minimum_components_amount = amount;
+ return *this;
+ }
+
+ PathGeneratorConfiguration& set_maximum_components_amount(std::size_t amount) {
+ maximum_components_amount = amount;
+ return *this;
+ }
+ };
+
+ /*!
+ * \class PathGeneratorConfiguration
+ * \brief Defines some parameters to customize the generation of
+ * paths by a PathGenerator.
+ */
+
+ /*!
+ * \variable PathGeneratorConfiguration::multi_device_path_probability
+ *
+ * Every path produced by a PathGenerator configured with a
+ * mutli_device_path_probability of n has a probability of n to be
+ * \e {Multi-Device} and a probability of 1.0 - n to not be \a
+ * {Multi-Device}.
+ *
+ * multi_device_path_probability should be a value in the range [0.0,
+ * 1.0].
+ */
+
+ /*!
+ * \variable PathGeneratorConfiguration::absolute_path_probability
+ *
+ * Every path produced by a PathGenerator configured with an
+ * absolute_path_probability of n has a probability of n to be \e
+ * {Absolute} and a probability of 1.0 - n to be \e {Relative}.
+ *
+ * absolute_path_probability should be a value in the range [0.0,
+ * 1.0].
+ */
+
+ /*!
+ * \variable PathGeneratorConfiguration::directory_path_probability
+ *
+ * Every path produced by a PathGenerator configured with a
+ * directory_path_probability of n has a probability of n to be \e
+ * {To a Directory} and a probability of 1.0 - n to be \e {To a
+ * File}.
+ *
+ * directory_path_probability should be a value in the range [0.0,
+ * 1.0].
+ */
+
+ /*!
+ * \variable PathGeneratorConfiguration::has_trailing_separator_probability
+ *
+ * Every path produced by a PathGenerator configured with an
+ * has_trailing_separator_probability of n has a probability of n
+ * to \e {Have a Trailing Separator} and a probability of 1.0 - n
+ * to not \e {Have a Trailing Separator}, when this is applicable.
+ *
+ * has_trailing_separator_probability should be a value in the
+ * range [0.0, 1.0].
+ */
+
+ /*!
+ * \variable PathGeneratorConfiguration::minimum_components_amount
+ *
+ * Every path produced by a PathGenerator configured with a
+ * minimum_components_amount of n will be the concatenation of at
+ * least n non \e {device}, non \e {root}, non \e {separator}
+ * components.
+ *
+ * minimum_components_amount should be greater than zero and less
+ * than maximum_components_amount.
+ */
+
+ /*!
+ * \variable PathGeneratorConfiguration::maximum_components_amount
+ *
+ * Every path produced by a PathGenerator configured with a
+ * maximum_components_amount of n will be the concatenation of at
+ * most n non \e {device}, non \e {root}, non \e {separator} components.
+ *
+ * maximum_components_amount should be greater than or equal to
+ * minimum_components_amount.
+ */
+
+
+ namespace QDOC_CATCH_GENERATORS_PRIVATE_NAMESPACE {
+
+ class PathGenerator : public Catch::Generators::IGenerator<QString> {
+ public:
+ PathGenerator(
+ Catch::Generators::GeneratorWrapper<QString>&& device_component_generator,
+ Catch::Generators::GeneratorWrapper<QString>&& root_component_generator,
+ Catch::Generators::GeneratorWrapper<QString>&& directory_component_generator,
+ Catch::Generators::GeneratorWrapper<QString>&& filename_component_generator,
+ Catch::Generators::GeneratorWrapper<QString>&& separator_component_generator,
+ PathGeneratorConfiguration configuration = PathGeneratorConfiguration{}
+ ) : device_component_generator{QDOC_CATCH_GENERATORS_UTILITIES_ABSOLUTE_NAMESPACE::handler(std::move(device_component_generator))},
+ root_component_generator{QDOC_CATCH_GENERATORS_UTILITIES_ABSOLUTE_NAMESPACE::handler(std::move(root_component_generator))},
+ directory_component_generator{QDOC_CATCH_GENERATORS_UTILITIES_ABSOLUTE_NAMESPACE::handler(std::move(directory_component_generator))},
+ filename_component_generator{QDOC_CATCH_GENERATORS_UTILITIES_ABSOLUTE_NAMESPACE::handler(std::move(filename_component_generator))},
+ separator_component_generator{QDOC_CATCH_GENERATORS_UTILITIES_ABSOLUTE_NAMESPACE::handler(std::move(separator_component_generator))},
+ random_engine{std::random_device{}()},
+ components_amount_distribution{configuration.minimum_components_amount, configuration.maximum_components_amount},
+ is_multi_device_distribution{configuration.multi_device_path_probability},
+ is_absolute_path_distribution{configuration.absolute_path_probability},
+ is_directory_path_distribution{configuration.directory_path_probability},
+ has_trailing_separator{configuration.has_trailing_separator_probability},
+ current_path{}
+ {
+ assert(configuration.minimum_components_amount > 0);
+ assert(configuration.minimum_components_amount <= configuration.maximum_components_amount);
+
+ if (!next())
+ Catch::throw_exception("Not enough values to initialize the first string");
+ }
+
+ QString const& get() const override { return current_path; }
+
+ bool next() override {
+ std::size_t components_amount{components_amount_distribution(random_engine)};
+
+ current_path = "";
+
+ // REMARK: As per our specification of a path, we
+ // do not count device components, and separators,
+ // when considering the amount of components in a
+ // path.
+ // This is a tradeoff that is not necessarily
+ // precise.
+ // Counting those kinds of components, on one
+ // hand, would allow a device component to stands
+ // on its own as a path, for example "C:", which
+ // might actually be correct in some path format.
+ // On the other hand, counting those kinds of
+ // components makes the construction of paths for
+ // our model much more complex with regards, for
+ // example, to the amount of component.
+ //
+ // Counting device components, since they can
+ // appear both in relative and absolute paths,
+ // makes the minimum amount of components
+ // different for different kinds of paths.
+ //
+ // Since absolute paths always require a root
+ // component, the minimum amount of components for
+ // a multi-device absolute path is 2.
+ //
+ // But an absolute path that is not multi-device
+ // would only require one minimum component.
+ //
+ // Similarly, problems arise with the existence of
+ // Windows' relative multi-device path, which
+ // require a leading separator component after a
+ // device component.
+ //
+ // This problem mostly comes from our model
+ // simplifying the definition of paths quite a bit
+ // into binary-forms.
+ // This simplifies the code and its structure,
+ // sacrificing some precision.
+ // The lost precision is almost none for POSIX
+ // based paths, but is graver for DOS paths, since
+ // they have a more complex specification.
+ //
+ // Currently, we expect that the paths that QDoc
+ // will encounter will mostly be in POSIX-like
+ // forms, even on Windows, and aim to support
+ // that, such that the simplification of code is
+ // considered a better tradeoff compared to the
+ // loss of precision.
+ //
+ // If this changes, the model should be changed to
+ // pursue a Windows-first modeling, moving the
+ // categorization of paths from the current binary
+ // model to the absolute, drive-relative and
+ // relative triptych that Windows uses.
+ // This more complex model should be able to
+ // completely describe posix paths too, making it
+ // a superior choice as long as the complexity is
+ // warranted.
+ //
+ // Do note that the model similarly can become
+ // inconsistent when used to generate format of
+ // paths such as the one used in some resource
+ // systems.
+ // Those are considered out-of-scope for our needs
+ // and were not taken into account when developing
+ // this generator.
+ if (is_multi_device_distribution(random_engine)) {
+ if (!device_component_generator.next()) return false;
+ current_path += device_component_generator.get();
+ }
+
+ // REMARK: Similarly to not counting other form of
+ // components, we do not count root components
+ // towards the amounts of components that the path
+ // has to simplify the code.
+ // To support the "special" root path on, for
+ // example, posix systems, we require a more
+ // complex branching logic that changes based on
+ // the path being absolute or not.
+ //
+ // We don't expect root to be a particularly
+ // useful path for QDoc purposes and expect to not
+ // have to consider it for our tests.
+ // If consideration for it become required, it is
+ // possible to test it directly in the affected
+ // systemss as a special case.
+ //
+ // If most systems are affected by the handling of
+ // a root path, then the model should be slightly
+ // changed to accommodate its generation.
+ if (is_absolute_path_distribution(random_engine)) {
+ if (!root_component_generator.next()) return false;
+
+ current_path += root_component_generator.get();
+ }
+
+ std::size_t prefix_components_amount{std::max(std::size_t{1}, components_amount) - 1};
+ while (prefix_components_amount > 0) {
+ if (!directory_component_generator.next()) return false;
+ if (!separator_component_generator.next()) return false;
+
+ current_path += directory_component_generator.get() + separator_component_generator.get();
+ --prefix_components_amount;
+ }
+
+ if (is_directory_path_distribution(random_engine)) {
+ if (!directory_component_generator.next()) return false;
+ current_path += directory_component_generator.get();
+
+ if (has_trailing_separator(random_engine)) {
+ if (!separator_component_generator.next()) return false;
+ current_path += separator_component_generator.get();
+ }
+ } else {
+ if (!filename_component_generator.next()) return false;
+ current_path += filename_component_generator.get();
+ }
+
+ return true;
+ }
+
+ private:
+ Catch::Generators::GeneratorWrapper<QString> device_component_generator;
+ Catch::Generators::GeneratorWrapper<QString> root_component_generator;
+ Catch::Generators::GeneratorWrapper<QString> directory_component_generator;
+ Catch::Generators::GeneratorWrapper<QString> filename_component_generator;
+ Catch::Generators::GeneratorWrapper<QString> separator_component_generator;
+
+ std::mt19937 random_engine;
+ std::uniform_int_distribution<std::size_t> components_amount_distribution;
+ std::bernoulli_distribution is_multi_device_distribution;
+ std::bernoulli_distribution is_absolute_path_distribution;
+ std::bernoulli_distribution is_directory_path_distribution;
+ std::bernoulli_distribution has_trailing_separator;
+
+ QString current_path;
+ };
+
+ } // end QDOC_CATCH_GENERATORS_PRIVATE_NAMESPACE
+
+/*!
+ * Returns a generator that produces QStrings that represent a
+ * path in a filesystem.
+ *
+ * A path is formed by the following components, loosely based
+ * on the abstraction that is used by std::filesystem::path:
+ *
+ * \list
+ * \li \b {device}:
+ * Represents the device on the filesystem that
+ * the path should be considered in terms of.
+ * This is an optional components that is sometimes
+ * present on multi-device systems, such as Windows, to
+ * distinguish which device the path refers to.
+ * When present, it always appears before any other
+ * component.
+ * \li \b {root}:
+ * A special sequence that marks the path as absolute.
+ * This is an optional component that is present, always,
+ * in absolute paths.
+ * \li \b {directory}:
+ * A component that represents a directory on the
+ * filesystem that the path "passes-trough".
+ * Zero or more of this components can be present in the
+ * path.
+ * A path pointing to a directory on the filesystem that
+ * is not \e {root} always ends with a component of this
+ * type.
+ * \li \b {filename}:
+ * A component that represents a file on the
+ * filesystem.
+ * When this component is present, it is present only once
+ * and always as the last component of the path.
+ * A path that has such a component is a path that points
+ * to a file on the filesystem.
+ * For some path formats, there is no difference in the
+ * format of a \e {filename} and a \e {directory}.
+ * \li \b {separator}:
+ * A component that is interleaved between other types of
+ * components to separate them so that they are
+ * recognizable.
+ * A path that points to a directory on the filesystem may
+ * sometimes have a \e {separator} at the end, after the
+ * ending \e {directory} component.
+ * \endlist
+ *
+ * Each component is representable as a string and a path is a
+ * concatenation of the string representation of some
+ * components, with the following rules:
+ *
+ * \list
+ * \li There is at most one \e {device} component.
+ * \li If a \e {device} component is present it always
+ * precedes all other components.
+ * \li There is at most one \e {root} component.
+ * \li If a \e {root} component is present it:
+ * \list
+ * \li Succeeds the \e {device} component if it is present.
+ * \li Precedes every other components if the \e {device}
+ * component is not present.
+ * \endlist
+ * \li There are zero or more \e {directory} component.
+ * \li There is at most one \e {filename} component.
+ * \li If a \e {filename} component is present it always
+ * succeeds all other components.
+ * \li Between any two successive \e {directory} components
+ * there is a \e {separator} component.
+ * \li Between each successive \e {directory} and \e
+ * {filename} component there is a \e {separator} component.
+ * \li If the last component is a \e {directory} component it
+ * can be optionally followed by a \e {separator} component.
+ * \li At least one component that is not a \e {device}, a \e
+ * {root} or \e {separator} component is present.
+ * \endlist
+ *
+ * For example, if "C:" is a \e {device} component, "\\" is a
+ * \e {root} component, "\\" is a \e {separator} component,
+ * "directory" is a \e {directory} component and "filename" is
+ * a \e {filename} component, the following are all paths:
+ *
+ * "C:\\directory", "C:\\directory\\directory", "C:filename",
+ * "directory\\directory\\", "\\directory\\filename", "filename".
+ *
+ * While the following aren't:
+ *
+ * "C:", "C:\\", "directory\\C:", "foo", "C:filename\\",
+ * "filename\\directory\\filename", "filename\\filename",
+ * "directorydirectory"."
+ *
+ * The format of different components type can be the same.
+ * For example, the \e {root} and \e {separator} component in
+ * the above example.
+ * For the purpose of generation, we do not care about the
+ * format itself and consider a component of a certain type
+ * depending only on how it is generated/where it is generated
+ * from.
+ *
+ * For example, if every component is formatted as the string
+ * "a", the string "aaa" could be a generated path.
+ * By the string alone, it is not possible to simply discern
+ * which components form it, but it would be possible to
+ * generate it if the first "a" is a \a {device} component,
+ * the second "a" is a \e {root} component and the third "a"
+ * is a \e {directory} or \e {filename} component.
+ *
+ * A path, is further said to have some properties, pairs of
+ * which are exclusive to each other.
+ *
+ * A path is said to be:
+ *
+ * \list
+ * \li \b {Multi-Device}:
+ * When it contains a \e {device} component.
+ * \li \b {Absolute}:
+ * When it contains a \e {root} component.
+ * If the path is \e {Absolute} it is not \e {Relative}.
+ * \li \b {Relative}:
+ * When it does not contain a \e {root} component.
+ * If the path is \e {Relative} it is not \e {Absolute}.
+ * \li \b {To a Directory}:
+ * When its last component is a \e {directory} component
+ * or a \e {directory} component followed by a \e
+ * {separator} component.
+ * If the path is \e {To a Directory} it is not \e {To a
+ * File}.
+ * \li \b {To a File}:
+ * When its last component is a \e {filename}.
+ * If the path is \e {To a File} it is not \e {To a
+ * Directory}.
+ * \endlist
+ *
+ * All path are \e {Relative/Absolute}, \e {To a
+ * Directory/To a File} and \e {Multi-Device} or not.
+ *
+ * Furthermore, a path that is \e {To a Directory} and whose
+ * last component is a \e {separator} component is said to \e
+ * {Have a Trailing Separator}.
+ */
+ inline Catch::Generators::GeneratorWrapper<QString> path(
+ Catch::Generators::GeneratorWrapper<QString>&& device_generator,
+ Catch::Generators::GeneratorWrapper<QString>&& root_component_generator,
+ Catch::Generators::GeneratorWrapper<QString>&& directory_generator,
+ Catch::Generators::GeneratorWrapper<QString>&& filename_generator,
+ Catch::Generators::GeneratorWrapper<QString>&& separator_generator,
+ PathGeneratorConfiguration configuration = PathGeneratorConfiguration{}
+ ) {
+ return Catch::Generators::GeneratorWrapper<QString>(
+ std::unique_ptr<Catch::Generators::IGenerator<QString>>(
+ new QDOC_CATCH_GENERATORS_PRIVATE_NAMESPACE::PathGenerator(std::move(device_generator), std::move(root_component_generator), std::move(directory_generator), std::move(filename_generator), std::move(separator_generator), configuration)
+ )
+ );
+ }
+
+ namespace QDOC_CATCH_GENERATORS_PRIVATE_NAMESPACE {
+
+ // REMARK: We need a bounded length for the generation of path
+ // components as strings.
+ // We trivially do not want components to be the empty string,
+ // such that we have a minimum length of 1, but the maximum
+ // length is more malleable.
+ // We don't want components that are too long to avoid
+ // incurring in a big performance overhead, as we may generate
+ // many of them.
+ // At the same time, we want some freedom in having diffent
+ // length components.
+ // The value that was chosen is based on the general value for
+ // POSIX's NAME_MAX, which seems to tend to be 14 on many systems.
+ // We see this value as a small enough but not too much value
+ // that further brings with itself a relation to paths,
+ // increasing our portability even if it is out of scope, as
+ // almost no modern respects NAME_MAX.
+ // We don't use POSIX's NAME_MAX directly as it may not be available
+ // on all systems.
+ inline static constexpr std::size_t minimum_component_length{1};
+ inline static constexpr std::size_t maximum_component_length{14};
+
+ /*!
+ * Returns a generator that generates strings that are
+ * suitable to be used as a root component in POSIX paths.
+ *
+ * As per
+ * \l {https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap03.html#tag_03_02},
+ * this is any sequence of slash characters that is not of
+ * length 2.
+ */
+ inline Catch::Generators::GeneratorWrapper<QString> posix_root() {
+ return uniformly_valued_oneof(
+ QDOC_CATCH_GENERATORS_UTILITIES_ABSOLUTE_NAMESPACE::move_into_vector(
+ string(character('/', '/'), 1, 1),
+ string(character('/', '/'), 3, maximum_component_length)
+ ),
+ std::vector{1, maximum_component_length - 3}
+ );
+ }
+
+ /*!
+ * Returns a generator that generates strings that are
+ * suitable to be used as directory components in POSIX paths
+ * and that use an alphabet that should generally be supported
+ * by other systems.
+ *
+ * Components of this kind use the \l
+ * {https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap03.html#tag_03_282}{Portable Filename Character Set}.
+ */
+ inline Catch::Generators::GeneratorWrapper<QString> portable_posix_directory_name() {
+ return string(
+ QDOC_CATCH_GENERATORS_QCHAR_ALPHABETS_NAMESPACE::portable_posix_filename(),
+ minimum_component_length, maximum_component_length
+ );
+ }
+
+ /*!
+ * Returns a generator that generates strings that are
+ * suitable to be used as filenames in POSIX paths and that
+ * use an alphabet that should generally be supported by
+ * other systems.
+ *
+ * Filenames of this kind use the \l
+ * {https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap03.html#tag_03_282}{Portable Filename Character Set}.
+ */
+ inline Catch::Generators::GeneratorWrapper<QString> portable_posix_filename() {
+ // REMARK: "." and ".." always represent directories so we
+ // avoid generating them. Other than this, there is no
+ // difference between a file name and a directory name.
+ return filter([](auto& filename) { return filename != "." && filename != ".."; }, portable_posix_directory_name());
+ }
+
+ /*!
+ * Returns a generator that generates strings that can be used
+ * as POSIX compliant separators.
+ *
+ * As per \l
+ * {https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap03.html#tag_03_271},
+ * a separator is a sequence of one or more slashes.
+ */
+ inline Catch::Generators::GeneratorWrapper<QString> posix_separator() {
+ return string(character('/', '/'), minimum_component_length, maximum_component_length);
+ }
+
+ /*!
+ * Returns a generator that generates strings that can be
+ * suitably used as logical drive names in Windows' paths.
+ *
+ * As per \l
+ * {https://docs.microsoft.com/en-us/dotnet/standard/io/file-path-formats#traditional-dos-paths}
+ * and \l
+ * {https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-getlogicaldrives},
+ * they are composed of a single letter.
+ * Each generated string always follows the lettet with a
+ * colon, as it is specifically intended for path usages,
+ * where this is required.
+ *
+ * We use only uppercase letters for the drives names albeit,
+ * depending on case sensitivity, lowercase letter could be
+ * used.
+ */
+ inline Catch::Generators::GeneratorWrapper<QString> windows_logical_drives() {
+ // REMARK: If a Windows path is generated on Windows
+ // itself, we expect that it may be used to interact with
+ // the filesystem, similar to how we expect a POSIX path
+ // to be used on Linux.
+ // For this reason, we only generate a specific drive, the one
+ // that contains the current working directory, so that we
+ // know it is an actually available drive and to contain the
+ // possible modifications to the filesystem to an easily
+ // foundable place.
+
+#if defined(Q_OS_WINDOWS)
+
+ auto root_device{QStorageInfo{QDir()}.rootPath().first(1) + ":"};
+
+ return cycle(Catch::Generators::value(std::move(root_device)));
+
+#else
+
+ return Catch::Generators::map(
+ [](QString letter){ return letter + ':';},
+ string(QDOC_CATCH_GENERATORS_QCHAR_ALPHABETS_NAMESPACE::ascii_uppercase(), 1, 1)
+ );
+
+#endif
+ }
+
+ /*!
+ * Returns a generator that generate strings that can be used
+ * as separators in Windows based paths.
+ *
+ * As per \l
+ * {https://docs.microsoft.com/en-us/dotnet/api/system.io.path.directoryseparatorchar?view=net-6.0}
+ * and \l
+ * {https://docs.microsoft.com/en-us/dotnet/standard/io/file-path-formats#canonicalize-separators},
+ * this is a sequence of one or more backward or forward slashes.
+ */
+ inline Catch::Generators::GeneratorWrapper<QString> windows_separator() {
+ return uniform_oneof(
+ QDOC_CATCH_GENERATORS_UTILITIES_ABSOLUTE_NAMESPACE::move_into_vector(
+ string(character('\\', '\\'), minimum_component_length, maximum_component_length),
+ string(character('/', '/'), minimum_component_length, maximum_component_length)
+ )
+ );
+ }
+
+ } // end QDOC_CATCH_GENERATORS_PRIVATE_NAMESPACE
+
+ /*!
+ * Returns a generator that generates strings representing
+ * POSIX compatible paths.
+ *
+ * The generated paths follows the format specified in \l
+ * {https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap03.html#tag_03_271}.
+ *
+ * The optional length-requirements, such as PATH_MAX and
+ * NAME_MAX, are relaxed away as they are generally not
+ * respected by modern systems.
+ *
+ * It is possible to set the probability of obtaining a
+ * relative or absolute path through \a
+ * absolute_path_probability and the one of obtaining a path
+ * potentially pointing ot a directory or on a file through \a
+ * directory_path_probability.
+ */
+ inline Catch::Generators::GeneratorWrapper<QString> relaxed_portable_posix_path(double absolute_path_probability = 0.5, double directory_path_probability = 0.5) {
+ return path(
+ // POSIX path are never multi-device, so that we have
+ // provide an empty device component generator and set
+ // the probability for Multi-Device paths to zero.
+ string(character(), 0, 0),
+ QDOC_CATCH_GENERATORS_PRIVATE_NAMESPACE::posix_root(),
+ QDOC_CATCH_GENERATORS_PRIVATE_NAMESPACE::portable_posix_directory_name(),
+ QDOC_CATCH_GENERATORS_PRIVATE_NAMESPACE::portable_posix_filename(),
+ QDOC_CATCH_GENERATORS_PRIVATE_NAMESPACE::posix_separator(),
+ PathGeneratorConfiguration{}
+ .set_multi_device_path_probability(0.0)
+ .set_absolute_path_probability(absolute_path_probability)
+ .set_directory_path_probability(directory_path_probability)
+ );
+ }
+
+ /*!
+ * Returns a generator that produces strings that represents
+ * traditional DOS paths as defined in \l
+ * {https://docs.microsoft.com/en-us/dotnet/standard/io/file-path-formats#traditional-dos-paths}.
+ *
+ * The directory and filename components of a path generated
+ * in this way are, currently, restricted to use a portable
+ * character set as defined by POSIX.
+ *
+ * Do note that most paths themselves, will not be portable, on
+ * the whole, albeit they may be valid paths on other systems, as
+ * Windows uses a path system that is generally incompatible with
+ * other systems.
+ *
+ * Some possibly valid special path, such as a "C:" or "\"
+ * will never be generated.
+ */
+ inline Catch::Generators::GeneratorWrapper<QString> traditional_dos_path(
+ double absolute_path_probability = 0.5,
+ double directory_path_probability = 0.5,
+ double multi_device_path_probability = 0.5
+ ) {
+ return path(
+ QDOC_CATCH_GENERATORS_PRIVATE_NAMESPACE::windows_logical_drives(),
+ QDOC_CATCH_GENERATORS_PRIVATE_NAMESPACE::windows_separator(),
+ // REMAKR: Windows treats trailing dots as if they were a
+ // component of their own, that is, as the special
+ // relative paths.
+ // This seems to not be correctly handled by Qt's
+ // filesystem methods, resulting in inconsistencies when
+ // one such path is encountered.
+ // To avoid the issue, considering that an equivalent path
+ // can be formed by actually having the dots on their own
+ // as a component, we filter out all those paths that have
+ // trailing dots but are not only composed of dots.
+ Catch::Generators::filter(
+ [](auto& path){ return !(path.endsWith(".") && path.contains(QRegularExpression("[^.]"))) ; },
+ QDOC_CATCH_GENERATORS_PRIVATE_NAMESPACE::portable_posix_directory_name()
+ ),
+ QDOC_CATCH_GENERATORS_PRIVATE_NAMESPACE::portable_posix_filename(),
+ QDOC_CATCH_GENERATORS_PRIVATE_NAMESPACE::windows_separator(),
+ PathGeneratorConfiguration{}
+ .set_multi_device_path_probability(multi_device_path_probability)
+ .set_absolute_path_probability(absolute_path_probability)
+ .set_directory_path_probability(directory_path_probability)
+ );
+ }
+
+ // TODO: Find a good way to test the following functions.
+ // native_path can probably be tied to the tests for the
+ // OS-specific functions, with TEMPLATE_TEST_CASE.
+ // The other ones may follow a similar pattern but require a bit
+ // more work so that they tie to a specific case instead of the
+ // general one.
+ // Nonetheless, this approach is both error prone and difficult to
+ // parse, because of the required if preprocessor directives,
+ // and should be avoided if possible.
+
+ /*!
+ * Returns a generator that generates QStrings that represents
+ * paths native to the underlying OS.
+ *
+ * On Windows, paths that refer to a drive always refer to the
+ * root drive.
+ *
+ * native* functions should always be chosen when using paths for
+ * testing interfacing with the filesystem itself.
+ *
+ * System outside Linux, macOS or Windows are not supported.
+ */
+ inline Catch::Generators::GeneratorWrapper<QString> native_path(double absolute_path_probability = 0.5, double directory_path_probability = 0.5) {
+#if defined(Q_OS_LINUX) || defined(Q_OS_MACOS)
+
+ return relaxed_portable_posix_path(absolute_path_probability, directory_path_probability);
+
+#elif defined(Q_OS_WINDOWS)
+
+ // REMARK: When generating native paths for testing we
+ // generally want to avoid relative paths that are
+ // drive-specific, as we want them to be tied to a specific
+ // working directory that may not be the current directory on
+ // the drive.
+ // Hence, we avoid generating paths that may have a drive component.
+ // For tests where those kind of paths are interesting, a
+ // specific Windows-only test should be made, using
+ // traditional_dos_path to generate drive-relative paths only.
+ return traditional_dos_path(absolute_path_probability, directory_path_probability, 0.0);
+
+#endif
+ }
+
+ /*!
+ * Returns a generator that generates QStrings that represents
+ * paths native to the underlying OS and that are always \e
+ * {Relative}.
+ *
+ * Avoids generating paths that refer to a directory that is not
+ * included in the path itself.
+ *
+ * System outside Linux, macOS or Windows are not supported.
+ */
+ inline Catch::Generators::GeneratorWrapper<QString> native_relative_path(double directory_path_probability = 0.5) {
+ // REMARK: When testing, we generally use some specific
+ // directory as a root for relative paths.
+ // We want the generated path to be relative to that
+ // directory because we need a clean state for the test to
+ // be reliable.
+ // When generating paths, it is possible, correctly, to
+ // have a path that refers to that directory or some
+ // parent of it, removing us from the clean state that we
+ // need.
+ // To avoid that, we filter out paths that end up referring to a directory that is not under our "root" directory.
+ //
+ // We can think of each generated component moving us
+ // further down or up, in case of "..", a directory
+ // hierarchy, or keeping us at the same place in case of
+ // ".".
+ // Any path that ends up under our original "root"
+ // directory will safely keep our clean state for testing.
+ //
+ // Each "." keeps us at the same level in the hierarchy.
+ // Each ".." moves us up one level in the hierarchy.
+ // Each component that is not "." or ".." moves us down
+ // one level into the hierarchy.
+ //
+ // Then, to avoid referring to the "root" directory or one
+ // of its parents, we need to balance out each "." and
+ // ".." with the components that precedes or follow their
+ // appearance.
+ //
+ // Since "." keeps us at the same level, it can appear how
+ // many times it wants as long as the path referes to the
+ // "root" directory or a directory or file under it and at
+ // least one other component referes to a directory or
+ // file that is under the "root" directory.
+ //
+ // Since ".." moves us one level up in the hierarchy, a
+ // sequence of n ".." components is safe when at least n +
+ // 1 non "." or ".." components appear before it.
+ //
+ // To avoid the above problem, we filter away paths that
+ // do not respect those rules.
+ return Catch::Generators::filter(
+ [](auto& path){
+ QStringList components{path.split(QRegularExpression{R"((\\|\/)+)"}, Qt::SkipEmptyParts)};
+ int depth{0};
+
+ for (auto& component : components) {
+ if (component == "..")
+ --depth;
+ else if (component != ".")
+ ++depth;
+
+ if (depth < 0) return false;
+ }
+
+ return (depth > 0);
+ },
+ native_path(0.0, directory_path_probability)
+ );
+ }
+
+ /*!
+ * Returns a generator that generates QStrings that represents
+ * paths native to the underlying OS and that are always \e
+ * {Relative} and \e {To a File}.
+ *
+ * System outside Linux, macOS or Windows are not supported.
+ */
+ inline Catch::Generators::GeneratorWrapper<QString> native_relative_file_path() {
+ return native_relative_path(0.0);
+ }
+
+ /*!
+ * Returns a generator that generates QStrings that represents
+ * paths native to the underlying OS and that are always \e
+ * {Relative} and \e {To a Directory}.
+ *
+ * System outside Linux, macOS or Windows are not supported.
+ */
+ inline Catch::Generators::GeneratorWrapper<QString> native_relative_directory_path() {
+ return native_relative_path(1.0);
+ }
+
+} // end QDOC_CATCH_GENERATORS_ROOT_NAMESPACE
diff --git a/src/qdoc/catch_generators/src/catch_generators/generators/qchar_generator.h b/src/qdoc/catch_generators/src/catch_generators/generators/qchar_generator.h
new file mode 100644
index 000000000..33efc5ea4
--- /dev/null
+++ b/src/qdoc/catch_generators/src/catch_generators/generators/qchar_generator.h
@@ -0,0 +1,110 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#pragma once
+
+#include "../namespaces.h"
+#include "../utilities/semantics/move_into_vector.h"
+#include "combinators/oneof_generator.h"
+
+#include <catch/catch.hpp>
+
+#include <random>
+
+#include <QChar>
+
+namespace QDOC_CATCH_GENERATORS_ROOT_NAMESPACE {
+ namespace QDOC_CATCH_GENERATORS_PRIVATE_NAMESPACE {
+
+ class QCharGenerator : public Catch::Generators::IGenerator<QChar> {
+ public:
+ QCharGenerator(
+ char16_t lower_bound = std::numeric_limits<char16_t>::min(),
+ char16_t upper_bound = std::numeric_limits<char16_t>::max()
+ ) : random_engine{std::random_device{}()},
+ distribution{static_cast<unsigned int>(lower_bound), static_cast<unsigned int>(upper_bound)}
+ {
+ assert(lower_bound <= upper_bound);
+ static_cast<void>(next());
+ }
+
+ QChar const& get() const override { return current_character; }
+
+ bool next() override {
+ current_character = QChar(static_cast<char16_t>(distribution(random_engine)));
+
+ return true;
+ }
+
+ private:
+ QChar current_character;
+
+ std::mt19937 random_engine;
+ std::uniform_int_distribution<unsigned int> distribution;
+ };
+
+ } // end QDOC_CATCH_GENERATORS_PRIVATE_NAMESPACE
+
+
+ /*!
+ * Returns a generator of that generates elements of QChar whose
+ * ucs value is in the range [\a lower_bound, \a upper_bound].
+ *
+ * When \a lower_bound = \a upper_bound, the generator infinitely
+ * generates the same character.
+ */
+ inline Catch::Generators::GeneratorWrapper<QChar> character(char16_t lower_bound = std::numeric_limits<char16_t>::min(), char16_t upper_bound = std::numeric_limits<char16_t>::max()) {
+ return Catch::Generators::GeneratorWrapper<QChar>(std::unique_ptr<Catch::Generators::IGenerator<QChar>>(new QDOC_CATCH_GENERATORS_PRIVATE_NAMESPACE::QCharGenerator(lower_bound, upper_bound)));
+ }
+
+
+ namespace QDOC_CATCH_GENERATORS_QCHAR_ALPHABETS_NAMESPACE {
+
+ namespace QDOC_CATCH_GENERATORS_TRAITS_NAMESPACE {
+
+ enum class Alphabets : std::size_t {digit, ascii_lowercase, ascii_uppercase, ascii_alpha, ascii_alphanumeric, portable_posix_filename};
+
+ template<Alphabets alphabet>
+ struct sizeof_alphabet;
+
+ template<Alphabets alphabet>
+ inline constexpr std::size_t sizeof_alphabet_v = sizeof_alphabet<alphabet>::value;
+
+ template <> struct sizeof_alphabet<Alphabets::digit> { static constexpr std::size_t value{'9' - '0'}; };
+ template <> struct sizeof_alphabet<Alphabets::ascii_lowercase> { static constexpr std::size_t value{'z' - 'a'}; };
+ template<> struct sizeof_alphabet<Alphabets::ascii_uppercase> { static constexpr std::size_t value{'Z' - 'A'}; };
+ template<> struct sizeof_alphabet<Alphabets::ascii_alpha> { static constexpr std::size_t value{sizeof_alphabet_v<Alphabets::ascii_lowercase> + sizeof_alphabet_v<Alphabets::ascii_uppercase>}; };
+ template<> struct sizeof_alphabet<Alphabets::ascii_alphanumeric>{ static constexpr std::size_t value{sizeof_alphabet_v<Alphabets::ascii_alpha> + sizeof_alphabet_v<Alphabets::digit>}; };
+
+ } // end QDOC_CATCH_GENERATORS_TRAITS_NAMESPACE
+
+
+ inline Catch::Generators::GeneratorWrapper<QChar> digit() {
+ return Catch::Generators::GeneratorWrapper<QChar>(std::unique_ptr<Catch::Generators::IGenerator<QChar>>(new QDOC_CATCH_GENERATORS_PRIVATE_NAMESPACE::QCharGenerator('0', '9')));
+ }
+
+ inline Catch::Generators::GeneratorWrapper<QChar> ascii_lowercase() {
+ return Catch::Generators::GeneratorWrapper<QChar>(std::unique_ptr<Catch::Generators::IGenerator<QChar>>(new QDOC_CATCH_GENERATORS_PRIVATE_NAMESPACE::QCharGenerator('a', 'z')));
+ }
+
+ inline Catch::Generators::GeneratorWrapper<QChar> ascii_uppercase() {
+ return Catch::Generators::GeneratorWrapper<QChar>(std::unique_ptr<Catch::Generators::IGenerator<QChar>>(new QDOC_CATCH_GENERATORS_PRIVATE_NAMESPACE::QCharGenerator('A', 'Z')));
+ }
+
+ inline Catch::Generators::GeneratorWrapper<QChar> ascii_alpha() {
+ return uniform_oneof(QDOC_CATCH_GENERATORS_UTILITIES_ABSOLUTE_NAMESPACE::move_into_vector(ascii_lowercase(), ascii_uppercase()));
+ }
+
+ inline Catch::Generators::GeneratorWrapper<QChar> ascii_alphanumeric() {
+ return uniformly_valued_oneof(QDOC_CATCH_GENERATORS_UTILITIES_ABSOLUTE_NAMESPACE::move_into_vector(ascii_alpha(), digit()), std::vector{traits::sizeof_alphabet_v<traits::Alphabets::ascii_alpha> , traits::sizeof_alphabet_v<traits::Alphabets::digit>});
+ }
+
+ inline Catch::Generators::GeneratorWrapper<QChar> portable_posix_filename() {
+ return uniformly_valued_oneof(QDOC_CATCH_GENERATORS_UTILITIES_ABSOLUTE_NAMESPACE::move_into_vector(ascii_alphanumeric(), character('.', '.'), character('-', '-'), character('_', '_')),
+ std::vector{traits::sizeof_alphabet_v<traits::Alphabets::ascii_alphanumeric>, std::size_t{1}, std::size_t{1}, std::size_t{1}});
+ }
+
+ } // end QDOC_CATCH_GENERATORS_QCHAR_ALPHABETS_NAMESPACE
+
+
+} // end QDOC_CATCH_GENERATORS_ROOT_NAMESPACE
diff --git a/src/qdoc/catch_generators/src/catch_generators/generators/qstring_generator.h b/src/qdoc/catch_generators/src/catch_generators/generators/qstring_generator.h
new file mode 100644
index 000000000..fe854d22f
--- /dev/null
+++ b/src/qdoc/catch_generators/src/catch_generators/generators/qstring_generator.h
@@ -0,0 +1,92 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#pragma once
+
+#include "../namespaces.h"
+#include "qchar_generator.h"
+#include "../utilities/semantics/generator_handler.h"
+
+#include <catch/catch.hpp>
+
+#include <random>
+
+#include <QString>
+
+namespace QDOC_CATCH_GENERATORS_ROOT_NAMESPACE {
+ namespace QDOC_CATCH_GENERATORS_PRIVATE_NAMESPACE {
+
+ class QStringGenerator : public Catch::Generators::IGenerator<QString> {
+ public:
+ QStringGenerator(Catch::Generators::GeneratorWrapper<QChar>&& character_generator, qsizetype minimum_length, qsizetype maximum_length)
+ : character_generator{QDOC_CATCH_GENERATORS_UTILITIES_ABSOLUTE_NAMESPACE::handler(std::move(character_generator))},
+ random_engine{std::random_device{}()},
+ length_distribution{minimum_length, maximum_length},
+ current_string{}
+ {
+ assert(minimum_length >= 0);
+ assert(maximum_length >= 0);
+ assert(minimum_length <= maximum_length);
+
+ if (!next())
+ Catch::throw_exception("Not enough values to initialize the first string");
+ }
+
+ QString const& get() const override { return current_string; }
+
+ bool next() override {
+ qsizetype length{length_distribution(random_engine)};
+
+ current_string = QString();
+ for (qsizetype length_index{0}; length_index < length; ++length_index) {
+ if (!character_generator.next()) return false;
+
+ current_string += character_generator.get();
+ }
+
+ return true;
+ }
+
+ private:
+ Catch::Generators::GeneratorWrapper<QChar> character_generator;
+
+ std::mt19937 random_engine;
+ std::uniform_int_distribution<qsizetype> length_distribution;
+
+ QString current_string;
+ };
+
+ } // end QDOC_CATCH_GENERATORS_PRIVATE_NAMESPACE
+
+ /*!
+ * Returns a generator that generates elements of QString from
+ * some amount of elements taken from \a character_generator.
+ *
+ * The generated strings will have a length in the range
+ * [\a minimum_length, \a maximum_length].
+ *
+ * For compatibility with the Qt API, it is possible to provide
+ * negative bounds for the length. This is, nonetheless,
+ * considered an error such that the bounds should always be
+ * greater or equal to zero.
+ *
+ * It is similarly considered an error to have minimum_length <=
+ * maximum_length.
+ *
+ * The provided generator will generate elements until \a
+ * character_generator is exhausted.
+ */
+ inline Catch::Generators::GeneratorWrapper<QString> string(Catch::Generators::GeneratorWrapper<QChar>&& character_generator, qsizetype minimum_length, qsizetype maximum_length) {
+ return Catch::Generators::GeneratorWrapper<QString>(std::unique_ptr<Catch::Generators::IGenerator<QString>>(new QDOC_CATCH_GENERATORS_PRIVATE_NAMESPACE::QStringGenerator(std::move(character_generator), minimum_length, maximum_length)));
+ }
+
+ /*!
+ * Returns an infinite generator whose elements are the empty
+ * QString.
+ */
+ inline Catch::Generators::GeneratorWrapper<QString> empty_string() {
+ return Catch::Generators::GeneratorWrapper<QString>(std::unique_ptr<Catch::Generators::IGenerator<QString>>(new QDOC_CATCH_GENERATORS_PRIVATE_NAMESPACE::QStringGenerator(character(), 0, 0)));
+ }
+
+
+} // end QDOC_CATCH_GENERATORS_ROOT_NAMESPACE
diff --git a/src/qdoc/catch_generators/src/catch_generators/namespaces.h b/src/qdoc/catch_generators/src/catch_generators/namespaces.h
new file mode 100644
index 000000000..3c956d44f
--- /dev/null
+++ b/src/qdoc/catch_generators/src/catch_generators/namespaces.h
@@ -0,0 +1,14 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#pragma once
+
+#define QDOC_CATCH_GENERATORS_ROOT_NAMESPACE qdoc::catch_generators
+
+#define QDOC_CATCH_GENERATORS_PRIVATE_NAMESPACE details
+
+#define QDOC_CATCH_GENERATORS_TRAITS_NAMESPACE traits
+
+#define QDOC_CATCH_GENERATORS_QCHAR_ALPHABETS_NAMESPACE alphabets
+
+#define QDOC_CATCH_GENERATORS_UTILITIES_ABSOLUTE_NAMESPACE QDOC_CATCH_GENERATORS_ROOT_NAMESPACE::QDOC_CATCH_GENERATORS_PRIVATE_NAMESPACE::utils
diff --git a/src/qdoc/catch_generators/src/catch_generators/utilities/semantics/copy_value.h b/src/qdoc/catch_generators/src/catch_generators/utilities/semantics/copy_value.h
new file mode 100644
index 000000000..57798be1a
--- /dev/null
+++ b/src/qdoc/catch_generators/src/catch_generators/utilities/semantics/copy_value.h
@@ -0,0 +1,26 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#pragma once
+
+#include "../../namespaces.h"
+
+#include <type_traits>
+
+namespace QDOC_CATCH_GENERATORS_UTILITIES_ABSOLUTE_NAMESPACE {
+
+ /*!
+ * Forces \value to be copied in an expression context.
+ *
+ * This is used in contexts where inferences of a type that
+ * requires generality might identify a reference when ownership
+ * is required.
+ *
+ * Note that the compiler might optmize the copy away. This is a
+ * non-issue as we are only interested in breaking lifetime
+ * dependencies.
+ */
+ template<typename T>
+ std::remove_reference_t<T> copy_value(T value) { return value; }
+
+} // end QDOC_CATCH_GENERATORS_UTILITIES_ABSOLUTE_NAMESPACE
diff --git a/src/qdoc/catch_generators/src/catch_generators/utilities/semantics/generator_handler.h b/src/qdoc/catch_generators/src/catch_generators/utilities/semantics/generator_handler.h
new file mode 100644
index 000000000..328627512
--- /dev/null
+++ b/src/qdoc/catch_generators/src/catch_generators/utilities/semantics/generator_handler.h
@@ -0,0 +1,97 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#pragma once
+
+#include "../../namespaces.h"
+
+#include <catch/catch.hpp>
+
+#include <optional>
+#include <cassert>
+
+namespace QDOC_CATCH_GENERATORS_UTILITIES_ABSOLUTE_NAMESPACE {
+
+ template<typename T>
+ class GeneratorHandler : public Catch::Generators::IGenerator<T> {
+ public:
+
+ GeneratorHandler(Catch::Generators::GeneratorWrapper<T>&& generator)
+ : generator{std::move(generator)},
+ first_call{true}
+ {}
+
+ T const& get() const override {
+ assert(!first_call);
+ return generator.get();
+ }
+
+ bool next() override {
+ if (first_call) {
+ first_call = false;
+ return true;
+ }
+
+ return generator.next();
+ }
+
+ private:
+ Catch::Generators::GeneratorWrapper<T> generator;
+ bool first_call;
+ };
+
+
+ /*!
+ * Returns a generator wrapping \a generator that ensures that
+ * changes its semantics so that the first call to get should be
+ * preceded by a call to next.
+ *
+ * Catch generators require that is valid to call get and obtain a
+ * valid value on a generator that was just created.
+ * That is, generators should be non-empty and their first value
+ * should be initialized on construction.
+ *
+ * Normally, this is not a problem, and the next implementation of
+ * the generator can be simply called in the constructor.
+ * But when a generator depends on other generators, doing so will
+ * generally skip the first value that the generator
+ * produces, as the wrapping generator will need to advance the
+ * underlying generator, losing the value in the process.
+ * This is in particular, a problem, on generators that are finite
+ * or infinite and ordered.
+ *
+ * To solve the issue, the original value can be saved before
+ * advancing the generator or some code can be duplicated or
+ * abstracted so that what a new element can be generated without
+ * advancing the underlying generator.
+ *
+ * While this is acceptable, it can be error prone on more complex
+ * generators, generators that randomly access a collection of
+ * generators and so on.
+ *
+ * To simplify this process, this generator changes the semantics
+ * of the wrapped generator such that the first value of the
+ * generator is produced after the first call to next and the
+ * generator is considered in an invalid state before the first
+ * advancement.
+ *
+ * In this way, by wrapping all generators that a generator
+ * depends on, the implementation required for the first value is
+ * the same as the one required for all following values, with
+ * regards to the sequencing of next and get operations,
+ * simplifying the implementation of dependent generators.
+ *
+ * Do note that, while the generator returned by this function
+ * implments the generator interface that Catch2 requires, it
+ * cannot be normally used as a generator as it fails to comply
+ * with the first value semantics that a generator requires.
+ * Indeed, it should only be used as an intermediate wrapper for
+ * the implementation of generators that depends on other
+ * generators.
+ */
+ template<typename T>
+ inline Catch::Generators::GeneratorWrapper<T> handler(Catch::Generators::GeneratorWrapper<T>&& generator) {
+ return Catch::Generators::GeneratorWrapper<T>(std::unique_ptr<Catch::Generators::IGenerator<T>>(new GeneratorHandler(std::move(generator))));
+ }
+
+} // end QDOC_CATCH_GENERATORS_UTILITIES_ABSOLUTE_NAMESPACE
diff --git a/src/qdoc/catch_generators/src/catch_generators/utilities/semantics/move_into_vector.h b/src/qdoc/catch_generators/src/catch_generators/utilities/semantics/move_into_vector.h
new file mode 100644
index 000000000..5e780085b
--- /dev/null
+++ b/src/qdoc/catch_generators/src/catch_generators/utilities/semantics/move_into_vector.h
@@ -0,0 +1,62 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#pragma once
+
+#include "../../namespaces.h"
+
+#include <vector>
+#include <tuple>
+
+namespace QDOC_CATCH_GENERATORS_UTILITIES_ABSOLUTE_NAMESPACE {
+
+ namespace QDOC_CATCH_GENERATORS_TRAITS_NAMESPACE {
+
+ /*!
+ * Returns the type of the first element of Args.
+ *
+ * Args is expected to have at least one
+ */
+ template<typename... Args>
+ using first_from_pack_t = std::tuple_element_t<0, std::tuple<Args...>>;
+
+ } // end QDOC_CATCH_GENERATORS_TRAITS_NAMESPACE
+
+
+ /*!
+ * Builds an std::vector by moving \a movables into it.
+ *
+ * \a movables must be made of homogenous types.
+ *
+ * This function is intended to allow the construction of an
+ * std::vector<T>, where T is a move only type, as an expression,
+ * to lighten the idiom.
+ *
+ * For example, Catch's GeneratorWrapper<T> adapts a
+ * std::unique_ptr, which is move only, making it impossible to
+ * build a std::vector from them in place.
+ *
+ * Then, everywhere this is needed, a more complex approach of
+ * generating the collection of objects, generating a vector of a
+ * suitable size and iterating the objects to move-emplace them in
+ * the vector is required.
+ *
+ * This not only complicates the code but is incompatible with a
+ * GENERATE expression, making it extremely hard, noisy and error
+ * prone to use them together.
+ *
+ * In those cases, then, a call to move_into_vector can be used as
+ * an expression to circumvent the problem.
+ */
+ template<typename... MoveOnlyTypes>
+ inline auto move_into_vector(MoveOnlyTypes... movables) {
+ std::vector<QDOC_CATCH_GENERATORS_TRAITS_NAMESPACE::first_from_pack_t<MoveOnlyTypes...>>
+ moved_into_vector;
+ moved_into_vector.reserve(sizeof...(movables));
+
+ (moved_into_vector.emplace_back(std::move(movables)), ...);
+
+ return moved_into_vector;
+ }
+
+} // end QDOC_CATCH_GENERATORS_UTILITIES_ABSOLUTE_NAMESPACE
diff --git a/src/qdoc/catch_generators/src/catch_generators/utilities/statistics/distribution.h b/src/qdoc/catch_generators/src/catch_generators/utilities/statistics/distribution.h
new file mode 100644
index 000000000..4374993bf
--- /dev/null
+++ b/src/qdoc/catch_generators/src/catch_generators/utilities/statistics/distribution.h
@@ -0,0 +1,158 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#pragma once
+
+#include "../../namespaces.h"
+
+#include <functional>
+#include <optional>
+#include <ostream>
+#include <unordered_map>
+
+namespace QDOC_CATCH_GENERATORS_UTILITIES_ABSOLUTE_NAMESPACE {
+
+ template<typename T>
+ using Histogram = std::unordered_map<T, std::size_t>;
+
+ template<typename InputIt, typename GroupBy>
+ auto make_histogram(InputIt begin, InputIt end, GroupBy&& group_by) {
+ Histogram<std::invoke_result_t<GroupBy, decltype(*begin)>> histogram{};
+
+ while (begin != end) {
+ auto key{std::invoke(std::forward<GroupBy>(group_by), *begin)};
+
+ histogram.try_emplace(key, 0);
+ histogram[key] += 1;
+ ++begin;
+ }
+
+ return histogram;
+ }
+
+ template<typename T>
+ struct DistributionError {
+ T value;
+ double probability;
+ double expected_probability;
+ };
+
+ template<typename T>
+ inline std::ostream& operator<<(std::ostream& os, const DistributionError<T>& error) {
+ return os << "DistributionError{" <<
+ "The value { " << error.value <<
+ " } appear with a probability of { " << error.probability <<
+ " } while a probability of { " << error.expected_probability << " } was expected." <<
+ "}";
+ }
+
+ // REMARK: The following should really return an Either of unit/error
+ // but std::variant in C++ is both extremely unusable and comes with a
+ // strong overhead unless certain conditions are met.
+ // For this reason, we keep to the less intutitive optional error.
+
+ /*!
+ * Returns true when the given \a sequence approximately respects a
+ * given distribution.
+ *
+ * The \a sequence respects a given distribution when the count of
+ * each collection of values is a percentage of the total values that
+ * is near the percentage probability described by distribution.
+ *
+ * The values in \a sequence are collected according to \a group_by.
+ * \a group_by, given an element of \a sequence, should return a value
+ * of some type that represent the category of the inspected value.
+ * Values that have the same category share their count.
+ *
+ * The distribution that should be respected is given by \a
+ * probability_of. \a probability_of is a function that takes a
+ * category that was produced from a call to \a group_by and returns
+ * the expect probability, in percentage, of apperance for that
+ * category.
+ *
+ * The given probability is then compared to the one found by counting
+ * the element of \a sequence under \a group_by, to ensure that it
+ * matches.
+ *
+ * The margin of error for the comparison is given, in percentage
+ * points, by \a margin.
+ * The approximation uses an absolute comparison and scales the
+ * margin inversely based on the size of \a sequence, to account for the
+ * precision of the data set itself.
+ *
+ * When the distribution is not respected, a DistributionError is
+ * returned enclosed in an optional value.
+ * The error allows reports which the first category for which the
+ * comparison failed, along with its expected probability and the one
+ * that was actually inferred from \a sequence.
+ */
+ template<typename T, typename GroupBy, typename ProbabilityOf>
+ std::optional<DistributionError<T>> respects_distribution(std::vector<T>&& sequence, GroupBy&& group_by, ProbabilityOf&& probability_of, double margin = 33) {
+ std::size_t data_point_amount{sequence.size()};
+
+ // REMARK: We scale the margin based on the data set to allow for
+ // an easier change in downstream tests.
+ // The precision required for the approximation will vary
+ // depending on how many values we generate.
+ // The amount of values we generate depends on how much time we
+ // want the tests to take.
+ // This amount may change in the future. For example, as code is
+ // added and tests are added, we might need some expensive
+ // computations here and there.
+ // Sometimes, this will increase the test suite runtime without an
+ // obvious way of improving the performance of the underlying code
+ // to reduce it.
+ // In those cases, the total run time can be decreased by running
+ // less generations for battle-tested tests.
+ // If some code has not been changed for a long time, it will have
+ // had thousands of generations by that point, giving us a good
+ // degree of certainty of it not being bugged (for whatever bugs
+ // the tests account for).
+ // Then, running a certain amount of generation is not required
+ // anymore such that some of them can be optimized out.
+ // For tests like the one using this function, where our ability
+ // to test is always dependent on the amount of generations,
+ // changing the generated amount will mean that we will need to
+ // change our conditions too, potentially changing the meaning of
+ // the test.
+ // To take this into account, we perform a scaling on the
+ // condition itself, so that if the amount of data points that are
+ // generated changes, we do not generally have to change anything
+ // in the condition.
+ //
+ // For this case, we scale logarithmically_10 for the simple
+ // reason that we tend to generate values in power of tens,
+ // starting with the 100 values default that Quickcheck used.
+ //
+ // The default value for the margin on which the scaling is based,
+ // was chosen heuristically.
+ // As we expect generation under 10^3 to be generally meaningless
+ // for this kind of testing, the value was chosen so that it would
+ // start to normalize around that amount.
+ // Deviation of about 5-10% were identified trough various
+ // generations for an amount of data points near 1000, while a
+ // deviation of about 1-3% was identified with about 10000 values.
+ // With the chosen default value, the scaling approaches those
+ // percentage points with some margin of error.
+ //
+ // We expect up to a 10%, or a bit more, deviation to be suitable
+ // for our purposes, as it would still allow for a varied
+ // distribution in downstream consumers.
+ double scaled_margin{margin * (1.0/std::log10(data_point_amount))};
+
+ auto histogram{make_histogram(sequence.begin(), sequence.end(), std::forward<GroupBy>(group_by))};
+
+ for (auto& bin : histogram) {
+ auto [key, count] = bin;
+
+ double actual_percentage{percent_of(static_cast<double>(count), static_cast<double>(data_point_amount))};
+ double expected_percentage{std::invoke(std::forward<ProbabilityOf>(probability_of), key)};
+
+ if (!(actual_percentage == Approx(expected_percentage).margin(scaled_margin)))
+ return std::make_optional(DistributionError<T>{key, actual_percentage, expected_percentage});
+ }
+
+ return std::nullopt;
+ }
+
+} // end QDOC_CATCH_GENERATORS_UTILITIES_ABSOLUTE_NAMESPACE
diff --git a/src/qdoc/catch_generators/src/catch_generators/utilities/statistics/percentages.h b/src/qdoc/catch_generators/src/catch_generators/utilities/statistics/percentages.h
new file mode 100644
index 000000000..2d80a459f
--- /dev/null
+++ b/src/qdoc/catch_generators/src/catch_generators/utilities/statistics/percentages.h
@@ -0,0 +1,49 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#pragma once
+
+#include "../../namespaces.h"
+
+#include <cassert>
+
+namespace QDOC_CATCH_GENERATORS_UTILITIES_ABSOLUTE_NAMESPACE {
+
+ /*!
+ * Returns the percentage of \amount over \a total.
+ *
+ * \a amount needs to be greater or equal to zero and \a total
+ * needs to be greater than zero.
+ */
+ inline double percent_of(double amount, double total) {
+ assert(amount >= 0.0);
+ assert(total > 0.0);
+
+ return (amount / total) * 100.0;
+ }
+
+ /*!
+ * Given the cardinality of a set, returns the percentage
+ * probability that applied to every element of the set generates
+ * a uniform distribution.
+ */
+ inline double uniform_probability(std::size_t cardinality) {
+ assert(cardinality > 0);
+
+ return (100.0 / static_cast<double>(cardinality));
+ }
+
+ /*!
+ * Returns a percentage probability that is equal to \a
+ * probability.
+ *
+ * \a probability must be in the range [0.0, 1.0]
+ */
+ inline double probability_to_percentage(double probability) {
+ assert(probability >= 0.0);
+ assert(probability <= 1.0);
+
+ return probability * 100.0;
+ }
+
+} // end QDOC_CATCH_GENERATORS_UTILITIES_ABSOLUTE_NAMESPACE
diff --git a/src/qdoc/catch_generators/tests/CMakeLists.txt b/src/qdoc/catch_generators/tests/CMakeLists.txt
new file mode 100644
index 000000000..5a4b8667d
--- /dev/null
+++ b/src/qdoc/catch_generators/tests/CMakeLists.txt
@@ -0,0 +1,20 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+qt_internal_add_test(tst_QDoc_Catch_Generators
+ SOURCES
+ ${CMAKE_CURRENT_LIST_DIR}/main.cpp
+ ${CMAKE_CURRENT_LIST_DIR}/generators/catch_qchar_generator.cpp
+ ${CMAKE_CURRENT_LIST_DIR}/generators/catch_qstring_generator.cpp
+ ${CMAKE_CURRENT_LIST_DIR}/generators/catch_k_partition_of_r_generator.cpp
+ ${CMAKE_CURRENT_LIST_DIR}/generators/catch_path_generator.cpp
+
+ ${CMAKE_CURRENT_LIST_DIR}/generators/combinators/catch_oneof_generator.cpp
+ ${CMAKE_CURRENT_LIST_DIR}/generators/combinators/catch_cycle_generator.cpp
+
+ ${CMAKE_CURRENT_LIST_DIR}/utilities/semantics/catch_generator_handler.cpp
+ LIBRARIES
+ Qt::QDocCatchPrivate
+ Qt::QDocCatchConversionsPrivate
+ Qt::QDocCatchGeneratorsPrivate
+)
diff --git a/src/qdoc/catch_generators/tests/generators/catch_k_partition_of_r_generator.cpp b/src/qdoc/catch_generators/tests/generators/catch_k_partition_of_r_generator.cpp
new file mode 100644
index 000000000..27b79c511
--- /dev/null
+++ b/src/qdoc/catch_generators/tests/generators/catch_k_partition_of_r_generator.cpp
@@ -0,0 +1,41 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include <catch_generators/namespaces.h>
+#include <catch_generators/generators/k_partition_of_r_generator.h>
+
+#include <catch/catch.hpp>
+
+#include <numeric>
+
+using namespace QDOC_CATCH_GENERATORS_ROOT_NAMESPACE;
+
+SCENARIO("Generating a k-partition of a real number", "[Partition][Reals]") {
+ GIVEN("A real number r greater or equal to zero") {
+ double r = GENERATE(take(10, random(0.0, 1000000.0)));
+
+ AND_GIVEN("An amount of desired elements k greater than zero") {
+ std::size_t k = GENERATE(take(10, random(1, 100)));
+
+ WHEN("A k-partition of r is generated") {
+ auto k_partition = GENERATE_COPY(take(10, k_partition_of_r(r, k)));
+
+ THEN("The partition contains k elements") {
+ REQUIRE(k_partition.size() == k);
+
+ AND_THEN("The sum of those elements is r") {
+ REQUIRE(std::accumulate(k_partition.begin(), k_partition.end(), 0.0) == Approx(r));
+ }
+ }
+ }
+ }
+ }
+}
+
+TEST_CASE("All 1-partition of r are singleton collection with r as their element", "[Partition][Reals][SpecialCase]") {
+ double r = GENERATE(take(10, random(0.0, 1000000.0)));
+ auto k_partition = GENERATE_COPY(take(10, k_partition_of_r(r, 1)));
+
+ REQUIRE(k_partition.size() == 1);
+ REQUIRE(k_partition.front() == r);
+}
diff --git a/src/qdoc/catch_generators/tests/generators/catch_path_generator.cpp b/src/qdoc/catch_generators/tests/generators/catch_path_generator.cpp
new file mode 100644
index 000000000..deb33421b
--- /dev/null
+++ b/src/qdoc/catch_generators/tests/generators/catch_path_generator.cpp
@@ -0,0 +1,755 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include <catch_generators/namespaces.h>
+#include <catch_generators/generators/qchar_generator.h>
+#include <catch_generators/generators/qstring_generator.h>
+#include <catch_generators/generators/path_generator.h>
+#include <catch_generators/generators/combinators/cycle_generator.h>
+#include <catch_generators/utilities/statistics/percentages.h>
+#include <catch_generators/utilities/statistics/distribution.h>
+#include <catch_generators/utilities/semantics/copy_value.h>
+
+#include <catch_conversions/qt_catch_conversions.h>
+
+#include <catch/catch.hpp>
+
+#include <QString>
+#include <QStringList>
+#include <QRegularExpression>
+
+using namespace QDOC_CATCH_GENERATORS_ROOT_NAMESPACE;
+using namespace QDOC_CATCH_GENERATORS_UTILITIES_ABSOLUTE_NAMESPACE;
+
+using namespace Qt::StringLiterals;
+
+TEST_CASE("A path generated with a multi_device_path_probability of 1.0 always contains a device component.", "[Path][Content][SpecialCase]") {
+ QString device_component_value{"C:"};
+ auto path_generator = path(
+ Catch::Generators::value(copy_value(device_component_value)),
+ empty_string(),
+ empty_string(),
+ empty_string(),
+ empty_string(),
+ PathGeneratorConfiguration{}.set_multi_device_path_probability(1.0)
+ );
+
+ auto generated_path = GENERATE_REF(take(100, std::move(path_generator)));
+
+ REQUIRE(generated_path.contains(device_component_value));
+}
+
+TEST_CASE("A path generated with a multi_device_path_probability of 0.0 never contains a device component.", "[Path][Content][SpecialCase]") {
+ QString device_component_value{"C:"};
+ auto path_generator = path(
+ Catch::Generators::value(copy_value(device_component_value)),
+ empty_string(),
+ empty_string(),
+ empty_string(),
+ empty_string(),
+ PathGeneratorConfiguration{}.set_multi_device_path_probability(0.0)
+ );
+
+ auto generated_path = GENERATE_REF(take(100, std::move(path_generator)));
+
+ REQUIRE(!generated_path.contains(device_component_value));
+}
+
+TEST_CASE("A path generated with an absolute_path_probability of 1.0 always contains a root component.", "[Path][Content][SpecialCase]") {
+ QString root_component_value{"\\"};
+ auto path_generator = path(
+ empty_string(),
+ Catch::Generators::value(copy_value(root_component_value)),
+ empty_string(),
+ empty_string(),
+ empty_string(),
+ PathGeneratorConfiguration{}.set_absolute_path_probability(1.0)
+ );
+
+ auto generated_path = GENERATE_REF(take(100, std::move(path_generator)));
+
+ REQUIRE(generated_path.contains(root_component_value));
+}
+
+TEST_CASE("A path generated with an absolute_path_probability of 0.0 never contains a root component.", "[Path][Content][SpecialCase]") {
+ QString root_component_value{"\\"};
+ auto path_generator = path(
+ empty_string(),
+ Catch::Generators::value(copy_value(root_component_value)),
+ empty_string(),
+ empty_string(),
+ empty_string(),
+ PathGeneratorConfiguration{}.set_absolute_path_probability(0.0)
+ );
+
+ auto generated_path = GENERATE_REF(take(100, std::move(path_generator)));
+
+ REQUIRE(!generated_path.contains(root_component_value));
+}
+
+TEST_CASE("A path generated with a directory_path_probability of 1.0 always ends with a root, directory or directory followed by separator component.", "[Path][Content][SpecialCase]") {
+ QString root_component_value{"root"};
+ QString directory_component_value{"dir"};
+ QString separator_component_value{"sep"};
+
+ auto path_generator = path(
+ cycle(Catch::Generators::value(QString("device"))),
+ cycle(Catch::Generators::value(copy_value(root_component_value))),
+ cycle(Catch::Generators::value(copy_value(directory_component_value))),
+ cycle(Catch::Generators::value(QString("filename"))),
+ cycle(Catch::Generators::value(copy_value(separator_component_value))),
+ PathGeneratorConfiguration{}.set_directory_path_probability(1.0)
+ );
+
+ auto generated_path = GENERATE_REF(take(100, std::move(path_generator)));
+
+ REQUIRE((
+ generated_path.endsWith(root_component_value) ||
+ generated_path.endsWith(directory_component_value) ||
+ generated_path.endsWith(directory_component_value + separator_component_value)
+ ));
+}
+
+TEST_CASE("A path generated with a directory_path_probability of 0.0 always ends with a filename component.", "[Path][Content][SpecialCase]") {
+ QString filename_component_value{"file"};
+
+ auto path_generator = path(
+ cycle(Catch::Generators::value(QString("device"))),
+ cycle(Catch::Generators::value(QString("root"))),
+ cycle(Catch::Generators::value(QString("dir"))),
+ cycle(Catch::Generators::value(copy_value(filename_component_value))),
+ cycle(Catch::Generators::value(QString("sep"))),
+ PathGeneratorConfiguration{}.set_directory_path_probability(0.0)
+ );
+
+ auto generated_path = GENERATE_REF(take(100, std::move(path_generator)));
+
+ REQUIRE(generated_path.endsWith(filename_component_value));
+}
+
+TEST_CASE("A directory path generated with a has_trailing_separator_probability of 1.0 always ends with a separator component.", "[Path][Content][SpecialCase]") {
+ QString separator_component_value{"sep"};
+
+ auto path_generator = path(
+ cycle(Catch::Generators::value(QString("device"))),
+ cycle(Catch::Generators::value(QString("root"))),
+ cycle(Catch::Generators::value(QString("directory"))),
+ cycle(Catch::Generators::value(QString("filename"))),
+ cycle(Catch::Generators::value(copy_value(separator_component_value))),
+ PathGeneratorConfiguration{}.set_directory_path_probability(1.0).set_has_trailing_separator_probability(1.0)
+ );
+
+ auto generated_path = GENERATE_REF(take(100, std::move(path_generator)));
+
+ REQUIRE(generated_path.endsWith(separator_component_value));
+}
+
+TEST_CASE("A directory path generated with a has_trailing_separator_probability of 0.0 never ends with a separator component.", "[Path][Content][SpecialCase]") {
+ QString separator_component_value{"sep"};
+
+ auto path_generator = path(
+ cycle(Catch::Generators::value(QString("device"))),
+ cycle(Catch::Generators::value(QString("root"))),
+ cycle(Catch::Generators::value(QString("directory"))),
+ cycle(Catch::Generators::value(QString("filename"))),
+ cycle(Catch::Generators::value(copy_value(separator_component_value))),
+ PathGeneratorConfiguration{}.set_directory_path_probability(1.0).set_has_trailing_separator_probability(0.0)
+ );
+
+ auto generated_path = GENERATE_REF(take(100, std::move(path_generator)));
+
+ REQUIRE(!generated_path.endsWith(separator_component_value));
+}
+
+SCENARIO("Binding a path to a component range", "[Path][Bounds]") {
+ GIVEN("A minimum amount of components") {
+ auto minimum_components_amount = GENERATE(take(100, random(std::size_t{1}, std::size_t{100})));
+
+ AND_GIVEN("A maximum amount of components that is greater or equal than the minimum amount of components") {
+ auto maximum_components_amount = GENERATE_COPY(take(100, random(minimum_components_amount, std::size_t{100})));
+
+ WHEN("A path is generated from those bounds") {
+ QString countable_component_value{"a"};
+
+ QString generated_path = GENERATE_COPY(
+ take(1,
+ path(
+ empty_string(),
+ empty_string(),
+ cycle(Catch::Generators::value(copy_value(countable_component_value))),
+ cycle(Catch::Generators::value(copy_value(countable_component_value))),
+ empty_string(),
+ PathGeneratorConfiguration{}.set_minimum_components_amount(minimum_components_amount).set_maximum_components_amount(maximum_components_amount)
+ )
+ )
+ );
+
+ THEN("The amount of non device, non root, non separator components in the generated path is in the range [minimum_components_amount, maximum_components_amount]") {
+ std::size_t components_amount{static_cast<std::size_t>(generated_path.count(countable_component_value))};
+
+ REQUIRE(components_amount >= minimum_components_amount);
+ REQUIRE(components_amount <= maximum_components_amount);
+ }
+ }
+ }
+ }
+}
+
+TEST_CASE(
+ "When the maximum amount of components and the minimum amount of components are equal, all generated paths have the same amount of non device, non root, non separator components",
+ "[Path][Bounds][SpecialCase]")
+{
+ auto components_amount = GENERATE(take(10, random(std::size_t{1}, std::size_t{100})));
+
+ QString countable_component_value{"a"};
+ QString generated_path = GENERATE_COPY(
+ take(10,
+ path(
+ empty_string(),
+ empty_string(),
+ cycle(Catch::Generators::value(copy_value(countable_component_value))),
+ cycle(Catch::Generators::value(copy_value(countable_component_value))),
+ empty_string(),
+ PathGeneratorConfiguration{}.set_minimum_components_amount(components_amount).set_maximum_components_amount(components_amount)
+ )
+ )
+ );
+
+ REQUIRE(static_cast<std::size_t>(generated_path.count(countable_component_value)) == components_amount);
+}
+
+SCENARIO("The format of a path", "[Path][Contents]") {
+ GIVEN("A series of components generators") {
+ // TODO: Could probably move this to the global scope to
+ // lighen the tests.
+ QString device_component_value{"device"};
+ QString root_component_value{"root"};
+ QString directory_component_value{"dir"};
+ QString filename_component_value{"file"};
+ QString separator_component_value{"sep"};
+
+ auto device_component_generator = cycle(Catch::Generators::value(copy_value(device_component_value)));
+ auto root_component_generator = cycle(Catch::Generators::value(copy_value(root_component_value)));
+ auto directory_component_generator = cycle(Catch::Generators::value(copy_value(directory_component_value)));
+ auto filename_component_generator = cycle(Catch::Generators::value(copy_value(filename_component_value)));
+ auto separator_component_generator = cycle(Catch::Generators::value(copy_value(separator_component_value)));
+
+ AND_GIVEN("A generator of paths using those components generator") {
+ // TODO: We should actually randomize the configuration by
+ // making a simple generator for it.
+ auto path_generator = path(
+ std::move(device_component_generator),
+ std::move(root_component_generator),
+ std::move(directory_component_generator),
+ std::move(filename_component_generator),
+ std::move(separator_component_generator)
+ );
+
+ WHEN("A path is generated from that generator") {
+ auto generated_path = GENERATE_REF(take(10, std::move(path_generator)));
+
+ THEN("At most one device component is in the generated path") {
+ REQUIRE(generated_path.count(device_component_value) <= 1);
+ }
+
+ THEN("At most one root component is in the generated path") {
+ REQUIRE(generated_path.count(root_component_value) <= 1);
+ }
+
+ THEN("At most one filename component is in the generated path") {
+ REQUIRE(generated_path.count(filename_component_value) <= 1);
+ }
+
+ THEN("At least one non device, non root, non separator component is in the generated path") {
+ REQUIRE((generated_path.contains(directory_component_value) || generated_path.contains(filename_component_value)));
+ }
+
+ THEN("There is a separator component between any two successive directory components") {
+ // REMARK: To test this condition, which is not
+ // easy to test directly, as, if the generator is
+ // working as it should, the concept of successive
+ // directories stops existing.
+ // To test it, then, we split the condition into
+ // two parts, that are easier to test, that
+ // achieve the same effect.
+ // First, if all directories have a separator
+ // component between them, it is impossible to
+ // have a directory component that is directly
+ // followed by another directory component.
+ // Second, when this holds, any two directory
+ // components must have one or more non-directory
+ // components between them.
+ // For those directories that have exactly one
+ // component between them, it must be a separator.
+ // This is equivalent to the original condition as
+ // long as it is not allowed for anything else to
+ // be between two directory components that have
+ // exactly one component between them.
+ // This is true at the time of writing of this
+ // test, such that this will work correctly, but
+ // if this changes the test is invalidated.
+ // If a test for the original condition is found
+ // that is not contrived (as it is possible to
+ // test the original condition but it is a bit
+ // more complex than we would like the test to
+ // be), it should replace this current
+ // implementation to improve the resiliency of the
+ // test.
+ REQUIRE_FALSE(generated_path.contains(directory_component_value + directory_component_value));
+
+ auto successive_directories_re{
+ QRegularExpression(u"%1(%2)%3"_s.arg(directory_component_value)
+ .arg(QStringList{device_component_value, root_component_value, filename_component_value, separator_component_value}.join("|"))
+ .arg(directory_component_value)
+ )};
+
+ auto successive_directories_match(successive_directories_re.match(generated_path));
+ while (successive_directories_match.hasMatch()) {
+ auto in_between_component{successive_directories_match.captured(1)};
+
+ // TODO: Having this in a loop makes it so
+ // the amount of assertions will vary slightly
+ // per-run.
+ // It would be better to avoid this, even if
+ // it should not really be a problem
+ // generally.
+ // Try to find a better way to express this
+ // condition that does not require a loop.
+ // This could be as easy as just collection
+ // the results and then using a std::all_of.
+ REQUIRE(in_between_component == separator_component_value);
+
+ successive_directories_match = successive_directories_re.match(generated_path, successive_directories_match.capturedEnd(1));
+ }
+ }
+
+
+ THEN("There is a separator component between each successive directory and filename components") {
+ REQUIRE_FALSE(generated_path.contains(directory_component_value + filename_component_value));
+
+ auto successive_directory_filename_re{
+ QRegularExpression(u"%1(%2)%3"_s.arg(directory_component_value)
+ .arg(QStringList{device_component_value, root_component_value, filename_component_value, separator_component_value}.join("|"))
+ .arg(filename_component_value)
+ )};
+
+ auto successive_directory_filename_match(successive_directory_filename_re.match(generated_path));
+ while (successive_directory_filename_match.hasMatch()) {
+ auto in_between_component{successive_directory_filename_match.captured(1)};
+
+ REQUIRE(in_between_component == separator_component_value);
+
+ successive_directory_filename_match = successive_directory_filename_re.match(generated_path, successive_directory_filename_match.capturedEnd(1));
+ }
+ }
+ }
+ }
+
+ AND_GIVEN("A generator of paths using those components generator that generates Multi-Device paths") {
+ auto path_generator = path(
+ std::move(device_component_generator),
+ std::move(root_component_generator),
+ std::move(directory_component_generator),
+ std::move(filename_component_generator),
+ std::move(separator_component_generator),
+ PathGeneratorConfiguration{}.set_multi_device_path_probability(1.0)
+ );
+
+ WHEN("A path is generated from that generator") {
+ auto generated_path = GENERATE_REF(take(10, std::move(path_generator)));
+
+ THEN("Exactly one device component is in the generated path") {
+ REQUIRE(generated_path.count(device_component_value) == 1);
+
+ AND_THEN("The device component is the first component in the generated path") {
+ REQUIRE(generated_path.startsWith(device_component_value));
+ }
+ }
+ }
+ }
+
+ AND_GIVEN("A generator of paths using those components generator that generates Absolute paths") {
+ auto path_generator = path(
+ std::move(device_component_generator),
+ std::move(root_component_generator),
+ std::move(directory_component_generator),
+ std::move(filename_component_generator),
+ std::move(separator_component_generator),
+ PathGeneratorConfiguration{}.set_absolute_path_probability(1.0)
+ );
+
+ WHEN("A path is generated from that generator") {
+ auto generated_path = GENERATE_REF(take(10, std::move(path_generator)));
+
+ THEN("Exactly one root component is in the generated path") {
+ REQUIRE(generated_path.count(root_component_value) == 1);
+ }
+ }
+ }
+
+ AND_GIVEN("A generator of paths using those components generator that generates Absolute paths that are not Multi-Device") {
+ auto path_generator = path(
+ std::move(device_component_generator),
+ std::move(root_component_generator),
+ std::move(directory_component_generator),
+ std::move(filename_component_generator),
+ std::move(separator_component_generator),
+ PathGeneratorConfiguration{}.set_multi_device_path_probability(0.0).set_absolute_path_probability(1.0)
+ );
+
+ WHEN("A path is generated from that generator") {
+ auto generated_path = GENERATE_REF(take(10, std::move(path_generator)));
+
+ THEN("The root component is the first component in the generated path") {
+ REQUIRE(generated_path.startsWith(root_component_value));
+ }
+ }
+ }
+
+ AND_GIVEN("A generator of paths using those components generator that generates Multi-Device, Absolute paths") {
+ auto path_generator = path(
+ std::move(device_component_generator),
+ std::move(root_component_generator),
+ std::move(directory_component_generator),
+ std::move(filename_component_generator),
+ std::move(separator_component_generator),
+ PathGeneratorConfiguration{}.set_multi_device_path_probability(1.0).set_absolute_path_probability(1.0)
+ );
+
+ WHEN("A path is generated from that generator") {
+ auto generated_path = GENERATE_REF(take(10, std::move(path_generator)));
+
+ THEN("The root component succeeds the device component in the generated path") {
+ REQUIRE(generated_path.contains(device_component_value + root_component_value));
+ }
+ }
+ }
+
+ AND_GIVEN("A generator of paths using those components generator that generates paths that are To a Directory and do not Have a Trailing Separator") {
+ auto path_generator = path(
+ std::move(device_component_generator),
+ std::move(root_component_generator),
+ std::move(directory_component_generator),
+ std::move(filename_component_generator),
+ std::move(separator_component_generator),
+ PathGeneratorConfiguration{}.set_directory_path_probability(1.0).set_has_trailing_separator_probability(0.0)
+ );
+
+ WHEN("A path is generated from that generator") {
+ auto generated_path = GENERATE_REF(take(10, std::move(path_generator)));
+
+ THEN("The last component of in the path is a directory component") {
+ REQUIRE(generated_path.endsWith(directory_component_value));
+ }
+ }
+ }
+
+ AND_GIVEN("A generator of paths using those components generator that generates paths that are To a Directory and Have a Trailing Separator") {
+ auto path_generator = path(
+ std::move(device_component_generator),
+ std::move(root_component_generator),
+ std::move(directory_component_generator),
+ std::move(filename_component_generator),
+ std::move(separator_component_generator),
+ PathGeneratorConfiguration{}.set_directory_path_probability(1.0).set_has_trailing_separator_probability(1.0)
+ );
+
+ WHEN("A path is generated from that generator") {
+ auto generated_path = GENERATE_REF(take(10, std::move(path_generator)));
+
+ THEN("The last component in the path is a separator component that is preceded by a directory component") {
+ REQUIRE(generated_path.endsWith(directory_component_value + separator_component_value));
+ }
+ }
+ }
+
+
+ AND_GIVEN("A generator of paths using those components generator that generates paths that are To a File") {
+ auto path_generator = path(
+ std::move(device_component_generator),
+ std::move(root_component_generator),
+ std::move(directory_component_generator),
+ std::move(filename_component_generator),
+ std::move(separator_component_generator),
+ PathGeneratorConfiguration{}.set_directory_path_probability(0.0)
+ );
+
+ WHEN("A path is generated from that generator") {
+ auto generated_path = GENERATE_REF(take(10, std::move(path_generator)));
+
+ THEN("Exactly one filename component is in the path") {
+ REQUIRE(generated_path.contains(filename_component_value));
+
+ AND_THEN("The filename component is the last component in the path") {
+ REQUIRE(generated_path.endsWith(filename_component_value));
+ }
+ }
+ }
+ }
+ }
+}
+
+// REMARK: [mayfail][distribution]
+SCENARIO("Observing the distribution of paths based on their configuration", "[Path][Statistics][!mayfail]") {
+ GIVEN("A series of components generators") {
+ QString device_component_value{"device"};
+ QString root_component_value{"root"};
+ QString directory_component_value{"dir"};
+ QString filename_component_value{"file"};
+ QString separator_component_value{"sep"};
+
+ auto device_component_generator = cycle(Catch::Generators::value(copy_value(device_component_value)));
+ auto root_component_generator = cycle(Catch::Generators::value(copy_value(root_component_value)));
+ auto directory_component_generator = cycle(Catch::Generators::value(copy_value(directory_component_value)));
+ auto filename_component_generator = cycle(Catch::Generators::value(copy_value(filename_component_value)));
+ auto separator_component_generator = cycle(Catch::Generators::value(copy_value(separator_component_value)));
+
+ AND_GIVEN("A generator of paths using those components generator that produces paths that are Multi-Device with a probability of n") {
+ double multi_device_path_probability = GENERATE(take(10, random(0.0, 1.0)));
+
+ auto path_generator = path(
+ std::move(device_component_generator),
+ std::move(root_component_generator),
+ std::move(directory_component_generator),
+ std::move(filename_component_generator),
+ std::move(separator_component_generator),
+ PathGeneratorConfiguration{}.set_multi_device_path_probability(multi_device_path_probability)
+ );
+
+ WHEN("A certain amount of paths are generated from that generator") {
+ auto paths = GENERATE_REF(take(1, chunk(10000, std::move(path_generator))));
+
+ THEN("The amount of paths that are Multi-Device approximately respects the given probability and the amount of paths that are not approximately respects a probability of 1 - n") {
+ auto maybe_distribution_error{respects_distribution(
+ std::move(paths),
+ [&device_component_value](const QString& path){ return (path.startsWith(device_component_value)) ? "Multi-Device" : "Non Multi-Device"; },
+ [multi_device_path_probability](const QString& key){ return probability_to_percentage((key == "Multi-Device") ? multi_device_path_probability : 1 - multi_device_path_probability); }
+ )};
+
+ REQUIRE_FALSE(maybe_distribution_error);
+ }
+ }
+ }
+
+ AND_GIVEN("A generator of paths using those components generator that produces paths that are Absolute with a probability of n") {
+ double absolute_path_probability = GENERATE(take(10, random(0.0, 1.0)));
+
+ auto path_generator = path(
+ std::move(device_component_generator),
+ std::move(root_component_generator),
+ std::move(directory_component_generator),
+ std::move(filename_component_generator),
+ std::move(separator_component_generator),
+ PathGeneratorConfiguration{}.set_absolute_path_probability(absolute_path_probability)
+ );
+
+ WHEN("A certain amount of paths are generated from that generator") {
+ auto paths = GENERATE_REF(take(1, chunk(10000, std::move(path_generator))));
+
+ THEN("The amount of paths that are Absolute approximately respects the given probability and the amount of paths that are Relative approximately respects a probability of 1 - n") {
+ auto maybe_distribution_error{respects_distribution(
+ std::move(paths),
+ [&root_component_value](const QString& path){ return (path.contains(root_component_value)) ? "Absolute" : "Relative"; },
+ [absolute_path_probability](const QString& key){ return probability_to_percentage((key == "Absolute") ? absolute_path_probability : 1 - absolute_path_probability); }
+ )};
+
+ REQUIRE_FALSE(maybe_distribution_error);
+ }
+ }
+ }
+
+ AND_GIVEN("A generator of paths using those components generator that produces paths that are To a Directory with a probability of n") {
+ double directory_path_probability = GENERATE(take(10, random(0.0, 1.0)));
+
+ auto path_generator = path(
+ std::move(device_component_generator),
+ std::move(root_component_generator),
+ std::move(directory_component_generator),
+ std::move(filename_component_generator),
+ std::move(separator_component_generator),
+ PathGeneratorConfiguration{}.set_directory_path_probability(directory_path_probability)
+ );
+
+ WHEN("A certain amount of paths are generated from that generator") {
+ auto paths = GENERATE_REF(take(1, chunk(10000, std::move(path_generator))));
+
+ THEN("The amount of paths that are To a Directory approximately respects the given probability and the amount of paths that are To a File approximately respects a probability of 1 - n") {
+ auto maybe_distribution_error{respects_distribution(
+ std::move(paths),
+ [&filename_component_value](const QString& path){ return (path.contains(filename_component_value)) ? "To a File" : "To a Directory"; },
+ [directory_path_probability](const QString& key){ return probability_to_percentage((key == "To a Directory") ? directory_path_probability : 1 - directory_path_probability); }
+ )};
+
+ REQUIRE_FALSE(maybe_distribution_error);
+ }
+ }
+ }
+
+ AND_GIVEN("A generator of paths using those components generator that produces paths that are To a Directory with a probability of n to Have a Trailing Separator") {
+ double has_trailing_separator_probability = GENERATE(take(10, random(0.0, 1.0)));
+
+ auto path_generator = path(
+ std::move(device_component_generator),
+ std::move(root_component_generator),
+ std::move(directory_component_generator),
+ std::move(filename_component_generator),
+ std::move(separator_component_generator),
+ PathGeneratorConfiguration{}.set_directory_path_probability(1.0).set_has_trailing_separator_probability(has_trailing_separator_probability)
+ );
+
+ WHEN("A certain amount of paths are generated from that generator") {
+ auto paths = GENERATE_REF(take(1, chunk(10000, std::move(path_generator))));
+
+ THEN("The amount of paths that are Have a Trailing Separator approximately respects the given probability and the amount of paths that do not Have a Trailing Separator approximately respects a probability of 1 - n") {
+ auto maybe_distribution_error{respects_distribution(
+ std::move(paths),
+ [&separator_component_value](const QString& path){ return (path.endsWith(separator_component_value)) ? "Have a Trailing Separator" : "Doesn't Have a Trailing Separator"; },
+ [has_trailing_separator_probability](const QString& key){ return probability_to_percentage((key == "Have a Trailing Separator") ? has_trailing_separator_probability : 1 - has_trailing_separator_probability); }
+ )};
+
+ REQUIRE_FALSE(maybe_distribution_error);
+ }
+ }
+ }
+ }
+}
+
+TEST_CASE("The first component of the passed in device components generator is not lost", "[Path][GeneratorFirstElement][SpecialCase]") {
+ QString device_component_generator_first_value{"device"};
+
+ auto generated_path = GENERATE_COPY(take(1,
+ path(
+ values({device_component_generator_first_value, QString{""}}),
+ empty_string(),
+ empty_string(),
+ empty_string(),
+ empty_string(),
+ PathGeneratorConfiguration{}
+ .set_multi_device_path_probability(1.0)
+ .set_minimum_components_amount(1)
+ .set_maximum_components_amount(1)
+ )
+ ));
+
+ REQUIRE(generated_path.contains(device_component_generator_first_value));
+}
+
+TEST_CASE("The first component of the passed in root components generator is not lost", "[Path][GeneratorFirstElement][SpecialCase]") {
+ QString root_component_generator_first_value{"root"};
+
+ auto generated_path = GENERATE_COPY(take(1,
+ path(
+ empty_string(),
+ values({root_component_generator_first_value, QString{""}}),
+ empty_string(),
+ empty_string(),
+ empty_string(),
+ PathGeneratorConfiguration{}
+ .set_absolute_path_probability(1.0)
+ .set_minimum_components_amount(1)
+ .set_maximum_components_amount(1)
+ )
+ ));
+
+ REQUIRE(generated_path.contains(root_component_generator_first_value));
+}
+
+TEST_CASE("The first component of the passed in directory components generator is not lost", "[Path][GeneratorFirstElement][SpecialCase]") {
+ QString directory_component_generator_first_value{"dir"};
+
+ auto generated_path = GENERATE_COPY(take(1,
+ path(
+ empty_string(),
+ empty_string(),
+ values({directory_component_generator_first_value, QString{""}}),
+ empty_string(),
+ empty_string(),
+ PathGeneratorConfiguration{}
+ .set_directory_path_probability(1.0)
+ .set_minimum_components_amount(1)
+ .set_maximum_components_amount(1)
+ )
+ ));
+
+ REQUIRE(generated_path.contains(directory_component_generator_first_value));
+}
+
+TEST_CASE("The first component of the passed in filename components generator is not lost", "[Path][GeneratorFirstElement][SpecialCase]") {
+ QString filename_component_generator_first_value{"dir"};
+
+ auto generated_path = GENERATE_COPY(take(1,
+ path(
+ empty_string(),
+ empty_string(),
+ empty_string(),
+ values({filename_component_generator_first_value, QString{""}}),
+ empty_string(),
+ PathGeneratorConfiguration{}
+ .set_directory_path_probability(0.0)
+ .set_minimum_components_amount(1)
+ .set_maximum_components_amount(1)
+ )
+ ));
+
+ REQUIRE(generated_path.contains(filename_component_generator_first_value));
+}
+
+TEST_CASE("The first component of the passed in separator components generator is not lost", "[Path][GeneratorFirstElement][SpecialCase]") {
+ QString separator_component_generator_first_value{"sep"};
+
+ auto generated_path = GENERATE_COPY(take(1,
+ path(
+ empty_string(),
+ empty_string(),
+ empty_string(),
+ empty_string(),
+ values({separator_component_generator_first_value, QString{""}}),
+ PathGeneratorConfiguration{}
+ .set_directory_path_probability(0.0)
+ .set_minimum_components_amount(2)
+ .set_maximum_components_amount(2)
+ )
+ ));
+
+ REQUIRE(generated_path.contains(separator_component_generator_first_value));
+}
+
+SCENARIO("Generating paths that are suitable to be used on POSIX systems", "[Path][POSIX][Content]") {
+ GIVEN("A generator that generates Strings representing paths on a POSIX system that are portable") {
+ auto path_generator = relaxed_portable_posix_path();
+
+ WHEN("A path is generated from it") {
+ auto generated_path = GENERATE_REF(take(100, std::move(path_generator)));
+
+ THEN("The path is composed only by one or more characters in the class [-_./a-zA-Z0-9]") {
+ REQUIRE(QRegularExpression{R"(\A[-_.\/a-zA-Z0-9]+\z)"}.match(generated_path).hasMatch());
+ }
+ }
+ }
+}
+
+SCENARIO("Generating paths that are suitable to be used on Windows", "[Path][Windows][Content]") {
+ GIVEN("A generator that generates Strings representing paths on a Windows system") {
+ auto path_generator = traditional_dos_path();
+
+ WHEN("A path is generated from it") {
+ auto generated_path = GENERATE_REF(take(100, std::move(path_generator)));
+
+ CAPTURE(generated_path);
+
+ THEN("The path starts with an uppercase letter followed by a colon, a backward or forward slash or a character in the class [-_.a-zA-Z0-9]") {
+ QRegularExpression beginning_re{"([A-Z]:|\\|\\/|[-_.a-zA-Z0-9])"};
+
+ auto beginning_match{beginning_re.match(generated_path)};
+
+ REQUIRE(beginning_match.hasMatch());
+
+ generated_path.remove(0, beginning_match.capturedEnd());
+
+ AND_THEN("The rest of the path is composed by zero or more characters in the class [-_./\\a-zA-Z0-9]") {
+ REQUIRE(QRegularExpression{R"(\A[-_.\/\\a-zA-Z0-9]*\z)"}.match(generated_path).hasMatch());
+ }
+ }
+ }
+ }
+}
diff --git a/src/qdoc/catch_generators/tests/generators/catch_qchar_generator.cpp b/src/qdoc/catch_generators/tests/generators/catch_qchar_generator.cpp
new file mode 100644
index 000000000..718da7307
--- /dev/null
+++ b/src/qdoc/catch_generators/tests/generators/catch_qchar_generator.cpp
@@ -0,0 +1,102 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include <catch_generators/namespaces.h>
+#include <catch_generators/generators/qchar_generator.h>
+
+#include <catch_conversions/qt_catch_conversions.h>
+
+#include <catch/catch.hpp>
+
+#include <QChar>
+
+using namespace QDOC_CATCH_GENERATORS_ROOT_NAMESPACE;
+using namespace QDOC_CATCH_GENERATORS_QCHAR_ALPHABETS_NAMESPACE;
+
+SCENARIO("Binding a generated QChar to a range", "[QChar][Bounds]") {
+ GIVEN("A lower bound") {
+ auto lower_bound = GENERATE(take(100, random(
+ static_cast<unsigned int>(std::numeric_limits<char16_t>::min()),
+ static_cast<unsigned int>(std::numeric_limits<char16_t>::max())
+ )));
+
+ AND_GIVEN("An upper bound that is greater or equal than the lower bound") {
+ auto upper_bound = GENERATE_COPY(take(100, random(lower_bound, static_cast<unsigned int>(std::numeric_limits<char16_t>::max()))));
+
+ WHEN("A QChar is generated from those bounds") {
+ QChar generated_character = GENERATE_COPY(take(1, character(lower_bound, upper_bound)));
+
+ THEN("The generated character has a unicode value in the range [lower_bound, upper_bound]") {
+ REQUIRE(generated_character.unicode() >= lower_bound);
+ REQUIRE(generated_character.unicode() <= upper_bound);
+ }
+ }
+ }
+ }
+}
+
+TEST_CASE(
+ "When lower_bound and upper_bound are equal, let their value be n, the only generated character is the one with unicode value n",
+ "[QChar][Bounds]"
+) {
+ auto bound = GENERATE(take(100, random(
+ static_cast<unsigned int>(std::numeric_limits<char16_t>::min()),
+ static_cast<unsigned int>(std::numeric_limits<char16_t>::max())
+ )));
+ auto generated_character = GENERATE_COPY(take(100, character(bound, bound)));
+
+ REQUIRE(generated_character.unicode() == bound);
+}
+
+TEST_CASE("When generating digits, each generated character is in the class [0-9]", "[QChar][SpecialCase]") {
+ auto generated_character = GENERATE(take(100, digit()));
+
+ REQUIRE(generated_character >= '0');
+ REQUIRE(generated_character <= '9');
+}
+
+TEST_CASE("When generating lowercase ascii characters, each generated character is in the class [a-z]", "[QChar][SpecialCase]") {
+ auto generated_character = GENERATE(take(100, ascii_lowercase()));
+
+ REQUIRE(generated_character >= 'a');
+ REQUIRE(generated_character <= 'z');
+}
+
+TEST_CASE("When generating uppercase ascii characters, each generated character is in the class [A-Z]", "[QChar][SpecialCase]") {
+ auto generated_character = GENERATE(take(100, ascii_uppercase()));
+
+ REQUIRE(generated_character >= 'A');
+ REQUIRE(generated_character <= 'Z');
+}
+
+TEST_CASE("When generating ascii alphabetic characters, each generated character is in the class [a-zA-Z]", "[QChar][SpecialCase]") {
+ auto generated_character = GENERATE(take(100, ascii_alpha()));
+
+ REQUIRE((
+ (generated_character >= 'a' && generated_character <= 'z') ||
+ (generated_character >= 'A' && generated_character <= 'Z')
+ ));
+}
+
+TEST_CASE("When generating ascii alphabetic characters, each generated character is in the class [a-zA-Z0-9]", "[QChar][SpecialCase]") {
+ auto generated_character = GENERATE(take(100, ascii_alpha()));
+
+ REQUIRE((
+ (generated_character >= 'a' && generated_character <= 'z') ||
+ (generated_character >= 'A' && generated_character <= 'Z') ||
+ (generated_character >= '0' && generated_character <= '9')
+ ));
+}
+
+TEST_CASE("When generating portable posix filename, each generated character is in the class [-_.a-zA-Z0-9]", "[QChar][SpecialCase]") {
+ auto generated_character = GENERATE(take(100, ascii_alpha()));
+
+ REQUIRE((
+ (generated_character == '-') ||
+ (generated_character == '_') ||
+ (generated_character == '.') ||
+ (generated_character >= 'a' && generated_character <= 'z') ||
+ (generated_character >= 'A' && generated_character <= 'Z') ||
+ (generated_character >= '0' && generated_character <= '9')
+ ));
+}
diff --git a/src/qdoc/catch_generators/tests/generators/catch_qstring_generator.cpp b/src/qdoc/catch_generators/tests/generators/catch_qstring_generator.cpp
new file mode 100644
index 000000000..0e92f6900
--- /dev/null
+++ b/src/qdoc/catch_generators/tests/generators/catch_qstring_generator.cpp
@@ -0,0 +1,89 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include <catch_generators/namespaces.h>
+#include <catch_generators/generators/qchar_generator.h>
+#include <catch_generators/generators/qstring_generator.h>
+
+#include <catch_conversions/qt_catch_conversions.h>
+
+#include <catch/catch.hpp>
+
+using namespace QDOC_CATCH_GENERATORS_ROOT_NAMESPACE;
+
+#include <algorithm>
+
+SCENARIO("Binding a QString to a length range", "[QString][Bounds]") {
+ GIVEN("A minimum length") {
+ auto minimum_length = GENERATE(take(100, random(0, 100)));
+
+ AND_GIVEN("A maximum length that is greater or equal than the minimum length") {
+ auto maximum_length = GENERATE_COPY(take(100, random(minimum_length, 100)));
+
+ WHEN("A QString is generated from those bounds") {
+ QString generated_string = GENERATE_COPY(take(1, string(character(), minimum_length, maximum_length)));
+
+ THEN("The generated string's length is in the range [minimum_length, maximum_length]") {
+ REQUIRE(generated_string.size() >= minimum_length);
+ REQUIRE(generated_string.size() <= maximum_length);
+ }
+ }
+ }
+ }
+}
+
+TEST_CASE("When the maximum length and the minimum length are zero all generated strings are the empty string", "[QString][Bounds][SpecialCase][BoundingValue]") {
+ QString generated_string = GENERATE(take(100, string(character(), 0, 0)));
+
+ REQUIRE(generated_string.isEmpty());
+}
+
+TEST_CASE("When the maximum length and the minimum length are equal, all generated strings have the same length equal to the given length", "[QString][Bounds][SpecialCase]") {
+ auto length = GENERATE(take(100, random(0, 100)));
+ auto generated_string = GENERATE_COPY(take(100, string(character(), length, length)));
+
+ REQUIRE(generated_string.size() == length);
+}
+
+SCENARIO("Limiting the characters that can compose a QString", "[QString][Contents]") {
+ GIVEN("A list of characters candidates") {
+ auto lower_character_bound = GENERATE(take(10, random(
+ static_cast<unsigned int>(std::numeric_limits<char16_t>::min()),
+ static_cast<unsigned int>(std::numeric_limits<char16_t>::max())
+ )));
+ auto upper_character_bound = GENERATE_COPY(take(10, random(lower_character_bound, static_cast<unsigned int>(std::numeric_limits<char16_t>::max()))));
+
+ auto character_candidates = character(lower_character_bound, upper_character_bound);
+
+ WHEN("A QString is generated from that list") {
+ QString generated_string = GENERATE_REF(take(100, string(std::move(character_candidates), 1, 50)));
+
+ THEN("The string is composed only of characters that are in the list of characters") {
+ REQUIRE(
+ std::all_of(
+ generated_string.cbegin(), generated_string.cend(),
+ [lower_character_bound, upper_character_bound](QChar element){ return element.unicode() >= lower_character_bound && element.unicode() <= upper_character_bound; }
+ )
+ );
+ }
+ }
+ }
+}
+
+TEST_CASE("The strings generated by a generator of empty string are all empty", "[QString][Contents]") {
+ QString generated_string = GENERATE(take(100, empty_string()));
+
+ REQUIRE(generated_string.isEmpty());
+}
+
+
+TEST_CASE("The first element of the passsed in generator is not lost", "[QString][GeneratorFirstElement][SpecialCase]") {
+ QChar first_value{'a'};
+
+ // REMARK: We use two values to avoid having the generator throw
+ // an exception if the first element is actually lost.
+ auto character_generator{Catch::Generators::values({first_value, QChar{'b'}})};
+ auto generated_string = GENERATE_REF(take(1, string(std::move(character_generator), 1, 1)));
+
+ REQUIRE(generated_string == QString{first_value});
+}
diff --git a/src/qdoc/catch_generators/tests/generators/combinators/catch_cycle_generator.cpp b/src/qdoc/catch_generators/tests/generators/combinators/catch_cycle_generator.cpp
new file mode 100644
index 000000000..5bf98d73a
--- /dev/null
+++ b/src/qdoc/catch_generators/tests/generators/combinators/catch_cycle_generator.cpp
@@ -0,0 +1,70 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include <catch_generators/namespaces.h>
+#include <catch_generators/generators/combinators/cycle_generator.h>
+
+#include <catch/catch.hpp>
+
+using namespace QDOC_CATCH_GENERATORS_ROOT_NAMESPACE;
+
+// REMARK: We use fixed-values-generators for those tests so that it
+// is trivial to identify when their generation will end, which
+// values we should expect and how many values we should expect.
+// This is unfortunately not general, but we don't have, by default,
+// enough tools to generalize this without having to provide our own
+// (being able to generate fixed values from a vector) and adding more
+// to the complexity, which is already high.
+
+TEST_CASE(
+ "The xn + m element, where 0 < m < n, from a repeating generator whose underlying generator produces n elements, will produce an element equivalent to the mth element of the generation produced by the underlying generator",
+ "[Cycle][Combinators]"
+) {
+ std::size_t n{10};
+
+ auto owned_generator{Catch::Generators::values({'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j'})};
+ auto owned_generator_copy{Catch::Generators::values({'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j'})};
+
+ auto original_generation = GENERATE_REF(take(1, chunk(n, std::move(owned_generator_copy))));
+
+ std::size_t x = GENERATE(take(10, random(std::size_t{0}, std::size_t{20})));
+ std::size_t m = GENERATE_COPY(take(10, random(std::size_t{1}, std::size_t{n})));
+
+ auto repeating_generator = cycle(std::move(owned_generator));
+ auto repeating_generation = GENERATE_REF(take(1, chunk((x * n) + m, std::move(repeating_generator))));
+
+ REQUIRE(repeating_generation.back() == original_generation[m - 1]);
+}
+
+SCENARIO("Repeating a generation ad infinitum", "[Cycle][Combinators]") {
+ GIVEN("Some finite generator") {
+ std::size_t values_amount{3};
+
+ auto owned_generator{Catch::Generators::values({'a', 'b', 'c'})};
+ auto owned_generator_copy{Catch::Generators::values({'a', 'b', 'c'})};
+
+ AND_GIVEN("A way to repeat the generation of that generator infinitely") {
+ auto repeating_generator = cycle(std::move(owned_generator));
+
+ WHEN("Generating exactly enough values to exhaust the original generator") {
+ auto repeating_generation = GENERATE_REF(take(1, chunk(values_amount, std::move(repeating_generator))));
+ auto original_generation = GENERATE_REF(take(1, chunk(values_amount, std::move(owned_generator_copy))));
+
+ THEN("The repeating generator behaves equally to the original finite generator") {
+ REQUIRE(repeating_generation == original_generation);
+ }
+ }
+
+ WHEN("Generating exactly n times the amount of values required to exhaust the original generator") {
+ std::size_t n = GENERATE(take(10, random(2, 10)));
+
+ auto original_generation = GENERATE_REF(take(1, chunk(values_amount, std::move(owned_generator_copy))));
+ auto repeating_generation = GENERATE_REF(take(n, chunk(values_amount, std::move(repeating_generator))));
+
+ THEN("The n generation of the repeating generator are always the same as the generation of the original generation") {
+ REQUIRE(repeating_generation == original_generation);
+ }
+ }
+ }
+ }
+}
diff --git a/src/qdoc/catch_generators/tests/generators/combinators/catch_oneof_generator.cpp b/src/qdoc/catch_generators/tests/generators/combinators/catch_oneof_generator.cpp
new file mode 100644
index 000000000..4d5666213
--- /dev/null
+++ b/src/qdoc/catch_generators/tests/generators/combinators/catch_oneof_generator.cpp
@@ -0,0 +1,362 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include <catch_conversions/std_catch_conversions.h>
+
+#include <catch_generators/namespaces.h>
+#include <catch_generators/generators/k_partition_of_r_generator.h>
+#include <catch_generators/generators/combinators/oneof_generator.h>
+#include <catch_generators/generators/combinators/cycle_generator.h>
+#include <catch_generators/utilities/statistics/percentages.h>
+#include <catch_generators/utilities/statistics/distribution.h>
+#include <catch_generators/utilities/semantics/copy_value.h>
+
+#include <catch/catch.hpp>
+
+#include <cmath>
+#include <iterator>
+#include <vector>
+#include <algorithm>
+#include <unordered_map>
+
+using namespace QDOC_CATCH_GENERATORS_ROOT_NAMESPACE;
+using namespace QDOC_CATCH_GENERATORS_UTILITIES_ABSOLUTE_NAMESPACE;
+
+SCENARIO("Choosing between one of many generators", "[OneOf][Combinators]") {
+ GIVEN("Some generators producing values of the same type") {
+ auto generators_amount = GENERATE(take(10, random(1, 10)));
+ auto generators_values = GENERATE_COPY(take(10, chunk(generators_amount, random(0, 100000))));
+
+ std::vector<Catch::Generators::GeneratorWrapper<int>> generators;
+ generators.reserve(generators_amount);
+ std::transform(
+ generators_values.begin(), generators_values.end(), std::back_inserter(generators),
+ [](auto& value){ return Catch::Generators::value(copy_value(value)); }
+ );
+
+ AND_GIVEN("A generator choosing between them based on some distribution") {
+ std::vector<double> weights = GENERATE_COPY(take(10, k_partition_of_r(100.0, generators_amount)));
+ auto choosing_generator = oneof(std::move(generators), std::move(weights));
+
+ WHEN("A value is extracted from the choosing generator") {
+ auto generated_value = GENERATE_REF(take(100, std::move(choosing_generator)));
+
+ THEN("The generated value is a member of one of the original generators") {
+ REQUIRE(std::find(generators_values.cbegin(), generators_values.cend(), generated_value) != generators_values.cend());
+ }
+ }
+ }
+
+ AND_GIVEN("A generator choosing between them with the same probability") {
+ auto choosing_generator = uniform_oneof(std::move(generators));
+
+ WHEN("A value is extracted from the choosing generator") {
+ auto generated_value = GENERATE_REF(take(100, std::move(choosing_generator)));
+
+ THEN("The generated value is a member of one of the original generators") {
+ REQUIRE(std::find(generators_values.cbegin(), generators_values.cend(), generated_value) != generators_values.cend());
+ }
+ }
+ }
+
+ AND_GIVEN("A generator choosing between them such that each possible value has the same probability of being chosen") {
+ auto choosing_generator = uniformly_valued_oneof(std::move(generators), std::vector(generators_amount, std::size_t{1}));
+
+ WHEN("A value is extracted from the choosing generator") {
+ auto generated_value = GENERATE_REF(take(100, std::move(choosing_generator)));
+
+ THEN("The generated value is a member of one of the original generators") {
+ REQUIRE(std::find(generators_values.cbegin(), generators_values.cend(), generated_value) != generators_values.cend());
+ }
+ }
+ }
+ }
+}
+
+// TODO: The following is a generally complex test. Nonetheless, we
+// can probably ease some of the complexity by moving it out into some
+// generators or by abstracting it a little to remove the need to know
+// some of the implementation details.
+// Check if this is possible.
+
+// REMARK: [mayfail][distribution]
+// This tests cannot be precise as it depends on randomized output.
+// For this reason, we mark it as !mayfail.
+// This allows us to see cases where it fails without having the
+// test-run itself fail.
+// We generally expect this test to not fail, but it may fail randomly
+// every now and then simply because of how a correctly randomized
+// distribution may behave.
+// As long as this test doesn't fail consistently, with values that
+// shows an unsustainable deviation, it should be considered to be
+// working.
+SCENARIO("Observing the distribution of generators that are chosen from", "[OneOf][Combinators][Statistics][!mayfail]") {
+ GIVEN("Some generators producing values of the same type") {
+ std::size_t generators_amount = GENERATE(take(10, random(1, 10)));
+
+ // REMARK: To test the distribution, we want to have some
+ // amount of generators to choose from whose generated values
+ // can be uniquely reconducted to the generating generator so
+ // that we may count how many times a specific generator was
+ // chosen.
+ // The easiest way would be to have generators that produce a
+ // single value.
+ // Nonetheless, to test the version that provides an
+ // approximate uniform distribution over the values themselves
+ // correctly, we need to have generators that can produce a
+ // different amount of elements.
+ // When that is not the case, indeed, a generator that
+ // approximately distributes uniformly over values is
+ // equivalent to one that approximately distributes uniformely
+ // over the generators themselves.
+ // As such, we use ranges of positive integers, as they are
+ // the simplest multi-valued finite generator that can be dinamically
+ // construted, while still providing an easy way to infer the
+ // amount of values it contains so that we can derive the
+ // cardinality of our domain.
+ // We produce those ranges as disjoint subsequent ranges
+ // starting from 0 upward.
+ // We require the ranges to be disjoint so that we do not lose
+ // the ability of uniquely identifying a generator that
+ // produced the value.
+ //
+ // To do so, we generate a series of disjoint least upper
+ // bounds for the ranges.
+ // Then, we produce the ith range by using the successor of
+ // the (i - 1)th upper bound as its lower bound and the ith
+ // upper bound as its upper bound.
+ //
+ // We take further care to ensure that the collection of upper
+ // bounds is sorted, as this simplifies to a linear search our
+ // need to index the collection of generators to find the
+ // identifying generator and its associated probability.
+ std::vector<std::size_t> generators_bounds(generators_amount, 0);
+ std::vector<Catch::Generators::GeneratorWrapper<std::size_t>> generators;
+ generators.reserve(generators_amount);
+
+ std::size_t lowest_bound{0};
+ std::size_t generators_step{1000};
+ std::size_t lower_bound_offset{1};
+
+ generators_bounds[0] = Catch::Generators::random(lowest_bound, generators_step).get();
+ generators.push_back(Catch::Generators::random(lowest_bound, generators_bounds[0]));
+
+ // We use this one to group together values that are generated
+ // from the same generator and to provide an index for that
+ // generator to use for finding its associated probability.
+ // Since our generators are defined by their upper bounds and
+ // the collection of upper bounds is sorted, the first
+ // encountered upper bound that is not less than the value
+ // itself must be the least upper bound of the generator that
+ // produced the value.
+ // Then, the index of that upper bound must be the same as the
+ // index of the producing generator and its associated
+ // probability.
+ auto find_index_of_producing_generator = [&generators_bounds](auto value) {
+ return static_cast<std::size_t>(std::distance(
+ generators_bounds.begin(),
+ std::find_if(generators_bounds.begin(), generators_bounds.end(), [&value](auto element){ return value <= element; })
+ ));
+ };
+
+ for (std::size_t index{1}; index < generators_amount; ++index) {
+ generators_bounds[index] = Catch::Generators::random(generators_bounds[index - 1] + lower_bound_offset + 1, generators_bounds[index - 1] + lower_bound_offset + 1 + generators_step).get();
+ generators.push_back(Catch::Generators::random(generators_bounds[index - 1] + lower_bound_offset, generators_bounds[index]));
+ }
+
+ AND_GIVEN("A probability of being chosen, in percentage, for each of the generators, such that the sum of the percentages is one hundred") {
+ std::vector<double> probabilities = GENERATE_COPY(take(10, k_partition_of_r(100.0, generators_amount)));
+
+ AND_GIVEN("A choosing generator for those generators based on the given probabilities") {
+ auto choosing_generator = oneof(std::move(generators), probabilities);
+
+ WHEN("A certain amount of values are generated from the choosing generator") {
+ auto values = GENERATE_REF(take(1, chunk(10000, std::move(choosing_generator))));
+
+ THEN("The distribution of elements for each generator approximately respects the weight that was given to it") {
+ auto maybe_distribution_error{respects_distribution(
+ std::move(values),
+ find_index_of_producing_generator,
+ [&probabilities](auto key){ return probabilities[key]; }
+ )};
+
+ REQUIRE_FALSE(maybe_distribution_error);
+ }
+ }
+ }
+ }
+
+ AND_GIVEN("A choosing generator for those generators that will choose each generator with the same probability") {
+ auto choosing_generator = uniform_oneof(std::move(generators));
+
+ WHEN("A certain amount of values are generated from the choosing generator") {
+ auto values = GENERATE_REF(take(1, chunk(10000, std::move(choosing_generator))));
+
+ THEN("The distribution of elements approximates uniformity over the generators") {
+ double probability{uniform_probability(generators_amount)};
+
+ auto maybe_distribution_error{respects_distribution(
+ std::move(values),
+ find_index_of_producing_generator,
+ [&probability](auto _){ (void)(_); return probability; }
+ )};
+
+ REQUIRE_FALSE(maybe_distribution_error);
+ }
+ }
+ }
+
+ AND_GIVEN("A choosing generator for those generators that will choose each generator such that each possible value has the same probability of being chosen") {
+ // REMARK: We need to know the total amount of
+ // unique values that can be generated by our
+ // generators, so that we can construct an
+ // appropriate distribution.
+ // Since our generators are ranges defined by the
+ // collection of upper bounds we can find their
+ // length by finding the difference between
+ // adjacent elements of the collection.
+ //
+ // Some more care must be taken to ensure tha the
+ // correct amount is produced.
+ // Since we need our ranges to be disjoint, we
+ // apply a small offset from the element of the
+ // upper bounds that is used as a lower bound,
+ // since that upper bound is inclusive for the
+ // range that precedes the one we are making the
+ // calculation for.
+ //
+ // Furthermore, the first range is treated
+ // specially.
+ // As no range precedes it, it doesn't need any
+ // offset to be applied.
+ // Additionally, we implicitly use 0 as the first
+ // lower bound, such that the length of the first
+ // range is indeed equal to its upper bound.
+ //
+ // To account for this, we remove that offset from
+ // the total amount for each range after the first
+ // one and use the first upper bound as a seeding
+ // value to account for the length of the first
+ // range.
+ std::vector<std::size_t> generators_cardinality(generators_amount, generators_bounds[0]);
+
+ std::adjacent_difference(generators_bounds.begin(), generators_bounds.end(), generators_bounds.begin());
+ std::transform(std::next(generators_cardinality.begin()), generators_cardinality.end(), std::next(generators_cardinality.begin()), [](auto element){ return element - 1; });
+
+ std::size_t output_cardinality{std::accumulate(generators_cardinality.begin(), generators_cardinality.end(), std::size_t{0})};
+
+ auto choosing_generator = uniformly_valued_oneof(std::move(generators), std::move(generators_cardinality));
+
+ WHEN("A certain amount of values are generated from the choosing generator") {
+ auto values = GENERATE_REF(take(1, chunk(10000, std::move(choosing_generator))));
+
+ THEN("The distribution of elements approximates uniformity for each value") {
+ double probability{uniform_probability(output_cardinality)};
+
+ auto maybe_distribution_error{respects_distribution(
+ std::move(values),
+ [](auto value){ return value; },
+ [&probability](auto _){ (void)(_); return probability; }
+ )};
+
+ REQUIRE_FALSE(maybe_distribution_error);
+ }
+ }
+ }
+ }
+}
+
+TEST_CASE("A generator with a weight of zero is never chosen when choosing between many generators", "[OneOf][Combinators][SpecialCase]") {
+ auto excluded_value = GENERATE(take(100, random(0, 10000)));
+
+ std::vector<Catch::Generators::GeneratorWrapper<int>> generators;
+ generators.reserve(2);
+ generators.emplace_back(Catch::Generators::random(excluded_value + 1, std::numeric_limits<int>::max()));
+ generators.emplace_back(Catch::Generators::value(copy_value(excluded_value)));
+
+ auto generated_value = GENERATE_REF(take(100, oneof(std::move(generators), std::vector{100.0, 0.0})));
+
+ REQUIRE(generated_value != excluded_value);
+}
+
+TEST_CASE("The first element of the passed in generators are not lost", "[OneOf][Combinators][GeneratorFirstElement][SpecialCase]") {
+ // REMARK: We want to test that, for each generator, the first
+ // time it is chosen the first value is produced.
+ // This is complicated because of the fact that OneOf chooses
+ // random generators in a random order.
+ // This means that some generators may never be chosen, never be
+ // chosen more than once and so on.
+ // Furthermore, this specific test is particularly important only
+ // for finite generators or non-completely random, ordered,
+ // infinite generators.
+ // Additionally, we need to ensure that we test with multiple
+ // generators, as this test is a consequence of a first bugged
+ // implementation where only the first chosen generator respected
+ // the first value, which would pass a test where a single
+ // generator is used.
+ //
+ // This is non-trivial due to the randomized nature of OneOf.
+ // It can be simplified if we express it in a non-deterministic
+ // way and mark it as mayfail, where we can recognize with a good
+ // certainty that the test is actually passing.
+ //
+ // To avoid having this flaky test, we approach it as follows:
+ //
+ // We provide some amount of infinite generators. Those generators
+ // are ensured to produce one specific value as their first value
+ // and then infinitely produce a different value.
+ // We ensure that each generator that is provided produces unique
+ // values, that is, no two generators produce a first value or 1 <
+ // nth value that is equal to the one produced by another
+ // generator.
+ //
+ // Then we pass those generators to oneof and generate enough
+ // values such that at least one of the generators must have been
+ // chosen twice or more, at random.
+ //
+ // We count the appearances of each value in the produced set.
+ // Then, if a value that is generated by the 1 < nth choice of a
+ // specific generator is encountered, we check that the first
+ // value that the specific generator would produce is in the set
+ // of values that were generated.
+ // That is, if a generator has produced his non-first value, it
+ // must have been chosen twice or more.
+ // This in turn implies that the first time that the generator was
+ // chosen, its first value was actually produced.
+
+ struct IncreaseAfterFirst {
+ std::size_t increase;
+ bool first_application = true;
+
+ std::size_t operator()(std::size_t value) {
+ if (first_application) {
+ first_application = false;
+ return value;
+ }
+
+ return value + increase;
+ }
+ };
+
+ std::size_t maximum_generator_amount{100};
+ auto generators_amount = GENERATE_COPY(take(10, random(std::size_t{1}, maximum_generator_amount)));
+
+ std::vector<Catch::Generators::GeneratorWrapper<std::size_t>> generators;
+ generators.reserve(generators_amount);
+
+ for (std::size_t index{0}; index < generators_amount; ++index) {
+ generators.push_back(Catch::Generators::map(IncreaseAfterFirst{maximum_generator_amount}, cycle(Catch::Generators::value(copy_value(index)))));
+ }
+
+ auto values = GENERATE_REF(take(1, chunk(generators_amount + 1, uniform_oneof(std::move(generators)))));
+ auto histogram{make_histogram(values.begin(), values.end(), [](auto e){ return e; })};
+
+ for (std::size_t index{0}; index < generators_amount; ++index) {
+ std::size_t second_value{index + maximum_generator_amount};
+ histogram.try_emplace(second_value, 0);
+
+ if (histogram[second_value] > 0) {
+ REQUIRE(histogram.find(index) != histogram.end());
+ }
+ }
+}
diff --git a/src/qdoc/catch_generators/tests/main.cpp b/src/qdoc/catch_generators/tests/main.cpp
new file mode 100644
index 000000000..48ce73f12
--- /dev/null
+++ b/src/qdoc/catch_generators/tests/main.cpp
@@ -0,0 +1,13 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#define CATCH_CONFIG_RUNNER
+#include <catch/catch.hpp>
+
+// A custom main was provided to avoid linking errors when using minGW
+// that were appearing in CI.
+// See https://github.com/catchorg/Catch2/issues/1287
+int main(int argc, char *argv[])
+{
+ return Catch::Session().run(argc, argv);
+}
diff --git a/src/qdoc/catch_generators/tests/utilities/semantics/catch_generator_handler.cpp b/src/qdoc/catch_generators/tests/utilities/semantics/catch_generator_handler.cpp
new file mode 100644
index 000000000..b99a6515d
--- /dev/null
+++ b/src/qdoc/catch_generators/tests/utilities/semantics/catch_generator_handler.cpp
@@ -0,0 +1,28 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include <catch/catch.hpp>
+
+#include <catch_generators/namespaces.h>
+#include <catch_generators/utilities/semantics/generator_handler.h>
+
+using namespace QDOC_CATCH_GENERATORS_UTILITIES_ABSOLUTE_NAMESPACE;
+
+TEST_CASE(
+ "Calling next 0 < n times and then calling get on a GeneratorHandler wrapping a generator behaves the same as only calling next (n-1) times and then get on the generator that is wrapped",
+ "[GeneratorHandler][Utilities][Semantics][Generators]"
+) {
+ auto n = GENERATE(take(100, random(1, 100)));
+ auto generator_values = GENERATE_COPY(take(1, chunk(n, random(0, 100000))));
+
+ auto generator_handler = handler(Catch::Generators::from_range(generator_values.begin(), generator_values.end()));
+ auto generator{Catch::Generators::from_range(generator_values.begin(), generator_values.end())};
+
+ generator_handler.next();
+ for (int times{1}; times < n; ++times) {
+ generator_handler.next();
+ generator.next();
+ }
+
+ REQUIRE(generator_handler.get() == generator.get());
+}
diff --git a/src/qdoc/clangcodeparser.h b/src/qdoc/clangcodeparser.h
deleted file mode 100644
index cd1f34eeb..000000000
--- a/src/qdoc/clangcodeparser.h
+++ /dev/null
@@ -1,89 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the tools applications of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#ifndef CLANGCODEPARSER_H
-#define CLANGCODEPARSER_H
-
-#include "cppcodeparser.h"
-
-#include <QtCore/qtemporarydir.h>
-
-typedef struct CXTranslationUnitImpl *CXTranslationUnit;
-
-QT_BEGIN_NAMESPACE
-
-class ClangCodeParser : public CppCodeParser
-{
-public:
- ~ClangCodeParser() override = default;
-
- void initializeParser() override;
- void terminateParser() override;
- QString language() override;
- QStringList headerFileNameFilter() override;
- QStringList sourceFileNameFilter() override;
- void parseHeaderFile(const Location &location, const QString &filePath) override;
- void parseSourceFile(const Location &location, const QString &filePath) override;
- void precompileHeaders() override;
- Node *parseFnArg(const Location &location, const QString &fnSignature, const QString &idTag) override;
- static const QByteArray &fn() { return s_fn; }
-
-private:
- void getDefaultArgs(); // FIXME: Clean up API
- void getMoreArgs(); // FIXME: Clean up API
-
- void buildPCH();
-
- void printDiagnostics(const CXTranslationUnit &translationUnit) const;
-
- QString m_version {};
- QMultiHash<QString, QString> m_allHeaders {}; // file name->path
- QList<QByteArray> m_includePaths {};
- QScopedPointer<QTemporaryDir> m_pchFileDir {};
- QByteArray m_pchName {};
- QList<QByteArray> m_defines {};
- std::vector<const char *> m_args {};
- QList<QByteArray> m_moreArgs {};
- QStringList m_namespaceScope {};
- static QByteArray s_fn;
-};
-
-QT_END_NAMESPACE
-
-#endif
diff --git a/src/qdoc/codechunk.h b/src/qdoc/codechunk.h
deleted file mode 100644
index 4a54cfdc9..000000000
--- a/src/qdoc/codechunk.h
+++ /dev/null
@@ -1,99 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2019 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the tools applications of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#ifndef CODECHUNK_H
-#define CODECHUNK_H
-
-#include <QtCore/qstring.h>
-
-QT_BEGIN_NAMESPACE
-
-// ### get rid of that class
-
-class CodeChunk
-{
-public:
- CodeChunk() : m_hotspot(-1) { }
-
- void append(const QString &lexeme);
- void appendHotspot()
- {
- if (m_hotspot == -1)
- m_hotspot = m_str.length();
- }
-
- [[nodiscard]] bool isEmpty() const { return m_str.isEmpty(); }
- void clear() { m_str.clear(); }
- [[nodiscard]] QString toString() const { return m_str; }
- [[nodiscard]] QString left() const
- {
- return m_str.left(m_hotspot == -1 ? m_str.length() : m_hotspot);
- }
- [[nodiscard]] QString right() const
- {
- return m_str.mid(m_hotspot == -1 ? m_str.length() : m_hotspot);
- }
-
-private:
- QString m_str {};
- qsizetype m_hotspot {};
-};
-
-inline bool operator==(const CodeChunk &c, const CodeChunk &d)
-{
- return c.toString() == d.toString();
-}
-
-inline bool operator!=(const CodeChunk &c, const CodeChunk &d)
-{
- return !(c == d);
-}
-
-inline bool operator<(const CodeChunk &c, const CodeChunk &d)
-{
- return c.toString() < d.toString();
-}
-
-inline bool operator>(const CodeChunk &c, const CodeChunk &d)
-{
- return d < c;
-}
-
-inline bool operator<=(const CodeChunk &c, const CodeChunk &d)
-{
- return !(c > d);
-}
-
-inline bool operator>=(const CodeChunk &c, const CodeChunk &d)
-{
- return !(c < d);
-}
-
-QT_END_NAMESPACE
-
-#endif
diff --git a/src/qdoc/codeparser.cpp b/src/qdoc/codeparser.cpp
deleted file mode 100644
index a7e9abc00..000000000
--- a/src/qdoc/codeparser.cpp
+++ /dev/null
@@ -1,246 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the tools applications of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include "codeparser.h"
-
-#include "config.h"
-#include "generator.h"
-#include "node.h"
-#include "qdocdatabase.h"
-
-#include <QtCore/qregularexpression.h>
-
-QT_BEGIN_NAMESPACE
-
-QList<CodeParser *> CodeParser::s_parsers;
-bool CodeParser::s_showInternal = false;
-
-/*!
- The constructor adds this code parser to the static
- list of code parsers.
- */
-CodeParser::CodeParser()
-{
- m_qdb = QDocDatabase::qdocDB();
- s_parsers.prepend(this);
-}
-
-/*!
- The destructor removes this code parser from the static
- list of code parsers.
- */
-CodeParser::~CodeParser()
-{
- s_parsers.removeAll(this);
-}
-
-/*!
- Initialize the code parser base class.
- */
-void CodeParser::initializeParser()
-{
- s_showInternal = Config::instance().showInternal();
-}
-
-/*!
- Terminating a code parser is trivial.
- */
-void CodeParser::terminateParser()
-{
- // nothing.
-}
-
-QStringList CodeParser::headerFileNameFilter()
-{
- return sourceFileNameFilter();
-}
-
-void CodeParser::parseHeaderFile(const Location &location, const QString &filePath)
-{
- parseSourceFile(location, filePath);
-}
-
-/*!
- All the code parsers in the static list are initialized here,
- after the qdoc configuration variables have been set.
- */
-void CodeParser::initialize()
-{
- for (const auto &parser : qAsConst(s_parsers))
- parser->initializeParser();
-}
-
-/*!
- All the code parsers in the static list are terminated here.
- */
-void CodeParser::terminate()
-{
- for (const auto parser : s_parsers)
- parser->terminateParser();
-}
-
-CodeParser *CodeParser::parserForLanguage(const QString &language)
-{
- for (const auto parser : qAsConst(s_parsers)) {
- if (parser->language() == language)
- return parser;
- }
- return nullptr;
-}
-
-CodeParser *CodeParser::parserForHeaderFile(const QString &filePath)
-{
- QString fileName = QFileInfo(filePath).fileName();
-
- for (const auto &parser : qAsConst(s_parsers)) {
- const QStringList headerPatterns = parser->headerFileNameFilter();
- for (const auto &pattern : headerPatterns) {
- auto re = QRegularExpression::fromWildcard(pattern, Qt::CaseInsensitive);
- if (re.match(fileName).hasMatch())
- return parser;
- }
- }
- return nullptr;
-}
-
-CodeParser *CodeParser::parserForSourceFile(const QString &filePath)
-{
- QString fileName = QFileInfo(filePath).fileName();
-
- for (const auto &parser : s_parsers) {
- const QStringList sourcePatterns = parser->sourceFileNameFilter();
- for (const QString &pattern : sourcePatterns) {
- auto re = QRegularExpression::fromWildcard(pattern, Qt::CaseInsensitive);
- if (re.match(fileName).hasMatch())
- return parser;
- }
- }
- return nullptr;
-}
-
-static QSet<QString> commonMetaCommands_;
-/*!
- Returns the set of strings representing the common metacommands.
- */
-const QSet<QString> &CodeParser::commonMetaCommands()
-{
- if (commonMetaCommands_.isEmpty()) {
- commonMetaCommands_ << COMMAND_ABSTRACT << COMMAND_DEFAULT << COMMAND_DEPRECATED << COMMAND_INGROUP
- << COMMAND_INJSMODULE << COMMAND_INMODULE << COMMAND_INPUBLICGROUP
- << COMMAND_INQMLMODULE << COMMAND_INTERNAL << COMMAND_NOAUTOLIST
- << COMMAND_NONREENTRANT << COMMAND_OBSOLETE << COMMAND_PRELIMINARY
- << COMMAND_QMLABSTRACT << COMMAND_QMLDEFAULT << COMMAND_QMLINHERITS
- << COMMAND_QMLREADONLY << COMMAND_QMLREQUIRED << COMMAND_QTCMAKEPACKAGE
- << COMMAND_QTVARIABLE << COMMAND_REENTRANT << COMMAND_SINCE
- << COMMAND_STARTPAGE << COMMAND_SUBTITLE << COMMAND_THREADSAFE
- << COMMAND_TITLE << COMMAND_WRAPPER;
- }
- return commonMetaCommands_;
-}
-
-/*!
- \internal
- */
-void CodeParser::extractPageLinkAndDesc(QStringView arg, QString *link, QString *desc)
-{
- QRegularExpression bracedRegExp(
- QRegularExpression::anchoredPattern(QLatin1String(R"(\{([^{}]*)\}(?:\{([^{}]*)\})?)")));
- auto match = bracedRegExp.match(arg);
- if (match.hasMatch()) {
- *link = match.captured(1);
- *desc = match.captured(2);
- if (desc->isEmpty())
- *desc = *link;
- } else {
- qsizetype spaceAt = arg.indexOf(QLatin1Char(' '));
- if (arg.contains(QLatin1String(".html")) && spaceAt != -1) {
- *link = arg.left(spaceAt).trimmed().toString();
- *desc = arg.mid(spaceAt).trimmed().toString();
- } else {
- *link = arg.toString();
- *desc = *link;
- }
- }
-}
-
-/*!
- \internal
- */
-void CodeParser::setLink(Node *node, Node::LinkType linkType, const QString &arg)
-{
- QString link;
- QString desc;
- extractPageLinkAndDesc(arg, &link, &desc);
- node->setLink(linkType, link, desc);
-}
-
-/*!
- \brief Test for whether a doc comment warrants warnings.
-
- Returns true if qdoc should report that it has found something
- wrong with the qdoc comment in \a doc. Sometimes, qdoc should
- not report the warning, for example, when the comment contains
- the \c internal command, which normally means qdoc will not use
- the comment in the documentation anyway, so there is no point
- in reporting warnings about it.
- */
-bool CodeParser::isWorthWarningAbout(const Doc &doc)
-{
- return (s_showInternal || !doc.metaCommandsUsed().contains(QStringLiteral("internal")));
-}
-
-/*!
- For each node that is part of C++ API and produces a documentation
- page, this function ensures that the node belongs to a module.
- */
-void CodeParser::checkModuleInclusion(Node *n)
-{
- if (n->physicalModuleName().isEmpty()) {
- if (n->isInAPI() && !n->name().isEmpty()) {
- switch (n->nodeType()) {
- case Node::Class:
- case Node::Struct:
- case Node::Union:
- case Node::Namespace:
- case Node::HeaderFile:
- break;
- default:
- return;
- }
- n->setPhysicalModuleName(Generator::defaultModuleName());
- m_qdb->addToModule(Generator::defaultModuleName(), n);
- n->doc().location().warning(
- QStringLiteral("Documentation for %1 '%2' has no \\inmodule command; "
- "using project name by default: %3")
- .arg(Node::nodeTypeString(n->nodeType()), n->name(),
- n->physicalModuleName()));
- }
- }
-}
-
-QT_END_NAMESPACE
diff --git a/src/qdoc/codeparser.h b/src/qdoc/codeparser.h
deleted file mode 100644
index dd484db99..000000000
--- a/src/qdoc/codeparser.h
+++ /dev/null
@@ -1,164 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the tools applications of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#ifndef CODEPARSER_H
-#define CODEPARSER_H
-
-#include "node.h"
-
-#include <QtCore/qset.h>
-
-QT_BEGIN_NAMESPACE
-
-class Location;
-class QString;
-class QDocDatabase;
-
-class CodeParser
-{
-public:
- CodeParser();
- virtual ~CodeParser();
-
- virtual void initializeParser();
- virtual void terminateParser();
- virtual QString language() = 0;
- virtual QStringList headerFileNameFilter();
- virtual QStringList sourceFileNameFilter() = 0;
- virtual void parseHeaderFile(const Location &location, const QString &filePath);
- virtual void parseSourceFile(const Location &location, const QString &filePath) = 0;
- virtual void precompileHeaders() {}
- virtual Node *parseFnArg(const Location &, const QString &, const QString & = QString())
- {
- return nullptr;
- }
-
- [[nodiscard]] const QString &currentFile() const { return m_currentFile; }
- [[nodiscard]] const QString &moduleHeader() const { return m_moduleHeader; }
- void setModuleHeader(const QString &t) { m_moduleHeader = t; }
- void checkModuleInclusion(Node *n);
-
- static void initialize();
- static void terminate();
- static CodeParser *parserForLanguage(const QString &language);
- static CodeParser *parserForHeaderFile(const QString &filePath);
- static CodeParser *parserForSourceFile(const QString &filePath);
- static void setLink(Node *node, Node::LinkType linkType, const QString &arg);
- static bool isWorthWarningAbout(const Doc &doc);
-
-protected:
- const QSet<QString> &commonMetaCommands();
- static void extractPageLinkAndDesc(QStringView arg, QString *link, QString *desc);
- static bool showInternal() { return s_showInternal; }
- QString m_moduleHeader {};
- QString m_currentFile {};
- QDocDatabase *m_qdb {};
-
-private:
- static QList<CodeParser *> s_parsers;
- static bool s_showInternal;
-};
-
-#define COMMAND_ABSTRACT Doc::alias(QLatin1String("abstract"))
-#define COMMAND_CLASS Doc::alias(QLatin1String("class"))
-#define COMMAND_DEFAULT Doc::alias(QLatin1String("default"))
-#define COMMAND_DEPRECATED Doc::alias(QLatin1String("deprecated")) // ### don't document
-#define COMMAND_DONTDOCUMENT Doc::alias(QLatin1String("dontdocument"))
-#define COMMAND_ENUM Doc::alias(QLatin1String("enum"))
-#define COMMAND_EXAMPLE Doc::alias(QLatin1String("example"))
-#define COMMAND_EXTERNALPAGE Doc::alias(QLatin1String("externalpage"))
-#define COMMAND_FN Doc::alias(QLatin1String("fn"))
-#define COMMAND_GROUP Doc::alias(QLatin1String("group"))
-#define COMMAND_HEADERFILE Doc::alias(QLatin1String("headerfile"))
-#define COMMAND_INGROUP Doc::alias(QLatin1String("ingroup"))
-#define COMMAND_INHEADERFILE Doc::alias(QLatin1String("inheaderfile"))
-#define COMMAND_INJSMODULE Doc::alias(QLatin1String("injsmodule"))
-#define COMMAND_INMODULE Doc::alias(QLatin1String("inmodule")) // ### don't document
-#define COMMAND_INPUBLICGROUP Doc::alias(QLatin1String("inpublicgroup"))
-#define COMMAND_INQMLMODULE Doc::alias(QLatin1String("inqmlmodule"))
-#define COMMAND_INTERNAL Doc::alias(QLatin1String("internal"))
-#define COMMAND_JSATTACHEDMETHOD Doc::alias(QLatin1String("jsattachedmethod"))
-#define COMMAND_JSATTACHEDPROPERTY Doc::alias(QLatin1String("jsattachedproperty"))
-#define COMMAND_JSATTACHEDSIGNAL Doc::alias(QLatin1String("jsattachedsignal"))
-#define COMMAND_JSBASICTYPE Doc::alias(QLatin1String("jsbasictype"))
-#define COMMAND_JSMETHOD Doc::alias(QLatin1String("jsmethod"))
-#define COMMAND_JSMODULE Doc::alias(QLatin1String("jsmodule"))
-#define COMMAND_JSPROPERTY Doc::alias(QLatin1String("jsproperty"))
-#define COMMAND_JSPROPERTYGROUP Doc::alias(QLatin1String("jspropertygroup"))
-#define COMMAND_JSSIGNAL Doc::alias(QLatin1String("jssignal"))
-#define COMMAND_JSTYPE Doc::alias(QLatin1String("jstype"))
-#define COMMAND_MACRO Doc::alias(QLatin1String("macro"))
-#define COMMAND_MODULE Doc::alias(QLatin1String("module"))
-#define COMMAND_NAMESPACE Doc::alias(QLatin1String("namespace"))
-#define COMMAND_NEXTPAGE Doc::alias(QLatin1String("nextpage"))
-#define COMMAND_NOAUTOLIST Doc::alias(QLatin1String("noautolist"))
-#define COMMAND_NONREENTRANT Doc::alias(QLatin1String("nonreentrant"))
-#define COMMAND_OBSOLETE Doc::alias(QLatin1String("obsolete"))
-#define COMMAND_OVERLOAD Doc::alias(QLatin1String("overload"))
-#define COMMAND_PAGE Doc::alias(QLatin1String("page"))
-#define COMMAND_PRELIMINARY Doc::alias(QLatin1String("preliminary"))
-#define COMMAND_PREVIOUSPAGE Doc::alias(QLatin1String("previouspage"))
-#define COMMAND_PROPERTY Doc::alias(QLatin1String("property"))
-#define COMMAND_QMLABSTRACT Doc::alias(QLatin1String("qmlabstract"))
-#define COMMAND_QMLATTACHEDMETHOD Doc::alias(QLatin1String("qmlattachedmethod"))
-#define COMMAND_QMLATTACHEDPROPERTY Doc::alias(QLatin1String("qmlattachedproperty"))
-#define COMMAND_QMLATTACHEDSIGNAL Doc::alias(QLatin1String("qmlattachedsignal"))
-#define COMMAND_QMLBASICTYPE Doc::alias(QLatin1String("qmlbasictype"))
-#define COMMAND_QMLCLASS Doc::alias(QLatin1String("qmlclass"))
-#define COMMAND_QMLDEFAULT Doc::alias(QLatin1String("qmldefault"))
-#define COMMAND_QMLINHERITS Doc::alias(QLatin1String("inherits"))
-#define COMMAND_QMLINSTANTIATES Doc::alias(QLatin1String("instantiates"))
-#define COMMAND_QMLMETHOD Doc::alias(QLatin1String("qmlmethod"))
-#define COMMAND_QMLMODULE Doc::alias(QLatin1String("qmlmodule"))
-#define COMMAND_QMLPROPERTY Doc::alias(QLatin1String("qmlproperty"))
-#define COMMAND_QMLPROPERTYGROUP Doc::alias(QLatin1String("qmlpropertygroup"))
-#define COMMAND_QMLREADONLY Doc::alias(QLatin1String("readonly"))
-#define COMMAND_QMLREQUIRED Doc::alias(QLatin1String("required"))
-#define COMMAND_QMLSIGNAL Doc::alias(QLatin1String("qmlsignal"))
-#define COMMAND_QMLTYPE Doc::alias(QLatin1String("qmltype"))
-#define COMMAND_QTCMAKEPACKAGE Doc::alias(QLatin1String("qtcmakepackage"))
-#define COMMAND_QTVARIABLE Doc::alias(QLatin1String("qtvariable"))
-#define COMMAND_REENTRANT Doc::alias(QLatin1String("reentrant"))
-#define COMMAND_REIMP Doc::alias(QLatin1String("reimp"))
-#define COMMAND_RELATES Doc::alias(QLatin1String("relates"))
-#define COMMAND_SINCE Doc::alias(QLatin1String("since"))
-#define COMMAND_STRUCT Doc::alias(QLatin1String("struct"))
-#define COMMAND_SUBTITLE Doc::alias(QLatin1String("subtitle"))
-#define COMMAND_STARTPAGE Doc::alias(QLatin1String("startpage"))
-#define COMMAND_THREADSAFE Doc::alias(QLatin1String("threadsafe"))
-#define COMMAND_TITLE Doc::alias(QLatin1String("title"))
-#define COMMAND_TYPEALIAS Doc::alias(QLatin1String("typealias"))
-#define COMMAND_TYPEDEF Doc::alias(QLatin1String("typedef"))
-#define COMMAND_VARIABLE Doc::alias(QLatin1String("variable"))
-#define COMMAND_VERSION Doc::alias(QLatin1String("version"))
-#define COMMAND_UNION Doc::alias(QLatin1String("union"))
-#define COMMAND_WRAPPER Doc::alias(QLatin1String("wrapper"))
-
-QT_END_NAMESPACE
-
-#endif
diff --git a/src/qdoc/collectionnode.cpp b/src/qdoc/collectionnode.cpp
deleted file mode 100644
index 5e91ab6d8..000000000
--- a/src/qdoc/collectionnode.cpp
+++ /dev/null
@@ -1,153 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the tools applications of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include "collectionnode.h"
-
-#include <QtCore/qstringlist.h>
-
-QT_BEGIN_NAMESPACE
-
-/*!
- \class CollectionNode
- \brief A class for holding the members of a collection of doc pages.
- */
-
-/*!
- Appends \a node to the collection node's member list, if
- and only if it isn't already in the member list.
- */
-void CollectionNode::addMember(Node *node)
-{
- if (!m_members.contains(node))
- m_members.append(node);
-}
-
-/*!
- Returns \c true if this collection node contains at least
- one namespace node.
- */
-bool CollectionNode::hasNamespaces() const
-{
- return std::any_of(m_members.cbegin(), m_members.cend(), [](const Node *member) {
- return member->isClassNode() && member->isInAPI();
- });
-}
-
-/*!
- Returns \c true if this collection node contains at least
- one class node.
- */
-bool CollectionNode::hasClasses() const
-{
- return std::any_of(m_members.cbegin(), m_members.cend(), [](const Node *member) {
- return member->isClassNode() && member->isInAPI();
- });
-}
-
-/*!
- Loads \a out with all this collection node's members that
- are namespace nodes.
- */
-void CollectionNode::getMemberNamespaces(NodeMap &out)
-{
- out.clear();
- for (const auto &member : qAsConst(m_members)) {
- if (member->isNamespace() && member->isInAPI())
- out.insert(member->name(), member);
- }
-}
-
-/*!
- Loads \a out with all this collection node's members that
- are class nodes.
- */
-void CollectionNode::getMemberClasses(NodeMap &out) const
-{
- out.clear();
- for (const auto &member : qAsConst(m_members)) {
- if (member->isClassNode() && member->isInAPI())
- out.insert(member->name(), member);
- }
-}
-
-/*!
- Returns the logical module version.
-*/
-QString CollectionNode::logicalModuleVersion() const
-{
- QStringList version;
- version << m_logicalModuleVersionMajor << m_logicalModuleVersionMinor;
- version.removeAll(QString());
- return version.join(".");
-}
-
-/*!
- This function splits \a arg on the blank character to get a
- logical module name and version number. If the version number
- is present, it splits the version number on the '.' character
- to get a major version number and a minor version number. If
- the version number is present, both the major and minor version
- numbers should be there, but the minor version number is not
- absolutely necessary.
- */
-void CollectionNode::setLogicalModuleInfo(const QString &arg)
-{
- QStringList blankSplit = arg.split(QLatin1Char(' '));
- m_logicalModuleName = blankSplit[0];
- if (blankSplit.size() > 1) {
- QStringList dotSplit = blankSplit[1].split(QLatin1Char('.'));
- m_logicalModuleVersionMajor = dotSplit[0];
- if (dotSplit.size() > 1)
- m_logicalModuleVersionMinor = dotSplit[1];
- else
- m_logicalModuleVersionMinor = "0";
- }
-}
-
-/*!
- This function accepts the logical module \a info as a string
- list. If the logical module info contains the version number,
- it splits the version number on the '.' character to get the
- major and minor version numbers. Both major and minor version
- numbers should be provided, but the minor version number is
- not strictly necessary.
- */
-void CollectionNode::setLogicalModuleInfo(const QStringList &info)
-{
- m_logicalModuleName = info[0];
- if (info.size() > 1) {
- QStringList dotSplit = info[1].split(QLatin1Char('.'));
- m_logicalModuleVersionMajor = dotSplit[0];
- if (dotSplit.size() > 1)
- m_logicalModuleVersionMinor = dotSplit[1];
- else
- m_logicalModuleVersionMinor = "0";
- }
-}
-
-QT_END_NAMESPACE
diff --git a/src/qdoc/collectionnode.h b/src/qdoc/collectionnode.h
deleted file mode 100644
index 2d8dcf008..000000000
--- a/src/qdoc/collectionnode.h
+++ /dev/null
@@ -1,86 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the tools applications of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#ifndef COLLECTIONNODE_H
-#define COLLECTIONNODE_H
-
-#include "pagenode.h"
-
-#include <QtCore/qglobal.h>
-#include <QtCore/qstring.h>
-
-QT_BEGIN_NAMESPACE
-
-class CollectionNode : public PageNode
-{
-public:
- CollectionNode(NodeType type, Aggregate *parent, const QString &name)
- : PageNode(type, parent, name)
- {
- }
-
- [[nodiscard]] bool isCollectionNode() const override { return true; }
- [[nodiscard]] QString qtVariable() const override { return m_qtVariable; }
- void setQtVariable(const QString &v) override { m_qtVariable = v; }
- [[nodiscard]] QString qtCMakeComponent() const override { return m_qtCMakeComponent; }
- void setQtCMakeComponent(const QString &target) override { m_qtCMakeComponent = target; }
- void addMember(Node *node) override;
- [[nodiscard]] bool hasNamespaces() const override;
- [[nodiscard]] bool hasClasses() const override;
- void getMemberNamespaces(NodeMap &out) override;
- void getMemberClasses(NodeMap &out) const override;
- [[nodiscard]] bool wasSeen() const override { return m_seen; }
-
- [[nodiscard]] QString fullTitle() const override { return title(); }
- [[nodiscard]] QString logicalModuleName() const override { return m_logicalModuleName; }
- [[nodiscard]] QString logicalModuleVersion() const override;
- [[nodiscard]] QString logicalModuleIdentifier() const override
- {
- return m_logicalModuleName + m_logicalModuleVersionMajor;
- }
- void setLogicalModuleInfo(const QString &arg) override;
- void setLogicalModuleInfo(const QStringList &info) override;
-
- [[nodiscard]] const NodeList &members() const { return m_members; }
-
- void markSeen() { m_seen = true; }
- void markNotSeen() { m_seen = false; }
-
-private:
- bool m_seen { false };
- NodeList m_members {};
- QString m_logicalModuleName {};
- QString m_logicalModuleVersionMajor {};
- QString m_logicalModuleVersionMinor {};
- QString m_qtVariable {};
- QString m_qtCMakeComponent {};
-};
-
-QT_END_NAMESPACE
-
-#endif // COLLECTIONNODE_H
diff --git a/src/qdoc/cppcodemarker.h b/src/qdoc/cppcodemarker.h
deleted file mode 100644
index 4b8986608..000000000
--- a/src/qdoc/cppcodemarker.h
+++ /dev/null
@@ -1,62 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the tools applications of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#ifndef CPPCODEMARKER_H
-#define CPPCODEMARKER_H
-
-#include "codemarker.h"
-
-QT_BEGIN_NAMESPACE
-
-class CppCodeMarker : public CodeMarker
-{
-public:
- CppCodeMarker() = default;
- ~CppCodeMarker() override = default;
-
- bool recognizeCode(const QString &code) override;
- bool recognizeExtension(const QString &ext) override;
- bool recognizeLanguage(const QString &lang) override;
- [[nodiscard]] Atom::AtomType atomType() const override;
- QString markedUpCode(const QString &code, const Node *relative,
- const Location &location) override;
- QString markedUpSynopsis(const Node *node, const Node *relative, Section::Style style) override;
- QString markedUpQmlItem(const Node *node, bool summary) override;
- QString markedUpName(const Node *node) override;
- QString markedUpEnumValue(const QString &enumValue, const Node *relative) override;
- QString markedUpIncludes(const QStringList &includes) override;
- QString functionBeginRegExp(const QString &funcName) override;
- QString functionEndRegExp(const QString &funcName) override;
-
-private:
- QString addMarkUp(const QString &protectedCode, const Node *relative, const Location &location);
-};
-
-QT_END_NAMESPACE
-
-#endif
diff --git a/src/qdoc/cppcodeparser.h b/src/qdoc/cppcodeparser.h
deleted file mode 100644
index e14ce9b3d..000000000
--- a/src/qdoc/cppcodeparser.h
+++ /dev/null
@@ -1,100 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the tools applications of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#ifndef CPPCODEPARSER_H
-#define CPPCODEPARSER_H
-
-#include "codeparser.h"
-
-QT_BEGIN_NAMESPACE
-
-class ClassNode;
-class ExampleNode;
-class FunctionNode;
-class Aggregate;
-
-class CppCodeParser : public CodeParser
-{
- struct ExtraFuncData
- {
- Aggregate *root; // Used as the parent.
- Node::NodeType type; // The node type: Function, etc.
- bool isAttached; // If true, the method is attached.
- bool isMacro; // If true, we are parsing a macro signature.
- ExtraFuncData() : root(nullptr), type(Node::Function), isAttached(false), isMacro(false) {}
- };
-
-public:
- CppCodeParser();
-
- void initializeParser() override;
- void terminateParser() override;
- QString language() override { return QStringLiteral("Cpp"); }
- QStringList headerFileNameFilter() override;
- QStringList sourceFileNameFilter() override;
- FunctionNode *parseMacroArg(const Location &location, const QString &macroArg);
- FunctionNode *parseOtherFuncArg(const QString &topic, const Location &location,
- const QString &funcArg);
- static bool isJSMethodTopic(const QString &t);
- static bool isQMLMethodTopic(const QString &t);
- static bool isJSPropertyTopic(const QString &t);
- static bool isQMLPropertyTopic(const QString &t);
-
-protected:
- static const QSet<QString> &topicCommands();
- static const QSet<QString> &metaCommands();
- virtual Node *processTopicCommand(const Doc &doc, const QString &command,
- const ArgPair &arg);
- void processQmlProperties(const Doc &doc, NodeList &nodes, DocList &docs);
- bool splitQmlPropertyArg(const QString &arg, QString &type, QString &module, QString &element,
- QString &name, const Location &location);
- void processMetaCommand(const Doc &doc, const QString &command, const ArgPair &argLocPair,
- Node *node);
- void processMetaCommands(const Doc &doc, Node *node);
- void processMetaCommands(NodeList &nodes, DocList &docs);
- void processTopicArgs(const Doc &doc, const QString &topic, NodeList &nodes, DocList &docs);
- [[nodiscard]] bool hasTooManyTopics(const Doc &doc) const;
-
-private:
- void setExampleFileLists(ExampleNode *en);
-
-protected:
- typedef bool (Node::*NodeTypeTestFunc)() const;
- QMap<QString, NodeTypeTestFunc> m_nodeTypeTestFuncMap;
- QMap<QString, Node::NodeType> m_nodeTypeMap;
-
-private:
- static QSet<QString> m_excludeDirs;
- static QSet<QString> m_excludeFiles;
- QString m_exampleNameFilter;
- QString m_exampleImageFilter;
-};
-
-QT_END_NAMESPACE
-
-#endif
diff --git a/src/qdoc/doc/corefeatures.qdoc b/src/qdoc/doc/corefeatures.qdoc
deleted file mode 100644
index 1bb8e9fb1..000000000
--- a/src/qdoc/doc/corefeatures.qdoc
+++ /dev/null
@@ -1,35 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the documentation of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:FDL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Free Documentation License Usage
-** Alternatively, this file may be used under the terms of the GNU Free
-** Documentation License version 1.3 as published by the Free Software
-** Foundation and appearing in the file included in the packaging of
-** this file. Please review the following information to ensure
-** the GNU Free Documentation License version 1.3 requirements
-** will be met: https://www.gnu.org/licenses/fdl-1.3.html.
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-/*!
- \page corefeatures.html
- \title Core Features
-
- \input examples/signalandslots.qdocinc
- \input examples/objectmodel.qdocinc
- \input examples/layoutmanagement.qdocinc
-*/
diff --git a/src/qdoc/doc/examples/main.cpp b/src/qdoc/doc/examples/main.cpp
deleted file mode 100644
index ed56efce7..000000000
--- a/src/qdoc/doc/examples/main.cpp
+++ /dev/null
@@ -1,41 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the tools applications of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include <QApplication>
-#include <QPushButton>
-
-int main(int argc, char *argv[])
-{
- QApplication app(argc, argv);
-
- QPushButton hello("Hello world!");
- hello.resize(100, 30);
-
- hello.show();
- return app.exec();
-}
diff --git a/src/qdoc/doc/examples/minimum.qdocconf b/src/qdoc/doc/examples/minimum.qdocconf
deleted file mode 100644
index 8f53ffc11..000000000
--- a/src/qdoc/doc/examples/minimum.qdocconf
+++ /dev/null
@@ -1,36 +0,0 @@
-# QDoc is a tool that constantly evolves to suit our needs,
-# and there are some compatibility issues between old and new
-# practices. For that reason, any QDoc configuration file needs to
-# include compat.qdocconf.
-#include(compat.qdocconf)
-
-# QDoc needs lists of file extensions to know which files to process in
-# different situations. Uncomment the following include statement to get
-# a pre-defined list of file extensions.
-#include(fileextensions.qdocconf)
-
-# You can also specify file extensions manually.
-headers.fileextensions = "*.h *.hpp"
-sources.fileextensions = "*.cpp *.qml *.qdoc"
-
-# The outputdir variable specifies the directory
-# where QDoc will put the generated documentation.
-outputdir = html
-
-# The headerdirs variable specifies the directories
-# containing the header files associated
-# with the .cpp source files used in the documentation.
-headerdirs = .
-
-# The sourcedirs variable specifies the
-# directories containing the .cpp or .qdoc
-# files used in the documentation.
-sourcedirs = .
-
-# The exampledirs variable specifies the directories containing
-# the source code of the example files.
-exampledirs = .
-
-# The imagedirs variable specifies the
-# directories containing the images used in the documentation.
-imagedirs = ./images
diff --git a/src/qdoc/doc/images/happy.gif b/src/qdoc/doc/images/happy.gif
deleted file mode 100644
index a4597f6fa..000000000
--- a/src/qdoc/doc/images/happy.gif
+++ /dev/null
Binary files differ
diff --git a/src/qdoc/doc/images/qa-table.png b/src/qdoc/doc/images/qa-table.png
deleted file mode 100644
index 5818739fa..000000000
--- a/src/qdoc/doc/images/qa-table.png
+++ /dev/null
Binary files differ
diff --git a/src/qdoc/doc/images/qt-logo.png b/src/qdoc/doc/images/qt-logo.png
deleted file mode 100644
index b63f1384b..000000000
--- a/src/qdoc/doc/images/qt-logo.png
+++ /dev/null
Binary files differ
diff --git a/src/qdoc/doc/qdoc-manual-topiccmds.qdoc b/src/qdoc/doc/qdoc-manual-topiccmds.qdoc
deleted file mode 100644
index e0ab113a9..000000000
--- a/src/qdoc/doc/qdoc-manual-topiccmds.qdoc
+++ /dev/null
@@ -1,1627 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the documentation of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:FDL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Free Documentation License Usage
-** Alternatively, this file may be used under the terms of the GNU Free
-** Documentation License version 1.3 as published by the Free Software
-** Foundation and appearing in the file included in the packaging of
-** this file. Please review the following information to ensure
-** the GNU Free Documentation License version 1.3 requirements
-** will be met: https://www.gnu.org/licenses/fdl-1.3.html.
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-/*!
- \page 13-qdoc-commands-topics.html
- \previouspage Command Index
- \nextpage Context Commands
-
- \title Topic Commands
-
- A topic command tells QDoc which source code element is being
- documented. Some topic commands allow you to create documentation
- pages that aren't tied to any underlying source code element.
-
- When QDoc processes a QDoc comment, it tries to connect the
- comment to an element in the source code by first looking for a
- topic command that names the source code element. If there is no
- topic command, QDoc tries to connect the comment to the source
- code element that immediately follows the comment. If it can't do
- either of these and if there is no topic command that indicates
- the comment does not have an underlying source code element (e.g.
- \l{page-command} {\\page}), then the comment is discarded.
-
- \target topic argument
-
- The name of the entity being documented is usually the only
- argument for a topic command. Use the complete name. Sometimes
- there can be a second parameter in the argument. See e.g. \l
- {page-command} {\\page}.
-
- \code
- \enum QComboBox::InsertPolicy
- \endcode
-
- The \l {fn-command} {\\fn} command is a special case. For the \l
- {fn-command} {\\fn} command, use the function's signature
- including the class qualifier.
-
- \code
- \fn void QGraphicsWidget::setWindowFlags(Qt::WindowFlags wFlags)
- \endcode
-
- A topic command can appear anywhere in a comment but must stand
- alone on its own line. It is good practice is to let the topic command
- be the first line of the comment. If the argument spans several
- lines, make sure that each line (except the last one) is ended
- with a backslash. Moreover, QDoc counts parentheses, which means
- that if it encounters a '(' it considers everything until the
- closing ')' as its argument.
-
- If a topic command is repeated with different arguments, the
- same documentation will appear for both the units.
-
- \code
- / *!
- \fn void PreviewWindow::setWindowFlags()
- \fn void ControllerWindow::setWindowFlags()
-
- Sets the widgets flags using the QWidget::setWindowFlags()
- function.
-
- Then runs through the available window flags, creating a text
- that contains the names of the flags that matches the flags
- parameter, displaying the text in the widgets text editor.
- * /
- \endcode
-
- The \c PreviewWindow::setWindowFlags() and \c
- ControllerWindow::setWindowFlags() functions will get the same
- documentation.
-
- \target class-command
- \section1 \\class
-
- The \\class command is for documenting a C++ class. The argument
- is the complete name of the class. The command tells QDoc that a
- class is part of the public API, and lets you enter a detailed
- description.
-
- \code
- / *!
- \class QMap::iterator
-
- \brief The QMap::iterator class provides an STL-style
- non-const iterator for QMap and QMultiMap.
-
- QMap features both \l{STL-style iterators} and
- \l{Java-style iterators}. The STL-style iterators ...
- * /
- \endcode
-
- The HTML documentation for the named class is written to a
- \c{.html} file named from the class name, in lower case, and with
- the double colon qualifier(s) replaced with '-'. For example, the
- documentation for the \c QMap::Iterator class is written to \c
- qmap-iterator.html.
-
- \target framework
-
- The file contains the class description from the \\class comment,
- plus the documentation generated from QDoc comments for all the
- class members: a list of the class's types, properties,
- functions, signals, and slots.
-
- In addition to the detailed description of the class, the \\class
- comment typically contains a \l {brief-command} {\\brief} command
- and one or more \l{Markup Commands}. See the \\class command for
- any of the Qt class for examples. Here is a very simple example:
-
- \code
- / *!
- \class PreviewWindow
- \brief The PreviewWindow class is a custom widget.
- displaying the names of its currently set
- window flags in a read-only text editor.
-
- \ingroup miscellaneous
-
- The PreviewWindow class inherits QWidget. The widget
- displays the names of its window flags set with the \l
- {function} {setWindowFlags()} function. It is also
- provided with a QPushButton that closes the window.
-
- ...
-
- \sa QWidget
- * /
- \endcode
-
- The way QDoc renders this \\class will depend a lot on your \c
- {style.css} file, but the general outline of the class reference
- page will look like this:
-
- \quotation
- \raw HTML
- <h1>PreviewWindow Class Reference</h1>
- \endraw
-
- The PreviewWindow class is a custom widget displaying
- the names of its currently set window flags in a
- read-only text editor. \l {preview window} {More...}
-
- \raw HTML
- <h3>Properties</h3>
- \endraw
-
- \list
- \li 52 properties inherited from QWidget
- \li 1 property inherited from QObject
- \endlist
-
- \raw HTML
- <h3>Public Functions</h3>
- \endraw
-
- \list
- \li \l {constructor} {PreviewWindow}(QWidget *parent = 0)
- \li void \l {function} {setWindowFlags}(Qt::WindowFlags flags)
- \endlist
-
- \list
- \li 183 public functions inherited from QWidget
- \li 28 public functions inherited from QObject
- \endlist
-
- \raw HTML
- <h3>Public Slots</h3>
- \endraw
-
- \list
- \li 17 public slots inherited from QWidget
- \li 1 public slot inherited from QObject
- \endlist
-
- \raw HTML
- <h3>Additional Inherited Members</h3>
- \endraw
-
- \list
- \li 1 signal inherited from QWidget
- \li 1 signal inherited from QObject
- \li 4 static public members inherited from QWidget
- \li 4 static public members inherited from QObject
- \li 39 protected functions inherited from QWidget
- \li 7 protected functions inherited from QObject
- \endlist
-
- \target preview window
-
- \raw HTML
- <hr />
- <h2>Detailed Description</h2>
- \endraw
-
- The PreviewWindow class is a custom widget displaying
- the names of its currently set window flags in a
- read-only text editor.
-
- The PreviewWindow class inherits QWidget. The widget
- displays the names of its window flags set with the \l
- {function} {setWindowFlags()} function. It is also
- provided with a QPushButton that closes the window.
-
- ...
-
- See also QWidget.
-
- \raw HTML
- <hr />
- <h2>Member Function Documentation</h2>
- \endraw
-
- \target constructor
- \raw HTML
- <h3>PreviewWindow(QWidget *parent = 0)</h3>
- \endraw
-
- Constructs a preview window widget with \e parent.
-
- \target function
- \raw HTML
- <h3>setWindowFlags(Qt::WindowFlags flags)</h3>
- \endraw
-
- Sets the widgets flags using the
- QWidget::setWindowFlags() function.
-
- Then runs through the available window flags,
- creating a text that contains the names of the flags
- that matches the flags parameter, displaying
- the text in the widgets text editor.
- \endquotation
-
- \target enum-command
- \section1 \\enum
-
- The \\enum command is for documenting a C++ enum type. The
- argument is the full name of the enum type.
-
- The enum values are documented in the \\enum comment using the \l
- {value-command} {\\value} command. If an enum value is not
- documented with \\value, QDoc emits a warning. These warnings can
- be avoided using the \l {omitvalue-command} {\\omitvalue} command
- to tell QDoc that an enum value should not be documented. The enum
- documentation will be included on the class reference page, header
- file page, or namespace page where the enum type is defined. For
- example, consider the enum type \c {Corner} in the Qt namespace:
-
- \code
- enum Corner {
- TopLeftCorner = 0x00000,
- TopRightCorner = 0x00001,
- BottomLeftCorner = 0x00002,
- BottomRightCorner = 0x00003
- #if defined(QT3_SUPPORT) && !defined(Q_MOC_RUN)
- ,TopLeft = TopLeftCorner,
- TopRight = TopRightCorner,
- BottomLeft = BottomLeftCorner,
- BottomRight = BottomRightCorner
- #endif
- };
- \endcode
-
- This enum can be cocumented this way:
-
- \code
- / *!
- \enum Qt::Corner
-
- This enum type specifies a corner in a rectangle:
-
- \value TopLeftCorner
- The top-left corner of the rectangle.
- \value TopRightCorner
- The top-right corner of the rectangle.
- \value BottomLeftCorner
- The bottom-left corner of the rectangle.
- \value BottomRightCorner
- The bottom-right corner of the rectangle.
-
- \omitvalue TopLeft
- \omitvalue TopRight
- \omitvalue BottomLeft
- \omitvalue BottomRight
- Bottom-right (omitted; not documented).
- * /
- \endcode
-
- Note the inclusion of the namespace qualifier. QDoc will render
- this enum type in \c {qt.html} like this:
-
- \quotation
- \raw HTML
- <h3 class="fn"><a name="Corner-enum"></a>enum Qt::Corner</h3>
-
- <p>This enum type specifies a corner in a rectangle:</p>
-
- <table border="1" cellpadding="2" cellspacing="1" width="100%">
- <tr>
- <th width="25%">Constant</th>
- <th width="15%">Value</th>
- <th width="60%">Description</th>
- </tr>
-
- <tr>
- <td valign="top"><tt>Qt::TopLeftCorner</tt></td>
- <td align="center" valign="top"><tt>0x00000</tt></td>
- <td valign="top">The top-left corner of the rectangle.</td>
- </tr>
-
- <tr>
- <td valign="top"><tt>Qt::TopRightCorner</tt></td>
- <td align="center" valign="top"><tt>0x00001</tt></td>
- <td valign="top">The top-right corner of the rectangle.</td>
- </tr>
-
- <tr>
- <td valign="top"><tt>Qt::BottomLeftCorner</tt></td>
- <td align="center" valign="top"><tt>0x00002</tt></td>
- <td valign="top">The bottom-left corner of the rectangle.</td>
- </tr>
-
- <tr>
- <td valign="top"><tt>Qt::BottomRightCorner</tt></td>
- <td align="center" valign="top"><tt>0x00003</tt></td>
- <td valign="top">The bottom-right corner of the rectangle.</td>
- </tr>
-
- </table>
- \endraw
- \endquotation
-
- See also \l {value-command} {\\value} and \l {omitvalue-command} {\\omitvalue}.
-
- \target example-command
- \section1 \\example
-
- The \\example command is for documenting an example. The argument
- is the example's path relative to one of the paths listed in the
- \l {exampledirs-variable} {exampledirs} variable in the QDoc
- configuration file.
-
- The documentation page will be output to \c {modulename-path-to-example}.html.
- QDoc will add a list of all the example's source and images files at the end
- of the page, unless \l {noautolist-command}{\\noautolist} command is used or
- the configuration variable \l {url.examples-variable}{url.examples} is defined
- for the project.
-
- For example, if \l {exampledirs-variable} {exampledirs} contains
- \c $QTDIR/examples/widgets/imageviewer, then
-
- \code
- / *!
- \example widgets/imageviewer
- \title ImageViewer Example
- \subtitle
-
- The example shows how to combine QLabel and QScrollArea
- to display an image.
-
- ...
- * /
- \endcode
-
- QDoc renders this example in widgets-imageviewer.html:
-
- \quotation
- \raw HTML
- <center><h1>Image Viewer Example</h1></center>
- \endraw
-
- The example shows how to combine QLabel and QScrollArea
- to display an image.
-
- Files:
- \list
- \li \l{http://doc.qt.io/qt-5/qtwidgets-widgets-imageviewer-imageviewer-cpp.html}
- {widgets/imageviewer/imageviewer.cpp}
- \li \l{http://doc.qt.io/qt-5/qtwidgets-widgets-imageviewer-imageviewer-h.html}
- {widgets/imageviewer/imageviewer.h}
- \li \l{http://doc.qt.io/qt-5/qtwidgets-widgets-imageviewer-main-cpp.html}
- {widgets/imageviewer/main.cpp}
- \endlist
-
-
- ...
- \endquotation
-
- \b {See also:} \l {generatelist-command}{\\generatelist examplefiles},
- \l {noautolist-command}{\\noautolist},
- \l {url.examples-variable}{url.examples},
- \l {meta-command}{\\meta}
-
- \target externalpage-command
- \section1 \\externalpage
-
- The \\externalpage command assigns a title to an external URL.
-
- \code
- / *!
- \externalpage http://doc.qt.io/
- \title Qt Documentation Site
- * /
- \endcode
-
- This allows you to include a link to the external page in your
- documentation this way:
-
- \code
- / *!
- At the \l {Qt Documentation Site} you can find the latest
- documentation for Qt, Qt Creator, the Qt SDK and much more.
- * /
- \endcode
-
- QDoc renders this as:
-
- \quotation
- At the \l {http://doc.qt.io/}{Qt Documentation Site}
- you can find the latest documentation for Qt, Qt Creator, the Qt SDK
- and much more.
- \endquotation
-
- To achieve the same result without using the \\externalpage
- command, you would have to hard-code the address into your
- documentation:
-
- \code
- / *!
- At the \l {http://doc.qt.io/}{Qt Documentation Site}
- you can find the latest documentation for Qt, Qt Creator, the Qt SDK
- and much more.
- * /
- \endcode
-
- The \\externalpage command makes it easier to maintain the
- documentation. If the address changes, you only need to change the
- argument of the \\externalpage command.
-
- \target fn-command
- \section1 \\fn (function)
-
- The \\fn command is for documenting a function. The argument is
- the function's signature, including its template parameters (if
- any), return type, const-ness, and list of formal arguments with
- types. If the named function doesn't exist, QDoc emits a warning.
-
- Since QDoc version 6.0, the \\fn command can be used for documenting
- class members that are not explicitly declared in the header,
- but are implicitly generated by the compiler; default constructor
- and destructor, copy constructor and move-copy constructor,
- assignment operator, and move-assignment operator.
-
- \note The \\fn command is QDoc's default command: when no
- topic command can be found in a QDoc comment, QDoc tries to tie
- the documentation to the following code as if it is the
- documentation for a function. Hence, it is normally not necessary
- to include this command when documenting a function, if the
- function's QDoc comment is written immediately above the function
- implementation in the \c .cpp file. But it must be present when
- documenting an inline function in the \c .cpp file that is
- implemented in the \c .h file.
-
- \code
- / *!
- \fn bool QToolBar::isAreaAllowed(Qt::ToolBarArea area) const
-
- Returns \c true if this toolbar is dockable in the given
- \a area; otherwise returns \c false.
- * /
- \endcode
-
- QDoc renders this as:
-
- \quotation
- \raw HTML
- <h3>bool QToolBar::isAreaAllowed(Qt::ToolBarArea area) const
- </h3>
- \endraw
-
- Returns \c true if this toolbar is dockable in the given
- \a area; otherwise returns \c false.
- \endquotation
-
- \note Running in debug mode (pass the \c {-debug} command line option
- or set the \c QDOC_DEBUG environment variable before invoking QDoc)
- can help troubleshoot \\fn commands that QDoc fails to parse. In
- debug mode, additional diagnostic information is available.
-
- See also \l {overload-command} {\\overload}.
-
- \target group-command
- \section1 \\group
-
- The \\group command creates a separate page that lists the classes
- belonging to the group. The argument is the group name.
-
- A class is included in a group by using the \l {ingroup-command}
- {\\ingroup} command. Overview pages can also be related to a group
- using the same command, but the list of overview pages must be
- requested explicitly using the \l {generatelist-command}
- {\\generatelist} command (see example below).
-
- The \\group command is typically followed by a \l {title-command}
- {\\title} command and a short introduction to the group. The
- HTML page for the group is written to a \c {.html} file put in
- <lower-case>\e{group}.html.
-
- Each class name is listed as a link to the class reference page
- followed by the text from the class's \l {brief-command} {\\brief}
- texts.
-
- \code
- / *!
- \group io
-
- \title Input/Output and Networking
-
- These classes are used to handle input and output to
- and from external devices, processes, files etc., as
- well as manipulating files and directories.
- * /
- \endcode
-
- QDoc generates a group page in \c{io.html} that will look
- like this:
-
- \quotation
- \raw HTML
-
- <h1>Input/Output and Networking</h1>
-
- <p>These classes are used to handle input and output
- to and from external devices, processes, files etc., as
- well as manipulating files and directories.</p>
-
- <p>
- <table width="100%">
- <tr valign="top" bgcolor="#e0e0e0">
- <td><b>
- <a href="http://doc.qt.io/qt-5/qabstractsocket.html">QAbstractSocket</a>
- </b></td>
- <td>
- The base functionality common to all socket types
- </td></tr>
-
- <tr valign="top" bgcolor="#e0e0e0">
- <td><b>
- <a href="http://doc.qt.io/qt-5/qbuffer.html">QBuffer</a>
- </b></td>
- <td>
- QIODevice interface for a QByteArray
- </td></tr>
-
- <tr valign="top" bgcolor="#e0e0e0">
- <td><b>
- <a href="http://doc.qt.io/qt-5/qclipboard.html">QClipboard</a>
- </b></td>
- <td>
- Access to the window system clipboard
- </td></tr>
- </table>
- \endraw
- \endquotation
-
- Note that overview pages related to the group, must be listed
- explicitly using the \l {generatelist-command} {\\generatelist}
- command with the \c related argument.
-
- \code
- / *!
- \group architecture
-
- \title Architecture
-
- These documents describe aspects of Qt's architecture
- and design, including overviews of core Qt features and
- technologies.
-
- \generatelist{related}
- * /
- \endcode
-
- See also \l {ingroup-command} {\\ingroup} and \l
- {generatelist-command} {\\generatelist}.
-
- \target headerfile-command
- \section1 \\headerfile
-
- The \\headerfile command is for documenting the global functions,
- types and macros that are declared in a header file, but not in a
- namespace. The argument is the name of the header file. The HTML
- page is written to a \c {.html} file constructed from the header
- file argument.
-
- The documentation for a function, type, or macro that is declared
- in the header file being documented, is included in the header file
- page using the \l {relates-command} {\\relates} command.
-
- If the argument doesn't exist as a header file, the \\headerfile
- command creates a documentation page for the header file anyway.
-
- \code
- / *!
- \headerfile <QtAlgorithms>
-
- \title Generic Algorithms
-
- \brief The <QtAlgorithms> header file provides
- generic template-based algorithms.
-
- Qt provides a number of global template functions in \c
- <QtAlgorithms> that work on containers and perform
- well-know algorithms.
- * /
- \endcode
-
- QDoc generates a header file page \c{qtalgorithms.html} that looks
- like this:
-
- \quotation
- \raw HTML
- <center><h1>&lt;QtAlgorithms&gt; -
- Generic Algorithms</h1></center>
- <p>The <QtAlgorithms> header file provides generic
- template-based algorithms.
- <a href="13-qdoc-commands-topics.html#header-command">More...</a>
- </p>
-
- <h3>Functions</h3>
- <ul>
- <li>RandomAccessIterator
- <a href="http://doc.qt.io/qt-5/qtalgorithms-obsolete.html#qBinaryFind">qBinaryFind</a></b>
- (RandomAccessIterator begin, RandomAccessIterator end,
- const T & value)</li>
- <li>...</li></ul>
- <hr />
- \endraw
-
- \target header
-
- \raw HTML
- <h2>Detailed Description</h2>
- <p>The <QtAlgorithms> header file provides generic
- template-based algorithms. </p>
- \endraw
-
- Qt provides a number of global template functions in \c
- <QtAlgorithms> that work on containers and perform
- well-know algorithms.
-
- ...
- \endquotation
-
- See also \l {inheaderfile-command}{\\inheaderfile}.
-
- \target macro-command
- \section1 \\macro
-
- The \\macro command is for documenting a C++ macro. The argument
- is the macro in one of three styles: function-like macros like
- Q_ASSERT(), declaration-style macros like Q_PROPERTY(), and macros
- without parentheses like Q_OBJECT.
-
- The \\macro comment must contain a \l {relates-command}
- {\\relates} command that attaches the macro comment to a class,
- header file, or namespace. Otherwise, the documentation will be
- lost. Here are three example macro comments followed by what they
- might look like in \c {qtglobal.html} or \c {qobject.html}:
-
- \code
- / *!
- \macro void Q_ASSERT(bool test)
- \relates <QtGlobal>
-
- Prints a warning message containing the source code
- file name and line number if \a test is false.
-
- ...
-
- \sa Q_ASSERT_X(), qFatal(), {Debugging Techniques}
- * /
- \endcode
-
- \quotation
- \raw HTML
- <h3>void Q_ASSERT ( bool <i>test</i> )</h3>
- \endraw
-
- Prints a warning message containing the source code
- file name and line number if \a test is false.
-
- ...
-
- See also Q_ASSERT_X(), qFatal() and \l {Debugging Techniques}.
-
- \endquotation
-
- \code
- / *!
- \macro Q_PROPERTY(...)
- \relates QObject
-
- This macro declares a QObject property. The syntax is:
-
- ...
-
- \sa {Qt's Property System}
- * /
- \endcode
-
- \quotation
- \raw HTML
- <h3>Q_PROPERTY ( ... )</h3>
- \endraw
-
- This macro declares a QObject property. The syntax is:
-
- ...
-
- See also \l {Qt's Property System}.
- \endquotation
-
- \code
- / *!
- \macro Q_OBJECT
- \relates QObject
-
- The Q_OBJECT macro must appear in the private section
- of a class definition that declares its own signals and
- slots, or that uses other services provided by Qt's
- meta-object system.
-
- ...
-
- \sa {Meta-Object System}, {Signals and Slots}, {Qt's
- Property System}
- * /
- \endcode
-
- \quotation
- \raw HTML
- <h3>Q_OBJECT</h3>
- \endraw
-
- The Q_OBJECT macro must appear in the private section
- of a class definition that declares its own signals and
- slots or that uses other services provided by Qt's
- meta-object system.
-
- ...
-
- See also \l {Meta-Object System}, \l {Signals &
- Slots} and \l {Qt's Property System}.
- \endquotation
-
- \target module-command
- \section1 \\module
-
- The \\module creates a page that lists the classes belonging to
- the module specified by the command's argument. A class included
- in the module by including the \l {inmodule-command} {\\inmodule}
- command in the \\class comment.
-
- The \\module command is typically followed by a \l {title-command}
- {\\title} and a \l {brief-command} {\\brief} command. Each class
- is listed as a link to the class reference page followed by the
- text from the class's \l {brief-command} {\\brief} command. For
- example:
-
- \code
- / *!
- \module QtNetwork
-
- \title Qt Network Module
-
- \brief Contains classes for writing TCP/IP clients and servers.
-
- The network module provides classes to make network
- programming easier and portable. It offers both
- high-level classes such as QNetworkAccessManager that
- implements application-level protocols, and
- lower-level classes such as QTcpSocket, QTcpServer, and
- QUdpSocket.
- * /
- \endcode
-
- QDoc renders this in \c {qtnetwork.html} like this:
-
- \quotation
- \raw HTML
- <h1><center>Qt Network Module</center></h1>
- \endraw
-
- The Qt Network module offers classes that allow you to
- write TCP/IP clients and servers.\l {module
- details} {More...}
-
- \raw HTML
- <p>
- <table width="100%">
- <tr valign="top" bgcolor="#d0d0d0">
- <td><b>
- <a href="https://doc.qt.io/qt-5/qabstractsocket.html">QAbstractSocket</a>
- </b></td>
- <td>
- The base functionality common to all socket types
- </td></tr>
-
- <tr valign="top" bgcolor="#d0d0d0">
- <td>...</td>
- <td>...</td>
- </tr>
- </table>
-
- <p><hr /></p>
- \endraw
-
- \target module details
-
- \raw HTML
- <h2>Detailed Description</h2>
-
- <p>
- The Qt Network module offers classes that allow you to
- write TCP/IP clients and servers.
- </p>
-
- <p>
- The network module provides classes to make network
- programming easier and portable. It offers both
- high-level classes such as QNetworkAccessManager that
- implements application-level protocols, and
- lower-level classes such as QTcpSocket, QTcpServer, and
- QUdpSocket.
- </p>
- \endraw
-
- ...
-
- \endquotation
-
- The \l {noautolist-command} {\\noautolist} command can be used here
- to omit the automatically generated list of classes at the end.
-
- See also \l {inmodule-command} {\\inmodule}
-
- \target namespace-command
- \section1 \\namespace
-
- The \\namespace command is for documenting the contents of the C++
- namespace named as its argument. The reference page QDoc generates
- for a namespace is similar to the reference page it generates for a
- C++ class.
-
- \code
- / *!
- \namespace Qt
-
- \brief Contains miscellaneous identifiers used throughout the Qt library.
- * /
- \endcode
-
- QDoc renders this in \c{qt.html} like this:
-
- \quotation
- \raw HTML
- <center><h1>Qt Namespace</h1></center>
- <p>The Qt namespace contains miscellaneous
- identifiers used throughout the Qt library.
- <a href="13-qdoc-commands-topics.html#name">More...</a>
- </p>
-
- <pre>#include &lt;Qt&gt;</pre>
- <ul>
- <li>
- <a href="https://doc.qt.io/qt-5/qt-obsolete.html">
- Obsolete members</a></li>
- </ul>
-
-
- <h3>Types</h3>
- <ul>
- <li>flags
- <a href="https://doc.qt.io/qt-5/qt.html#AlignmentFlag-enum">Alignment</a></b></li>
- <li>...</li></ul>
- <hr />
- \endraw
-
- \target name
-
- \raw HTML
- <h2>Detailed Description</h2>
- <p>Contains miscellaneous identifiers
- used throughout the Qt library.</p>
- \endraw
-
- ...
- \endquotation
-
- Note that in C++, a particular namespace can be used in more
- than one module, but when C++ elements from different modules
- are declared in the same namespace, the namespace itself must
- be documented in one module only. For example, namespace Qt in
- the example above contains types and functions from both QtCore
- and QtGui, but it is documented with the \\namespace command
- only in QtCore.
-
- \target page-command
- \section1 \\page
-
- The \\page command is for creating a stand-alone documentation
- page. The argument can consist of two parts separated by a
- space. The first part is the name of the file where QDoc should
- store the page. The second part, if present, is a word that
- specifies the page type. Currently, the second part can be one of
- the following list of words:
-
- \list
-
- \li api - This is the type of page used for C++ class references and
- QML type references. You should never use this one for the pages
- you write, because this one is reserved for QDoc.
-
- \li attribution - A page describing (code) attributions.
-
- \li example - A page that describes a working example.
-
- \li faq - A frequently asked question.
-
- \li howto - A user guide on how to use some components of the
- software.
-
- \li overview - For text pages that provide an overview of some
- important subject.
-
- \li tutorial - For text pages that are part of a tutorial.
-
- \endlist
-
- The page title is set using the \l {title-command} {\\title}
- command.
-
- \code
- / *!
- \page aboutqt.html
-
- \title About Qt
-
- Qt is a C++ toolkit for cross-platform GUI
- application development. Qt provides single-source
- portability across Microsoft Windows, macOS, Linux,
- and all major commercial Unix variants.
-
- Qt provides application developers with all the
- functionality needed to build applications with
- state-of-the-art graphical user interfaces. Qt is fully
- object-oriented, easily extensible, and allows true
- component programming.
-
- ...
- * /
- \endcode
-
- QDoc renders this page in \c {aboutqt.html}.
-
- \target property-command
- \section1 \\property
-
- The \\property command is for documenting a Qt property. The
- argument is the full property name.
-
- A property is defined using the Q_PROPERTY() macro. The macro
- takes as arguments the property's name and its set, reset and get
- functions.
-
- \code
- Q_PROPERTY(QString state READ state WRITE setState)
- \endcode
-
- The set, reset and get functions don't need to be documented,
- documenting the property is sufficient. QDoc will generate a list
- of the access function that will appear in the property
- documentation which in turn will be located in the documentation
- of the class that defines the property.
-
- The \\property command comment typically includes a \l
- {brief-command} {\\brief} command. For properties the \l
- {brief-command} {\\brief} command's argument is a sentence
- fragment that will be included in a one line description of the
- property. The command follows the same rules for the \l
- {brief-property} {description} as the \l {variable-command}
- {\\variable} command.
-
- \code
- / *!
- \property QPushButton::flat
- \brief Whether the border is disabled.
-
- This property's default is false.
- * /
- \endcode
-
- QDoc includes this in \c {qpushbutton.html} like this:
-
- \quotation
- \raw HTML
- <h3>flat : bool</h3>
- \endraw
-
- This property holds whether the border is disabled.
-
- This property's default is false.
-
- Access functions:
-
- \list
- \li \b { bool isFlat () const}
- \li \b { void setFlat ( bool )}
- \endlist
-
- \endquotation
-
- \code
- / *!
- \property QWidget::width
- \brief The width of the widget excluding any window frame.
-
- See the \l {Window Geometry} documentation for an
- overview of window geometry.
-
- \sa geometry, height, size
- * /
- \endcode
-
- QDoc includes this in \c {qwidget.html} like this:
-
- \quotation
- \raw HTML
- <h3>width : const int</h3>
- \endraw
-
- This property holds the width of the widget excluding
- any window frame.
-
- See the \l {Window Geometry} documentation for an
- overview of window geometry.
-
- Access functions:
-
- \list
- \li \b { int width () const}
- \endlist
-
- See also \l{QWidget::geometry} {geometry},
- \l{QWidget::height} {height}, and \l{QWidget::size} {size}.
- \endquotation
-
- \target qmlattachedproperty-command
- \section1 \\qmlattachedproperty
-
- The \\qmlattachedproperty command is for documenting a QML
- property that will be attached to some QML type. See
- \l{http://doc.qt.io/qt-5/qtqml-syntax-objectattributes.html#attached-properties-and-attached-signal-handlers}
- {Attached Properties}. The argument is the rest of the line. The
- argument text should be the property type, followed by the QML
- element name where the property is being declared, the \c{::}
- qualifier, and finally the property name. If we have a QML
- attached property named \c isCurrentItem in QML \c ListView,
- and the property has type \c {bool}, the \\qmlattachedproperty for
- it would look like this:
-
- \code
- / *!
- \qmlattachedproperty bool ListView::isCurrentItem
- This attached property is \c true if this delegate is the current
- item; otherwise false.
-
- It is attached to each instance of the delegate.
-
- This property may be used to adjust the appearance of the current
- item, for example:
-
- \snippet doc/src/snippets/declarative/listview/listview.qml isCurrentItem
- * /
- \endcode
-
- QDoc includes this attached property on the QML reference page for the
- \l{http://doc.qt.io/qt-5/qml-qtquick-listview.html#isCurrentItem-attached-prop}
- {ListView} element.
-
- \target qmlattachedsignal-command
- \section1 \\qmlattachedsignal
-
- The \\qmlattachedsignal command is for documenting an attachable
- \l{Signal and Handler Event System}{signal}. The \\qmlattachedsignal
- command is used just like the \l{qmlsignal-command} {\\qmlsignal} command.
-
- The argument is the rest of the line. It should be the name of the
- QML type where the signal is declared, the \c{::}
- qualifier, and finally the signal name. For example, a QML
- attached signal named \c add() in the \c GridView
- element is documented like this:
-
- \code
- / *!
- \qmlattachedsignal GridView::add()
- This attached signal is emitted immediately after an item is added to the view.
- * /
- \endcode
-
- QDoc includes this documentation on the QML reference page for the
- \l GridView element.
-
- \target qmlbasictype-command
- \section1 \\qmlbasictype
-
- The \\qmlbasictype command is for documenting a basic type for QML.
- The argument is the type name. The type must be included in the
- QML basic types group using the \l{ingroup-command}{\\ingroup}
- command as shown below. This will cause QDoc to include the
- documentation for the type on the
- \l{http://doc.qt.io/qt-5/qtqml-typesystem-basictypes.html}
- {QML Basic Types} page. The \l{brief-command} {\\brief} command
- is also required, because it appears on the
- \l{http://doc.qt.io/qt-5/qtqml-typesystem-basictypes.html}
- {QML Basic Types} page as well.
-
- \code
- / *!
- \qmlbasictype int
- \ingroup qmlbasictypes
-
- \brief An integer is a whole number, for example 0, 10, or -20.
-
- An integer is a whole number, e.g. 0, 10, or -20. The possible
- \c int values range from around -2000000000 to around
- 2000000000, although most elements will only accept a reduced
- range (which they mention in their documentation).
-
- Example:
- \qml
- Item { width: 100; height: 200 }
- \endqml
-
- \sa {QML Basic Types}
- * /
- \endcode
-
- QDoc outputs this as \l{http://doc.qt.io/qt-5/qml-int.html}
- {qml-int.html}.
-
- \target qmlclass-command
- \section1 \\qmlclass
-
- This command is deprecated. Use \l{qmltype-command} {\\qmltype}
- instead.
-
- The \\qmlclass command is for documenting a QML type that is
- instantiated by a C++ class. The command has two arguments. The
- first argument is the name of the QML type. The second argument
- is the name of the C++ class that instantiates the QML type.
-
- \code
- / *!
- \qmlclass Transform QGraphicsTransform
- \ingroup qml-transform-elements
- \since 4.7
- \brief Provides a way of building advanced transformations on Items.
-
- The Transform element is a base type which cannot be
- instantiated directly. The following concrete Transform types
- are available:
-
- \list
- \li \l Rotation
- \li \l Scale
- \li \l Translate
- \endlist
-
- The Transform elements let you create and control advanced
- transformations that can be configured independently using
- specialized properties.
-
- You can assign any number of Transform elements to an \l
- Item. Each Transform is applied in order, one at a time.
-
- * /
- \endcode
-
- This example generates the
- \l {https://doc.qt.io/qt-5/qml-qtquick-transform.html} {QML Transform}
- page. The \\qmlclass comment should include the \l
- {since-command} {\\since} command, because all QML types are
- new. It should also include the \l{brief-command} {\\brief}
- command. If a type is a member of a group of QML
- types, it should also include one or more \l{ingroup-command}
- {\\ingroup} commands.
-
- \target qmlmethod-command
- \section1 \\qmlmethod
-
- The \\qmlmethod command is for documenting a QML method. The
- argument is the complete method signature, including return
- type and parameter names and types.
-
- \code
- / *!
- \qmlmethod void TextInput::select(int start, int end)
-
- Causes the text from \a start to \a end to be selected.
-
- If either start or end is out of range, the selection is not changed.
-
- After having called this, selectionStart will become the lesser, and
- selectionEnd the greater (regardless of the order passed to this method).
-
- \sa selectionStart, selectionEnd
- * /
- \endcode
-
- QDoc includes this documentation on the element reference page for the
- \l{http://doc.qt.io/qt-5/qml-qtquick-textinput.html#select-method}
- {TextInput} element.
-
- \target qmltype-command
- \section1 \\qmltype
-
- The \\qmltype command is for documenting a QML type. The command
- has one argument, which is the name of the QML type.
-
- If the QML type is instantiated by a C++ class, that class must be
- specified using the \l{instantiates-command} {\\instantiates}
- context command.
-
- \code
- / *!
- \qmltype Transform
- \instantiates QGraphicsTransform
- \inqmlmodule QtQuick
-
- \brief Provides a way to build advanced transformations on Items.
-
- The Transform element is a base type which cannot be
- instantiated directly.
- * /
- \endcode
-
- Here, the \e{\\qmltype} comment includes \l{instantiates-command}
- {\\instantiates} to specify that a Transform is instantiated by
- the C++ class QGraphicsTransform. A \\qmltype comment should
- always include a \l {since-command} {\\since} command, because all
- QML types are new. It should also include a \l{brief-command}
- {\\brief} description. If a QML type is a member of a QML type group,
- the \\qmltype comment should include one or more \l{ingroup-command}
- {\\ingroup} commands.
-
-
- \target qmlproperty-command
- \section1 \\qmlproperty
-
- The \\qmlproperty command is for documenting a QML property. The
- argument is the rest of the line. The argument text should be the
- property type, followed by the QML type name, the \c{::}
- qualifier, and finally the property name. If we have a QML
- property named \c x in QML type \c Translate, and the property
- has type \c {real}, the \\qmlproperty for it would look like this:
-
- \code
- / *!
- \qmlproperty real Translate::x
-
- The translation along the X axis.
- * /
- \endcode
-
- QDoc includes this QML property on the QML reference page for the
- \l {http://doc.qt.io/qt-5/qml-qtquick-translate.html} {Translate}
- element.
-
- If the QML property is of enumeration type, or it holds a bit-wise
- combination of flags, the \l{value-command}{\\value} command can
- be used to document the acceptable values.
-
- \target qmlsignal-command
- \section1 \\qmlsignal
-
- The \\qmlsignal command is for documenting a QML signal.
- The argument is the rest of the line. The arguments should be: the QML type
- where the signal is declared, the \c{::} qualifier, and finally the signal
- name. If we have a QML signal named \c clicked(), the documentation for it
- would look like this:
-
- \code
- / *!
- \qmlsignal QtQuick::MouseArea::clicked(MouseEvent mouse)
- This signal is emitted when there is a click. A click is defined as a
- press followed by a release, both inside the MouseArea.
- * /
- \endcode
-
- QDoc includes this documentation on the QML reference page for the
- \l{http://doc.qt.io/qt-5/qml-qtquick-mousearea.html#clicked-signal}
- {MouseArea} element.
-
- \target qmlmodule-command
- \section1 \\qmlmodule
-
- Use the \c{\\qmlmodule} command to create a \c QML module page. A QML
- module page is a collection of QML types or any related material. The
- command takes an optional \c <VERSION> number argument, and is similar
- to the \l{group-command}.
-
- A QML class can be associated with a module by adding the
- \l{inqmlmodule-command}{\\inqmlmodule} command to the comment-block that
- documents the class. You can link to any member of a QML module using the
- module name and two colons (\c{::}) prefix.
-
- \code
- \beginqdoc
- A link to the TabWidget of the UI Component is \l {UIComponent::TabWidget}.
- \endqdoc
- \endcode
-
- QDoc generates a page for the module that lists all the members of the
- module.
-
- \code
- \qmlmodule ClickableComponents
-
- This is a list of the Clickable Components set. A Clickable component
- responds to a \c clicked() event.
- \endcode
-
- \target inqmlmodule-command
- \section1 \\inqmlmodule
-
- A QML class may belong to a \l{qmlmodule-command}{QML module} by inserting
- the \l{inqmlmodule-command}{\\inqmlmodule} command as a topic command, with
- the module name (without a version number) as the only argument. Every
- member of a group must be linked to using the module name and two colons
- (\c{::}).
-
- \code
- \qmltype ClickableButton
- \inqmlmodule ClickableComponents
-
- A clickable button that responds to the \c click() event.
- \endcode
-
- To link to the \c ClickableButton, use the
- \c{\l ClickableComponents::ClickableButton} format.
-
- The \l {noautolist-command} {\\noautolist} command can be used here
- to omit the automatically generated list of types at the end.
-
- \target instantiates-command
- \section1 \\instantiates
-
- The \\instantiates command is used in the \l{qmltype-command} {QML
- type} comment of an elemental QML type to specify the name of the
- C++ class that instantiates the QML type.
-
- If the QML type is not instantiated by a C++ class, this command
- is not used.
-
- \code
- / *!
- \qmltype Transform
- \instantiates QGraphicsTransform
- \inqmlmodule QtQuick
-
- \brief Provides a way to build advanced transformations on Items.
-
- The Transform element is a base type which cannot be
- instantiated directly.
- * /
- \endcode
-
- Here, the \e{\\qmltype} comment includes \l{instantiates-command}
- {\\instantiates} to specify that a Transform is instantiated by
- the C++ class QGraphicsTransform.
-
- \target typealias-command
- \section1 \\typealias
-
- The \\typealias command is similar to \l {typedef-command}{\\typedef},
- but specific to documenting a C++ type alias:
-
- \code
- class Foo
- {
- public:
- using ptr = void*;
- // ...
- }
- \endcode
-
- This can be documented as
-
- \badcode *
- /\1!
- \typealias Foo::ptr
- \1/
- \endcode
-
- QDoc will automatically generate a sentence in the documentation describing
- the alias:
-
- \quotation
- This is a type alias for \c {void*}.
- \endquotation
-
- The \\typealias command was introduced in QDoc 5.15.
-
- See also \l {typedef-command}{\\typedef}.
-
- \target typedef-command
- \section1 \\typedef
-
- The \\typedef command is for documenting a C++ typedef. The
- argument is the name of the typedef. The documentation for
- the typedef will be included in the reference documentation
- for the class, namespace, or header file in which the typedef
- is declared. To relate the \\typedef to a class, namespace, or
- header file, the \\typedef comment must contain a
- \l {relates-command} {\\relates} command.
-
- \code
- / *!
- \typedef QObjectList
- \relates QObject
-
- Synonym for QList<QObject>.
- * /
- \endcode
-
- QDoc includes this in \c {qobject.html} as:
-
- \quotation
- \raw HTML
- <h3>typedef QObjectList</h3>
- \endraw
-
- Synonym for QList<QObject>.
- \endquotation
-
- Another, although more rare, example:
-
- \code
- / *!
- \typedef QMsgHandler
- \relates QtGlobal
-
- This is a typedef for a pointer to a function with the
- following signature:
-
- \code
- void myMsgHandler(QtMsgType, const char *);
- \ endcode
-
- \sa QtMsgType, qInstallMessageHandler()
- * /
- \endcode
-
- QDoc includes this in \c {qtglobal.html} as:
-
- \quotation
- \raw HTML
- <h3>typedef QtMsgHandler</h3>
- \endraw
-
- This is a typedef for a pointer to a function with the
- following signature:
-
- \raw HTML
- <tt>
- <pre> void myMsgHandler(QtMsgType, const char *);</pre>
- </tt>
- \endraw
-
- See also QtMsgType and qInstallMessageHandler().
- \endquotation
-
- Other typedefs are located on the reference page for the class
- that defines them.
-
- \code
- / *!
- \typedef QList::Iterator
-
- Qt-style synonym for QList::iterator.
- * /
- \endcode
-
- QDoc includes this one on the reference page for class QList as:
-
- \quotation
- \raw HTML
- <h3>typedef QList::Iterator</h3>
- \endraw
-
- Qt-style synonym for QList::iterator.
- \endquotation
-
- See also \l {typealias-command}{\\typealias}.
-
- \target variable-command
- \section1 \\variable
-
- The \\variable command is for documenting a class member variable
- or a constant. The argument is the variable or constant name. The
- \\variable command comment includes a \l {brief-command} {\\brief}
- command. QDoc generates the documentation based on the text from
- \\brief command.
-
- The documentation will be located in the in the associated class,
- header file, or namespace documentation.
-
- In case of a member variable:
-
- \code
- / *!
- \variable QStyleOption::palette
- \brief The palette that should be used when painting
- the control
- * /
- \endcode
-
- QDoc includes this in qstyleoption.html as:
-
- \quotation
- \raw HTML
- <h3>
- <a href="http://doc.qt.io/qt-5/qpalette.html">
- QPalette
- </a>
- QStyleOption::palette
- </h3>
- \endraw
-
- This variable holds the palette that should be used
- when painting the control.
- \endquotation
-
- You can also document constants with the \\variable command. For
- example, suppose you have the \c Type and \c UserType constants in
- the QTreeWidgetItem class:
-
- \code
- enum { Type = 0, UserType = 1000 };
- \endcode
-
- For these, the \\variable command can be used this way:
-
- \code
- / *!
- \variable QTreeWidgetItem::Type
-
- The default type for tree widget items.
-
- \sa UserType, type()
- * /
- \endcode
- \code
- / *!
- \variable QTreeWidgetItem::UserType
-
- The minimum value for custom types. Values below
- UserType are reserved by Qt.
-
- \sa Type, type()
- * /
- \endcode
-
- QDoc includes these in qtreewidget.html as:
-
- \quotation
- \raw HTML
- <h3>
- const int QTreeWidgetItem::Type
- </h3>
- \endraw
-
- The default type for tree widget items.
-
- See also \l {QTreeWidgetItem::UserType} {UserType} and \l
- {QTreeWidgetItem::type()} {type()}.
-
- \raw HTML
- <h3>
- const int QTreeWidgetItem::UserType
- </h3>
- \endraw
-
- The minimum value for custom types. Values below
- UserType are reserved by Qt.
-
- See also \l {QTreeWidgetItem::Type} {Type} and
- \l{QTreeWidgetItem::type()} {type()}.
-
- \endquotation
-*/
diff --git a/src/qdoc/doc/qdoc-minimum-qdocconf.qdoc b/src/qdoc/doc/qdoc-minimum-qdocconf.qdoc
deleted file mode 100644
index 0955a414d..000000000
--- a/src/qdoc/doc/qdoc-minimum-qdocconf.qdoc
+++ /dev/null
@@ -1,89 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the documentation of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:FDL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Free Documentation License Usage
-** Alternatively, this file may be used under the terms of the GNU Free
-** Documentation License version 1.3 as published by the Free Software
-** Foundation and appearing in the file included in the packaging of
-** this file. Please review the following information to ensure
-** the GNU Free Documentation License version 1.3 requirements
-** will be met: https://www.gnu.org/licenses/fdl-1.3.html.
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-/*!
-\page qdoc-minimum-qdocconf.html
-\keyword minimal-qdocconf
-\title A Minimal qdocconf File
-
-\brief Describes a minimal .qdocconf file
-
-Below you will find the full contents of qtgui.qdocconf. The subsequent section
-will discuss every statement in the qdocconf file.
-
-Each line from the qdocconf file is first quoted. Below each statement you will
-find the meaning.
-
-\badcode
- include(compat.qdocconf)
- outputdir = html
- headerdirs = .
- sourcedirs = .
- exampledirs = .
- imagedirs = ./images
-\endcode
-
-\b Notes:
-
-\badcode
- include(compat.qdocconf)
-\endcode
-
-For compatibility with older versions of QDoc, it is recommended
-to include compat.qdocconf.
-
-\code
- outputdir = html
-\endcode
-
-QDoc will put the documentation generated in the html directory.
-
-\badcode
- headerdirs = .
-\endcode
-
-The header file associated with the \e .cpp source files can be found in the
-current directory.
-
-\badcode
- sourcedirs = .
-\endcode
-
-The current directory is the directory containing the source files: the \e .cpp
-and \e .qdoc files used in the documentation.
-
-\badcode
- exampledirs = .
-\endcode
-
-The source code of the example files can be found in the current directory.
-
-\badcode
- imagedirs = ./images
-\endcode
-
-The image files can be found in the underlying directory \c images.
-*/
diff --git a/src/qdoc/docprivate.cpp b/src/qdoc/docprivate.cpp
deleted file mode 100644
index d72cc247b..000000000
--- a/src/qdoc/docprivate.cpp
+++ /dev/null
@@ -1,55 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the tools applications of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-#include "docprivate.h"
-
-#include "text.h"
-
-#include <QtCore/qhash.h>
-
-QT_BEGIN_NAMESPACE
-
-/*!
- Deletes the DocPrivateExtra.
- */
-DocPrivate::~DocPrivate()
-{
- delete extra;
-}
-
-void DocPrivate::addAlso(const Text &also)
-{
- m_alsoList.append(also);
-}
-
-void DocPrivate::constructExtra()
-{
- if (extra == nullptr)
- extra = new DocPrivateExtra;
-}
-
-QT_END_NAMESPACE
diff --git a/src/qdoc/docutilities.h b/src/qdoc/docutilities.h
deleted file mode 100644
index 28ba0e609..000000000
--- a/src/qdoc/docutilities.h
+++ /dev/null
@@ -1,55 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2020 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the tools applications of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-#ifndef DOCUTILITIES_H
-#define DOCUTILITIES_H
-
-#include "macro.h"
-#include "singleton.h"
-
-#include <QtCore/qglobal.h>
-#include <QtCore/qhash.h>
-#include <QtCore/qstring.h>
-#include <QtCore/qmap.h>
-
-QT_BEGIN_NAMESPACE
-
-typedef QMap<QString, QString> QStringMap;
-typedef QHash<QString, int> QHash_QString_int;
-typedef QHash<QString, Macro> QHash_QString_Macro;
-
-struct DocUtilities : public Singleton<DocUtilities>
-{
-public:
- QStringMap aliasMap;
- QHash_QString_int cmdHash;
- QHash_QString_Macro macroHash;
-};
-
-QT_END_NAMESPACE
-
-#endif // DOCUTILITIES_H
diff --git a/src/qdoc/editdistance.cpp b/src/qdoc/editdistance.cpp
deleted file mode 100644
index 64b18604b..000000000
--- a/src/qdoc/editdistance.cpp
+++ /dev/null
@@ -1,93 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the tools applications of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include "editdistance.h"
-
-QT_BEGIN_NAMESPACE
-
-int editDistance(const QString &s, const QString &t)
-{
-#define D(i, j) d[(i)*n + (j)]
- int i;
- int j;
- qsizetype m = s.length() + 1;
- qsizetype n = t.length() + 1;
- int *d = new int[m * n];
- int result;
-
- for (i = 0; i < m; ++i)
- D(i, 0) = i;
- for (j = 0; j < n; ++j)
- D(0, j) = j;
- for (i = 1; i < m; ++i) {
- for (j = 1; j < n; ++j) {
- if (s[i - 1] == t[j - 1]) {
- D(i, j) = D(i - 1, j - 1);
- } else {
- int x = D(i - 1, j);
- int y = D(i - 1, j - 1);
- int z = D(i, j - 1);
- D(i, j) = 1 + qMin(qMin(x, y), z);
- }
- }
- }
- result = D(m - 1, n - 1);
- delete[] d;
- return result;
-#undef D
-}
-
-QString nearestName(const QString &actual, const QSet<QString> &candidates)
-{
- if (actual.isEmpty())
- return QString();
-
- int deltaBest = 10000;
- int numBest = 0;
- QString best;
-
- for (const auto &candidate : candidates) {
- if (candidate[0] == actual[0]) {
- int delta = editDistance(actual, candidate);
- if (delta < deltaBest) {
- deltaBest = delta;
- numBest = 1;
- best = candidate;
- } else if (delta == deltaBest) {
- ++numBest;
- }
- }
- }
-
- if (numBest == 1 && deltaBest <= 2 && actual.length() + best.length() >= 5)
- return best;
-
- return QString();
-}
-
-QT_END_NAMESPACE
diff --git a/src/qdoc/editdistance.h b/src/qdoc/editdistance.h
deleted file mode 100644
index 9727b6ddd..000000000
--- a/src/qdoc/editdistance.h
+++ /dev/null
@@ -1,42 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the tools applications of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#ifndef EDITDISTANCE_H
-#define EDITDISTANCE_H
-
-#include <QtCore/qset.h>
-#include <QtCore/qstring.h>
-
-QT_BEGIN_NAMESPACE
-
-int editDistance(const QString &s, const QString &t);
-QString nearestName(const QString &actual, const QSet<QString> &candidates);
-
-QT_END_NAMESPACE
-
-#endif
diff --git a/src/qdoc/enumitem.h b/src/qdoc/enumitem.h
deleted file mode 100644
index bf360f56a..000000000
--- a/src/qdoc/enumitem.h
+++ /dev/null
@@ -1,55 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the tools applications of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#ifndef ENUMITEM_H
-#define ENUMITEM_H
-
-#include <QtCore/qglobal.h>
-#include <QtCore/qstring.h>
-
-#include <utility>
-
-QT_BEGIN_NAMESPACE
-
-class EnumItem
-{
-public:
- EnumItem() = default;
- EnumItem(QString name, QString value) : m_name(std::move(name)), m_value(std::move(value)) { }
-
- [[nodiscard]] const QString &name() const { return m_name; }
- [[nodiscard]] const QString &value() const { return m_value; }
-
-private:
- QString m_name {};
- QString m_value {};
-};
-
-QT_END_NAMESPACE
-
-#endif // ENUMITEM_H
diff --git a/src/qdoc/enumnode.cpp b/src/qdoc/enumnode.cpp
deleted file mode 100644
index 2e4b82307..000000000
--- a/src/qdoc/enumnode.cpp
+++ /dev/null
@@ -1,94 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2020 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the tools applications of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include "enumnode.h"
-
-#include "aggregate.h"
-#include "typedefnode.h"
-
-QT_BEGIN_NAMESPACE
-
-/*!
- \class EnumNode
- */
-
-/*!
- Add \a item to the enum type's item list.
- */
-void EnumNode::addItem(const EnumItem &item)
-{
- m_items.append(item);
- m_names.insert(item.name());
-}
-
-/*!
- Returns the access level of the enumeration item named \a name.
- Apparently it is private if it has been omitted by qdoc's
- omitvalue command. Otherwise it is public.
- */
-Access EnumNode::itemAccess(const QString &name) const
-{
- if (doc().omitEnumItemNames().contains(name))
- return Access::Private;
- return Access::Public;
-}
-
-/*!
- Returns the enum value associated with the enum \a name.
- */
-QString EnumNode::itemValue(const QString &name) const
-{
- for (const auto &item : qAsConst(m_items)) {
- if (item.name() == name)
- return item.value();
- }
- return QString();
-}
-
-/*!
- Clone this node on the heap and make the clone a child of
- \a parent.
-
- Returns a pointer to the clone.
- */
-Node *EnumNode::clone(Aggregate *parent)
-{
- auto *en = new EnumNode(*this); // shallow copy
- en->setParent(nullptr);
- parent->addChild(en);
-
- return en;
-}
-
-void EnumNode::setFlagsType(TypedefNode *typedefNode)
-{
- m_flagsType = typedefNode;
- typedefNode->setAssociatedEnum(this);
-}
-
-QT_END_NAMESPACE
diff --git a/src/qdoc/enumnode.h b/src/qdoc/enumnode.h
deleted file mode 100644
index 84a074d10..000000000
--- a/src/qdoc/enumnode.h
+++ /dev/null
@@ -1,73 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2020 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the tools applications of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#ifndef ENUMNODE_H
-#define ENUMNODE_H
-
-#include "access.h"
-#include "node.h"
-#include "typedefnode.h"
-
-#include <QtCore/qglobal.h>
-#include <QtCore/qlist.h>
-#include <QtCore/qset.h>
-#include <QtCore/qstring.h>
-
-QT_BEGIN_NAMESPACE
-
-class Aggregate;
-
-class EnumNode : public Node
-{
-public:
- EnumNode(Aggregate *parent, const QString &name, bool isScoped = false)
- : Node(Enum, parent, name), m_isScoped(isScoped)
- {
- }
-
- void addItem(const EnumItem &item);
- void setFlagsType(TypedefNode *typedefNode);
- bool hasItem(const QString &name) const { return m_names.contains(name); }
- bool isScoped() const { return m_isScoped; }
-
- const QList<EnumItem> &items() const { return m_items; }
- Access itemAccess(const QString &name) const;
- const TypedefNode *flagsType() const { return m_flagsType; }
- QString itemValue(const QString &name) const;
- Node *clone(Aggregate *parent) override;
-
-private:
- QList<EnumItem> m_items {};
- QSet<QString> m_names {};
- const TypedefNode *m_flagsType { nullptr };
- bool m_isScoped { false };
-};
-
-QT_END_NAMESPACE
-
-#endif // ENUMNODE_H
diff --git a/src/qdoc/examplenode.h b/src/qdoc/examplenode.h
deleted file mode 100644
index 0ce2294f2..000000000
--- a/src/qdoc/examplenode.h
+++ /dev/null
@@ -1,67 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the tools applications of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#ifndef EXAMPLENODE_H
-#define EXAMPLENODE_H
-
-#include "pagenode.h"
-
-#include <QtCore/qglobal.h>
-#include <QtCore/qstring.h>
-#include <QtCore/qstringlist.h>
-
-QT_BEGIN_NAMESPACE
-
-class ExampleNode : public PageNode
-{
-public:
- ExampleNode(Aggregate *parent, const QString &name) : PageNode(Node::Example, parent, name) {}
- [[nodiscard]] QString imageFileName() const override { return m_imageFileName; }
- void setImageFileName(const QString &ifn) override { m_imageFileName = ifn; }
- [[nodiscard]] const QStringList &files() const { return m_files; }
- [[nodiscard]] const QStringList &images() const { return m_images; }
- [[nodiscard]] const QString &projectFile() const { return m_projectFile; }
- void setFiles(const QStringList &files, const QString &projectFile)
- {
- m_files = files;
- m_projectFile = projectFile;
- }
- void setImages(const QStringList &images) { m_images = images; }
- void appendFile(QString &file) { m_files.append(file); }
- void appendImage(QString &image) { m_images.append(image); }
-
-private:
- QString m_imageFileName {};
- QString m_projectFile {};
- QStringList m_files {};
- QStringList m_images {};
-};
-
-QT_END_NAMESPACE
-
-#endif // EXAMPLENODE_H
diff --git a/src/qdoc/externalpagenode.cpp b/src/qdoc/externalpagenode.cpp
deleted file mode 100644
index 8882ed8f7..000000000
--- a/src/qdoc/externalpagenode.cpp
+++ /dev/null
@@ -1,53 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2020 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the tools applications of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include "externalpagenode.h"
-
-QT_BEGIN_NAMESPACE
-
-/*!
- \class ExternalPageNode
-
- \brief The ExternalPageNode represents an external documentation page.
-
- Qdoc can generate links to pages that are not part of the documentation
- being generated. 3rd party software pages are often referenced by links
- from the QT documentation. Qdoc creates an ExternalPageNode when it sees
- an \c {\\externalpage} command. The HTML generator can then use the node
- when it needs to create links to the external page.
-
- ExternalPageNode inherits PageNode.
-*/
-
-/*! \fn ExternalPageNode::ExternalPageNode(Aggregate *parent, const QString &name)
- The constructor creates an ExternalPageNode as a child node of \a parent.
- It's \a name is the argument from the \c {\\externalpage} command. The node
- type is Node::ExternalPage, and the page type is Node::ArticlePage.
- */
-
-QT_END_NAMESPACE
diff --git a/src/qdoc/externalpagenode.h b/src/qdoc/externalpagenode.h
deleted file mode 100644
index 91abd9ca9..000000000
--- a/src/qdoc/externalpagenode.h
+++ /dev/null
@@ -1,51 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2020 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the tools applications of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#ifndef EXTERNALPAGENODE_H
-#define EXTERNALPAGENODE_H
-
-#include "pagenode.h"
-
-#include <QtCore/qglobal.h>
-
-QT_BEGIN_NAMESPACE
-
-class ExternalPageNode : public PageNode
-{
-public:
- ExternalPageNode(Aggregate *parent, const QString &url)
- : PageNode(Node::ExternalPage, parent, url)
- {
- setPageType(Node::ArticlePage);
- setUrl(url);
- }
-};
-
-QT_END_NAMESPACE
-
-#endif // EXTERNALPAGENODE_H
diff --git a/src/qdoc/headernode.cpp b/src/qdoc/headernode.cpp
deleted file mode 100644
index f28d37362..000000000
--- a/src/qdoc/headernode.cpp
+++ /dev/null
@@ -1,68 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the tools applications of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include "headernode.h"
-
-QT_BEGIN_NAMESPACE
-
-/*!
- \class Headernode
- \brief This class represents a C++ header file.
- */
-
-HeaderNode::HeaderNode(Aggregate *parent, const QString &name) : Aggregate(HeaderFile, parent, name)
-{
- // Add the include file with enclosing angle brackets removed
- if (name.startsWith(QChar('<')) && name.length() > 2)
- Aggregate::addIncludeFile(name.mid(1).chopped(1));
- else
- Aggregate::addIncludeFile(name);
-}
-
-/*!
- Returns true if this header file node is not private and
- contains at least one public child node with documentation.
- */
-bool HeaderNode::docMustBeGenerated() const
-{
- if (isInAPI())
- return true;
- return hasDocumentedChildren();
-}
-
-/*!
- Returns true if this header file node contains at least one
- child that has documentation and is not private or internal.
- */
-bool HeaderNode::hasDocumentedChildren() const
-{
- return std::any_of(m_children.cbegin(), m_children.cend(),
- [](Node *child) { return child->isInAPI(); });
-}
-
-QT_END_NAMESPACE
diff --git a/src/qdoc/headernode.h b/src/qdoc/headernode.h
deleted file mode 100644
index 83ba581ae..000000000
--- a/src/qdoc/headernode.h
+++ /dev/null
@@ -1,72 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the tools applications of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#ifndef HEADERNODE_H
-#define HEADERNODE_H
-
-#include "aggregate.h"
-
-#include <QtCore/qglobal.h>
-#include <QtCore/qstring.h>
-
-QT_BEGIN_NAMESPACE
-
-class HeaderNode : public Aggregate
-{
-public:
- HeaderNode(Aggregate *parent, const QString &name);
- [[nodiscard]] bool docMustBeGenerated() const override;
- [[nodiscard]] bool isFirstClassAggregate() const override { return true; }
- [[nodiscard]] bool isRelatableType() const override { return true; }
- [[nodiscard]] QString title() const override { return (m_title.isEmpty() ? name() : m_title); }
- [[nodiscard]] QString subtitle() const override { return m_subtitle; }
- [[nodiscard]] QString fullTitle() const override
- {
- return (m_title.isEmpty() ? name() : name() + " - " + m_title);
- }
- bool setTitle(const QString &title) override
- {
- m_title = title;
- return true;
- }
- bool setSubtitle(const QString &subtitle) override
- {
- m_subtitle = subtitle;
- return true;
- }
- [[nodiscard]] QString nameForLists() const override { return title(); }
- [[nodiscard]] bool hasDocumentedChildren() const;
-
-private:
- QString m_title {};
- QString m_subtitle {};
-};
-
-QT_END_NAMESPACE
-
-#endif // HEADERNODE_H
diff --git a/src/qdoc/importrec.h b/src/qdoc/importrec.h
deleted file mode 100644
index aae89d0a6..000000000
--- a/src/qdoc/importrec.h
+++ /dev/null
@@ -1,58 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the tools applications of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#ifndef IMPORTREC_H
-#define IMPORTREC_H
-
-#include <QtCore/qglobal.h>
-#include <QtCore/qstring.h>
-
-#include <utility>
-
-QT_BEGIN_NAMESPACE
-
-struct ImportRec
-{
- QString m_moduleName {};
- QString m_majorMinorVersion {};
- QString m_importUri {}; // subdirectory of module directory
-
- ImportRec(QString name, QString version, QString importUri)
- : m_moduleName(std::move(name)),
- m_majorMinorVersion(std::move(version)),
- m_importUri(std::move(importUri))
- {
- }
- QString &name() { return m_moduleName; }
- QString &version() { return m_majorMinorVersion; }
- [[nodiscard]] bool isEmpty() const { return m_moduleName.isEmpty(); }
-};
-
-QT_END_NAMESPACE
-
-#endif // IMPORTREC_H
diff --git a/src/qdoc/jscodemarker.cpp b/src/qdoc/jscodemarker.cpp
deleted file mode 100644
index 7acdd0a25..000000000
--- a/src/qdoc/jscodemarker.cpp
+++ /dev/null
@@ -1,140 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the tools applications of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include "jscodemarker.h"
-
-#include "atom.h"
-#include "node.h"
-#include "qmlmarkupvisitor.h"
-#include "text.h"
-
-#ifndef QT_NO_DECLARATIVE
-# include <private/qqmljsast_p.h>
-# include <private/qqmljsengine_p.h>
-# include <private/qqmljslexer_p.h>
-# include <private/qqmljsparser_p.h>
-#endif
-
-QT_BEGIN_NAMESPACE
-
-/*!
- Returns \c true if the \a code is recognized by the parser.
- */
-bool JsCodeMarker::recognizeCode(const QString &code)
-{
-#ifndef QT_NO_DECLARATIVE
- QQmlJS::Engine engine;
- QQmlJS::Lexer lexer(&engine);
- QQmlJS::Parser parser(&engine);
-
- const QString &newCode = code;
- lexer.setCode(newCode, 1);
-
- return parser.parseProgram();
-#else
- Q_UNUSED(code);
- return false;
-#endif
-}
-
-/*!
- Returns \c true if \a ext is any of a list of file extensions
- for the QML language.
- */
-bool JsCodeMarker::recognizeExtension(const QString &ext)
-{
- return ext == "js";
-}
-
-/*!
- Returns \c true if the \a language is recognized. We recognize JavaScript and
- ECMAScript.
- */
-bool JsCodeMarker::recognizeLanguage(const QString &language)
-{
- return language == "JavaScript" || language == "ECMAScript";
-}
-
-/*!
- Returns the type of atom used to represent JavaScript code in the documentation.
-*/
-Atom::AtomType JsCodeMarker::atomType() const
-{
- return Atom::JavaScript;
-}
-
-QString JsCodeMarker::markedUpCode(const QString &code, const Node *relative,
- const Location &location)
-{
- return addMarkUp(code, relative, location);
-}
-
-QString JsCodeMarker::addMarkUp(const QString &code, const Node * /* relative */,
- const Location &location)
-{
-#ifndef QT_NO_DECLARATIVE
- QQmlJS::Engine engine;
- QQmlJS::Lexer lexer(&engine);
-
- QString newCode = code;
- QList<QQmlJS::SourceLocation> pragmas = extractPragmas(newCode);
- lexer.setCode(newCode, 1);
-
- QQmlJS::Parser parser(&engine);
- QString output;
-
- if (parser.parseProgram()) {
- QQmlJS::AST::Node *ast = parser.rootNode();
- // Pass the unmodified code to the visitor so that pragmas and other
- // unhandled source text can be output.
- QmlMarkupVisitor visitor(code, pragmas, &engine);
- QQmlJS::AST::Node::accept(ast, &visitor);
- if (visitor.hasError()) {
- location.warning(
- location.fileName()
- + QStringLiteral("Unable to analyze JavaScript. The output is incomplete."));
- }
- output = visitor.markedUpCode();
- } else {
- location.warning(
- location.fileName()
- + QStringLiteral("Unable to parse JavaScript: \"%1\" at line %2, column %3")
- .arg(parser.errorMessage())
- .arg(parser.errorLineNumber())
- .arg(parser.errorColumnNumber()));
- output = protect(code);
- }
- return output;
-#else
- Q_UNUSED(code);
- location.warning("QtDeclarative not installed; cannot parse QML or JS.");
- return QString();
-#endif
-}
-
-QT_END_NAMESPACE
diff --git a/src/qdoc/jscodemarker.h b/src/qdoc/jscodemarker.h
deleted file mode 100644
index 6f74753f8..000000000
--- a/src/qdoc/jscodemarker.h
+++ /dev/null
@@ -1,56 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the tools applications of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#ifndef JSCODEMARKER_H
-#define JSCODEMARKER_H
-
-#include "qmlcodemarker.h"
-
-QT_BEGIN_NAMESPACE
-
-class JsCodeMarker : public QmlCodeMarker
-{
-public:
- JsCodeMarker() = default;
- ~JsCodeMarker() override = default;
-
- bool recognizeCode(const QString &code) override;
- bool recognizeExtension(const QString &ext) override;
- bool recognizeLanguage(const QString &language) override;
- [[nodiscard]] Atom::AtomType atomType() const override;
-
- QString markedUpCode(const QString &code, const Node *relative,
- const Location &location) override;
-
-private:
- QString addMarkUp(const QString &code, const Node *relative, const Location &location);
-};
-
-QT_END_NAMESPACE
-
-#endif
diff --git a/src/qdoc/macro.h b/src/qdoc/macro.h
deleted file mode 100644
index 30cc088c8..000000000
--- a/src/qdoc/macro.h
+++ /dev/null
@@ -1,52 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2020 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the tools applications of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-#ifndef MACRO_H
-#define MACRO_H
-
-#include "location.h"
-
-#include <QtCore/qmap.h>
-#include <QtCore/qstring.h>
-
-QT_BEGIN_NAMESPACE
-
-/*!
- * Simple structure used by the Doc and DocParser classes.
- */
-struct Macro
-{
-public:
- QString m_defaultDef {};
- Location m_defaultDefLocation {};
- QMap<QString, QString> m_otherDefs {};
- int numParams {};
-};
-
-QT_END_NAMESPACE
-
-#endif // MACRO_H
diff --git a/src/qdoc/manifestwriter.h b/src/qdoc/manifestwriter.h
deleted file mode 100644
index 75e78ed27..000000000
--- a/src/qdoc/manifestwriter.h
+++ /dev/null
@@ -1,76 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the tools applications of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-#ifndef MANIFESTWRITER_H
-#define MANIFESTWRITER_H
-
-#include <QtCore/qlist.h>
-#include <QtCore/qset.h>
-#include <QtCore/qstring.h>
-
-QT_BEGIN_NAMESPACE
-
-class ExampleNode;
-class QDocDatabase;
-class QXmlStreamWriter;
-class ManifestWriter
-{
- struct ManifestMetaFilter
- {
- QSet<QString> m_names {};
- QSet<QString> m_attributes {};
- QSet<QString> m_tags {};
- };
-
-public:
- ManifestWriter();
- void generateManifestFiles();
- void generateManifestFile(const QString &manifest, const QString &element);
- void readManifestMetaContent();
- QString retrieveExampleInstallationPath(const ExampleNode *example) const;
-
-private:
- QSet<QString> m_tags {};
- QString m_manifestDir {};
- QString m_examplesPath {};
- QString m_outputDirectory {};
- QString m_project {};
- QDocDatabase *m_qdb { nullptr };
- QList<ManifestMetaFilter> m_manifestMetaContent {};
-
- void addTitleWordsToTags(const ExampleNode *example);
- void addWordsFromModuleNamesAsTags();
- void includeTagsAddedWithMetaCommand(const ExampleNode *example);
- void cleanUpTags();
- void writeTagsElement(QXmlStreamWriter *writer);
- template <typename F>
- void processManifestMetaContent(const QString &fullName, F matchFunc);
-};
-
-QT_END_NAMESPACE
-
-#endif // MANIFESTWRITER_H
diff --git a/src/qdoc/openedlist.h b/src/qdoc/openedlist.h
deleted file mode 100644
index 66daf4fda..000000000
--- a/src/qdoc/openedlist.h
+++ /dev/null
@@ -1,72 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the tools applications of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#ifndef OPENEDLIST_H
-#define OPENEDLIST_H
-
-#include "location.h"
-
-#include <QtCore/qstring.h>
-
-QT_BEGIN_NAMESPACE
-
-class OpenedList
-{
-public:
- enum ListStyle { Bullet, Tag, Value, Numeric, UpperAlpha, LowerAlpha, UpperRoman, LowerRoman };
-
- OpenedList() : sty(Bullet), ini(1), nex(0) {}
- explicit OpenedList(ListStyle style);
- OpenedList(const Location &location, const QString &hint);
-
- void next() { nex++; }
-
- [[nodiscard]] bool isStarted() const { return nex >= ini; }
- [[nodiscard]] ListStyle style() const { return sty; }
- [[nodiscard]] QString styleString() const;
- [[nodiscard]] int number() const { return nex; }
- [[nodiscard]] QString numberString() const;
- [[nodiscard]] QString prefix() const { return pref; }
- [[nodiscard]] QString suffix() const { return suff; }
-
-private:
- static int fromAlpha(const QString &str);
- static QString toRoman(int n);
- static int fromRoman(const QString &str);
-
- ListStyle sty;
- int ini;
- int nex;
- QString pref;
- QString suff;
-};
-Q_DECLARE_TYPEINFO(OpenedList, Q_RELOCATABLE_TYPE);
-
-QT_END_NAMESPACE
-
-#endif
diff --git a/src/qdoc/pagenode.h b/src/qdoc/pagenode.h
deleted file mode 100644
index d8f903a8e..000000000
--- a/src/qdoc/pagenode.h
+++ /dev/null
@@ -1,93 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2020 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the tools applications of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#ifndef PAGENODE_H
-#define PAGENODE_H
-
-#include "node.h"
-
-#include <QtCore/qglobal.h>
-#include <QtCore/qstring.h>
-#include <QtCore/qstringlist.h>
-
-QT_BEGIN_NAMESPACE
-
-class Aggregate;
-
-class PageNode : public Node
-{
-public:
- PageNode(Aggregate *parent, const QString &name) : Node(Page, parent, name) {}
- PageNode(NodeType type, Aggregate *parent, const QString &name) : Node(type, parent, name) {}
- PageNode(Aggregate *parent, const QString &name, PageType ptype) : Node(Page, parent, name)
- {
- setPageType(ptype);
- }
-
- [[nodiscard]] bool isPageNode() const override { return true; }
- [[nodiscard]] bool isTextPageNode() const override
- {
- return !isAggregate();
- } // PageNode but not Aggregate
-
- [[nodiscard]] QString title() const override { return m_title; }
- [[nodiscard]] QString subtitle() const override { return m_subtitle; }
- [[nodiscard]] QString fullTitle() const override;
- bool setTitle(const QString &title) override;
- bool setSubtitle(const QString &subtitle) override
- {
- m_subtitle = subtitle;
- return true;
- }
- [[nodiscard]] QString nameForLists() const override { return title(); }
-
- [[nodiscard]] virtual QString imageFileName() const { return QString(); }
- virtual void setImageFileName(const QString &) {}
-
- [[nodiscard]] bool noAutoList() const { return m_noAutoList; }
- void setNoAutoList(bool b) override { m_noAutoList = b; }
- [[nodiscard]] const QStringList &groupNames() const { return m_groupNames; }
- void appendGroupName(const QString &t) override { m_groupNames.append(t); }
-
- void setOutputFileName(const QString &f) override { m_outputFileName = f; }
- [[nodiscard]] QString outputFileName() const override { return m_outputFileName; }
-
-protected:
- friend class Node;
-
-protected:
- bool m_noAutoList { false };
- QString m_title {};
- QString m_subtitle {};
- QString m_outputFileName {};
- QStringList m_groupNames {};
-};
-
-QT_END_NAMESPACE
-
-#endif // PAGENODE_H
diff --git a/src/qdoc/proxynode.h b/src/qdoc/proxynode.h
deleted file mode 100644
index 65b59cc75..000000000
--- a/src/qdoc/proxynode.h
+++ /dev/null
@@ -1,48 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2020 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the tools applications of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#ifndef PROXYNODE_H
-#define PROXYNODE_H
-
-#include "aggregate.h"
-
-#include <QtCore/qglobal.h>
-
-QT_BEGIN_NAMESPACE
-
-class ProxyNode : public Aggregate
-{
-public:
- ProxyNode(Aggregate *parent, const QString &name);
- [[nodiscard]] bool docMustBeGenerated() const override { return true; }
- [[nodiscard]] bool isRelatableType() const override { return true; }
-};
-
-QT_END_NAMESPACE
-
-#endif // PROXYNODE_H
diff --git a/src/qdoc/puredocparser.cpp b/src/qdoc/puredocparser.cpp
deleted file mode 100644
index 095a2664b..000000000
--- a/src/qdoc/puredocparser.cpp
+++ /dev/null
@@ -1,126 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the tools applications of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include "puredocparser.h"
-
-#include "qdocdatabase.h"
-#include "tokenizer.h"
-
-#include <cerrno>
-
-QT_BEGIN_NAMESPACE
-
-/*!
- Returns a list of the kinds of files that the pure doc
- parser is meant to parse. The elements of the list are
- file suffixes.
- */
-QStringList PureDocParser::sourceFileNameFilter()
-{
- return QStringList() << "*.qdoc"
- << "*.qtx"
- << "*.qtt"
- << "*.js";
-}
-
-/*!
- Parses the source file identified by \a filePath and adds its
- parsed contents to the database. The \a location is used for
- reporting errors.
- */
-void PureDocParser::parseSourceFile(const Location &location, const QString &filePath)
-{
- QFile in(filePath);
- m_currentFile = filePath;
- if (!in.open(QIODevice::ReadOnly)) {
- location.error(
- QStringLiteral("Can't open source file '%1' (%2)").arg(filePath, strerror(errno)));
- m_currentFile.clear();
- return;
- }
-
- Location fileLocation(filePath);
- Tokenizer fileTokenizer(fileLocation, in);
- m_tokenizer = &fileTokenizer;
- m_token = m_tokenizer->getToken();
-
- /*
- The set of open namespaces is cleared before parsing
- each source file. The word "source" here means cpp file.
- */
- m_qdb->clearOpenNamespaces();
-
- processQdocComments();
- in.close();
- m_currentFile.clear();
-}
-
-/*!
- This is called by parseSourceFile() to do the actual parsing
- and tree building. It only processes qdoc comments. It skips
- everything else.
- */
-bool PureDocParser::processQdocComments()
-{
- const QSet<QString> &commands = topicCommands() + metaCommands();
-
- while (m_token != Tok_Eoi) {
- if (m_token == Tok_Doc) {
- QString comment = m_tokenizer->lexeme(); // returns an entire qdoc comment.
- Location start_loc(m_tokenizer->location());
- m_token = m_tokenizer->getToken();
-
- Doc::trimCStyleComment(start_loc, comment);
- Location end_loc(m_tokenizer->location());
-
- // Doc constructor parses the comment.
- Doc doc(start_loc, end_loc, comment, commands, topicCommands());
- const TopicList &topics = doc.topicsUsed();
- if (topics.isEmpty()) {
- doc.location().warning(QStringLiteral("This qdoc comment contains no topic command "
- "(e.g., '\\%1', '\\%2').")
- .arg(COMMAND_MODULE, COMMAND_PAGE));
- continue;
- }
- if (hasTooManyTopics(doc))
- continue;
-
- DocList docs;
- NodeList nodes;
- QString topic = topics[0].m_topic;
-
- processTopicArgs(doc, topic, nodes, docs);
- processMetaCommands(nodes, docs);
- } else {
- m_token = m_tokenizer->getToken();
- }
- }
- return true;
-}
-
-QT_END_NAMESPACE
diff --git a/src/qdoc/puredocparser.h b/src/qdoc/puredocparser.h
deleted file mode 100644
index 15717ec31..000000000
--- a/src/qdoc/puredocparser.h
+++ /dev/null
@@ -1,55 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the tools applications of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#ifndef PUREDOCPARSER_H
-#define PUREDOCPARSER_H
-
-#include "cppcodeparser.h"
-
-QT_BEGIN_NAMESPACE
-
-class Location;
-
-class PureDocParser : public CppCodeParser
-{
-public:
- PureDocParser() = default;
- ~PureDocParser() override = default;
-
- QStringList sourceFileNameFilter() override;
- void parseSourceFile(const Location &location, const QString &filePath) override;
-
-private:
- bool processQdocComments();
- Tokenizer *m_tokenizer { nullptr };
- int m_token { 0 };
-};
-
-QT_END_NAMESPACE
-
-#endif
diff --git a/src/qdoc/qdoc/CMakeLists.txt b/src/qdoc/qdoc/CMakeLists.txt
new file mode 100644
index 000000000..a1d31765e
--- /dev/null
+++ b/src/qdoc/qdoc/CMakeLists.txt
@@ -0,0 +1,117 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+if(CMAKE_VERSION VERSION_LESS "3.19" AND MSVC AND CMAKE_GENERATOR STREQUAL "Ninja Multi-Config")
+ message(WARNING "qdoc will not be built in this configuration.")
+ return()
+endif()
+
+if (MINGW)
+ set_property(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" PROPERTY _qt_skip_separate_debug_info ON)
+endif()
+
+
+#####################################################################
+## qdoc Tool:
+#####################################################################
+
+qt_get_tool_target_name(target_name qdoc)
+qt_internal_add_tool(${target_name}
+ TARGET_DESCRIPTION "Qt Documentation Compiler"
+ TOOLS_TARGET Tools
+ USER_FACING
+ SOURCES
+ src/qdoc/aggregate.cpp
+ src/qdoc/atom.cpp
+ src/qdoc/boundaries/filesystem/directorypath.cpp
+ src/qdoc/boundaries/filesystem/filepath.cpp
+ src/qdoc/boundaries/filesystem/resolvedfile.cpp
+ src/qdoc/clangcodeparser.cpp
+ src/qdoc/classnode.cpp
+ src/qdoc/codechunk.cpp
+ src/qdoc/codemarker.cpp
+ src/qdoc/codeparser.cpp
+ src/qdoc/collectionnode.cpp
+ src/qdoc/comparisoncategory.h
+ src/qdoc/config.cpp
+ src/qdoc/cppcodemarker.cpp
+ src/qdoc/cppcodeparser.cpp
+ src/qdoc/doc.cpp
+ src/qdoc/docbookgenerator.cpp
+ src/qdoc/docparser.cpp
+ src/qdoc/docprivate.cpp
+ src/qdoc/editdistance.cpp
+ src/qdoc/enumnode.cpp
+ src/qdoc/externalpagenode.cpp
+ src/qdoc/filesystem/fileresolver.cpp
+ src/qdoc/functionnode.cpp
+ src/qdoc/generator.cpp
+ src/qdoc/headernode.cpp
+ src/qdoc/helpprojectwriter.cpp
+ src/qdoc/htmlgenerator.cpp
+ src/qdoc/location.cpp
+ src/qdoc/main.cpp
+ src/qdoc/manifestwriter.cpp
+ src/qdoc/namespacenode.cpp
+ src/qdoc/node.cpp
+ src/qdoc/openedlist.cpp
+ src/qdoc/pagenode.cpp
+ src/qdoc/parameters.cpp
+ src/qdoc/parsererror.cpp
+ src/qdoc/propertynode.cpp
+ src/qdoc/proxynode.cpp
+ src/qdoc/puredocparser.cpp
+ src/qdoc/qdoccommandlineparser.cpp
+ src/qdoc/qdocdatabase.cpp
+ src/qdoc/qdocindexfiles.cpp
+ src/qdoc/qmlcodemarker.cpp
+ src/qdoc/qmlcodeparser.cpp
+ src/qdoc/qmlmarkupvisitor.cpp
+ src/qdoc/qmlpropertynode.cpp
+ src/qdoc/qmltypenode.cpp
+ src/qdoc/qmlvisitor.cpp
+ src/qdoc/quoter.cpp
+ src/qdoc/relatedclass.cpp
+ src/qdoc/sections.cpp
+ src/qdoc/sharedcommentnode.cpp
+ src/qdoc/tagfilewriter.cpp
+ src/qdoc/text.cpp
+ src/qdoc/tokenizer.cpp
+ src/qdoc/tree.cpp
+ src/qdoc/typedefnode.cpp
+ src/qdoc/utilities.cpp
+ src/qdoc/variablenode.cpp
+ src/qdoc/webxmlgenerator.cpp
+ src/qdoc/xmlgenerator.cpp
+ NO_UNITY_BUILD_SOURCES
+ src/qdoc/qmlmarkupvisitor.cpp # redefinition of 'samp'/'slt' (from codemarker.cpp)
+ INCLUDE_DIRECTORIES
+ ${CMAKE_CURRENT_LIST_DIR}/src
+ LIBRARIES
+ Qt::QmlPrivate
+ WrapLibClang::WrapLibClang
+ DEFINES
+ #(CLANG_RESOURCE_DIR=\"/clang//include\") # special case remove
+ CLANG_RESOURCE_DIR=${QT_LIBCLANG_RESOURCE_DIR}
+ # To provide the ability to workaround version-specific Clang issues.
+ # A re-export of (LLVM|CLANG)_VERSION_MAJOR done in WrapLibClang.cmake
+ LIBCLANG_VERSION_MAJOR=${QT_LIB_CLANG_VERSION_MAJOR}
+ QDOC2_COMPAT
+)
+qt_internal_return_unless_building_tools()
+
+# If libclangTooling.a is not built with -fPIE enabled we cannot link it to qdoc.
+# TODO: Re-enable PIE once clang is built with PIE in provisioning.
+set_target_properties(${target_name} PROPERTIES POSITION_INDEPENDENT_CODE FALSE)
+
+qt_internal_extend_target(${target_name} CONDITION (WIN32 AND ICC) OR MSVC
+ LINK_OPTIONS
+ "/STACK:4194304"
+)
+qt_internal_add_docs(${target_name}
+ doc/config/qdoc.qdocconf
+)
+
+if(QT_BUILD_TESTS)
+ add_subdirectory(tests)
+endif()
diff --git a/src/qdoc/doc/config/qdoc.qdocconf b/src/qdoc/qdoc/doc/config/qdoc.qdocconf
index 982615da9..d886ff75b 100644
--- a/src/qdoc/doc/config/qdoc.qdocconf
+++ b/src/qdoc/qdoc/doc/config/qdoc.qdocconf
@@ -56,4 +56,6 @@ ignorewords += QDoc
navigation.landingpage = "QDoc Manual"
-warninglimit = 0
+# Convenience macros for creating links to QDoc commands & configuration variables
+macro.qdoccmd = \\l{\1-command}{\\c{\\\1}}
+macro.qdocvar = \\l{\1-variable}{\\c{\1}}
diff --git a/src/qdoc/qdoc/doc/corefeatures.qdoc b/src/qdoc/qdoc/doc/corefeatures.qdoc
new file mode 100644
index 000000000..e1edc3a92
--- /dev/null
+++ b/src/qdoc/qdoc/doc/corefeatures.qdoc
@@ -0,0 +1,11 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+
+/*!
+ \page corefeatures.html
+ \title Core Features
+
+ \include examples/signalandslots.qdocinc
+ \include examples/objectmodel.qdocinc
+ \include examples/layoutmanagement.qdocinc
+*/
diff --git a/src/qdoc/doc/examples/cpp.qdoc.sample b/src/qdoc/qdoc/doc/examples/cpp.qdoc.sample
index 0524a6756..39c77f63c 100644
--- a/src/qdoc/doc/examples/cpp.qdoc.sample
+++ b/src/qdoc/qdoc/doc/examples/cpp.qdoc.sample
@@ -1,29 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the documentation of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:FDL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Free Documentation License Usage
-** Alternatively, this file may be used under the terms of the GNU Free
-** Documentation License version 1.3 as published by the Free Software
-** Foundation and appearing in the file included in the packaging of
-** this file. Please review the following information to ensure
-** the GNU Free Documentation License version 1.3 requirements
-** will be met: https://www.gnu.org/licenses/fdl-1.3.html.
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
//![class]
/*!
diff --git a/src/qdoc/doc/examples/layoutmanagement.qdocinc b/src/qdoc/qdoc/doc/examples/layoutmanagement.qdocinc
index 780b03c8f..780b03c8f 100644
--- a/src/qdoc/doc/examples/layoutmanagement.qdocinc
+++ b/src/qdoc/qdoc/doc/examples/layoutmanagement.qdocinc
diff --git a/src/qdoc/qdoc/doc/examples/main.cpp b/src/qdoc/qdoc/doc/examples/main.cpp
new file mode 100644
index 000000000..0898fa5f7
--- /dev/null
+++ b/src/qdoc/qdoc/doc/examples/main.cpp
@@ -0,0 +1,16 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include <QApplication>
+#include <QPushButton>
+
+int main(int argc, char *argv[])
+{
+ QApplication app(argc, argv);
+
+ QPushButton hello("Hello world!");
+ hello.resize(100, 30);
+
+ hello.show();
+ return app.exec();
+}
diff --git a/src/qdoc/doc/examples/mainwindow.cpp b/src/qdoc/qdoc/doc/examples/mainwindow.cpp
index 1de035b06..4e67f0a79 100644
--- a/src/qdoc/doc/examples/mainwindow.cpp
+++ b/src/qdoc/qdoc/doc/examples/mainwindow.cpp
@@ -1,30 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the tools applications of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#include <QtWidgets>
@@ -131,7 +106,8 @@ void MainWindow::createActions()
openAct->setShortcuts(QKeySequence::Open);
connect(openAct, SIGNAL(triggered()), this, SLOT(open()));
- foreach (const QByteArray &format, QImageWriter::supportedImageFormats()) {
+ const QByteArrayList formats = QImageWriter::supportedImageFormats();
+ for (const QByteArray &format : formats) {
QString text = tr("%1...").arg(QString(format).toUpper());
QAction *action = new QAction(text, this);
@@ -171,8 +147,7 @@ void MainWindow::createMenus()
//! [15] //! [16]
{
saveAsMenu = new QMenu(tr("&Save As"), this);
- foreach (QAction *action, saveAsActs)
- saveAsMenu->addAction(action);
+ saveAsMenu->addActions(saveAsActs);
fileMenu = new QMenu(tr("&File"), this);
fileMenu->addAction(openAct);
diff --git a/src/qdoc/qdoc/doc/examples/minimum.qdocconf b/src/qdoc/qdoc/doc/examples/minimum.qdocconf
new file mode 100644
index 000000000..fca0cd757
--- /dev/null
+++ b/src/qdoc/qdoc/doc/examples/minimum.qdocconf
@@ -0,0 +1,46 @@
+# QDoc is a tool that constantly evolves and there may be compatibility issues
+# between old and new practices. For that reason, QDoc configuration files in
+# the Qt Project includes compat.qdocconf:
+#include(compat.qdocconf)
+
+# Give the documentation project a title:
+project = My documentation project
+
+# Pass additional include paths to QDoc when parsing C++ code for documentation
+# comments.
+#includepaths += -I/some/path
+
+# QDoc needs a lists of file extensions to know which files to process in
+# different situations. Uncomment the following include statement to get
+# a pre-defined list of file extensions.
+#include(fileextensions.qdocconf)
+
+# You can also specify file extensions manually.
+headers.fileextensions = "*.h *.hpp"
+sources.fileextensions = "*.cpp *.qml *.qdoc"
+
+# The outputdir variable specifies the directory where QDoc places the generated
+# documentation.
+outputdir = public
+
+# The headerdirs variable specifies the directories that contain the header
+# files associated with the .cpp source files used in the documentation.
+headerdirs = .
+
+# The sourcedirs variable specifies the directories that contain the .cpp or
+# .qdoc files used in the documentation.
+sourcedirs = .
+
+# The exampledirs variable specifies the directories that contain the source
+# code of the example files.
+exampledirs = ./examples
+
+# The imagedirs variable specifies the directories that contain images used in
+# the documentation.
+imagedirs = ./images
+
+# Set a warning limit. QDoc will exit with a non-zero exit code if it generates
+# documentation warnings during the documentation build. Useful for tracking
+# down documentation issues.
+#warninglimit = 0
+#warninglimit.enabled = true
diff --git a/src/qdoc/doc/examples/objectmodel.qdocinc b/src/qdoc/qdoc/doc/examples/objectmodel.qdocinc
index 02b5991c4..02b5991c4 100644
--- a/src/qdoc/doc/examples/objectmodel.qdocinc
+++ b/src/qdoc/qdoc/doc/examples/objectmodel.qdocinc
diff --git a/src/qdoc/doc/examples/qml.qdoc.sample b/src/qdoc/qdoc/doc/examples/qml.qdoc.sample
index c54e9888d..e2c1277d2 100644
--- a/src/qdoc/doc/examples/qml.qdoc.sample
+++ b/src/qdoc/qdoc/doc/examples/qml.qdoc.sample
@@ -1,33 +1,9 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the documentation of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:FDL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Free Documentation License Usage
-** Alternatively, this file may be used under the terms of the GNU Free
-** Documentation License version 1.3 as published by the Free Software
-** Foundation and appearing in the file included in the packaging of
-** this file. Please review the following information to ensure
-** the GNU Free Documentation License version 1.3 requirements
-** will be met: https://www.gnu.org/licenses/fdl-1.3.html.
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
//![qmltype]
\qmltype TextEdit
- \instantiates QQuickTextEdit
+ \nativetype QQuickTextEdit
\inqmlmodule QtQuick
\ingroup qtquick-visual
\ingroup qtquick-input
diff --git a/src/qdoc/doc/examples/samples.qdocinc b/src/qdoc/qdoc/doc/examples/samples.qdocinc
index 1b83428b2..ea257852f 100644
--- a/src/qdoc/doc/examples/samples.qdocinc
+++ b/src/qdoc/qdoc/doc/examples/samples.qdocinc
@@ -1,29 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the documentation of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:FDL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Free Documentation License Usage
-** Alternatively, this file may be used under the terms of the GNU Free
-** Documentation License version 1.3 as published by the Free Software
-** Foundation and appearing in the file included in the packaging of
-** this file. Please review the following information to ensure
-** the GNU Free Documentation License version 1.3 requirements
-** will be met: https://www.gnu.org/licenses/fdl-1.3.html.
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
//! [qvector3d-class]
/*!
@@ -74,7 +50,7 @@
//! [sample-faq]
/*!
- \page altruism-faq.html faq
+ \page altruism-faq.html
\title Altruism Frequently Asked Questions
\brief All the questions about altruism, answered.
@@ -99,7 +75,7 @@
//! [sample-overview]
/*!
- \page overview-qt-technology.html overview
+ \page overview-qt-technology.html
\title Overview of a Qt Technology
\brief provides a technology never seen before.
diff --git a/src/qdoc/doc/examples/signalandslots.qdocinc b/src/qdoc/qdoc/doc/examples/signalandslots.qdocinc
index e14ede144..e14ede144 100644
--- a/src/qdoc/doc/examples/signalandslots.qdocinc
+++ b/src/qdoc/qdoc/doc/examples/signalandslots.qdocinc
diff --git a/src/qdoc/doc/files/basicqt.qdoc.sample b/src/qdoc/qdoc/doc/files/basicqt.qdoc.sample
index 1243387b2..1243387b2 100644
--- a/src/qdoc/doc/files/basicqt.qdoc.sample
+++ b/src/qdoc/qdoc/doc/files/basicqt.qdoc.sample
diff --git a/src/qdoc/doc/files/compat.qdocconf b/src/qdoc/qdoc/doc/files/compat.qdocconf
index 3e7ea6c89..94e2ffd7d 100644
--- a/src/qdoc/doc/files/compat.qdocconf
+++ b/src/qdoc/qdoc/doc/files/compat.qdocconf
@@ -1,5 +1,3 @@
-alias.include = input
-
macro.0 = "\\\\0"
macro.b = "\\\\b"
macro.n = "\\\\n"
diff --git a/src/qdoc/doc/files/qtgui.qdocconf b/src/qdoc/qdoc/doc/files/qtgui.qdocconf
index ae873e83d..ae873e83d 100644
--- a/src/qdoc/doc/files/qtgui.qdocconf
+++ b/src/qdoc/qdoc/doc/files/qtgui.qdocconf
diff --git a/src/qdoc/doc/images/happyguy.jpg b/src/qdoc/qdoc/doc/images/happyguy.jpg
index e8604793c..e8604793c 100644
--- a/src/qdoc/doc/images/happyguy.jpg
+++ b/src/qdoc/qdoc/doc/images/happyguy.jpg
Binary files differ
diff --git a/src/qdoc/doc/images/link-to-qquickitem.png b/src/qdoc/qdoc/doc/images/link-to-qquickitem.png
index 00e03c371..00e03c371 100644
--- a/src/qdoc/doc/images/link-to-qquickitem.png
+++ b/src/qdoc/qdoc/doc/images/link-to-qquickitem.png
Binary files differ
diff --git a/src/qdoc/doc/images/links-to-broken-links.png b/src/qdoc/qdoc/doc/images/links-to-broken-links.png
index 775143bd4..775143bd4 100644
--- a/src/qdoc/doc/images/links-to-broken-links.png
+++ b/src/qdoc/qdoc/doc/images/links-to-broken-links.png
Binary files differ
diff --git a/src/qdoc/doc/images/links-to-links.png b/src/qdoc/qdoc/doc/images/links-to-links.png
index 9d2cc2fae..9d2cc2fae 100644
--- a/src/qdoc/doc/images/links-to-links.png
+++ b/src/qdoc/qdoc/doc/images/links-to-links.png
Binary files differ
diff --git a/src/qdoc/qdoc/doc/images/qt-logo.png b/src/qdoc/qdoc/doc/images/qt-logo.png
new file mode 100644
index 000000000..835f5a3fe
--- /dev/null
+++ b/src/qdoc/qdoc/doc/images/qt-logo.png
Binary files differ
diff --git a/src/qdoc/doc/images/training.jpg b/src/qdoc/qdoc/doc/images/training.jpg
index c2ce5c3b2..c2ce5c3b2 100644
--- a/src/qdoc/doc/images/training.jpg
+++ b/src/qdoc/qdoc/doc/images/training.jpg
Binary files differ
diff --git a/src/qdoc/doc/images/windows-pushbutton.png b/src/qdoc/qdoc/doc/images/windows-pushbutton.png
index 14528d680..14528d680 100644
--- a/src/qdoc/doc/images/windows-pushbutton.png
+++ b/src/qdoc/qdoc/doc/images/windows-pushbutton.png
Binary files differ
diff --git a/src/qdoc/doc/images/windows-toolbutton.png b/src/qdoc/qdoc/doc/images/windows-toolbutton.png
index 9ceb846ed..9ceb846ed 100644
--- a/src/qdoc/doc/images/windows-toolbutton.png
+++ b/src/qdoc/qdoc/doc/images/windows-toolbutton.png
Binary files differ
diff --git a/src/qdoc/doc/qdoc-guide/qdoc-guide.qdoc b/src/qdoc/qdoc/doc/qdoc-guide/qdoc-guide.qdoc
index 372813b16..ff9ac808c 100644
--- a/src/qdoc/doc/qdoc-guide/qdoc-guide.qdoc
+++ b/src/qdoc/qdoc/doc/qdoc-guide/qdoc-guide.qdoc
@@ -1,35 +1,11 @@
-/****************************************************************************
-**
-** Copyright (C) 2020 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the documentation of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:FDL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Free Documentation License Usage
-** Alternatively, this file may be used under the terms of the GNU Free
-** Documentation License version 1.3 as published by the Free Software
-** Foundation and appearing in the file included in the packaging of
-** this file. Please review the following information to ensure
-** the GNU Free Documentation License version 1.3 requirements
-** will be met: https://www.gnu.org/licenses/fdl-1.3.html.
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2020 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
\page qdoc-guide.html
\title Getting Started with QDoc
\nextpage Installing Clang for QDoc
- Qt uses QDoc to generate its documentation set into HTML and DITA XML
+ Qt uses QDoc to generate its documentation set into HTML and DocBook XML
formats. QDoc uses a set of configuration files to generate documentation
from QDoc comments. The comments have types called
\l{writing-topic-commands}{topics} that determine whether a comment is a
@@ -89,11 +65,11 @@
can also be in other qdocconf files. The \c {include(<filepath>)} command
allows configuration files to include other configuration files.
- QDoc has two outputs, HTML documentation and documentation in DITA XML
+ QDoc has two outputs, HTML documentation and documentation in DocBook XML
format. The main distinction between the two outputs is that HTML
documentation needs to have its HTML styling information in the
- configuration files. DITA XML documentation does not, and a separate process
- can style the documentation in DITA at a later time. DITA XML is therefore
+ configuration files. DocBook documentation does not, and a separate process
+ can style the documentation in DocBook at a later time. DocBook therefore
more flexible in allowing different styles to apply to the same information.
To run QDoc, the project configuration file is supplied as an argument.
@@ -200,7 +176,7 @@
The \c{HTML.headerstyles} variable inserts the style information into the
HTML file and the \c{HTML.stylesheets} specifies which files QDoc should
- copy into the output directory. As well, QDoc will embed the string
+ copy into the output directory. In addition, QDoc will embed the string
in the \c postheader, \c footer, and related variables into each HTML file.
The \l {Format-specific Configuration Variables} article outlines the usage
@@ -262,7 +238,7 @@
Documentation is contained within QDoc \e comments, delimited by
\beginqdoc and \endqdoc comments. Note that these are valid comments
- in C++, QML, and JavaScript.
+ in C++ and QML.
Within a QDoc comment, \c {//!} is used as a single-line documentation
comment; the comment itself and anything after it, until a newline,
@@ -496,7 +472,7 @@
\list
\li \l{qmlattachedproperty-command}{\\qmlattachedproperty}
\li \l{qmlattachedsignal-command}{\\qmlattachedsignal}
- \li \l{qmlbasictype-command}{\\qmlbasictype}
+ \li \l{qmlvaluetype-command}{\\qmlvaluetype}
\li \l{qmltype-command}{\\qmltype} - creates a QML type documentation
\li \l{qmlmethod-command}{\\qmlmethod}
\li \l{qmlproperty-command}{\\qmlproperty}
@@ -504,7 +480,7 @@
\li \l{inherits-command}{\\inherits}
\li \l{qmlmodule-command}{\\qmlmodule}
\li \l{inqmlmodule-command}{\\inqmlmodule}
- \li \l{instantiates-command}{\\instantiates}
+ \li \l{nativetype-command}{\\nativetype}
\endlist
@@ -519,7 +495,7 @@
If your QML type is defined in a \e qml file, document it there.
If your QML type is represented by a C++ class, document it in the
\e cpp file for that C++ class and include an
- \l{instantiates-command}{\\instantiates} command to specify the
+ \l{nativetype-command}{\\nativetype} command to specify the
name of the C++ class. Don't document a QML type in a \e{cpp} file
if the QML type is defined in a \e{qml} file.
@@ -663,25 +639,32 @@
QDoc uses Clang when parsing C++ files as well as function signatures in
\l {fn-command} {\\fn} commands. Clang is part of
\l {https://llvm.org/}{the LLVM Compiler Infrastructure Project}.
- Therefore, if you are going to build QDoc from source, you need to install
- \l{http://clang.llvm.org}{LLVM 8.0} or greater first.
+ If you're going to build QDoc from source, you must install
+ \l{http://clang.llvm.org}{Clang 15.0} or later.
You can get Clang through various channels:
\list
- \li Qt provides \l{http://download.qt.io/development_releases/prebuilt/libclang/qt}
- {prebuilt Clang packages} that are also used for the Qt binaries in the online
- installer. They allow to link LLVM/clang libraries statically, but only support Release
- builds on Windows.
- \li Linux distributions often provide a package called libclang-dev or libclang-devel.
+ \li Qt provides the
+ \l{http://download.qt.io/development_releases/prebuilt/libclang/qt}
+ {prebuilt Clang packages} that are used for the Qt binaries in the
+ online installer. These let you link LLVM/Clang libraries statically,
+ but only support Release builds on Windows.
+ \li Linux distributions often provide a package called \e libclang-dev or
+ \e libclang-devel. Qt's build system instructs CMake to look for
+ \e {ClangConfig.cmake}, so make sure you install the package that
+ provides this file if you want to build QDoc. Running QDoc requires
+ only \e libclang.
\li On macOS, you can also use Homebrew's
\l{https://formulae.brew.sh/formula/llvm}{llvm formula}.
\endlist
- \note the prebuilt binaries from \l{http://releases.llvm.org/download.html} cannot
- be used, as they miss certain components required by QDoc.
+ \note the prebuilt binaries from \l{http://releases.llvm.org/download.html}
+ cannot be used, as they miss certain components required by QDoc.
If you install Clang in a custom location you need to tell CMake where
- to find it. This can be done by adding the installation path to
- the \c CMAKE_PREFIX_PATH CMake cache variable.
+ to find it. This can be done by specifying your LLVM installation path
+ using the \c LLVM_INSTALL_DIR environment variable when configuring Qt.
+ Alternatively, you can add the installation path to the \c CMAKE_PREFIX_PATH
+ CMake cache variable.
*/
diff --git a/src/qdoc/doc/qdoc-guide/qtwritingstyle-cpp.qdoc b/src/qdoc/qdoc/doc/qdoc-guide/qtwritingstyle-cpp.qdoc
index 9420c799b..43387ca7e 100644
--- a/src/qdoc/doc/qdoc-guide/qtwritingstyle-cpp.qdoc
+++ b/src/qdoc/qdoc/doc/qdoc-guide/qtwritingstyle-cpp.qdoc
@@ -1,29 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the documentation of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:FDL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Free Documentation License Usage
-** Alternatively, this file may be used under the terms of the GNU Free
-** Documentation License version 1.3 as published by the Free Software
-** Foundation and appearing in the file included in the packaging of
-** this file. Please review the following information to ensure
-** the GNU Free Documentation License version 1.3 requirements
-** will be met: https://www.gnu.org/licenses/fdl-1.3.html.
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
\page qtwritingstyle-cpp.html
diff --git a/src/qdoc/doc/qdoc-guide/qtwritingstyle-qml.qdoc b/src/qdoc/qdoc/doc/qdoc-guide/qtwritingstyle-qml.qdoc
index b8350485a..91d2094cb 100644
--- a/src/qdoc/doc/qdoc-guide/qtwritingstyle-qml.qdoc
+++ b/src/qdoc/qdoc/doc/qdoc-guide/qtwritingstyle-qml.qdoc
@@ -1,29 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the documentation of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:FDL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Free Documentation License Usage
-** Alternatively, this file may be used under the terms of the GNU Free
-** Documentation License version 1.3 as published by the Free Software
-** Foundation and appearing in the file included in the packaging of
-** this file. Please review the following information to ensure
-** the GNU Free Documentation License version 1.3 requirements
-** will be met: https://www.gnu.org/licenses/fdl-1.3.html.
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
\page qtwritingstyle-qml.html
@@ -39,14 +15,14 @@ documented with the QML \l{topic-commands}{topic commands}:
\list
\li \l{qmlattachedproperty-command}{\\qmlattachedproperty}
\li \l{qmlattachedsignal-command}{\\qmlattachedsignal}
-\li \l{qmlbasictype-command}{\\qmlbasictype}
+\li \l{qmlvaluetype-command}{\\qmlvaluetype}
\li \l{qmltype-command}{\\qmltype}
\li \l{qmlmethod-command}{\\qmlmethod}
\li \l{qmlproperty-command}{\\qmlproperty}
\li \l{qmlsignal-command}{\\qmlsignal}
\li \l{qmlmodule-command}{\\qmlmodule}
\li \l{inqmlmodule-command}{\\inqmlmodule}
-\li \l{instantiates-command}{\\instantiates}
+\li \l{nativetype-command}{\\nativetype}
\endlist
For QML types defined in \c .qml files, QDoc will parse the QML and determine
@@ -62,7 +38,7 @@ The \l{qmltype-command}{\\qmltype} command is for QML type documentation.
\snippet examples/qml.qdoc.sample qmltype
-The \l{instantiates-command}{\\instantiates} accepts the C++ class which
+The \l{nativetype-command}{\\nativetype} command accepts the C++ class which
implements the QML type as the argument. For types implemented in QML, this
is not needed.
@@ -117,11 +93,11 @@ These are the possible documentation styles for signals:
\li "Emitted when..."
\endlist
-\section1 Methods and JavaScript Functions
+\section1 Methods and QML Functions
Typically, function documentation immediately precedes the implementation of the
function in the \c .cpp file. The \l{topic-commands}{topic command} for
-functions is \l{fn-command}{\\fn}. For functions in QML or JavaScript, the
+functions is \l{fn-command}{\\fn}. For functions in QML, the
documentation must reside immediately above the function declaration.
The function documentation starts with a verb, indicating the operation the
diff --git a/src/qdoc/doc/qdoc-manual-cmdindex.qdoc b/src/qdoc/qdoc/doc/qdoc-manual-cmdindex.qdoc
index 03b6e2711..773ec1cfb 100644
--- a/src/qdoc/doc/qdoc-manual-cmdindex.qdoc
+++ b/src/qdoc/qdoc/doc/qdoc-manual-cmdindex.qdoc
@@ -1,29 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the documentation of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:FDL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Free Documentation License Usage
-** Alternatively, this file may be used under the terms of the GNU Free
-** Documentation License version 1.3 as published by the Free Software
-** Foundation and appearing in the file included in the packaging of
-** this file. Please review the following information to ensure
-** the GNU Free Documentation License version 1.3 requirements
-** will be met: https://www.gnu.org/licenses/fdl-1.3.html.
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
\page 27-qdoc-commands-alphabetical.html
@@ -39,17 +15,22 @@
\li \l {a-command} {\\a}
\li \l {abstract-command} {\\abstract}
\li \l {annotatedlist-command} {\\annotatedlist}
+ \li \l {attribution-command} {\\attribution}
\li \l {b-command} {\\b}
\li \l {badcode-command} {\\badcode}
\li \l {b-command} {\\bold} (deprecated, use \\b)
+ \li \l {br-command} {\\br}
\li \l {brief-command} {\\brief}
\li \l {c-command} {\\c}
\li \l {caption-command} {\\caption}
\li \l {class-command} {\\class}
\li \l {code-command} {\\code}
\li \l {codeline-command} {\\codeline}
+ \li \l {compares-command} {\\compares}
+ \li \l {compareswith-command} {\\compareswith}
\li \l {deprecated-command} {\\deprecated}
\li \l {default-command} {\\default}
+ \li \l {details-command} {\\details}
\li \l {div-command} {\\div}
\li \l {dots-command} {\\dots}
\li \l {e-command} {\\e}
@@ -74,7 +55,7 @@
\li \l {inlineimage-command} {\\inlineimage}
\li \l {inmodule-command} {\\inmodule}
\li \l {inqmlmodule-command} {\\inqmlmodule}
- \li \l {instantiates-command} {\\instantiates}
+ \li \l {instantiates-command} {\\instantiates} (deprecated, use \\nativetype)
\li \l {internal-command} {\\internal}
\li \l {keyword-command} {\\keyword}
\li \l {l-command} {\\l}
@@ -84,14 +65,14 @@
\li \l {macro-command} {\\macro}
\li \l {meta-command} {\\meta}
\li \l {module-command} {\\module}
+ \li \l {modulestate-command} {\\modulestate}
\li \l {namespace-command} {\\namespace}
+ \li \l {nativetype-command} {\\nativetype}
\li \l {nextpage-command} {\\nextpage}
- \li \l {newcode-command} {\\newcode}
\li \l {noautolist-command} {\\noautolist}
\li \l {nonreentrant-command} {\\nonreentrant}
\li \l {note-command} {\\note}
\li \l {li-command} {\\o} (deprecated, use \\li)
- \li \l {oldcode-command} {\\oldcode}
\li \l {omit-command} {\\omit}
\li \l {omitvalue-command} {\\omitvalue}
\li \l {overload-command} {\\overload}
@@ -106,15 +87,17 @@
\li \l {qmlabstract-command} {\\qmlabstract}
\li \l {qmlattachedproperty-command} {\\qmlattachedproperty}
\li \l {qmlattachedsignal-command} {\\qmlattachedsignal}
- \li \l {qmlbasictype-command} {\\qmlbasictype}
+ \li \l {qmlvaluetype-command} {\\qmlvaluetype}
\li \l {qmlclass-command} {\\qmlclass} (deprecated, use \\qmltype)
\li \l {qmldefault-command} {\\qmldefault}
+ \li \qdoccmd qmlenumeratorsfrom
\li \l {qmltype-command} {\\qmltype}
\li \l {qmlmethod-command} {\\qmlmethod}
\li \l {qmlproperty-command} {\\qmlproperty}
\li \l {qmlsignal-command} {\\qmlsignal}
\li \l {qmlmodule-command} {\\qmlmodule}
\li \l {qtcmakepackage-command} {\\qtcmakepackage}
+ \li \l {qtcmaketargetitem-command} {\\qtcmaketargetitem}
\li \l {quotation-command} {\\quotation}
\li \l {quotefile-command} {\\quotefile}
\li \l {quotefromfile-command} {\\quotefromfile}
@@ -131,6 +114,7 @@
\li \l {sectionThree-command} {\\section3}
\li \l {sectionFour-command} {\\section4}
\li \l {since-command} {\\since}
+ \li \l {sincelist-command} {\\sincelist}
\li \l {skipline-command} {\\skipline}
\li \l {skipto-command} {\\skipto}
\li \l {skipuntil-command} {\\skipuntil}
@@ -145,6 +129,7 @@
\li \l {target-command} {\\target}
\li \l {threadsafe-command} {\\threadsafe}
\li \l {title-command} {\\title}
+ \li \qdoccmd tm
\li \l {tt-command} {\\tt}
\li \l {typealias-command} {\\typealias}
\li \l {typedef-command} {\\typedef}
@@ -153,5 +138,6 @@
\li \l {variable-command} {\\variable}
\li \l {value-command} {\\value}
\li \l {warning-command} {\\warning}
+ \li \l {wrapper-command} {\\wrapper}
\endlist
*/
diff --git a/src/qdoc/doc/qdoc-manual-contextcmds.qdoc b/src/qdoc/qdoc/doc/qdoc-manual-contextcmds.qdoc
index d64d85115..7c62c08bd 100644
--- a/src/qdoc/doc/qdoc-manual-contextcmds.qdoc
+++ b/src/qdoc/qdoc/doc/qdoc-manual-contextcmds.qdoc
@@ -1,29 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the documentation of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:FDL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Free Documentation License Usage
-** Alternatively, this file may be used under the terms of the GNU Free
-** Documentation License version 1.3 as published by the Free Software
-** Foundation and appearing in the file included in the packaging of
-** this file. Please review the following information to ensure
-** the GNU Free Documentation License version 1.3 requirements
-** will be met: https://www.gnu.org/licenses/fdl-1.3.html.
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
\page 14-qdoc-commands-contextcommands.html
@@ -47,6 +23,9 @@
\list
\li \l {abstract-command} {\\abstract}
+ \li \l {attribution-command} {\\attribution}
+ \li \l {compares-command}{\\compares} (Since QDoc 6.7)
+ \li \l {compareswith-command}{\\compareswith} (Since QDoc 6.7)
\li \l {default-command} {\\default}
\li \l {deprecated-command}{\\deprecated}
\li \l {ingroup-command}{\\ingroup}
@@ -54,6 +33,7 @@
\li \l {inherits-command}{\\inherits}
\li \l {inmodule-command}{\\inmodule}
\li \l {internal-command}{\\internal}
+ \li \l {modulestate-command} {\\modulestate} (Since QDoc 6.5)
\li \l {nextpage-command}{\\nextpage}
\li \l {nonreentrant-command}{\\nonreentrant}
\li \l {overload-command}{\\overload}
@@ -61,7 +41,9 @@
\li \l {previouspage-command}{\\previouspage}
\li \l {qmlabstract-command} {\\qmlabstract}
\li \l {qmldefault-command} {\\qmldefault}
+ \li \qdoccmd qmlenumeratorsfrom (Since QDoc 6.8)
\li \l {qtcmakepackage-command} {\\qtcmakepackage}
+ \li \l {qtcmaketargetitem-command} {\\qtcmaketargetitem}
\li \l {readonly-command} {\\readonly}
\li \l {reentrant-command}{\\reentrant}
\li \l {reimp-command}{\\reimp}
@@ -72,6 +54,7 @@
\li \l {subtitle-command}{\\subtitle}
\li \l {threadsafe-command}{\\threadsafe}
\li \l {title-command}{\\title}
+ \li \l {wrapper-command}{\\wrapper}
\endlist
*/
@@ -90,41 +73,6 @@
\section1 Example
\quotefile files/basicqt.qdoc.sample
- QDoc renders the "Getting Started" page in \c{creatingdialogs.html}:
-
- \quotation
- \raw HTML
- <table border="0" cellpadding="0" cellspacing="5" width="100%">
-
- <tr>
- <p>
- [Previous: <a href="15-qdoc-commands-navigation.html#deadlink">
- Basic Qt</a>]
- [<a href="15-qdoc-commands-navigation.html#deadlink">Contents</a>]
- [Next: <a href="15-qdoc-commands-navigation.html#deadlink">
- Creating Dialogs</a>]
- </p>
-
- <h1 align="center">Getting Started<br /></h1>
-
- <p>
- This chapter shows how to combine basic C++ with the
- functionality provided by Qt to create a few small graphical
- interface (GUI) applications.
- </p>
-
- <p>
- [Previous: <a href="15-qdoc-commands-navigation.html#deadlink">
- Basic Qt</a>]
- [<a href="15-qdoc-commands-navigation.html#deadlink">Contents</a>]
- [Next: <a href="15-qdoc-commands-navigation.html#deadlink">
- Creating Dialogs</a>]
- </p>
-
- </table>
- \endraw
- \endquotation
-
The \l {startpage-command} {\\startpage} command creates a link to
the page the author wants as the first page of a multipage document.
@@ -209,6 +157,16 @@
generated. It the abstract QML type is not marked internal, it
will have a reference page in the documentation.
+ \target attribution-command
+ \section1 \\attribution
+
+ The \\attribution command marks a documented \qdoccmd page as license
+ attribution documentation.
+
+ The \l {annotatedattributions} {\\generatelist annotatedattributions}
+ command generates an annotated list of all license attribution pages
+ in the documentation project.
+
\target default-command
\section1 \\default
@@ -232,6 +190,69 @@
\1/
\endcode
+ \target compares-command
+ \section2 \\compares
+
+ Use the \c {\compares} command to describe the comparison results for the
+ documented C++ type when compared to itself. You must use this command in
+ conjunction with the \l {class-command}{\\class} command.
+
+ \c {\compares} takes one of the following arguments:
+
+ //! [comparison-categories]
+ \list
+ \li \c strong
+ \li \c partial
+ \li \c weak
+ \li \c equality
+ \endlist
+
+ \c {strong}, \c {partial}, and \c {weak} relate to the ordering.
+ \c {equality} means that the type is only compared for equality.
+ //! [comparison-categories]
+
+ This command was introduced to QDoc with Qt 6.7.
+
+ See also \l {compareswith-command}{\\compareswith}.
+
+ \target compareswith-command
+ \section1 \\compareswith
+
+ Use the \c {\compareswith .. \endcompareswith} pair of commands to
+ describe the comparison results for the documented C++ type when
+ compared to other types. \c {\compareswith} takes two or more
+ arguments: a comparison category, followed by a type name, or a
+ space-separated list of type names. Any text lines between
+ \c {\compareswith} and \c {\endcompareswith} commands are
+ considered further details that apply to all types subject
+ to the comparison category argument.
+
+ Types that have one or more space in their name, such as
+ \c{unsigned long}, should be enclosed in braces.
+
+ For example:
+
+ \badcode *
+ /\1!
+ ...
+ \compareswith strong int long {unsigned long} {unsigned int} char
+ ...
+ \endcompareswith
+ ...
+ \1/
+ \endcode
+
+ Argument enclosed in braces have their leading and trailing whitespaces
+ removed.
+ For example, \c{unsigned long} and \c{ unsigned long } are equivalent.
+
+ The comparison category argument must be one of the following:
+ \include qdoc-manual-contextcmds.qdoc comparison-categories
+
+ This command was introduced to QDoc with Qt 6.7.
+
+ See also \l {compares-command}{\\compares}.
+
\target qmldefault-command
\section1 \\qmldefault
@@ -240,8 +261,8 @@
{default property}. The word \c default is displayed in
the documentation of the property.
- \code
- / *!
+ \badcode *
+ /\1!
\qmlproperty list<Change> State::changes
This property holds the changes to apply for this state.
\qmldefault
@@ -249,12 +270,56 @@
By default, these changes are applied against the default state. If the state
extends another state, then the changes are applied against the state being
extended.
- * /
+ \1/
\endcode
See how QDoc renders this property on the reference page for the
\l {QtQuick::State::changes}{State} type.
+ \target qmlenumeratorsfrom-command
+ \section1 \\qmlenenumeratorsfrom
+
+ Use the \\qmlenumeratorsfrom command in a \qdoccmd qmlproperty topic
+ with a property type \e enumeration, to automatically replicate the
+ documentation for enumerators from a C++ \qdoccmd enum topic.
+
+ The command takes a fully qualified C++ enum as an argument,
+ and generates a list of enumerators and their descriptions.
+
+ \note The C++ enum must be documented in the same project; QDoc
+ cannot access its documentation if it's part of an external
+ documentation set that the current project \qdocvar depends
+ on.
+
+ By default, each enumerator is prefixed with the type name the
+ property belongs to, with \c{.} as the separator.
+
+ For example:
+
+ \badcode *
+ /\1!
+ \qmlproperty enumeration QtMultimedia::Camera::error
+ \qmlenumeratorsfrom QCamera::Error
+
+ //! Outputs documentation for 'Camera.NoError', 'Camera.CameraError'
+ \1/
+ \endcode
+
+ If the enumerators are registered to QML under a different type
+ name, this name (prefix) can be specified using the optional
+ argument in square brackets:
+
+ \badcode
+ \qmlenumeratorsfrom [Errors] QCamera::Error
+
+ //! Outputs documentation for 'Errors.NoError', 'Errors.CameraError'
+ \1/
+ \endcode
+
+ This command was introduced in QDoc 6.8.
+
+ See also \qdoccmd {qmlproperty}, \qdoccmd {enum}, and \qdoccmd {value}.
+
\target dontdocument-command
\section1 \\dontdocument
@@ -267,10 +332,10 @@
Below you will find the \\dontdocument command in the
dontdocument.qdoc for widgets:
- \badcode
- / *!
+ \badcode *
+ /\1!
\dontdocument (QTypeInfo QMetaTypeId)
- * /
+ \1/
\endcode
\target inheaderfile-command
@@ -328,41 +393,13 @@
functions. It is good practice to suggest an equivalent function
as an alternative.
- \code
- / *!
+ \badcode *
+ /\1!
\fn MyClass::MyDeprecatedFunction
\deprecated [6.2] Use MyNewFunction() instead.
- * /
+ \1/
\endcode
- QDoc renders this in \c{myclass-obsolete.html} as:
-
- \quotation
- \raw HTML
- <h1>Deprecated Members for MyClass</h1>
- \endraw
-
- \b {The following class members are deprecated.} We strongly
- advise against using them in new code.
-
- ...
-
- \list
- \li void MyDeprecatedFunction() \c (deprecated)
- \li ...
- \endlist
-
- \raw HTML
- <hr />
- <h2>Member Function Documentation</h2>
- <h3>void MyDeprecatedFunction ()</h3>
- <p>This function is deprecated since 6.2. It is provided to
- keep old source code working. We strongly advise against using
- it in new code. Use MyNewFunction() instead.</p>
- \endraw
- ...
- \endquotation
-
\target internal-command
\section1 \\internal
@@ -374,14 +411,14 @@
QDoc ignores the documentation as well as the documented item,
when generating the associated class reference documentation.
- \code
- / *!
+ \badcode *
+ /\1!
\internal
Tries to find the decimal separator. If it can't find
it and the thousand delimiter is != '.' it will try to
find a '.';
- * /
+ \1/
int QDoubleSpinBoxPrivate::findDelimiter
(const QString &str, int index) const
{
@@ -396,6 +433,32 @@
is called with the \c{-showinternal} command line option or the
\c{QDOC_SHOW_INTERNAL} environment variable is set.
+ \target modulestate-command
+ \section1 \\modulestate
+
+ The \\modulestate command can be used within a \\module or \\qmlmodule
+ topic to provide a module state description other than \e preliminary or
+ \e deprecated.
+
+ Rest of the line is taken as an argument that describes the module's
+ state. For example:
+
+ \badcode *
+ /*!
+ \module QtFoo
+ \modulestate Technical Preview
+ \1/
+ \endcode
+
+ QDoc will then add this information on the module page:
+
+ \quotation
+ This module is in \e {Technical Preview} state.
+ \endquotation
+
+ In HTML output, this state information appears also in the navigation
+ bar (breadcrumbs) of reference pages for the module's members.
+
\target preliminary-command
\section1 \\preliminary
@@ -408,49 +471,21 @@
function documentation, and marks the function as preliminary when
it appears in lists.
- \code
- / *!
+ \badcode *
+ /\1!
\preliminary
Returns information about the joining type attributes of the
character (needed for certain languages such as Arabic or
Syriac).
- * /
+ \1/
QChar::JoiningType QChar::joiningType() const
{
return QChar::joiningType(ucs);
}
\endcode
- QDoc renders this as:
-
- \quotation
- \raw HTML
- <h3>
- <a href="http://doc.qt.io/qt-5/qchar.html#JoiningType-enum">JoiningType</a>
- QChar::joiningType() const</h3>
- \endraw
-
- \b {This function is under development and
- subject to change.}
-
- Returns information about the joining type attributes of the
- character (needed for certain languages such as Arabic or
- Syriac).
- \endquotation
-
- And the function's entry in QChar's list of public functions will be
- rendered as:
-
- \quotation
- \list
- \li ...
- \li JoiningType \l {QChar::joiningType()} {joiningType}() const \c (preliminary)
- \li ...
- \endlist
- \endquotation
-
\target readonly-command
\section1 \\readonly
@@ -471,44 +506,38 @@
The \\since command tells in which minor release
the associated functionality was added.
- \code
- / *!
- \since 4.1
+ If the argument passed to \\since contains no spaces, it is assumed to be
+ a version number string for the Qt project, and QDoc will prefix it with
+ 'Qt' in the generated output. The argument can also contain the project
+ name explicitly:
- Returns an icon for \a standardIcon.
-
- ...
-
- \sa standardPixmap()
- * /
- QIcon QStyle::standardIcon(StandardPixmap standardIcon, const QStyleOption *option, const QWidget *widget) const
- {
- }
+ \badcode
+ \since MyFramework 2.0
\endcode
- QDoc renders this as:
+ In this case, the arguments (project and version) are used as is.
- \quotation
- \raw HTML
- <h3>QIcon QStyle::standardIcon(StandardPixmap standardIcon, const QStyleOption *option, const QWidget *widget) const</h3>
- \endraw
+ \section2 Inheritance of Since Information
- This function was introduced in Qt version 4.1
+ Since QDoc version 6.5, C++ classes and QML types inherit the \\since statement
+ from their respective \l {module-command}{module} or \l {qmlmodule-command}
+ {QML module}, unless \\since is explicitly used in the type documentation.
- Returns an icon for \a standardIcon.
+ \section2 Since Clause
- ...
+ The \\value command allows an optional \e {since} clause, enclosed in square
+ brackets, to immediately follow the command string. This is used for
+ marking specific C++ enum values with since information.
- See also \l {QStyle::standardPixmap()} {standardPixmap()}.
- \endquotation
+ See also \l {value-command}{\\value} and \l {ignoresince}.
- QDoc generates the "Qt" reference from the \l
- {25-qdoc-configuration-derivedprojects.html#project} {\c project}
- configuration variable. For that reason this reference will change
- according to the current documentation project.
+ \target wrapper-command
+ \section1 \\wrapper
- See also \l {project}
- {\c project}.
+ The \\wrapper command, when used in a C++ class documentation, marks the
+ class as a \e wrapper that provides access to a non-Qt API. This command
+ is used for suppressing warnings that might otherwise be generated for
+ members of such a class.
*/
@@ -543,8 +572,8 @@
\section1 Example
\target reentrant-example
- \code
- \beginqdoc
+ \badcode *
+ /\1!
\class QLocale
\brief The QLocale class converts between numbers and their
string representations in various languages.
@@ -571,62 +600,14 @@
threads are created.
\sa system(), c()
- \endqdoc
+ \1/
void QLocale::setDefault(const QLocale &locale)
{
default_d = locale.d;
}
\endcode
- QDoc renders this as:
-
- \quotation
- \raw HTML
- <h1><center>QLocale Class Reference</center></h1>
- \endraw
-
- The QLocale class converts between numbers and their string
- representations in various languages. More...
-
- \code
- #include <QLocale>
- \endcode
-
- \b {Note:} All the functions in this class are \l
- {17-qdoc-commands-thread.html#reentrant} {reentrant}, except \l
- {QLocale::setDefault()} {setDefault()}.
-
- ...
-
- \raw HTML
- <hr />
- <h2>Member Type Documentation</h2>
- \endraw
-
- ...
-
- \raw HTML
- <h3>void QLocale::setDefault ( const QLocale & locale ) </h3>
- \endraw
-
- Sets the global default locale to locale. These values are
- used when a QLocale object is constructed with no
- arguments. If this function is not called, the system's locale
- is used.
-
- \warning In a multithreaded application, the default locale
- should be set at application startup, before any non-GUI
- threads are created.
-
- \warning This function is not reentrant.
-
- See also \l {QLocale::system()} {system()} and \l
- {QLocale::c()} {c()}.
-
- ...
- \endquotation
-
- As shown above, QDoc generates a notification when a class is
+ QDoc generates a notification when a class is
declared reentrant, and lists the exceptions (the declared
nonreentrant functions). A link to the general documentation on \l
{17-qdoc-commands-thread.html#reentrant} {reentrancy and thread-safety} is
@@ -719,10 +700,10 @@
inheriting element's \l{qmltype-command}{\\qmltype} comment.
The argument is the name of the inherited QML type.
- \code
- / *!
+ \badcode *
+ /\1!
\qmltype PauseAnimation
- \instantiates QDeclarativePauseAnimation
+ \nativetype QDeclarativePauseAnimation
\ingroup qml-animation-transition
\since 4.7
\inherits Animation
@@ -740,7 +721,7 @@
}
\sa {QML Animation and Transitions}, {declarative/animation/basics}{Animation basics example}
- * /
+ \1/
\endcode
QDoc includes this line on the reference page for the
@@ -770,8 +751,8 @@
\e{This function overloads...} line of text with a link
to the documentation for the primary version of the function.
- \code
- / *!
+ \badcode *
+ /\1!
\overload addAction()
This convenience function creates a new action with an
@@ -780,7 +761,7 @@
returns it.
\sa QWidget::addAction()
- * /
+ \1/
QAction *QMenu::addAction(const QIcon &icon, const QString &text)
{
QAction *ret = new QAction(icon, text, this);
@@ -789,27 +770,6 @@
}
\endcode
- QDoc renders this as:
-
- \quotation
- \raw HTML
- <h3><a href="http://doc.qt.io/qt-5/qaction.html">QAction</a>
- * QMenu::addAction ( const QIcon & <i>icon</i>,
- const QString & <i>text</i> )
- </h3>
- \endraw
-
- This function overloads \l {QMenu::addAction()} {addAction()}
-
- This convenience function creates a new action with an
- \e icon and some \e text. The function adds the newly
- created action to the menu's list of actions, and
- returns it.
-
- See also
- \l {QWidget::addAction()} {QWidget::addAction}().
- \endquotation
-
If you don't include the function name with the \b{\\overload}
command, then instead of the "This function overloads..." line
with the link to the documentation for the primary version, you
@@ -831,10 +791,10 @@
QDoc will omit the reimplemented function from the class
reference.
- \code
- / *!
+ \badcode *
+ /\1!
\reimp
- * /
+ \1/
void QToolButton::nextCheckState()
{
Q_D(QToolButton);
@@ -856,14 +816,14 @@
global element to some class or header file. The argument is a
class name or header file. For template types, use the type name only.
- \code
- / *!
+ \badcode *
+ /\1!
\relates QChar
Reads a char from the stream \a in into char \a chr.
\sa {Format of the QDataStream operators}
- * /
+ \1/
QDataStream &operator>>(QDataStream &in, QChar &chr)
{
quint16 u;
@@ -895,34 +855,57 @@
\section2 \\ingroup
The \\ingroup command indicates that the given
- overview or documented class belongs to a certain group of
- related docmentation.
+ class, page, or other entity belongs to a certain group of
+ related documentation.
- A class or overview may belong to many groups.
+ An entity may belong to multiple groups.
The \\ingroup command's argument is a group name, but note
that the command considers the rest of the line as part of
its argument. Make sure that the group name is followed by
a linebreak.
- \code
- / *!
+ \badcode *
+ /\1!
\class QDir
\brief The QDir class provides access to directory
structures and their contents.
\ingroup io
...
- * /
+ \1/
+ \endcode
+
+ This adds the QDir class to the \c io group. An entry for QDir
+ will then appear on the list created with, for example,
+ the \l {annotatedlist-command} {\\annotatedlist} command with
+ an argument \c io.
+
+ QDoc automatically generates links to associated groups on a
+ C++ class, namespace, or header reference page. For example,
+ given the above documentation for class \QDir and the following
+ \l {group-command}{\\group} page:
+
+ \badcode *
+ /\1
+ \group io
+ \title Input/Output and Networking
+ ...
+ \1/
\endcode
- This will include the QDir class in the \c io group, which means,
- for example, that QDir will appear on the list created by calling
- the \l {group-command} {\\group} command with the \c io argument.
+ QDoc then outputs a statement on the QDir reference page:
+
+ \quotation
+ \list
+ \li \QDir is part of \l {Input/Output and Networking}.
+ \endlist
+ \endquotation
- To list overviews that are related to a certain group, you must
- generate the list explicitly using the \l {generatelist-command}
- {\\generatelist} command with the \c related argument.
+ For HTML output, QDoc also generates a link to the group
+ page as part of the navigation bar (breadcrumbs). If multiple
+ \\ingroup commands are used, the first one that references
+ a documented \\group is selected.
See also \l {group-command} {\\group}.
@@ -934,7 +917,7 @@
For the basic classes in Qt, a class's module is determined by its
location, namely its directory. However, for extensions like
- ActiveQt and Qt Designer, a class must be related to a module
+ ActiveQt and \QD, a class must be related to a module
explicitly.
The command's argument is a module name, but note that the command
@@ -949,7 +932,7 @@
\endcode
This ensures that the QDesignerTaskMenuExtension class is included
- in the Qt Designer module, which means, for example, that the
+ in the \QD module, which means, for example, that the
class will appear on the list created by calling the \l
{generatelist-command} {\\generatelist} command with the \c
{{classesbymodule QtDesigner}} argument.
@@ -978,8 +961,8 @@
The \\title command sets the title for a documentation page, or
allows you to override it.
- \code
- / *!
+ \badcode *
+ /\1!
\page signalandslots.html
\title Signals & Slots
@@ -990,22 +973,9 @@
from the features provided by other frameworks.
...
- * /
+ \1/
\endcode
- QDoc renders this as:
-
- \quotation
- \raw HTML
- <h1><center>Signal and Slots</center></h1>
- \endraw
-
- Signals and slots are used for communication between
- objects. The signals and slots mechanism is a central
- feature of Qt and probably the part that differs most
- from the features provided by other frameworks.
- ...
- \endquotation
See also \l {subtitle-command} {\\subtitle}.
\target subtitle-command
@@ -1013,8 +983,8 @@
The \\subtitle command sets a subtitle for a documentation page.
- \code
- \beginqdoc
+ \badcode *
+ /\1!
\page qtopiacore-overview.html
\title Qtopia Core
@@ -1024,23 +994,9 @@
complete and self-contained C++ GUI and platform
development tool for Linux-based embedded development.
...
- \endqdoc
+ \1/
\endcode
- QDoc renders this as:
-
- \quotation
- \raw HTML
- <h1><center>Qtopia Core</center></h1>
- <h2><center>Qt for Embedded Linux</center></h2>
- \endraw
-
- Qt/Embedded, the embedded Linux port of Qt, is a
- complete and self-contained C++ GUI and platform
- development tool for Linux-based embedded development.
- ...
- \endquotation
-
See also \l {title-command} {\\title}.
*/
diff --git a/src/qdoc/doc/qdoc-manual-intro.qdoc b/src/qdoc/qdoc/doc/qdoc-manual-intro.qdoc
index 8a89d6647..0aeb715c9 100644
--- a/src/qdoc/doc/qdoc-manual-intro.qdoc
+++ b/src/qdoc/qdoc/doc/qdoc-manual-intro.qdoc
@@ -1,29 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the documentation of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:FDL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Free Documentation License Usage
-** Alternatively, this file may be used under the terms of the GNU Free
-** Documentation License version 1.3 as published by the Free Software
-** Foundation and appearing in the file included in the packaging of
-** this file. Please review the following information to ensure
-** the GNU Free Documentation License version 1.3 requirements
-** will be met: https://www.gnu.org/licenses/fdl-1.3.html.
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
\page 01-qdoc-manual.html
@@ -35,13 +11,13 @@
QDoc is a tool used by Qt Developers to generate documentation for
software projects. It works by extracting \e {QDoc comments} from
project source files and then formatting these comments as HTML
- pages or DITA XML documents. QDoc finds QDoc comments in \c
+ pages or DocBook XML documents. QDoc finds QDoc comments in \c
{.cpp} files and in \c {.qdoc} files. QDoc does not look for QDoc
comments in \c {.h} files. A QDoc comment always begins with an
exclamation mark (\b{!})). For example:
- \code
- / *!
+ \badcode *
+ /\1!
\class QObject
\brief The QObject class is the base class of all Qt objects.
@@ -75,7 +51,7 @@
inheritance hierarchy by using the \c inherits() function.
....
- * /
+ \1/
\endcode
From the QDoc comment above, QDoc generates the HTML \l {QObject}
@@ -99,7 +75,7 @@
Configuration File} {QDoc configuration file}. The configuration
file is where you tell QDoc where to find the project source
files, header files, and \c {.qdoc} files. It is also where you
- tell QDoc what kind of output to generate (HTML, DITA XML,...),
+ tell QDoc what kind of output to generate (HTML, DocBook XML...),
and where to put the generated documentation. The configuration
file also contains other information for QDoc.
@@ -259,7 +235,7 @@
it will run. The default value is \e {HTML}, so if you don't set
\c {outputformats} in your configuration file, QDoc will generate
HTML output. That's usually what you will want anyway, but you can
- also specify \e {DITAXML} to get DITA XML output instead.
+ also specify \e {DocBook} to get DocBook output instead.
Next, QDoc uses the values of the
\l {headerdirs-variable}
diff --git a/src/qdoc/qdoc/doc/qdoc-manual-macros.qdoc b/src/qdoc/qdoc/doc/qdoc-manual-macros.qdoc
new file mode 100644
index 000000000..2d65b540b
--- /dev/null
+++ b/src/qdoc/qdoc/doc/qdoc-manual-macros.qdoc
@@ -0,0 +1,497 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+
+/*!
+ \page qdoc-macros.html
+ \previouspage 12-0-qdoc-commands-miscellaneous.html
+ \nextpage qdoc-macros-cmake-text-snippets.html
+ \title Macros
+
+ Use the \l {macro-variable}{macro} variable to create your own simple QDoc
+ commands.
+
+ Macros are useful for:
+
+ \list
+ \li Including recurring text, such as formatted text snippets,
+ into documentation.
+ \li Pulling in information, such as version strings, from the build
+ system into .documentation.
+ \li Maintaining a single location for strings that are likely to change
+ in the future, such as product names.
+ \li Making sure that a term or product name is used consistently.
+ \li Styling the generated documentation by using HTML and DocBook
+ elements, such as forced line breaks and special characters.
+ \li Using tabs to show examples of content for different programming
+ languages, operating systems, or tools, for example.
+ \endlist
+
+ \section1 Global Macros
+
+ For a set of predefined global macros that you can use in all documentation
+ projects and their current values, see
+ \l{https://github.com/qt/qtbase/blob/e4315204b1412d74842b3167c3eb9a49dc233355/doc/global/macros.qdocconf}
+ {/qtbase/doc/global/macros.qdocconf} and
+ \l{https://github.com/qt/qtbase/blob/e4315204b1412d74842b3167c3eb9a49dc233355/doc/global/htmltabs.qdocconf}
+ {/qtbase/doc/global/htmltabs.qdocconf}. They have macros that include:
+
+ \list
+ \li \l {CMake Text Snippets}
+ \li \l {HTML and DocBook Formatting}
+ \li \l {Product Names}
+ \li \l {Product Versions}
+ \li \l {Tabbed Content}
+ \li \l {youtube-macro}{Links to YouTube content}
+ \li \l {Miscellaneous Macros}
+ \endlist
+
+ \QOI copies the macros files to your Qt installation folder, so you can use
+ the macros even if you build the documentation using QDoc from an installed
+ Qt (as opposed to building QDoc yourself from sources).
+
+ \note The values of the global macros might change between Qt versions,
+ so the macros might be extended differently depending on the Qt version that
+ you use to build the documentation.
+
+ \section1 Adding Macros
+
+ You can add macros for a particular documentation project in the
+ .qdocconf file of the project or any .qdocconf file that you include into it.
+ If the macro could be useful in more than one documentation project, add it
+ to the global macros file.
+
+ For more information about the macro syntax and options, see
+ \l {macro-variable}{macro}.
+
+ \section1 Using Macros
+
+ You can use macros in the same way as the built-in QDoc commands in:
+
+ \list
+ \li Code source files, in documentation comments (.cpp, .qml)
+ \li Documentation source files (.qdoc, .qdocinc)
+ \endlist
+
+ For example:
+
+ \badcode
+ \QOI copies the macros files to your Qt installation folder.
+ \endcode
+
+ \section1 Obsolete Macros
+
+ The global macros file contains the following obsolete macros for
+ compatibility with older documentation sets.
+
+ \table
+ \header
+ \li Don't Use
+ \li Use
+ \row
+ \li \\gui
+ \li \l{uicontrol-command}{\\uicontrol}
+ \row
+ \li \\menu
+ \li \l{uicontrol-command}{\\uicontrol}
+ \row
+ \li \\param
+ \li \l{a-command}{\\a}
+ \row
+ \li \\return
+ \li Write \e Returns or \e returns.
+ \endtable
+
+ \sa {macro-variable}{macro}, {Unknown macro}
+*/
+
+/*!
+ \page qdoc-macros-cmake-text-snippets.html
+ \previouspage qdoc-macros.html
+ \nextpage qdoc-macros-formatting.html
+
+ \title CMake Text Snippets
+
+ See \l{https://github.com/qt/qtbase/blob/e4315204b1412d74842b3167c3eb9a49dc233355/doc/global/macros.qdocconf}
+ {/qtbase/doc/global/macros.qdocconf} for standard text snippets that you
+ should use when writing \l {Building with CMake}
+ {Qt-specific CMake documentation}:
+
+ \table
+ \header
+ \li Macro
+ \li Use
+ \row
+ \li \\cmakecommandandroidonly
+ \li For Android targets
+ \row
+ \li \\cmakecommandsince
+ \li To indicate which Qt version introduced the command
+ \row
+ \li \\cmakepropertyandroidonly
+ \li For Android targets
+ \row
+ \li \\cmakepropertyiosonly
+ \li For iOS targets
+ \row
+ \li \\cmakepropertysince
+ \li To indicate which Qt version introduced the property
+ \row
+ \li \\cmakepropertywebassemblyonly
+ \li For WebAssembly targets
+ \row
+ \li \\cmakevariableandroidonly
+ \li For Android targets
+ \row
+ \li \\cmakevariableiosonly
+ \li For iOS targets
+ \row
+ \li \\cmakevariablesince
+ \li To indicate which Qt version introduced the variable
+ \row
+ \li \\preliminarycmakecommand
+ \li To indicate that the command may change in future versions
+ \row
+ \li \\preliminarycmakeproperty
+ \li To indicate that the property may change in future versions
+ \row
+ \li \\preliminarycmakevariable
+ \li To indicate that the variable may change in future versions
+ \row
+ \li \\versionlessCMakeCommandsNote
+ \li For all commands, to tell users how to use versioned commands
+ \endtable
+
+ \sa {macro-variable}{macro}, {Macros}
+*/
+
+/*!
+ \page qdoc-macros-formatting.html
+ \previouspage qdoc-macros.html
+ \nextpage qdoc-macros-product-names.html
+
+ \title HTML and DocBook Formatting
+
+ See \l{https://github.com/qt/qtbase/blob/e4315204b1412d74842b3167c3eb9a49dc233355/doc/global/macros.qdocconf}
+ {/qtbase/doc/global/macros.qdocconf} for HTML and DocBook elements and
+ special characters that you can use in documentation source files.
+
+ \sa {Macros and Other Configurations}, {macro-variable}{macro}, {Macros}
+
+*/
+
+/*!
+ \page qdoc-macros-product-names.html
+ \previouspage qdoc-macros-formatting.html
+ \nextpage qdoc-macros-tabbed-content.html
+
+ \title Product Names
+
+ Use the following macros to refer to products:
+
+ \table
+ \header
+ \li Macro
+ \li Expands To
+ \row
+ \li \\B2Q
+ \li Boot to Qt
+ \row
+ \li \\B2QSS
+ \li Boot to Qt Software Stack
+ \row
+ \li \\B2QST
+ \li Boot to Qt Startup Screen
+ \row
+ \li \\IFW
+ \li Qt Installer Framework
+ \row
+ \li \\macos
+ \li macOS
+ \row
+ \li \\QA
+ \li Qt Assistant
+ \row
+ \li \\QB
+ \li Qt Bridge
+ \row
+ \li \\QBF
+ \li Qt Bridge for Figma
+ \row
+ \li \\QBPS
+ \li Qt Bridge for Adobe Photoshop
+ \row
+ \li \\QBSK
+ \li Qt Bridge for Sketch
+ \row
+ \li \\QBXD
+ \li Qt Bridge for Adobe XD
+ \row
+ \li \\QC
+ \li Qt Creator
+ \row
+ \li \\QD
+ \li Qt Widgets Designer
+ \row
+ \li \\QDS
+ \li Qt Design Studio
+ \row
+ \li \\QDV
+ \li Qt Design Viewer
+ \row
+ \li \\QfP
+ \li Qt for Python
+ \row
+ \li \\QL
+ \li Qt Linguist
+ \row
+ \li \\QMCU
+ \li Qt for MCUs
+ \row
+ \li \\QMLLS
+ \li QML Language Server
+ \row
+ \li \\QMLP
+ \li QML Profiler
+ \row
+ \li \\QMT
+ \li Qt Maintenance Tool
+ \row
+ \li \\QOI
+ \li Qt Online Installer
+ \row
+ \li \\QQEM
+ \li Qt Quick Effect Maker
+ \row
+ \li \\QQV
+ \li Qt QML Viewer
+ \row
+ \li \\QtAA
+ \li Qt for Android Automotive
+ \row
+ \li \\QtTAS
+ \li Qt Tools for Android Studio
+ \row
+ \li \\QUL
+ \li Qt Quick Ultralite
+ \endtable
+
+ \sa {macro-variable}{macro}, {Macros}
+*/
+
+/*!
+ \page qdoc-macros-product-versions.html
+ \previouspage qdoc-macros-product-names.html
+ \nextpage qdoc-macros-tabbed-content.html
+
+ \title Product Versions
+
+ Use the following macros to display version and status information about a
+ topic:
+
+ \table
+ \header
+ \li Macro
+ \li Expands To
+ \row
+ \li \\QtMajorVersion
+ \li The major version number based on the $QT_VER environment variable
+ \row
+ \li \\QtMinorVersion
+ \li The minor version number based on the $QT_VER environment variable
+ \row
+ \li \\QtVersion
+ \li The value of the $QT_VERSION environment variable
+ \row
+ \li \\QtVer
+ \li The value of the $QT_VER environment variable
+ \row
+ \li \\techpreview
+ \li The statement that a module or class is preliminary and might change
+ \endtable
+
+ \section1 \\techpreview
+
+ Appends the tech preview link to the brief sentence and adds the topic to
+ the \c tech_preview group.
+
+ Must be placed directly under a \brief command.
+
+ \sa {macro-variable}{macro}, {Macros}
+*/
+
+/*!
+ \page qdoc-macros-tabbed-content.html
+ \previouspage qdoc-macros-formatting.html
+ \nextpage qdoc-macros-youtube-videos.html
+
+ \title Tabbed Content
+
+ Use the following macros to create \e {tab groups} of labeled HTML tabs that
+ have comparable or alternative content. For example, you can show code
+ snippets for different programming languages.
+
+ \list
+ \li \l{tab-macro}{\\tab}
+ \li \l{tabcontent-macro}{\\tabcontent}
+ \li \l{endtabcontent-macro}{\\endtabcontent}
+ \endlist
+
+ \section1 Using Tab Macros
+
+ \badcode
+ \tab {name}{tab-id}{title}{checked}
+ \tabcontent {tab-id}
+ content
+ \endtabcontent
+ \endcode
+
+ \note These macros only work with the online template, so you usually
+ need to use conditional text to get good results also for the offline help.
+
+ For example, to show instructions for the CMake and qmake build systems in
+ tabs:
+
+ \badcode
+ \if defined(onlinedocs)
+ \tab {build-qt-app}{tab-cmake}{CMake}{checked}
+ \tab {build-qt-app}{tab-qmake}{qmake}{}
+ \tabcontent {tab-cmake}
+ \else
+ \section1 Using CMake
+ \endif
+ CMake-specific instructions go here
+ \if defined(onlinedocs)
+ \endtabcontent
+ \tabcontent {tab-qmake}
+ \else
+ \section1 Using qmake
+ \endif
+ qmake-specific instructions go here
+ \if defined(onlinedocs)
+ \endtabcontent
+ \endif
+ \endcode
+
+ \target tab-macro
+ \section1 \\tab
+
+ \badcode
+ \tab {name}{tab-id}{title}{checked}
+ \endcode
+
+ Specifies a tab in a tab group with a name, ID, title, and default state.
+
+ Use a unique \e {name} to specify which tabs belong to a tab group. That is,
+ all tabs in a group have the same name. To separate the tabs from each other,
+ give them each a unique \e {tab-id} within the group. Use this \e {tab-id}
+ as the value of the \\tabcontent macro to add content to the tab.
+
+ The \e {checked} argument selects the tab by default when the HTML page is
+ loaded. For the initially hidden tabs, pass an empty argument {}.
+
+ \target tabcontent-macro
+ \section1 \\tabcontent
+
+ \badcode
+ \tabcontent {tab-id}
+ \endcode
+
+ Adds content to the tab with the \e {tab-id} you specify.
+
+ \target endtabcontent-macro
+ \section1 \\endtabcontent
+
+ Marks the end of the tab content that you begin with \\tabcontent.
+
+ \sa {Macros}
+*/
+
+/*!
+ \page qdoc-macros-youtube-videos.html
+ \previouspage qdoc-macros-tabbed-content.html
+ \nextpage qdoc-macros-misc.html
+
+ \target youtube-macro
+ \title \\youtube
+
+ When generating online documentation, embeds a YouTube video in the HTML.
+ When generating offline documentation (.qch), adds an external link to the
+ video with a thumbnail image. The HTML docs show a thumbnail of the video
+ with a play button. You need to save the thumbnail in
+ \c {\images\extraimages\} in your project folder.
+
+ Use the following URL to open the thumbnail image in a browser:
+ \c {https://img.youtube.com/vi/<ID>/0.jpg}. The \c <ID> is the ID of
+ the video on YouTube. For example, if the URL to the video is
+ \c {https://www.youtube.com/watch?v=dQw4w9WgXcQ&feature=youtu.be},
+ the ID is \c dQw4w9WgXcQ. Save the image file as \c dQw4w9WgXcQ.jpg.
+
+ You must add the filename of the thumbnail file to
+ \c {\images\extraimages\extraimages.qdocconf}. For example:
+
+ \badcode
+ {HTML.extraimages,qhp.qtdesignstudio.extraFiles} += \
+ images/dQw4w9WgXcQ.jpg
+ \endcode
+
+ To add a link to the video in text, write:
+
+ \badcode
+ \youtube dQw4w9WgXcQ
+ \endcode
+
+ \sa {Macros}
+*/
+
+/*!
+ \page qdoc-macros-misc.html
+ \previouspage qdoc-macros-youtube-videos.html
+ \nextpage 21-0-qdoc-configuration.html
+
+ \title Miscellaneous Macros
+
+ You can use the following macros to affect the HTML output and to use
+ standard terminology:
+
+ \list
+ \li \l {borderedimage-macro}{\\borderedimage}
+ \li \l {examplecategory-macro}{\\examplecategory}
+ \li \l {key-macro}{\\key}
+ \li \l {nullptr-macro}{\\nullptr}
+ \li \l {summary-macro}{\\summary}
+ \endlist
+
+ \target borderedimage-macro
+ \section1 \\borderedimage
+
+ Like \l{image-command}{\\image}, but adds a border around the image.
+
+ \target examplecategory-macro
+ \section1 \\examplecategory
+
+ Describes the category an example is sorted to in the Qt Creator
+ \uicontrol Examples tab.
+
+ \target key-macro
+ \section1 \\key
+
+ Formats keyboard key names.
+
+ \target nullptr-macro
+ \section1 \\nullptr
+
+ Expands to the term to use for null pointers.
+
+ \target summary-macro
+ \section1 \\summary
+
+ Like \l{brief-command}{\\brief}, but replicates the sentence also as text.
+
+ Wrap the entire sentence within \c {{}}. For example:
+
+ \badcode
+ \summary {Creates a build target.}
+ \endcode
+
+ \sa {macro-variable}{macro}, {Macros}
+*/
diff --git a/src/qdoc/doc/qdoc-manual-markupcmds.qdoc b/src/qdoc/qdoc/doc/qdoc-manual-markupcmds.qdoc
index ce1bb4b3e..636e6f868 100644
--- a/src/qdoc/doc/qdoc-manual-markupcmds.qdoc
+++ b/src/qdoc/qdoc/doc/qdoc-manual-markupcmds.qdoc
@@ -1,34 +1,10 @@
-/****************************************************************************
-**
-** Copyright (C) 2020 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the documentation of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:FDL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Free Documentation License Usage
-** Alternatively, this file may be used under the terms of the GNU Free
-** Documentation License version 1.3 as published by the Free Software
-** Foundation and appearing in the file included in the packaging of
-** this file. Please review the following information to ensure
-** the GNU Free Documentation License version 1.3 requirements
-** will be met: https://www.gnu.org/licenses/fdl-1.3.html.
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2020 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
\page 03-qdoc-commands-markup.html
\previouspage Naming Things
- \nextpage Text Markup
+ \nextpage Macros
\title Markup Commands
@@ -41,11 +17,13 @@
\li \l {b-command} {\\b}
\li \l {badcode-command} {\\badcode}
\li \l {b-command} {\\bold} (deprecated, use \\b)
+ \li \l {br-command} {\\br}
\li \l {brief-command} {\\brief}
\li \l {c-command} {\\c}
\li \l {caption-command} {\\caption}
\li \l {code-command} {\\code}
\li \l {codeline-command} {\\codeline}
+ \li \l {details-command} {\\details}
\li \l {div-command} {\\div}
\li \l {dots-command} {\\dots}
\li \l {e-command} {\\e}
@@ -58,7 +36,6 @@
\li \l {if-command} {\\if}
\li \l {image-command} {\\image}
\li \l {include-command} {\\include}
- \li \l {include-command} {\\input}
\li \l {inlineimage-command} {\\inlineimage}
\li \l {keyword-command} {\\keyword}
\li \l {l-command} {\\l}
@@ -67,10 +44,8 @@
\li \l {list-command} {\\list}
\li \l {meta-command} {\\meta}
\li \l {noautolist-command} {\\noautolist}
- \li \l {newcode-command} {\\newcode}
\li \l {li-command} {\\o} (deprecated, use \\li)
\li \l {note-command} {\\note}
- \li \l {oldcode-command} {\\oldcode}
\li \l {omit-command} {\\omit}
\li \l {printline-command} {\\printline}
\li \l {printto-command} {\\printto}
@@ -86,6 +61,7 @@
\li \l {sectionTwo-command} {\\section2}
\li \l {sectionThree-command} {\\section3}
\li \l {sectionFour-command} {\\section4}
+ \li \l {sincelist-command} {\\sincelist}
\li \l {skipline-command} {\\skipline}
\li \l {skipto-command} {\\skipto}
\li \l {skipuntil-command} {\\skipuntil}
@@ -96,12 +72,15 @@
\li \l {table-command} {\\table}
\li \l {tableofcontents-command} {\\tableofcontents}
\li \l {target-command} {\\target}
+ \li \qdoccmd tm
\li \l {tt-command} {\\tt}
\li \l {uicontrol-command} {\\uicontrol}
\li \l {underline-command} {\\underline}
\li \l {raw-command} {\\unicode}
\li \l {warning-command} {\\warning}
- \li \l {backslash-command} {\\\\}
+ \li \l {backslash-sequence} {\\\\}
+ \li \l {endash-sequence} {\--}
+ \li \l {emdash-sequence} {-\--}
\endlist
*/
@@ -126,30 +105,6 @@
preceded by the \\a command. The parameter name is then rendered
in italics.
- \code
- / *!
- Constructs a line edit containing the text
- \a contents. The \a parent parameter is sent
- to the QWidget constructor.
- * /
-
- QLineEdit::QLineEdit(const QString &contents, QWidget *parent) :QWidget(parent)
- {
- ...
- }
-
- \endcode
-
- QDoc renders this as:
-
- \quotation
- \b {QLineEdit::QLineEdit ( const QString &
- contents, QWidget *parent )}
-
- Constructs a line edit containing the text \a contents.
- The \a parent parameter is sent to the QWidget constructor.
- \endquotation
-
The formal parameter name may be enclosed between curly brackets,
but that isn't required.
@@ -160,44 +115,55 @@
class names, and C++ keywords (for example, \c int and \c for) in the code
font.
- The command renders its argument using a monospace font. For
- example:
+ The command renders its argument using a monospace font. If the text to be
+ rendered in the code font contains spaces, enclose the entire text in curly
+ brackets:
\code
- / *!
- The \c AnalogClock class provides a clock widget with hour
- and minute hands that is automatically updated every
- few seconds.
- * /
+ \c {QLineEdit::QLineEdit(const QString &contents, QWidget *parent) : QWidget(parent)}
\endcode
- QDoc renders this as:
+ The \\c command accepts the special character \c \ within its
+ argument, which renders it as a normal character. So if you want
+ to use nested commands, you must use the \l {tt-command} {teletype
+ (\\tt)} command instead.
- \quotation
- The \c AnalogClock class provides a clock widget with hour
- and minute hands, which are automatically updated every
- few seconds.
- \endquotation
+ See also \l {tt-command} {\\tt} and \l {code-command} {\\code}.
- If the text to be rendered in the code font contains spaces, enclose the
- entire text in curly brackets.
+ \target details-command
+ \section1 \\details (collapsible)
- \code
- \c {QLineEdit::QLineEdit(const QString &contents, QWidget *parent) : QWidget(parent)}
+ The \\details and \\enddetails commands generates a collapsible <details>
+ element with a <summary> to control the hidden/visible state.
+
+ When generating HTML output, use the \\details and \\enddetails commands to
+ generate a collapsible \c{<details>} HTML element. The command takes an
+ optional summary string enclosed in curly braces. This optional argument
+ specifies a visible heading for the details.
+
+ For example, with the following input:
+ \badcode *
+ /\1!
+ \details {QDoc details}
+ \note You're looking at detailed information.
+ \enddetails
+ \1/
\endcode
- QDoc renders this as:
+ If QDoc is generating HTML, it will translate these commands to:
- \quotation
- \c {QLineEdit::QLineEdit(const QString &contents, QWidget *parent) : QWidget(parent)}
- \endquotation
+ \badcode
+ <summary>QDoc details</summary><div class="admonition note"><p><b>Note: </b>You're looking at detailed information.</p></div>
+ \endcode
- The \\c command accepts the special character \c \ within its
- argument, which renders it as a normal character. So if you want
- to use nested commands, you must use the \l {tt-command} {teletype
- (\\tt)} command instead.
+ QDoc renders this as:
- See also \l {tt-command} {\\tt} and \l {code-command} {\\code}.
+ \details {QDoc details}
+ \note You're looking at detailed information.
+ \enddetails
+
+ For any other output format, QDoc generates the contents as a normal paragraph,
+ ignoring the summary string. This command was introduced to QDoc in Qt6.6.
\target div-command
\section1 \\div
@@ -213,13 +179,12 @@
For example, we might want to render an inline image so that it
floats to the right of the current block of text:
- \code
- / *!
- \div {class="float-right"}
- \inlineimage qml-column.png
- \enddiv
-
- * /
+ \badcode *
+ /\1!
+ \div {class="float-right"}
+ \inlineimage qml-column.png
+ \enddiv
+ \1/
\endcode
If QDoc is generating HTML, it will translate these commands to:
@@ -319,8 +284,8 @@
For example, we might want to render the first word of each
element in a numeric list in blue.
- \code
- / *!
+ \badcode *
+ /\1!
Global variables with complex types:
\list 1
\li \span {class="variableName"} {mutableComplex1} in globals.cpp at line 14
@@ -328,7 +293,7 @@
\li \span {class="variableName"} {constComplex1} in globals.cpp at line 16
\li \span {class="variableName"} {constComplex2} in globals.cpp at line 17
\endlist
- * /
+ \1/
\endcode
Class \e {variableName} refers to a clause in your style.css.
@@ -356,6 +321,29 @@
See also \l {div-command} {\\div}.
+ \target tm-command
+ \section1 \\tm (trademark)
+
+ The \\tm command indicates that its argument is a trademark. QDoc appends
+ a trademark symbol `™` to the first occurrence of the argument when
+ generating a page.
+
+ In the project's configuration, the \c navigation.trademarkspage variable
+ is used for defining a title of a page that contains trademark-related
+ documentation.
+
+ \badcode
+ navigation.trademarkspage = Trademarks
+ \endcode
+
+ If set, each occurrence of the trademark symbol also links to the
+ trademarks page.
+
+ \note In section titles, the \\tm command is ignored and its argument
+ rendered as-is.
+
+ See also \l {sectionOne-command}{\\section1} and \qdocvar {navigation}.
+
\target tt-command
\section1 \\tt (teletype font)
@@ -365,24 +353,15 @@
(e.g. \l {e-command} {\\e}, \l {b-command} {\\b} and \l
{underline-command} {\\underline}).
- \code
- / *!
+ \badcode *
+ /\1!
After having populated the main container with
child widgets, \c setupUi() scans the main container's list of
slots for names with the form
\tt{on_\e{objectName}_\e{signalName}().}
- * /
+ \1/
\endcode
- QDoc renders this as:
-
- \quotation
- After having populated the main container with
- child widgets, \c setupUi() scans the main container's list of
- slots for names with the form
- \tt{on_\e{objectName}_\e{signalName}().}
- \endquotation
-
If the text to be rendered in the code font contains spaces, enclose the
entire text in curly brackets.
@@ -390,12 +369,6 @@
\tt {QLineEdit::QLineEdit(const QString &contents, QWidget *parent) :QWidget(parent)}
\endcode
- QDoc renders this as:
-
- \quotation
- \tt {QLineEdit::QLineEdit(const QString &contents, QWidget *parent) :QWidget(parent)}
- \endquotation
-
See also \l {c-command} {\\c}.
\target b-command
@@ -404,19 +377,17 @@
The \\b command renders its argument in bold font. This command used
to be called \\bold.
- \code
- / *!
+ \badcode *
+ /\1!
This is regular text; \b {this text is
rendered using the \\b command}.
- * /
+ \1/
\endcode
- QDoc renders this as:
+ \target br-command
+ \section1 \\br
- \quotation
- This is regular text; \b {this text is rendered using
- the \\b command}.
- \endquotation
+ The \\br command forces a line break.
\target e-command
\section1 \\e (emphasis, italics)
@@ -427,89 +398,26 @@
If the argument contains spaces or other punctuation, enclose the
argument in curly brackets.
- \code
- / *!
+ \badcode *
+ /\1!
Here, we render \e {a few words} in italics.
- * /
+ \1/
\endcode
- QDoc renders this as:
-
- \quotation
- Here, we render \e {a few words} in italics.
- \endquotation
-
If you want to use other QDoc commands within an argument that
contains spaces, you always need to enclose the argument in
- braces. But QDoc is smart enough to count parentheses [3], so you
+ braces. But QDoc is smart enough to count parentheses, so you
don't need braces in cases like this:
- \code
- / *!
+ \badcode *
+ /\1!
An argument can sometimes contain whitespaces,
for example: \e QPushButton(tr("A Brand New Button"))
- * /
+ \1/
\endcode
- QDoc renders this as:
-
- \quotation
- An argument can sometimes contain whitespaces,
- for example: \e QPushButton(tr("A Brand New Button"))
- \endquotation
-
- Finally, trailing punctuation is not included in an argument [4],
- nor is "'s" [5]
-
- \raw HTML
- <table align="center" cellpadding="2"
- cellspacing="1" border="0">
- <tr valign="top" bgcolor="#a2c511">
- <th></th>
- <th>QDoc Syntax</th>
- <th>Generated Documentation</th>
- </tr>
-
- <tr valign="top" bgcolor="#d0d0d0">
- <td>1</td>
- <td>A variation of a command button is a \e menu
- button.</td>
- <td>A variation of a command button is a <i>menu</i>
- button.</td>
- </tr>
-
- <tr valign="top" bgcolor="#c0c0c0">
- <td>2</td>
- <td>The QPushButton widget provides a
- \e {command button}.</td>
- <td>The QPushButton widget provides a
- <i>command button</i>.</td>
- </tr>
-
- <tr valign="top" bgcolor="#d0d0d0">
- <td>3</td>
- <td>Another class of buttons are option buttons
- \e (see QRadioButton).</td>
- <td>Another class of buttons are option buttons
- <i> (see QRadioButton)</i>.</td>
- </tr>
-
- <tr valign="top" bgcolor="#c0c0c0">
- <td>4</td>
- <td>A push button emits the signal \e clicked().</td>
- <td>A push button emits the signal <i>clicked</i>().</td>
- </tr>
-
- <tr valign="top" bgcolor="#d0d0d0">
- <td>5</td>
- <td>The \e QPushButton's checked property is
- false by default.</td>
- <td>The <i>QPushButton</i>'s checked property is
- false by default.</td>
- </tr>
-
- </table>
- \endraw
+ Finally, trailing punctuation is not included in an argument,
+ nor is "'s".
\target sub-command
\section1 \\sub
@@ -517,28 +425,17 @@
The \\sub command renders its argument lower than the baseline of
the regular text, using a smaller font.
- \code
- / *!
+ \badcode *
+ /\1!
Definition (Range): Consider the sequence
{x\sub n}\sub {n > 1} . The set
{x\sub 2, x\sub 3, x\sub 4, ...} = {x\sub n ; n = 2, 3, 4, ...}
is called the range of the sequence.
- * /
+ \1/
\endcode
- QDoc renders this as:
-
- \quotation
- Definition (Range): Consider the sequence
- {x\sub n}\sub {n > 1} . The set
-
- {x\sub 2, x\sub 3, x\sub 4, ...} = {x\sub n ; n = 2, 3, 4, ...}
-
- is called the range of the sequence.
- \endquotation
-
If the argument contains spaces or other punctuation, enclose the
argument in curly brackets.
@@ -548,26 +445,16 @@
The \\sup command renders its argument higher than
the baseline of the regular text, using a smaller font.
- \code
- / *!
+ \badcode *
+ /\1!
The series
1 + a + a\sup 2 + a\sup 3 + a\sup 4 + ...
is called the \i {geometric series}.
- * /
+ \1/
\endcode
- QDoc renders this as:
-
- \quotation
- The series
-
- 1 + a + a\sup 2 + a\sup 3 + a\sup 4 + ...
-
- is called the \e {geometric series}.
- \endquotation
-
If the argument contains spaces or other punctuation, enclose the
argument in curly brackets.
@@ -577,80 +464,116 @@
The \\uicontrol command is used to mark content as being used for UI
control elements. When using HTML, the output is rendered in bold.
- \sa \\b
+ See also \l {b-command}{\\b}.
\target underline-command
\section1 \\underline
The \\underline command renders its argument underlined.
- \code
- / *!
+ \badcode *
+ /\1!
The \underline {F}ile menu gives the users the possibility
to edit an existing file, or save a new or modified
file, and exit the application.
- * /
+ \1/
\endcode
- QDoc renders this as:
-
- \quotation
- The \underline {F}ile menu gives the users the possibility
- to edit an existing file, or save a new or modified
- file, and exit the application.
- \endquotation
-
If the argument contains spaces or other punctuation, enclose the
argument in curly brackets.
- \target backslash-command
+ \target backslash-sequence
\section1 \\\\ (double backslash)
- The \\\\ command expands to a double backslash.
+ The sequence \\\\ expands to a single backslash.
QDoc commands always start with a single backslash. To display a
- single backslash in the text you need to type two backslashes. If
- you want to display two backslashes, you need to type four.
+ single backslash in the text, you must type two backslashes. If
+ you want to display two backslashes, you must type four.
- \code
- / *!
+ \badcode *
+ /\1!
The \\\\ command is useful if you want a
backslash to appear verbatim, for example,
writing C:\\windows\\home\\.
- * /
+ \1/
\endcode
- QDoc renders this as:
-
- \quotation
- The \\\\ command is useful if you want a
- backslash to appear verbatim, for example,
- writing C:\\windows\\home\\.
- \endquotation
-
However, if you want your text to appear in a monospace font as
well, you can use the \l {c-command} {\\c} command instead, which
accepts and renders the backslash as any other character. For
example:
- \code
- / *!
+ \badcode *
+ /\1!
The \\c command is useful if you want a
backslash to appear verbatim, and the word
that contains it written in a monospace font,
like this: \c {C:\windows\home\}.
- * /
+ \1/
\endcode
- QDoc renders this as:
+ \target endash-sequence
+ \section1 \-- (en dash)
+ QDoc renders double hyphens as an en dash. QDoc markup commands
+ designed to make their input appear verbatim---such as the \\c
+ command---won't replace the double hyphens with an en dash character.
+ For example:
- \quotation
- The \\c command is useful if you want a
- backslash to appear verbatim, and the word
- that contains it written in a monospace font,
- like this: \c {C:\windows\home\}.
- \endquotation
+ \badcode *
+ /\1!
+ The \\c command -- useful if you want text in a monospace font --
+ is well documented.
+ \1/
+ \endcode
+
+ However, other commands may require that the hyphens are escaped to ensure
+ QDoc renders the output as expected. For example;
+
+ \badcode *
+ /\1!
+ This \l {endash-sequence}{link to the -- (endash) sequence}
+ isn't escaped and QDoc therefore renders an endash in the link
+ text. However, the escaped
+ \l {endash-sequence}{link to the \-- (endash) sequence}
+ renders both hyphens as intended.
+ \1/
+ \endcode
+
+ See also \l {emdash-sequence}{\-\-- (em dash)}.
+
+ \target emdash-sequence
+ \section1 -\-- (em dash)
+
+ QDoc renders triple hyphens as an en dash. QDoc markup commands
+ designed to make their input appear verbatim---such as the \\c
+ command---won't replace the triple hyphens with an en dash character.
+ For example:
+
+ \badcode *
+ /\1!
+ The \\c command---useful when you want text to be rendered
+ verbatim---is well documented.
+ \1/
+ \endcode
+
+ However, other commands may require that the hyphens are escaped to ensure
+ QDoc renders the output as expected. For example;
+
+ \badcode *
+ /\1!
+ This \l {emdash-sequence}{link to the --- (emdash) sequence}
+ isn't escaped and QDoc therefore renders an emdash in the link
+ text. However, the escaped
+ \l {emdash-sequence}{link to the -\-- (emdash) sequence}
+ renders both hyphens as intended.
+ \1/
+ \endcode
+ \note The escaped control sequence in this example is for the en dash.
+ This avoids a hyphen followed by an en dash in the output.
+
+ See also \l {endash-sequence}{\-- (en dash)}.
*/
@@ -667,7 +590,6 @@
correspond to the traditional section, subsection, etc used in
outlining.
- \target section-commands
\section1 Section commands
In general a document structuring command considers everything
@@ -682,136 +604,74 @@
There is a strict ordering of the section units:
- \code
- section1
- |
- section2
- |
- section3
- |
- section4
- \endcode
+ \code
+ section1
+ |
+ section2
+ |
+ section3
+ |
+ section4
+ \endcode
When sections are used, the first section command should be \c section1.
+ \badcode *
+ /\1!
+ \section1 Basic Qt
- \code
- / *!
- \section1 Basic Qt
-
- This is the first section.
-
-
- \section2 Getting Started
+ This is the first section.
- This is the first subsection.
+ \section2 Getting Started
- \section3 Hello Qt
+ This is the first subsection.
- This is the first subsubsection.
+ \section3 Hello Qt
- \section3 Making Connections
+ This is the first subsubsection.
- This is the second subsubsection.
+ \section3 Making Connections
- \section3 Using the Reference Documentation
+ This is the second subsubsection.
- This is the third subsubsection.
+ \section3 Using the Reference Documentation
- \section2 Creating Dialogs
+ This is the third subsubsection.
- This is the second subsection.
+ \section2 Creating Dialogs
- \section3 Subclassing QDialog
+ This is the second subsection.
- This is the first subsubsection.
- ...
+ \section3 Subclassing QDialog
+ This is the first subsubsection.
- \section1 Intermediate Qt
+ ...
- This is the second section.
+ \section1 Intermediate Qt
- \section2 Layout Management
+ This is the second section.
- This is the second section's first subsection.
+ \section2 Layout Management
- \section3 Basic Layouts
+ This is the second section's first subsection.
- This is the first subsubsection.
- ...
- * /
- \endcode
+ \section3 Basic Layouts
- QDoc renders this as:
+ This is the first subsubsection.
- \quotation
- \raw HTML
- <a name="Basic Qt">
- <h1>Basic Qt</h1>
- </a>
- <p>This is the first section.</p>
-
- <a name="Getting started">
- <h2>Getting Started</h2>
- </a>
- This is the first subsection.</p>
-
- <a name="Hello Qt">
- <h3>Hello Qt</h3>
- </a>
- <p>This is the first subsubsection.</p>
-
- <a name="Making Connections">
- <h3>Making Connections</h3>
- </a>
- <p>This is the second subsubsection.</p>
-
- <a name="Using the Reference Documentation">
- <h3>Using the Reference Documentation</h3>
- </a>
- <p>This is the third subsubsection.</p>
-
- <a name="Creating Dialogs">
- <h2>Creating Dialogs</h2>
- </a>
- <p>This is the second subsection.</p>
-
- <a name="Subclassing QDialog">
- <h3>Subclassing QDialog</h3>
- </a>
- <p>This is the first subsubsection.</p>
-
- ...
-
- <a name="Intermediate Qt">
- <h1>Intermediate Qt</h1>
- </a>
- <p>This is the second section.</p>
-
- <a name="Layout Management">
- <h2>Layout Management</h2>
- </a>
- <p>This is the second section's first subsection.</p>
-
- <a name="Basic Layouts">
- <h3>Basic Layouts</h3>
- </a>
- <p>This is the first subsubsection.</p>
-
- ...
-
- \endraw
- \endquotation
+ ...
+ \1/
+ \endcode
Each section is a logical unit in the document. The section
heading appears in the automatically generated table of contents
@@ -888,32 +748,16 @@
content that is not in a Qt language recognized by QDoc, use
\l {badcode-command}{\\badcode} instead.
- When processing any of the \\code, \l {newcode-command} {\\newcode} or \l
- {oldcode-command} {\\oldcode} commands, QDoc removes all
- indentation that is common for the verbatim code blocks within a
- \c{/}\c{*!} ... \c{*}\c{/} comment before it adds the standard
- indentation.
+ When processing the \\code command, QDoc removes all indentation
+ that is common for the verbatim code blocks within a \c{/}\c{*!}
+ ... \c{*}\c{/} comment before it adds the standard indentation.
\note This doesn't apply to externally quoted code using the \l
{quotefromfile-command} {\\quotefromfile} or \l
{quotefile-command} {\\quotefile} command.
- \code
- / *!
- \code
- #include <QApplication>
- #include <QPushButton>
-
- int main(int argc, char *argv[])
- {
- ...
- }
- \ endcode
- * /
- \endcode
-
- QDoc renders this as:
-
+ \code \\endcode *
+ /*!
\code
#include <QApplication>
#include <QPushButton>
@@ -922,13 +766,29 @@
{
...
}
- \endcode
+ \1
+ \2/
+ \endcode
Other QDoc commands are disabled within \\code... \\endcode, and
the special character '\\' is accepted and rendered like the rest
of the code, unless it is followed by a digit and parameters were
passed to \\code.
+ \section2 Highlighting and autolinking
+
+ The \\code commands attempts to parse its contents as code of a
+ specific language, as defined in the \l {language-variable}{language}
+ configuration variable. This provides highlighting and automatic
+ linking to types detected in the code.
+
+ As an exception since QDoc version 6.4, when the \\code command
+ is used within a QML-specific \l {Topic Commands}{topic}, QDoc
+ first attempts to recognize the code as QML; for other topics,
+ the language configuration variable takes precedence. To
+ explicitly mark the code snippet as QML, use the \l {qml-command}
+ {\\qml} command instead.
+
\section2 Code snippet parameters
Since QDoc version 5.12, \\code command accepts also optional
@@ -940,12 +800,12 @@
For example:
- \code
- / *!
- \code * hello
- /\1 \2 \1/
- \ endcode
- * /
+ \badcode 1 2 * endcode
+ /\3!
+ \code * hello
+ /\\1 \\2 \\1/
+ \\4
+ \3/
\endcode
For the above snippet, QDoc renders the word \e hello enclosed in
@@ -954,15 +814,12 @@
\section2 Including code from external files
To include code snippets from an external file, use the
- \l{07-0-qdoc-commands-includingexternalcode.html#snippet-command}
- {\\snippet} and
- \l{07-0-qdoc-commands-includingexternalcode.html#codeline-command}
+ \l{snippet-command}{\\snippet} and \l{codeline-command}
{\\codeline} commands.
- See also \l {c-command} {\\c}, \l {badcode-command} {\\badcode}, \l
- {07-0-qdoc-commands-includingexternalcode.html#quotefromfile-command}
- {\\quotefromfile}, \l{newcode-command} {\\newcode}, and \l {oldcode-command}
- {\\oldcode}.
+ See also \l {c-command}{\\c}, \l {qml-command}{\\qml},
+ \l {badcode-command}{\\badcode}, \l {quotefromfile-command}
+ {\\quotefromfile}, and \l {language-variable}{language}.
\target badcode-command
\section1 \\badcode
@@ -978,69 +835,17 @@
Like \\code, \\badcode accepts also optional parameters.
- \target newcode-command
- \section1 \\newcode
-
- The \\newcode, \\oldcode, and \\endcode commands enable you to
- show how to port a snippet of code to a new version of an API.
-
- The \\newcode command and its companion the \\oldcode command are
- a convenience combination of the \l {code-command} {\\code} commands:
- this combination provides a text relating the two code snippets to each
- other.
-
- The \\newcode command requires a preceding \\oldcode statement.
-
- Like the \l{code-command}{\\code} command, the \\newcode command renders its
- code on a new line in the documentation using a monospace font and the
- standard indentation.
-
- \code
- / *!
- \oldcode
- if (printer->setup(parent))
- ...
- \newcode
- QPrintDialog dialog(printer, parent);
- if (dialog.exec())
- ...
- \ endcode
- * /
- \endcode
-
- QDoc renders this as:
-
- \quotation
- \oldcode
- if (printer->setup(parent))
- ...
- \newcode
- QPrintDialog dialog(printer, parent);
- if (dialog.exec())
- ...
- \endcode
- \endquotation
-
- Other QDoc commands are disabled within \\oldcode ... \\endcode,
- and the '\\' character doesn't need to be escaped.
-
- \target oldcode-command
- \section1 \\oldcode
-
- The \\oldcode command requires a corresponding
- \\newcode statement; otherwise QDoc fails to parse the command
- and emits a warning.
-
- See also \l {newcode-command} {\\newcode}.
-
\target qml-command
\section1 \\qml
The \\qml and \\endqml commands enclose a snippet of QML source
- code.
+ code. Use these for proper syntax highlighting of QML code snippets.
+ The enclosed snippet must be complete as if it was a valid .qml file.
+ If the snippet is incomplete, QDoc will issue a warning and ignore the
+ snippet.
- \code
- / *!
+ \badcode *
+ /\1!
\qml
import QtQuick 2.0
@@ -1057,28 +862,9 @@
}
}
\endqml
- * /
+ \1/
\endcode
- QDoc renders this as:
-
- \qml
- import QtQuick 2.0
-
- Row {
- Rectangle {
- width: 100; height: 100
- color: "blue"
- transform: Translate { y: 20 }
- }
- Rectangle {
- width: 100; height: 100
- color: "red"
- transform: Translate { y: -20 }
- }
- }
- \endqml
-
Like the \l{code-command}{\\code} command, \\qml accepts optional
parameters.
*/
@@ -1120,27 +906,16 @@
monospace font and the standard indentation. The code is shown
verbatim.
- \code
- / *!
- This is a simple "Hello world" example:
-
- \quotefile examples/main.cpp
-
- It contains only the bare minimum you need
- to get a Qt application up and running.
- * /
- \endcode
-
- QDoc renders this as:
-
- \quotation
- This is a simple "Hello world" example:
+ \badcode *
+ /\1!
+ This is a simple "Hello world" example:
- \quotefile examples/main.cpp
+ \quotefile examples/main.cpp
- It contains only the bare minimum you need to get a Qt
- application up and running.
- \endquotation
+ It contains only the bare minimum you need
+ to get a Qt application up and running.
+ \1/
+ \endcode
See also \l {quotefromfile-command} {\\quotefromfile} and
\l {code-command} {\\code}.
@@ -1163,51 +938,28 @@
{\\skipuntil}. This enables you to quote specific portions of a
file.
- \code
- / *!
- The whole application is contained within
- the \c main() function:
-
- \quotefromfile examples/main.cpp
-
- \skipto main
- \printuntil app(argc, argv)
-
- First we create a QApplication object using
- the \c argc and \c argv parameters.
-
- \skipto QPushButton
- \printuntil resize
-
- Then we create a QPushButton, and give it a reasonable
- size using the QWidget::resize() function.
+ \badcode *
+ /\1!
+ The whole application is contained within
+ the \c main() function:
- ...
- * /
- \endcode
-
- QDoc renders this as:
-
- \quotation
- The whole application is contained within
- the \c main() function:
-
- \quotefromfile examples/main.cpp
+ \quotefromfile examples/main.cpp
- \skipto main
- \printuntil app(argc, argv)
+ \skipto main
+ \printuntil app(argc, argv)
- First we create a QApplication object using the \c argc
- and \c argv parameters.
+ First we create a QApplication object using
+ the \c argc and \c argv parameters.
- \skipto QPushButton
- \printuntil resize
+ \skipto QPushButton
+ \printuntil resize
- Then we create a QPushButton, and give it a reasonable
- size using the QWidget::resize() function.
+ Then we create a QPushButton, and give it a reasonable
+ size using the QWidget::resize() function.
- ...
- \endquotation
+ ...
+ \1/
+ \endcode
QDoc remembers which file it is quoting from, and the current
position in that file (see \l {file} {\\printline} for more
@@ -1232,58 +984,31 @@
using a monospace font and the standard indentation. The code is
shown verbatim.
- \code
- / *!
- There has to be exactly one QApplication object
- in every GUI application that uses Qt.
-
- \quotefromfile examples/main.cpp
-
- \printline QApplication
+ \badcode *
+ /\1!
+ There has to be exactly one QApplication object
+ in every GUI application that uses Qt.
- This line includes the QApplication class
- definition. QApplication manages various
- application-wide resources, such as the
- default font and cursor.
+ \quotefromfile examples/main.cpp
- \printline QPushButton
+ \printline QApplication
- This line includes the QPushButton class
- definition. The QPushButton widget provides a command
- button.
+ This line includes the QApplication class
+ definition. QApplication manages various
+ application-wide resources, such as the
+ default font and cursor.
- \printline main
+ \printline QPushButton
- The main function...
- * /
- \endcode
-
- QDoc renders this as:
-
- \quotation
- There has to be exactly one QApplication object
- in every GUI application that uses Qt.
-
- \quotefromfile examples/main.cpp
-
- \skipto QApplication
- \printline QApplication
-
- This line includes the QApplication class
- definition. QApplication manages various
- application-wide resources, such as the
- default font and cursor.
-
- \printline QPushButton
-
- This line includes the QPushButton class
- definition. The QPushButton widget provides a command
- button.
+ This line includes the QPushButton class
+ definition. The QPushButton widget provides a command
+ button.
- \printline main
+ \printline main
- The main function...
- \endquotation
+ The main function...
+ \1/
+ \endcode
\target file
@@ -1298,35 +1023,20 @@
If the substring argument is surrounded by slashes it is
interpreted as a \l {QRegularExpression}{regular expression}.
- \code
- / *!
- \quotefromfile examples/mainwindow.cpp
-
- \skipto closeEvent
- \printuntil /^\}/
-
- Close events are sent to widgets that the users want to
- close, usually by clicking \c File|Exit or by clicking
- the \c X title bar button. By reimplementing the event
- handler, we can intercept attempts to close the
- application.
- * /
- \endcode
-
- QDoc renders this as:
+ \badcode *
+ /\1!
+ \quotefromfile examples/mainwindow.cpp
- \quotation
- \quotefromfile examples/mainwindow.cpp
+ \skipto closeEvent
+ \printuntil /^\}/
- \skipto closeEvent
- \printuntil /^\}/
-
- Close events are sent to widgets that the users want to
- close, usually by clicking \c File|Exit or by clicking
- the \c X title bar button. By reimplementing the event
- handler, we can intercept attempts to close the
- application.
- \endquotation
+ Close events are sent to widgets that the users want to
+ close, usually by clicking \c File|Exit or by clicking
+ the \c X title bar button. By reimplementing the event
+ handler, we can intercept attempts to close the
+ application.
+ \1/
+ \endcode
(\l {widgets/scribble} {The complete example file...})
@@ -1359,32 +1069,18 @@
paragraph, using a monospace font and the standard
indentation. The code is shown verbatim.
- \code
- / *!
- The whole application is contained within the
- \c main() function:
-
- \quotefromfile examples/main.cpp
- \printto hello
+ \badcode *
+ /\1!
+ The whole application is contained within the
+ \c main() function:
- First we create a QApplication object using the \c argc and
- \c argv parameters...
- * /
- \endcode
-
- QDoc renders this as:
-
- \quotation
- The whole application is contained within the
- \c main() function:
-
- \quotefromfile examples/main.cpp
- \skipto main
- \printto hello
+ \quotefromfile examples/main.cpp
+ \printto hello
- First we create a QApplication object using the \c argc
- and \c argv parameters...
- \endquotation
+ First we create a QApplication object using the \c argc and
+ \c argv parameters...
+ \1/
+ \endcode
See also \l {printline-command} {\\printline} and \l
{printuntil-command} {\\printuntil}.
@@ -1409,37 +1105,20 @@
paragraph, using a monospace font and the standard
indentation. The code is shown verbatim.
- \code
- / *!
- The whole application is contained within the
- \c main() function:
-
- \quotefromfile examples/main.cpp
- \skipto main
- \printuntil hello
-
- First we create a QApplication object using the
- \c argc and \c argv parameters, then we create
- a QPushButton.
- * /
- \endcode
-
- QDoc renders this as:
-
- \quotation
- The whole application is contained within the
- \c main() function:
+ \badcode *
+ /\1!
+ The whole application is contained within the
+ \c main() function:
- \quotefromfile examples/main.cpp
- \skipto main
- \printuntil hello
+ \quotefromfile examples/main.cpp
+ \skipto main
+ \printuntil hello
- First we create a \l
- {http://doc.qt.io/qt-6/qapplication.html} {QApplication}
- object using the \c argc and \c argv parameters, then we
- create a \l
- {http://doc.qt.io/qt-6/qpushbutton.html} {QPushButton}.
- \endquotation
+ First we create a QApplication object using the
+ \c argc and \c argv parameters, then we create
+ a QPushButton.
+ \1/
+ \endcode
See also \l {printline-command} {\\printline} and \l
{printto-command} {\\printto}.
@@ -1461,39 +1140,22 @@
and it is used in conjunction with the \l {quotefromfile-command}
{\\quotefromfile} command.
- \code
- / *!
- QPushButton is a GUI push button that the user
- can press and release.
-
- \quotefromfile examples/main.cpp
- \skipline QApplication
- \printline QPushButton
-
- This line includes the QPushButton class
- definition. For each class that is part of the
- public Qt API, there exists a header file of
- the same name that contains its definition.
- * /
- \endcode
+ \badcode *
+ /\1!
+ QPushButton is a GUI push button that the user
+ can press and release.
- QDoc renders this as:
-
- \quotation
- \l
- QPushButton is a GUI push button that the user
- can press and release.
+ \quotefromfile examples/main.cpp
+ \skipline QApplication
+ \printline QPushButton
- \quotefromfile examples/main.cpp
- \skipto QApplication
- \skipline QApplication
- \printline QPushButton
+ This line includes the QPushButton class
+ definition. For each class that is part of the
+ public Qt API, there exists a header file of
+ the same name that contains its definition.
+ \1/
+ \endcode
- This line includes the QPushButton class
- definition. For each class that is part of the public
- Qt API, there exists a header file of the same name
- that contains its definition.
- \endquotation
See also \l {skipto-command} {\\skipto}, \l {skipuntil-command}
{\\skipuntil} and \l {dots} {\\dots}.
@@ -1518,38 +1180,22 @@
and it is used in conjunction with the \l {quotefromfile-command}
{\\quotefromfile} command.
- \code
- / *!
- The whole application is contained within
- the \c main() function:
-
- \quotefromfile examples/main.cpp
- \skipto main
- \printuntil }
-
- First we create a QApplication object. There
- has to be exactly one such object in
- every GUI application that uses Qt. Then
- we create a QPushButton, resize it to a reasonable
- size...
- * /
- \endcode
-
- QDoc renders this as:
-
- \quotation
- The whole application is contained within
- the \c main() function:
-
- \quotefromfile examples/main.cpp
- \skipto main
- \printuntil }
-
- First we create a QApplication object. There has to be
- exactly one such object in every GUI application that
- uses Qt. Then we create a QPushButton, resize it to a
- reasonable size ...
- \endquotation
+ \badcode *
+ /\1!
+ The whole application is contained within
+ the \c main() function:
+
+ \quotefromfile examples/main.cpp
+ \skipto main
+ \printuntil }
+
+ First we create a QApplication object. There
+ has to be exactly one such object in
+ every GUI application that uses Qt. Then
+ we create a QPushButton, resize it to a reasonable
+ size ...
+ \1/
+ \endcode
See also \l {skipline-command} {\\skipline}, \l
{skipuntil-command} {\\skipuntil} and \l {dots} {\\dots}.
@@ -1574,37 +1220,21 @@
and it is used in conjunction with the \l {quotefromfile-command}
{\\quotefromfile} command.
- \code
- / *!
- The first thing we did in the \c main() function
- was to create a QApplication object \c app.
-
- \quotefromfile examples/main.cpp
- \skipuntil show
- \dots
- \printuntil }
-
- In the end we must remember to make \c main() pass the
- control to Qt. QCoreApplication::exec() will return when
- the application exits...
- * /
- \endcode
-
- QDoc renders this as:
-
- \quotation
- The first thing we did in the \c main() function was to
- create a QApplication object \c app.
+ \badcode *
+ /\1!
+ The first thing we did in the \c main() function
+ was to create a QApplication object \c app.
- \quotefromfile examples/main.cpp
- \skipuntil show
- \dots
- \printuntil }
+ \quotefromfile examples/main.cpp
+ \skipuntil show
+ \dots
+ \printuntil }
- In the end we must remember to make \c main() pass the
- control to Qt. QCoreApplication::exec()
- will return when the application exits...
- \endquotation
+ In the end we must remember to make \c main() pass the
+ control to Qt. QCoreApplication::exec() will return when
+ the application exits...
+ \1/
+ \endcode
See also \l {skipline-command} {\\skipline}, \l {skipto-command}
{\\skipto} and \l {dots} {\\dots}.
@@ -1620,47 +1250,30 @@
stated on its own line. The dots are rendered on a new line, using
a monospace font.
- \code
- / *!
- \quotefromfile examples/main.cpp
- \skipto main
- \printuntil {
- \dots
- \skipuntil exec
- \printline }
- * /
- \endcode
-
- QDoc renders this as:
-
- \quotefromfile examples/main.cpp
- \skipto main
- \printuntil {
- \dots
- \skipuntil exec
- \printline }
+ \badcode *
+ /\1!
+ \quotefromfile examples/main.cpp
+ \skipto main
+ \printuntil {
+ \dots
+ \skipuntil exec
+ \printline }
+ \1/
+ \endcode
The default indentation is 4 spaces, but this can be adjusted
using the command's optional argument.
- \code
- / *!
+ \badcode *
+ /\1!
\dots 0
\dots
\dots 8
\dots 12
\dots 16
- * /
+ \1/
\endcode
- QDoc renders this as:
-
- \dots 0
- \dots
- \dots 8
- \dots 12
- \dots 16
-
See also \l {skipline-command} {\\skipline}, \l {skipto-command}
{\\skipto} and \l {skipuntil-command} {\\skipuntil}.
@@ -1740,44 +1353,37 @@
Here is an example using the \\l command to link to an external page:
- \code
- / *!
- Read the \l {http://doc.qt.io/qt-6/}
- {Qt 6 Documentation} carefully.
- * /
+ \badcode *
+ /\1!
+ Read the \l {http://doc.qt.io/qt-6/}
+ {Qt 6 Documentation} carefully.
+ \1/
\endcode
- QDoc renders this as:
-
- \quotation
- Read the \l {http://doc.qt.io/qt-6/}
- {Qt 6 Documentation} carefully.
- \endquotation
-
If the link target is equivalent to the link text, the second
argument can be omitted.
For example, if you have documentation like:
- \code
- / *!
- \target assertions
+ \badcode *
+ /\1!
+ \target assertions
- Assertions make some statement about the text at the
- point where they occur in the regexp, but they do not
- match any characters.
+ Assertions make some statement about the text at the
+ point where they occur in the regexp, but they do not
+ match any characters.
- ...
+ ...
- Regexps are built up from expressions, quantifiers, and
- \l {assertions} {assertions}.
- * /
+ Regexps are built up from expressions, quantifiers, and
+ \l {assertions} {assertions}.
+ \1/
\endcode
You can simplify this as follows:
- \code
- / *!
+ \badcode *
+ /\1!
\target assertions
Assertions make some statement about the text at the
@@ -1788,7 +1394,7 @@
Regexps are built up from expressions, quantifiers, and
\l assertions.
- * /
+ \1/
\endcode
For the one-parameter version, the braces can often be omitted.
@@ -1841,12 +1447,6 @@
\li \c {\l {QWidget::} {sizeHint()}}
\endlist
- QDoc renders this as:
-
- \quotation
- \l {QWidget::} {sizeHint()}
- \endquotation
-
\section2 Fixing Ambiguous Links
Because of the modularization of Qt beginning with Qt 5.0, The
@@ -1876,7 +1476,7 @@
\endlist
The \e {QML} in \e {square brackets} tells QDoc to accept a
- matching target only if the traget is on a QML page. Qdoc actually
+ matching target only if the target is on a QML page. Qdoc actually
finds the C++ namespace target first, but since that target is on
a C++ page, QDoc ignores it and keeps looking until it finds the
same target on a QML page.
@@ -1928,7 +1528,7 @@
\endlist
When a module name is used as the \e {square bracket} argument,
- QDoc will search for link the target in that module only. This
+ QDoc will search for the link target in that module only. This
makes searching for link targets more efficient.
Finally, the module name and entity type arguments can be
@@ -1981,32 +1581,19 @@
The \\sa command supports the same kind of links as the \l
{l-command} {\\l} command.
- \code
- / *!
- Appends the actions \a actions to this widget's
- list of actions.
+ \badcode *
+ /\1!
+ Appends the actions \a actions to this widget's
+ list of actions.
- \sa removeAction(), QMenu, addAction()
- * /
- void QWidget::addActions(QList<QAction *> actions)
- {
- ...
- }
+ \sa removeAction(), QMenu, addAction()
+ \1/
+ void QWidget::addActions(QList<QAction *> actions)
+ {
+ ...
+ }
\endcode
- QDoc renders this as:
-
- \quotation
- \b {void QWidget::addActions ( QList<QAction*>
- \e actions )}
-
- Appends the actions \e actions to this widget's list of
- actions.
-
- See also \l {QWidget::removeAction()} {removeAction()},
- \l QMenu, and \l {QWidget::addAction()} {addAction()}.
- \endquotation
-
See also \l {l-command} {\\l}, \l {target-command} {\\target} and
\l {keyword-command} {\\keyword}.
@@ -2018,13 +1605,13 @@
can link to using the \l {l-command} {\\l (link)} and \l
{sa-command} {\\sa (see also)} commands.
- The text up to the line break becomes the target name. Be sure to
- follow the target name with a line break. Curly brackets are not
+ \e{The text up to the line break becomes the target name}. Be sure
+ to follow the target name with a line break. Curly brackets are not
required around the target name, but they may be required when the
target name is used in a link command. See below.
- \code
- / *!
+ \badcode *
+ /\1!
\target capturing parentheses
\section1 Capturing Text
@@ -2032,7 +1619,7 @@
we can quantify and capture them.
...
- * /
+ \1/
\endcode
The target name \e{capturing parentheses} can be linked to
@@ -2045,6 +1632,25 @@
\note The brackets in the link example are required because the
target name contains spaces.
+ \section2 \\target in a \\table
+ When you use the \\target command in a table, make sure that the
+ \\target command follows a \l {li-command}{\\li}-command (table
+ cell), and that it's either on a separate line, or the last content
+ that occurs in the line it's in. This is due to how the \\target
+ command works; it consumes anything up to the next line break as
+ its parameter. In other words, if you have a table and need a
+ \\target within it, make sure that it follows the following
+ structure:
+
+ \badcode
+ \table
+ \row
+ \li \target my-target
+ My text goes here.
+ \li This is my next table cell.
+ \endtable
+ \endcode
+
See also \l {l-command} {\\l}, \l {sa-command} {\\sa} and \l
{keyword-command} {\\keyword}.
@@ -2067,8 +1673,8 @@
argument. Be sure to follow the keyword with a line break.
- \code
- / *!
+ \badcode *
+ /\1!
\class QRegularExpression
\reentrant
\brief The QRegularExpression class provides pattern
@@ -2083,24 +1689,17 @@
find patterns within text.
...
- * /
+ \1/
\endcode
The location marked with the keyword can be linked to with:
- \code
- / *!
- When a string is surrounded by slashes, it is
- interpreted as a \l {QRegularExpression}{regular expression}.
- * /
- \endcode
-
- QDoc renders this as:
-
- \quotation
+ \badcode *
+ /\1!
When a string is surrounded by slashes, it is
interpreted as a \l {regular expression}.
- \endquotation
+ \1/
+ \endcode
If the keyword text contains spaces, the brackets are required.
@@ -2138,8 +1737,8 @@
description with a line break. Curly brackets are required if the
description argument spans multiple lines.
- \code
- / *!
+ \badcode *
+ /\1!
Qt is a C++ toolkit for cross-platform GUI application development.
\image happyguy.jpg "Happy guy"
@@ -2147,21 +1746,9 @@
Qt provides single-source portability across Microsoft
Windows, macOS, Linux, and all major commercial Unix
variants. It is also available for embedded devices.
- * /
+ \1/
\endcode
- QDoc renders this as:
-
- \quotation
- Qt is a C++ toolkit for cross-platform GUI application development.
-
- \image happyguy.jpg image "Happy guy"
-
- Qt provides single-source portability across Microsoft
- Windows, macOS, Linux, and all major commercial Unix
- variants. It is also available for embedded devices.
- \endquotation
-
See also \l {inlineimage-command} {\\inlineimage} and \l
{caption-command} {\\caption}.
@@ -2173,100 +1760,53 @@
The command takes two arguments. The first argument is the name of
the image file. The second argument is optional and is a simple
- description of the image, equivalent to the HTML alt="" in an image
- tag. The description is used for tooltips, and for when a browser
- doesn't support images, like the Lynx text browser.
+ description of the image within braces {}, equivalent to the HTML
+ alt="" in an image tag. The description is used for tooltips, and
+ for when a browser doesn't support images, like the Lynx text browser.
The most common use of the \\inlineimage command is in lists and
tables. Here is an example of including inline images in a list:
- \code
- / *!
+ \badcode *
+ /\1!
\list 1
- \li \inlineimage happy.gif Oh so happy!
- \li \inlineimage happy.gif Oh so happy!
- \li \inlineimage happy.gif Oh so happy!
+ \li \inlineimage happy.gif {Oh so happy, I am a caption!}
+ \li \inlineimage happy.gif Oh so happy, but I'm not a caption.
\endlist
- * /
+ \1/
\endcode
- QDoc renders this as:
-
- \list 1
- \li \inlineimage happy.gif Oh so happy!
- \li \inlineimage happy.gif Oh so happy!
- \li \inlineimage happy.gif Oh so happy!
- \endlist
-
Here is an example of including inline images in a table:
- \code
- / *!
+ \badcode *
+ /\1!
\table
\header
\li Qt
\li Qt Creator
\row
- \li \inlineimage happy.gif Oh so happy!
+ \li \inlineimage happy.gif {Oh so happy!}
\li \inlineimage happy.gif Oh so happy!
\row
\li \inlineimage happy.gif Oh so happy!
- \li \inlineimage happy.gif Oh so happy!
+ \li \inlineimage happy.gif {Oh so happy!}
\endtable
- * /
+ \1/
\endcode
- QDoc renders this as:
-
- \raw HTML
- <table align="center" cellpadding="2"
- cellspacing="1" border="0">
- <tr valign="top" bgcolor="#a2c511">
- <th>Qt</th>
- <th>Qt Creator</th>
- </tr>
- <tr valign="top" bgcolor="#f0f0f0">
- <td><img src="images/happy.gif" alt="Oh so happy!" />
- </td>
- <td><img src="images/happy.gif" alt="Oh so happy!" />
- </td>
- </tr>
- <tr valign="top" bgcolor="#f0f0f0">
- <td><img src="images/happy.gif" alt="Oh so happy!"/>
- </td>
- <td><img src="images/happy.gif" alt="Oh so happy!" />
- </td>
- </tr>
- </table>
- \endraw
-
The command can also be used to insert an image inline with the
text.
- \code
- / *!
- \inlineimage training.jpg Qt Training
- The Qt Programming course is offered as a
- five day Open Enrollment Course. The classes
- are open to the public. Although the course is open
- to anyone who wants to learn, attendees should
- have significant experience in C++ development
- to derive maximum benefit from the course.
- * /
+ \badcode *
+ /\1!
+ \inlineimage training.jpg {Qt Training} The Qt Programming course is
+ offered as a five day Open Enrollment Course. The classes are open to
+ the public. Although the course is open to anyone who wants to learn,
+ attendees should have significant experience in C++ development to
+ derive maximum benefit from the course.
+ \1/
\endcode
- QDoc renders this as:
-
- \quotation
- \inlineimage training.jpg Qt Training
- The Qt Programming course is offered as a
- five day Open Enrollment Course. The classes
- are open to the public. Although the course is open
- to anyone who wants to learn, attendees should
- have significant experience in C++ development
- to derive maximum benefit from the course.
- \endquotation
-
See also \l {image-command} {\\image} and \l {caption-command} {\\caption}.
\target caption-command
@@ -2277,8 +1817,8 @@
The command takes all the text up to the end of the paragraph to
be the caption. Experiment until you get the effect you want.
- \code
- / *!
+ \badcode *
+ /\1!
\table 100%
\row
\li \image windows-pushbutton.png
@@ -2287,20 +1827,9 @@
\caption The QToolButton class provides a quick-access button to commands
or options, usually used inside a QToolBar.
\endtable
- * /
+ \1/
\endcode
- QDoc renders this as:
-
- \table 100%
- \row
- \li \image windows-pushbutton.png
- \caption The QPushButton widget provides a command button.
- \li \image windows-toolbutton.png
- \caption The QToolButton class provides a quick-access button to commands
- or options, usually used inside a QToolBar.
- \endtable
-
See also \l {image-command} {\\image} and \l {inlineimage-command}
{\\inlineimage}
*/
@@ -2327,15 +1856,15 @@
The command accepts a single argument specifying the table's width
as a percentage of the page width:
- \code
- / *!
- \table 100 %
+ \badcode *
+ /\1!
+ \table 100 %
- ...
+ ...
- \endtable
- * /
- \endcode
+ \endtable
+ \1/
+ \endcode
The code above ensures that the table will fill all available
space. If the table's width is smaller than 100 %, the table will
@@ -2347,119 +1876,50 @@
{header-command} {\\header} command which is a special kind of row
that has a special format.
- \code
- / *!
- \table
- \header
- \li Qt Core Feature
- \li Brief Description
- \row
- \li \l {Signal and Slots}
- \li Signals and slots are used for communication
- between objects.
- \row
- \li \l {Layout Management}
- \li The Qt layout system provides a simple
- and powerful way of specifying the layout
- of child widgets.
- \row
- \li \l {Drag and Drop}
- \li Drag and drop provides a simple visual
- mechanism which users can use to transfer
- information between and within applications.
- \endtable
- * /
- \endcode
-
- QDoc renders this as:
-
- \raw HTML
- <table align="center" cellpadding="2"
- cellspacing="1" border="0">
- <tr valign="top" bgcolor="#a2c511">
- <th>Qt Core Feature</th>
- <th>Brief Description</th>
- </tr>
-
- <tr valign="top" bgcolor="#d0d0d0">
- <td>
- <a href="http://doc.qt.io/qt-6/signalsandslots.html">
- Signals and Slots</a>
- </td>
- <td>Signals and slots are used for communication
- between objects.</td>
- </tr>
-
- <tr valign="top" bgcolor="#c0c0c0">
- <td>
- <a href="http://doc.qt.io/qt-6/layout.html">
- Layout Management</a></td>
- <td>The Qt layout system provides a simple
- and powerful way of specifying the layout
- of child widgets.</td>
- </tr>
-
- <tr valign="top" bgcolor="#d0d0d0">
- <td>
- <a href="http://doc.qt.io/qt-6/dnd.html">
- Drag and Drop</a></td>
- <td>Drag and drop provides a simple visual
- mechanism which users can use to transfer
- information between and within applications.</td>
- </tr>
-
- </table>
- \endraw
+ \badcode *
+ /\1!
+ \table
+ \header
+ \li Qt Core Feature
+ \li Brief Description
+ \row
+ \li \l {Signal and Slots}
+ \li Signals and slots are used for communication
+ between objects.
+ \row
+ \li \l {Layout Management}
+ \li The Qt layout system provides a simple
+ and powerful way of specifying the layout
+ of child widgets.
+ \row
+ \li \l {Drag and Drop}
+ \li Drag and drop provides a simple visual
+ mechanism which users can use to transfer
+ information between and within applications.
+ \endtable
+ \1/
+ \endcode
You can also make cells span several rows and columns. For
example:
- \code
- / *!
- \table
- \header
- \li {3,1} This header cell spans three columns,
- but only one row.
- \row
- \li {2, 1} This table cell spans two columns,
- but only one row
- \li {1, 2} This table cell spans only one column,
- but two rows.
- \row
- \li A regular table cell
- \li A regular table cell
- \endtable
- * /
- \endcode
-
- QDoc renders this as:
-
- \raw HTML
- <table align="center" cellpadding="2" cellspacing="1"
- border="0">
-
- <tr valign="top" bgcolor="#a2c511">
- <th colspan="3" rowspan=" 1">
- This header cell spans three columns, but only one row.
- </th>
- </tr>
-
- <tr valign="top" bgcolor="#d0d0d0">
- <td colspan="2" rowspan=" 1">
- This table cell spans two columns, but only one row.
- </td>
- <td rowspan=" 2">
- This table cell spans only one column, but two rows.
- </td>
- </tr>
-
- <tr valign="top" bgcolor="#c0c0c0">
- <td>A regular table cell</td>
- <td>A regular table cell</td>
- </tr>
-
- </table>
- \endraw
+ \badcode *
+ /\1!
+ \table
+ \header
+ \li {3,1} This header cell spans three columns,
+ but only one row.
+ \row
+ \li {2, 1} This table cell spans two columns,
+ but only one row
+ \li {1, 2} This table cell spans only one column,
+ but two rows.
+ \row
+ \li A regular table cell
+ \li A regular table cell
+ \endtable
+ \1/
+ \endcode
See also \l {header-command} {\\header}, \l {row-command} {\\row} and \l {li-command} {\\li}.
@@ -2476,40 +1936,19 @@
A header cell's text is centered within the table cell and
rendered using a bold font.
- \code
- / *!
- \table
- \header
- \li Qt Core Feature
- \li Brief Description
- \row
- \li \l {Signal and Slots}
- \li Signals and slots are used for communication
- between objects.
- \endtable
- * /
- \endcode
-
- QDoc renders this as:
-
- \raw HTML
- <table align="center" cellpadding="2"
- cellspacing="1" border="0">
- <tr valign="top" bgcolor="#a2c511">
- <th>Qt Core Feature</th>
- <th>Brief Description</th>
- </tr>
-
- <tr valign="top" bgcolor="#d0d0d0">
- <td>
- <a href="http://doc.qt.io/qt-6/signalsandslots.html">
- Signals and Slots</a>
- </td>
- <td>Signals and slots are used for communication
- between objects.</td>
- </tr>
- </table>
- \endraw
+ \badcode *
+ /\1!
+ \table
+ \header
+ \li Qt Core Feature
+ \li Brief Description
+ \row
+ \li \l {Signal and Slots}
+ \li Signals and slots are used for communication
+ between objects.
+ \endtable
+ \1/
+ \endcode
See also \l {table-command} {\\table}, \l {row-command} {\\row} and \l {li-command} {\\li}.
@@ -2528,69 +1967,29 @@
shades of grey, making it easier to distinguish the rows from each
other. The cells' contents is left aligned.
- \code
- / *!
- \table
- \header
- \li Qt Core Feature
- \li Brief Description
- \row
- \li \l {Signal and Slots}
- \li Signals and slots are used for communication
- between objects.
- \row
- \li \l {Layout Management}
- \li The Qt layout system provides a simple
- and powerful way of specifying the layout
- of child widgets.
- \row
- \li \l {Drag and Drop}
- \li Drag and drop provides a simple visual
- mechanism which users can use to transfer
- information between and within applications.
- \endtable
- * /
- \endcode
-
- QDoc renders this as:
-
- \raw HTML
- <table align="center" cellpadding="2"
- cellspacing="1" border="0">
- <tr valign="top" bgcolor="#a2c511">
- <th>Qt Core Feature</th>
- <th>Brief Description</th>
- </tr>
-
- <tr valign="top" bgcolor="#d0d0d0">
- <td>
- <a href="http://doc.qt.io/qt-6/signalsandslots.html">
- Signals and Slots</a>
- </td>
- <td>Signals and slots are used for communication
- between objects.</td>
- </tr>
-
- <tr valign="top" bgcolor="#c0c0c0">
- <td>
- <a href="http://doc.qt.io/qt-6/layout.html">
- Layout Management</a></td>
- <td>The Qt layout system provides a simple
- and powerful way of specifying the layout
- of child widgets.</td>
- </tr>
-
- <tr valign="top" bgcolor="#d0d0d0">
- <td>
- <a href="http://doc.qt.io/qt-6/dnd.html">
- Drag and Drop</a></td>
- <td>Drag and drop provides a simple visual
- mechanism which users can use to transfer
- information between and within applications.</td>
- </tr>
-
- </table>
- \endraw
+ \badcode *
+ /\1!
+ \table
+ \header
+ \li Qt Core Feature
+ \li Brief Description
+ \row
+ \li \l {Signal and Slots}
+ \li Signals and slots are used for communication
+ between objects.
+ \row
+ \li \l {Layout Management}
+ \li The Qt layout system provides a simple
+ and powerful way of specifying the layout
+ of child widgets.
+ \row
+ \li \l {Drag and Drop}
+ \li Drag and drop provides a simple visual
+ mechanism which users can use to transfer
+ information between and within applications.
+ \endtable
+ \1/
+ \endcode
See also \l {table-command} {\\table}, \l {header-command}
{\\header}, and \l {li-command} {\\li}.
@@ -2606,6 +2005,9 @@
ends at the next blank line or \\value. The arguments are rendered in a
table.
+ \note To include images in the \\value description, use the
+ \l {inlineimage-command}{\\inlineimage} command.
+
Without a \e since clause, a \\value command could look like this:
\code
@@ -2655,122 +2057,88 @@
list always contains one or more items. Lists can be nested. For
example:
- \code
- / *!
- \list
- \li Qt Reference Documentation: Getting Started
- \list
- \li How to Learn Qt
- \li Installation
- \list
- \li Qt/X11
- \li Qt/Windows
- \li Qt/Mac
- \li Qt/Embedded
- \endlist
- \li Tutorial and Examples
- \endlist
- \endlist
- * /
- \endcode
-
- QDoc renders this as:
-
- \list
- \li Qt Reference Documentation: Getting Started
- \list
- \li How to Learn Qt
- \li Installation
- \list
- \li Qt/X11
- \li Qt/Windows
- \li Qt/Mac
- \li Qt/Embedded
- \endlist
- \li Tutorial and Examples
- \endlist
- \endlist
+ \badcode *
+ /\1!
+ \list
+ \li Qt Reference Documentation: Getting Started
+ \list
+ \li How to Learn Qt
+ \li Installation
+ \list
+ \li Qt/X11
+ \li Qt/Windows
+ \li Qt/Mac
+ \li Qt/Embedded
+ \endlist
+ \li Tutorial and Examples
+ \endlist
+ \endlist
+ \1/
+ \endcode
The \\list command takes an optional argument providing
alternative appearances for the list items.
- \code
- / *!
- \list
- \li How to Learn Qt
- \li Installation
- \li Tutorial and Examples
- \endlist
- * /
- \endcode
-
- QDoc renders the list items with bullets (the default):
-
- \list
- \li How to Learn Qt
- \li Installation
- \li Tutorial and Examples
- \endlist
+ \badcode *
+ /\1!
+ \list
+ \li How to Learn Qt
+ \li Installation
+ \li Tutorial and Examples
+ \endlist
+ \1/
+ \endcode
If you provide 'A' as an argument to the \\list command, the
bullets are replaced with characters in alphabetical order:
- \list A
- \li How to Learn Qt
- \li Installation
- \li Tutorial and Examples
- \endlist
-
- If you replace 'A' with '1', the list items are numbered in
- ascending order:
+ \list A
+ \li How to Learn Qt
+ \li Installation
+ \li Tutorial and Examples
+ \endlist
- \list 1
- \li How to Learn Qt
- \li Installation
- \li Tutorial and Examples
+ If you replace 'A' with '1', the list items are numbered in
+ ascending order:
- \endlist
+ \list 1
+ \li How to Learn Qt
+ \li Installation
+ \li Tutorial and Examples
+ \endlist
If you provide 'i' as the argument, the bullets are replaced with
roman numerals:
- \list i
- \li How to Learn Qt
- \li Installation
- \li Tutorial and Examples
- \endlist
+ \list i
+ \li How to Learn Qt
+ \li Installation
+ \li Tutorial and Examples
+ \endlist
Finally, you can make the list items appear with roman numbers
following in ascending order if you provide 'I' as the optional
argument:
- \list I
- \li How to Learn Qt
- \li Installation
- \li Tutorial and Examples
- \endlist
+ \list I
+ \li How to Learn Qt
+ \li Installation
+ \li Tutorial and Examples
+ \endlist
You can also make the listing start at any character or number by
simply provide the number or character you want to start at. For
example:
- \code
- / *!
- \list G
- \li How to Learn Qt
- \li Installation
- \li Tutorial and Examples
- \endlist
- * /
- \endcode
-
- QDoc renders this as:
-
- \list G
- \li How to Learn Qt
- \li Installation
- \li Tutorial and Examples
- \endlist
+ \badcode *
+ /\1!
+ \list G
+ \li How to Learn Qt
+ \li Installation
+ \li Tutorial and Examples
+ \endlist
+ \1/
+ \endcode
See also \l {li-command} {\\li}.
@@ -2789,52 +2157,23 @@
If the command is used within a table, you can also specify
how many rows or columns the item should span.
- \code
- / *!
- \table
- \header
- \li {3,1} This header cell spans three columns
- but only one row.
- \row
- \li {2, 1} This table item spans two columns
- but only one row
- \li {1, 2} This table item spans only one column,
- but two rows.
- \row
- \li A regular table item
- \li A regular table item
- \endtable
- * /
- \endcode
-
- QDoc renders this as:
-
- \raw HTML
- <table align="center" cellpadding="2" cellspacing="1"
- border="0">
-
- <tr valign="top" bgcolor="#a2c511">
- <th colspan="3" rowspan=" 1">
- This header cell spans three columns, but only one row.
- </th>
- </tr>
-
- <tr valign="top" bgcolor="#d0d0d0">
- <td colspan="2" rowspan=" 1">
- This table item spans two columns, but only one row.
- </td>
- <td rowspan=" 2">
- This table item spans only one column, but two rows.
- </td>
- </tr>
-
- <tr valign="top" bgcolor="#c0c0c0">
- <td>A regular table item</td>
- <td>A regular table item</td>
- </tr>
-
- </table>
- \endraw
+ \badcode *
+ /\1!
+ \table
+ \header
+ \li {3,1} This header cell spans three columns
+ but only one row.
+ \row
+ \li {2, 1} This table item spans two columns
+ but only one row
+ \li {1, 2} This table item spans only one column,
+ but two rows.
+ \row
+ \li A regular table item
+ \li A regular table item
+ \endtable
+ \1/
+ \endcode
If not specified, the item will span one column and one row.
@@ -2864,42 +2203,42 @@
\b{<blockquote>} and \b{</blockquote>} in the html output,
e.g.:
- \code
- / *!
- Although the prospect of a significantly broader market is
- good news for Firstlogic, the notion also posed some
- challenges. Dave Dobson, director of technology for the La
- Crosse, Wisconsin-based company, said:
-
- \quotation
- As our solutions were being adopted into new
- environments, we saw an escalating need for easier
- integration with a wider range of enterprise
- applications.
- \endquotation
- * /
- \endcode
+ \badcode *
+ /\1!
+ Although the prospect of a significantly broader market is
+ good news for Firstlogic, the notion also posed some
+ challenges. Dave Dobson, director of technology for the La
+ Crosse, Wisconsin-based company, said:
+
+ \quotation
+ As our solutions were being adopted into new
+ environments, we saw an escalating need for easier
+ integration with a wider range of enterprise
+ applications.
+ \endquotation
+ \1/
+ \endcode
The text in the \b{\\quotation} block will appear in the generated HTML as:
- \code
+ \badcode
<blockquote>
<p>As our solutions were being adopted into new environments,
we saw an escalating need for easier integration with a wider
range of enterprise applications.</p>
- </blockquote>
+ </blockquote>
\endcode
The built-in style sheet for most browsers will render the
contents of the <blockquote> tag with left and right
indentations. The example above would be rendered as:
- \quotation
- As our solutions were being adopted into new
- environments, we saw an escalating need for easier
- integration with a wider range of enterprise
- applications.
- \endquotation
+ \quotation
+ As our solutions were being adopted into new
+ environments, we saw an escalating need for easier
+ integration with a wider range of enterprise
+ applications.
+ \endquotation
But you can redefine the \b{<blockquote>} tag in your style.css file.
@@ -2932,243 +2271,105 @@
\target brief-command
\section1 \\brief
- The \\brief command introduces a one-sentence description of a
- class, namespace, header file, property, or variable.
+ The \\brief command introduces a one-sentence description of
+ any of the \l{Topic Commands}.
The brief text is used to introduce the documentation of the
associated object, and in lists generated using the \l
{generatelist-command} {\\generatelist} command and the \l
{annotatedlist-command} {\\annotatedlist} command.
- The \\brief command can be used in two significant different ways:
- \l {brief class} {One for classes, namespaces and header files},
- and \l {brief-property} {one for properties and variables}.
-
- \target brief-property
-
- When the \\brief command is used to describe a property or a
- variable, the brief text must be a sentence fragment starting with
- "whether" (for a boolean property or variable) or starting with
- "the" (for any other property or variable).
+ The brief text will be displayed in the documentation
+ for that particular topic.
For example the boolean QWidget::isWindow property:
- \code
- / *!
- \property QWidget::isActiveWindow
- \brief Whether this widget's window is the active window.
-
- The active window is the window that contains the widget that
- has keyboard focus.
-
- When popup windows are visible, this property is \c true
- for both the active window \e and the popup.
-
- \sa activateWindow(), QApplication::activeWindow()
- * /
- \endcode
-
- and the QWidget::geometry property
-
- \code
- / *!
- \property QWidget::geometry
- \brief The geometry of the widget relative to its parent and
- excluding the window frame.
-
- When changing the geometry, the widget, if visible,
- receives a move event (moveEvent()) and/or a resize
- event (resizeEvent()) immediately.
-
- ...
+ \badcode *
+ /\1!
+ \property QWidget::isActiveWindow
+ \brief Whether this widget's window is the active window.
- \sa frameGeometry(), rect(), ...
- * /
- \endcode
+ The active window is the window that contains the widget that
+ has keyboard focus.
- QDoc renders this as:
+ When popup windows are visible, this property is \c true
+ for both the active window \e and the popup.
- \quotation
- \raw HTML
- <h3>geometry :
- <a href="http://doc.qt.io/qt-6/qrect.html">QRect</a>
- </h3>
- \endraw
+ \sa activateWindow(), QApplication::activeWindow()
+ \1/
+ \endcode
- This property holds the geometry of the widget relative
- to its parent and excluding the window frame.
+ and the QWidget::geometry property
- ...
+ \badcode *
+ /\1!
+ \property QWidget::geometry
+ \brief The geometry of the widget relative to its parent and
+ excluding the window frame.
- Access functions:
- \list
- \li \b {const QRect & geometry () const}
- \li \b {void setGeometry ( int x, int y, int w, int h )}
- \li \b {void setGeometry ( const QRect & )}
- \endlist
+ When changing the geometry, the widget, if visible,
+ receives a move event (moveEvent()) and/or a resize
+ event (resizeEvent()) immediately.
- See also \l
- {QWidget::frameGeometry()} {frameGeometry()}, \l
- {QWidget::rect()} {rect()}, ...
- \endquotation
+ ...
- \target brief class
+ \sa frameGeometry(), rect(), ...
+ \1/
+ \endcode
When the \\brief command is used to describe a class, we recommend
using a complete sentence like this:
- \code
- The <classname> class is|provides|contains|specifies...
- \endcode
+ \badcode
+ The <classname> class is|provides|contains|specifies...
+ \endcode
\warning Do not repeat your detailed description with the same sentence as
the brief statement will be the first paragraph of the detailed
description.
- \code
- / *!
- \class PreviewWindow
- \brief The PreviewWindow class is a custom widget
- displaying the names of its currently set
- window flags in a read-only text editor.
-
- The PreviewWindow class inherits QWidget. The widget
- displays the names of its window flags set with the
- setWindowFlags() function. It is also provided with a
- QPushButton that closes the window.
+ \badcode *
+ /\1!
+ \class PreviewWindow
+ \brief The PreviewWindow class is a custom widget
+ displaying the names of its currently set
+ window flags in a read-only text editor.
- ...
+ The PreviewWindow class inherits QWidget. The widget
+ displays the names of its window flags set with the
+ setWindowFlags() function. It is also provided with a
+ QPushButton that closes the window.
- \sa QWidget
- * /
- \endcode
+ ...
- QDoc renders this as:
-
- \quotation
- \raw HTML
- <h1>PreviewWindow Class Reference</h1>
- \endraw
-
- The PreviewWindow class is a custom widget displaying
- the names of its currently set window flags in a
- read-only text editor. \l {preview window} {More...}
-
- \raw HTML
- <h3>Properties</h3>
- \endraw
-
- \list
- \li 52 properties inherited from QWidget
- \li 1 property inherited from QObject
- \endlist
-
- \raw HTML
- <h3>Public Functions</h3>
- \endraw
-
- \list
- \li \l {constructor} {PreviewWindow}(QWidget *parent = 0)
- \li void \l {function} {setWindowFlags}(Qt::WindowFlags flags)
- \endlist
-
- \list
- \li 183 public functions inherited from QWidget
- \li 28 public functions inherited from QObject
- \endlist
-
- \raw HTML
- <h3>Public Slots</h3>
- \endraw
-
- \list
- \li 17 public slots inherited from QWidget
- \li 1 public slot inherited from QObject
- \endlist
-
- \raw HTML
- <h3>Additional Inherited Members</h3>
- \endraw
-
- \list
- \li 1 signal inherited from QWidget
- \li 1 signal inherited from QObject
- \li 4 static public members inherited from QWidget
- \li 4 static public members inherited from QObject
- \li 39 protected functions inherited from QWidget
- \li 7 protected functions inherited from QObject
- \endlist
-
- \target preview window
-
- \raw HTML
- <hr />
- <h2>Detailed Description</h2>
- \endraw
-
- The PreviewWindow class is a custom widget displaying
- the names of its currently set window flags in a
- read-only text editor.
-
- The PreviewWindow class inherits QWidget. The widget
- displays the names of its window flags set with the \l
- {function} {setWindowFlags()} function. It is also
- provided with a QPushButton that closes the window.
-
- ...
-
- See also QWidget.
-
- \raw HTML
- <hr />
- <h2>Member Function Documentation</h2>
- \endraw
-
- \target constructor
- \raw HTML
- <h3>PreviewWindow(QWidget *parent = 0)</h3>
- \endraw
-
- Constructs a preview window widget with \e parent.
-
- \target function
- \raw HTML
- <h3>setWindowFlags(Qt::WindowFlags flags)</h3>
- \endraw
-
- Sets the widgets flags using the
- QWidget::setWindowFlags() function.
-
- Then runs through the available window flags,
- creating a text that contains the names of the flags
- that matches the flags parameter, displaying
- the text in the widgets text editor.
- \endquotation
+ \sa QWidget
+ \1/
+ \endcode
Using \\brief in a \l{namespace-command}{\\namespace}:
- \code
- / *!
- \namespace Qt
+ \badcode *
+ /\1!
+ \namespace Qt
- \brief The Qt namespace contains miscellaneous identifiers
- used throughout the Qt library.
- * /
- \endcode
+ \brief The Qt namespace contains miscellaneous identifiers
+ used throughout the Qt library.
+ \1/
+ \endcode
Using \\brief in a \l{headerfile-command}{\\headerfile}:
- \code
- / *!
- \headerfile <QtGlobal>
- \title Global Qt Declarations
+ \badcode *
+ /\1!
+ \headerfile <QtGlobal>
+ \title Global Qt Declarations
- \brief The <QtGlobal> header file provides basic
- declarations and is included by all other Qt headers.
+ \brief The <QtGlobal> header file provides basic
+ declarations and is included by all other Qt headers.
- \sa <QtAlgorithms>
- * /
- \endcode
+ \sa <QtAlgorithms>
+ \1/
+ \endcode
See also \l{property-command} {\\property}, \l{class-command}
{\\class}, \l{namespace-command} {\\namespace} and
@@ -3185,50 +2386,50 @@
An example of a license agreement enclosed in \\legalese
and \\endlegalese:
- \code
- / *!
- \legalese
- Copyright 1996 Daniel Dardailler.
-
- Permission to use, copy, modify, distribute, and sell this
- software for any purpose is hereby granted without fee,
- provided that the above copyright notice appear in all
- copies and that both that copyright notice and this
- permission notice appear in supporting documentation, and
- that the name of Daniel Dardailler not be used in
- advertising or publicity pertaining to distribution of the
- software without specific, written prior permission. Daniel
- Dardailler makes no representations about the suitability of
- this software for any purpose. It is provided "as is"
- without express or implied warranty.
-
- Modifications Copyright 1999 Matt Koss, under the same
- license as above.
- \endlegalese
- * /
- \endcode
-
- It will appear in the generated HTML as:
-
- \code
- <div class="LegaleseLeft">
+ \badcode *
+ /\1!
+ \legalese
+ Copyright 1996 Daniel Dardailler.
+
+ Permission to use, copy, modify, distribute, and sell this
+ software for any purpose is hereby granted without fee,
+ provided that the above copyright notice appear in all
+ copies and that both that copyright notice and this
+ permission notice appear in supporting documentation, and
+ that the name of Daniel Dardailler not be used in
+ advertising or publicity pertaining to distribution of the
+ software without specific, written prior permission. Daniel
+ Dardailler makes no representations about the suitability of
+ this software for any purpose. It is provided "as is"
+ without express or implied warranty.
+
+ Modifications Copyright 1999 Matt Koss, under the same
+ license as above.
+ \endlegalese
+ \1/
+ \endcode
+
+ It will appear in the generated HTML as:
+
+ \badcode
+ <div class="LegaleseLeft">
<p>Copyright 1996 Daniel Dardailler.</p>
- <p>Permission to use, copy, modify, distribute, and sell
- this software for any purpose is hereby granted without fee,
- provided that the above copyright notice appear in all
- copies and that both that copyright notice and this
- permission notice appear in supporting documentation, and
- that the name of Daniel Dardailler not be used in
- advertising or publicity pertaining to distribution of the
- software without specific, written prior permission. Daniel
- Dardailler makes no representations about the suitability of
- this software for any purpose. It is provided "as is"
- without express or implied warranty.</p>
+ <p>Permission to use, copy, modify, distribute, and sell
+ this software for any purpose is hereby granted without fee,
+ provided that the above copyright notice appear in all
+ copies and that both that copyright notice and this
+ permission notice appear in supporting documentation, and
+ that the name of Daniel Dardailler not be used in
+ advertising or publicity pertaining to distribution of the
+ software without specific, written prior permission. Daniel
+ Dardailler makes no representations about the suitability of
+ this software for any purpose. It is provided "as is"
+ without express or implied warranty.</p>
<p>Modifications Copyright 1999 Matt Koss, under the same
- license as above.</p>
- </div>
- \endcode
+ license as above.</p>
+ </div>
+ \endcode
If the \\endlegalese command is omitted, QDoc will process the
\\legalese command but considers the rest of the documentation
@@ -3253,28 +2454,16 @@
The \\warning command prepends "Warning:" to the command's
argument, in bold font.
- \code
- / *!
- Qt::HANDLE is a platform-specific handle type
- for system objects. This is equivalent to
- \c{void *} on Windows and macOS, and to
- \c{unsigned long} on X11.
-
- \warning Using this type is not portable.
- * /
- \endcode
-
- QDoc renders this as:
-
- \quotation
- Qt::HANDLE is a platform-specific handle type
- for system objects. This is equivalent to
- \c{void *} on Windows and macOS, and to
- \c{unsigned long} on X11.
-
- \warning Using this type is not portable.
- \endquotation
+ \badcode *
+ /\1!
+ Qt::HANDLE is a platform-specific handle type
+ for system objects. This is equivalent to
+ \c{void *} on Windows and macOS, and to
+ \c{unsigned long} on X11.
+ \warning Using this type is not portable.
+ \1/
+ \endcode
*/
@@ -3296,18 +2485,17 @@
group, each member listed with its \e {brief} text. Below is an
example from the Qt Reference Documentation:
- \code
- / *!
- ...
- \section1 Drag and Drop Classes
-
- These classes deal with drag and drop and the necessary mime type
- encoding and decoding.
+ \badcode *
+ /\1!
+ ...
+ \section1 Drag and Drop Classes
- \annotatedlist draganddrop
+ These classes deal with drag and drop and the necessary mime type
+ encoding and decoding.
- * /
- \endcode
+ \annotatedlist draganddrop
+ \1/
+ \endcode
This generates a list of all the C++ classes and/or QML types in
the \e{draganddrop} group. A C++ class or QML type in the
@@ -3322,50 +2510,72 @@
and namespaces. This information will then appear in a table at the top of
the class or namespace documentation page. For example:
- \code
- / *!
- \namespace Foo
- \inheaderfile Bar
- \qtcmakepackage Baz
- \brief A namespace.
+ \badcode *
+ /*!
+ \namespace Foo
+ \inheaderfile Bar
+ \qtcmakepackage Baz
+ \brief A namespace.
- [...]
- * /
- \endcode
+ ...
+ \1/
+ \endcode
QDoc will output this as
- \raw HTML
- <h1 class="title">Foo Namespace</h1>
- <p>A namespace. <a href="#details">More...</a></p>
- <div class="table"><table class="alignedsummary">
- <tr><td class="memItemLeft rightAlign topAlign"> Header:</td><td class="memItemRight bottomAlign"> <span class="preprocessor">#include &lt;Bar&gt;</span>
- </td></tr><tr><td class="memItemLeft rightAlign topAlign"> CMake:</td><td class="memItemRight bottomAlign"> find_package(Qt6 COMPONENTS Baz REQUIRED)</td></tr>
- \endraw
+ \quotation
+ \raw HTML
+ <h1 class="title">Foo Namespace</h1>
+ <p>A namespace. <a>More...</a></p>
+ <div class="table"><table class="alignedsummary">
+ <tr><td class="memItemLeft rightAlign topAlign"> Header:</td><td class="memItemRight bottomAlign"> <span class="preprocessor">#include &lt;Bar&gt;</span></td></tr>
+ <tr><td class="memItemLeft rightAlign topAlign"> CMake:</td><td class="memItemRight bottomAlign"> find_package(Qt6 REQUIRED COMPONENTS Baz)</td></tr>
+ </table></div>
+ \endraw
+ \endquotation
+
+
+ \target qtcmaketargetitem-command
+ \section1 \\qtcmaketargetitem
+
+ Use the \\qtcmaketargetitem command to override the \e {item} part of the
+ CMake \c{target_link_libraries} information that's added to classes and
+ namespaces. The command must be used in conjunction with the
+ \l{module-command}{\\module} and \l{qtcmakepackage}{\\qtcmakepackage}}
+ commands.
+
+ \e{See also} \l{module-command}{\\module} and
+ \l{qtcmakepackage}{\\qtcmakepackage}}
\target generatelist-command
\section1 \\generatelist
The \\generatelist command expands to a list of links to the
- documentation entities in a group. Below is an example from the Qt
- Reference Documentation:
+ documentation entities grouped with an \l {ingroup-command}
+ {\\ingroup} command or entities that match one of the arguments
+ listed below. An example from the Qt Reference Documentation:
- \code
- / *!
- \page classes.html
- \title All Classes
+ \badcode *
+ /\1!
+ \page classes.html
+ \title All Classes
- For a shorter list that only includes the most
- frequently used classes, see \l{Qt's Main Classes}.
+ For a shorter list that only includes the most
+ frequently used classes, see \l{Qt's Main Classes}.
- \generatelist classes Q
- * /
- \endcode
+ \generatelist classes Q
+ \1/
+ \endcode
This generates the \e {All Classes} page. The command accepts the
following arguments:
+ \section2 \c {<group-name>}
+
+ With a group name as the only argument, QDoc lists all entities that
+ use the \c {\ingroup <group-name>} command.
+
\target table example
\section2 \c annotatedclasses
@@ -3429,8 +2639,8 @@
documentation. This command is used to generate the
\e {All Classes} page this way:
- \code
- / *!
+ \badcode *
+ /\1!
\page classes.html
\title All Classes
\ingroup classlists
@@ -3442,7 +2652,7 @@
list.
\generatelist classes Q
- * /
+ \1/
\endcode
A C++ class is documented with the \l {class-command} {\\class}
@@ -3457,20 +2667,19 @@
For example, this command can be used on a module page as follows:
- \code
- / *!
- \page phonon-module.html
- \module Phonon
- \title Phonon Module
- \ingroup modules
-
- \brief Contains namespaces and classes for multimedia functionality.
+ \badcode *
+ /\1!
+ \page phonon-module.html
+ \module Phonon
+ \title Phonon Module
+ \ingroup modules
- \generatelist{classesbymodule Phonon}
+ \brief Contains namespaces and classes for multimedia functionality.
- ...
+ \generatelist{classesbymodule Phonon}
- * /
+ ...
+ \1/
\endcode
Each class that is a member of the specified module must be marked
@@ -3480,40 +2689,17 @@
\section2 \c qmltypesbymodule
Similar to \c classesbymodule argument, but used for listing the
- QML types from the QML module specified with the second argument.
+ QML types (excluding QML value types) from the QML module specified
+ with the second argument.
\note Support for this argument was introduced in QDoc 5.6.
- \section2 \c jstypesbymodule
-
- Similar to \c classesbymodule argument, but used for listing the
- JavaScript types from the module specified with the second argument.
-
- \note Support for this argument was introduced in QDoc 5.6.
-
- \section2 \c {examplefiles [regular_expression]}
-
- The \c examplefiles argument lists the files that are part of
- an example project. The optional second argument is a regular
- expression; if provided, only the files whose path matches with
- the regular expression are listed.
-
- The \c examplefiles argument can be only used within example
- documentation (see \l {example-command}{\\example}), and is
- typically used together with the \l {noautolist-command}{\\noautolist}
- command.
-
- \section2 \c {exampleimages [regular_expression]}
+ \section2 \c qmlvaluetypesbymodule
- The \c exampleimages argument lists the images that are part of
- an example project. The optional second argument is a regular
- expression; if provided, only the image files whose path matches
- with the regular expression are listed.
+ Similar to \c qmltypesbymodule argument, but lists QML value
+ types instead.
- The \c exampleimages argument can be only used within example
- documentation (see \l {example-command}{\\example}), and is
- typically used together with the \l {noautolist-command}{\\noautolist}
- command.
+ \note Support for this argument was introduced in QDoc 6.7.
\section2 \c functionindex
@@ -3522,8 +2708,8 @@
only to generate the \e {Qt function index} page
this way:
- \code
- / *!
+ \badcode *
+ /\1!
\page functions.html
\title All Functions
\ingroup funclists
@@ -3536,7 +2722,7 @@
class or header file where it is declared and documented.
\generatelist functionindex
- * /
+ \1/
\endcode
\section2 \c legalese
@@ -3552,14 +2738,14 @@
{\\group} pages. Qt uses it to generate the \e {overviews} page
this way:
- \code
- / *!
+ \badcode *
+ /\1!
\page overviews.html
\title All Overviews and HOWTOs
\generatelist overviews
- * /
+ \1/
\endcode
\section2 \c attributions
@@ -3575,8 +2761,8 @@
group. For example, the page for the \e {Programming with Qt}
page is generated this way:
- \code
- / *!
+ \badcode *
+ /\1!
\group qt-basic-concepts
\title Programming with Qt
@@ -3589,7 +2775,7 @@
techniuqes used in Qt development.
\generatelist {related}
- * /
+ \1/
\endcode
Each page listed on this group page contains the command:
@@ -3608,26 +2794,26 @@
The command reads the rest of the line and parses it as an C++ #if
statement.
- \code
- / *!
- \if defined(opensourceedition)
+ \badcode *
+ /\1!
+ \if defined(opensourceedition)
- \note This edition is for the development of
- \l{Qt Open Source Edition} {Free and Open Source}
- software only; see \l{Qt Commercial Editions}.
+ \note This edition is for the development of
+ \l{Qt Open Source Edition} {Free and Open Source}
+ software only; see \l{Qt Commercial Editions}.
- \endif
- * /
- \endcode
+ \endif
+ \1/
+ \endcode
This QDoc comment will only be rendered if the \c
opensourceedition preprocessor symbol is defined, and specified in
the \l {defines-variable} {defines} variable in the configuration
file to make QDoc process the code within #ifdef and #endif:
- \code
- defines = opensourceedition
- \endcode
+ \badcode
+ defines = opensourceedition
+ \endcode
You can also define the preprocessor symbol manually on the
command line. For more information see the documentation of the \l
@@ -3682,25 +2868,25 @@
\l{sources.fileextensions-variable}{sources.fileextensions};
for example, \c .qdocinc.
- The command can have either one or two arguments. The first
+ The command can have one or more arguments. The first
argument is always a file name. The contents of the file must be
QDoc input, in other words, a sequence of QDoc commands and text, but
without the enclosing QDoc comment \c{/}\c{*!} ... \c{*}\c{/} delimiters.
- If you want to include the entire named file, don't use the second
- argument. If you want to include only part of the file, see the
+ If you want to include the entire named file, leave the second argument
+ empty. If you want to include only part of the file, see the
\l{2-argument-form}{two argument form} below. Here is an example
of the one argument form:
- \code
- / *!
- \page corefeatures.html
- \title Core Features
+ \badcode *
+ /\1!
+ \page corefeatures.html
+ \title Core Features
- \include examples/signalandslots.qdocinc
- \include examples/objectmodel.qdocinc
- \include examples/layoutmanagement.qdocinc
- * /
- \endcode
+ \include examples/signalandslots.qdocinc
+ \include examples/objectmodel.qdocinc
+ \include examples/layoutmanagement.qdocinc
+ \1/
+ \endcode
QDoc renders this page \l{corefeatures.html} {as shown here}.
@@ -3710,69 +2896,94 @@
It is a waste of time to make a separate \c .qdocinc file for every
QDoc include snippet you want to use in multiple places in the
documentation, especially given that you probably have to put the
- copyright/license notice in every one of these files. So if you
- have a large number of snippets to be included, you can put them all in a
- single file if you want, and surround each one with:
- \code
- //! [snippet-id1]
+ copyright/license notice in every one of these files. If you
+ have multiple snippets to be included, you can put them all in a
+ single file and surround each one with:
- QDoc commands and text...
+ \badcode
+ //! [snippet-id1]
+
+ QDoc commands and text...
//! [snippet-id1]
- //! [snippet-id2]
+ //! [snippet-id2]
- More QDoc commands and text...
+ More QDoc commands and text...
//! [snippet-id2]
\endcode
Then you can use the two-argument form of the command:
- \code
- \input examples/signalandslots.qdocinc snippet-id2
- \input examples/objectmodel.qdocinc another-snippet-id
+ \badcode
+ \include examples/signalandslots.qdocinc snippet-id2
+ \include examples/objectmodel.qdocinc another-snippet-id
\endcode
- It works as expected. The sequence of QDoc commands and text found
- between the two tags with the same name as the second argument is
- sent to the QDoc input stream. You can even have nested snippets.
+ The sequence of QDoc commands and text found between the two tags
+ with the same name as the second argument is sent to the QDoc input
+ stream. You can even have nested snippets.
\note Snippet identifiers work also within documentation comment
(\beginqdoc .. \endqdoc) blocks, so it's not necessary to use a
separate \c .qdocinc file. When processing a comment block, QDoc
removes any \c {//!} comment lines from the generated output.
+ \section2 Extra arguments
+
+ Since QDoc 6.3, any further arguments passed to the \\include command
+ are used for injecting strings into the included content. To inject a
+ string to a specific location in the content, add a backslash followed
+ by a digit (1..9). The digits correspond with the order of the argument
+ list. Enclose arguments in curly braces to ensure that QDoc renders the
+ entire argument, including possible whitespace characters, as you expect.
+
+ \important Each additional argument (including the snippet ID) must be
+ enclosed in braces. If you want to include the entire file,
+ use an empty snippet ID: \c {{}}.
+
+ For example, given the following snippet in a file \c includes.qdocinc:
+
+ \badcode
+ //! [usage]
+ To enable \e{\1}, select \uicontrol {\2} > \uicontrol Enable.
+ //! [usage]
+ \endcode
+
+ Then, the following \\include line:
+
+ \badcode
+ \include includes.qdocinc {usage} {detailed output} {Verbose}
+ \endcode
+
+ Renders
+ \quotation
+ To enable \e {detailed output}, select \uicontrol {Verbose} >
+ \uicontrol Enable.
+ \endquotation
+
\target meta-command
\section1 \\meta
- The \\meta command is used for adding metadata to example documentation,
- and when generating HTML output for specifying the \e maintainer(s) of a
- C++ class.
-
+ The \\meta command is used for adding metadata to documentation.
The command has two arguments: the first argument is the name of the
- metadata attribute, and the second argument is the
- value for the attribute. Each argument should be enclosed in curly
- brackets, as shown in this example:
-
- \code
- / *!
- \class QWidget
- \brief The QWidget class is the base class of all user interface objects.
+ metadata attribute, and the second argument is the value for the attribute.
+ Each argument should be enclosed in curly brackets, as shown in this
+ example:
- \ingroup basicwidgets
+ \badcode *
+ /\1!
+ \example demos/coffee
+ \title Coffee Machine
+ \brief A Qt Quick application with a state-based custom user interface.
- \meta {technology} {User Interface}
- \meta {platform} {macOS 10.6}
- \meta {platform} {MeeGo}
- \meta {audience} {user}
- \meta {audience} {programmer}
- \meta {audience} {designer}
- * /
+ \meta {tags} {quick,embedded,states,touch}
+ \meta {category} {Application Examples}
+ \1/
\endcode
- When running QDoc to generate HTML, the example above will have no
- effect on the generated output.
+ A number of metadata attributes have a specific purpose:
\b {Example Metadata}
@@ -3782,14 +2993,16 @@
and module name. These tags are displayed in Qt Creator's Welcome mode,
helping users navigate the list of examples.
- Additional tags can be created with \c {\meta {tag} {tag1,[tag2,...]}}.
+ Additional tags can be created with \c {\meta {tag} {tag1}}
+ or \c {\meta {tags} {tag1,[tag2,...]}}.
For example:
- \badcode
- / *!
+
+ \badcode *
+ /\1!
\example helloworld
\title Hello World Example
- \meta {tag} {tutorial,basic}
- * /
+ \meta {tags} {tutorial,basic}
+ \1/
\endcode
This would result in the following tags: \e {tutorial,basic,hello,world}.
@@ -3801,7 +3014,7 @@
file, effectively removing it from Qt Creator's Welcome mode.
\badcode
- \meta tag broken
+ \meta {tag} {broken}
\endcode
\b {Example Install Paths}
@@ -3810,16 +3023,31 @@
location of an installed example. This value overrides the one that is set
using the \c examplesinstallpath configuration variable.
- \badcode
- / *!
+ \badcode *
+ /\1!
\example helloworld
\title Hello World Example
\meta {installpath} {tutorials}
- * /
+ \1/
\endcode
See also \l {examplesinstallpath}.
+ \b {Status}
+
+ A \c status argument for the \\meta command adds a custom status description
+ for a \l {class-command}{\\class} or a \l {qmltype-command}{\\qmltype}. This
+ description will then appear in a table at the top of the type reference page.
+
+ \badcode *
+ /\1!
+ \class QNativeInterface::QAndroidApplication
+ \meta {status} {Android-specific}
+ \1/
+ \endcode
+
+ See also \l {Status}{status-related commands}.
+
\target noautolist-command
\section1 \\noautolist
@@ -3850,120 +3078,72 @@
delimit parts of the documentation that you want QDoc to skip. For
example:
- \code
- / *!
- \table
- \row
- \li Basic Widgets
- \li Basic GUI widgets such as buttons, comboboxes
- and scrollbars.
-
- \omit
- \row
- \li Component Model
- \li Interfaces and helper classes for the Qt
- Component Model.
- \endomit
-
- \row
- \li Database Classes
- \li Database related classes, e.g. for SQL databases.
- \endtable
- * /
- \endcode
-
- QDoc renders this as:
-
- \raw HTML
- <table align="center" cellpadding="2"
- cellspacing="1" border="0">
+ \badcode *
+ /\1!
+ \table
+ \row
+ \li Basic Widgets
+ \li Basic GUI widgets such as buttons, comboboxes
+ and scrollbars.
- <tr valign="top" bgcolor="#d0d0d0">
- <td>Basic Widgets</td>
- <td>Basic GUI widgets such as buttons, comboboxes
- and scrollbars.</td>
- </tr>
+ \omit
+ \row
+ \li Component Model
+ \li Interfaces and helper classes for the Qt
+ Component Model.
+ \endomit
- <tr valign="top" bgcolor="#c0c0c0">
- <td>Database Classes</td>
- <td>Database related classes, e.g. for SQL databases.</td>
- </tr>
- </table>
- \endraw
+ \row
+ \li Database Classes
+ \li Database related classes, e.g. for SQL databases.
+ \endtable
+ \1/
+ \endcode
\target raw-command
- \section1 \\raw (avoid)
+ \section1 \\raw (avoid!)
The \\raw command and the corresponding
\\endraw command delimit a block of raw mark-up language code.
- \note Avoid using this command if possible. If you are trying to generate
- special table or list behavior, try to get the behavior you want
+ \warning Avoid using this command if possible. If you are trying to
+ generate special table or list behavior, try to get the behavior you want
using the \l {span-command} {\\span} and \l {div-command} {\\div}
commands in your \l {table-command} {\\table} or \l {list-command}
{\\list}.
The command takes an argument specifying the code's format.
- Currently, the only supported format is HTML.
- The \\raw command is useful if you want some special HTML effects
- in your documentation.
+ QDoc generates the given code only when generating the format that
+ was specified by the user.
- \code
- / *!
- Qt has some predefined QColor objects.
-
- \raw HTML
- <style type="text/css" id="colorstyles">
- #color-blue { background-color: #0000ff; color: #ffffff }
- #color-darkBlue { background-color: #000080; color: #ffffff }
- #color-cyan { background-color: #00ffff; color: #000000 }
- </style>
-
- <p>
- <tt id="color-blue">Blue(#0000ff)</tt>,
- <tt id="color-darkBlue">dark blue(#000080)</tt> and
- <tt id="color-cyan">cyan(#00ffff)</tt>.
- </p>
- \endraw
- * /
- \endcode
+ For example, "\\raw HTML" will only generate code when QDoc
+ generates HTML documentation.
- QDoc renders this as:
+ \note You can often achieve the intended purpose by using QDoc commands,
+ while reducing the chance of mistakes or content becoming unmaintained.
- \quotation
- Qt has some predefined QColor objects.
-
- \raw HTML
- <style type="text/css" id="colorstyles">
- #color-blue { background-color: #0000ff; color: #ffffff }
- #color-darkBlue { background-color: #000080; color: #ffffff }
- #color-cyan { background-color: #00ffff; color: #000000 }
- </style>
-
- <p>
- <tt id="color-blue">Blue(#0000ff)</tt>,
- <tt id="color-darkBlue">dark blue(#000080)</tt> and
- <tt id="color-cyan">cyan(#00ffff)</tt>.
- </p>
- \endraw
- \endquotation
-
- \note But you can achieve the exact same thing using QDoc
- commands. In this case, all you have to do is include the color
- styles in your style.css file. Then you can write:
-
- \code
- \tt {\span {id="color-blue"} {Blue(#0000ff)}},
- \tt {\span {id="color-darkBlue"} {dark blue(#000080)}} and
- \tt {\span {id="color-cyan"} {cyan(#00ffff)}}.
- \endcode
+ \target sincelist-command
+ \section1 \\sincelist
- ...which is rendered as:
+ The \\sincelist command expands to a detailed breakdown of new
+ inclusions to the documented API in a specified version. Example
+ usage:
- \tt {\span {id="color-blue"} {Blue(#0000ff)}},
- \tt {\span {id="color-darkBlue"} {dark blue(#000080)}} and
- \tt {\span {id="color-cyan"} {cyan(#00ffff)}}.
+ \badcode * \QtMajorVersion \QtMinorVersion \QtVer
+ /\1!
+ \page newclasses\2\3.html
+ \title New Classes and Functions in \4
+ \brief A comprehensive list of new classes and functions in \4.
+
+ \sincelist \4
+ \1/
+ \endcode
+
+ \\sincelist takes a single argument, a version string. The generated
+ output includes all functionality that is marked with a
+ \l {since-command}{\\since} command or a \l {since clause} matching
+ the version string.
\target unicode-command
\section1 \\unicode
@@ -3973,25 +3153,6 @@
The command takes an argument specifying the character as an
integer. By default, base 10 is assumed, unless a '0x' or '0'
- prefix is specified (for base 16 and 8, respectively). For
- example:
-
- \code
- O G\unicode{0xEA}nio e as Rosas
-
- \unicode 0xC0 table en famille avec 15 \unicode 0x20AC par jour
-
- \unicode 0x3A3 \e{a}\sub{\e{i}}
- \endcode
-
- QDoc renders this as:
-
- \quotation
- O G\unicode{0xEA}nio e as Rosas
-
- \unicode 0xC0 table en famille avec 15 \unicode 0x20AC par jour
-
- \unicode 0x3A3 \e{a}\sub{\e{i}}
- \endquotation
+ prefix is specified (for base 16 and 8, respectively).
*/
diff --git a/src/qdoc/doc/qdoc-manual-qdocconf.qdoc b/src/qdoc/qdoc/doc/qdoc-manual-qdocconf.qdoc
index 3cdec870e..d6fc3305e 100644
--- a/src/qdoc/doc/qdoc-manual-qdocconf.qdoc
+++ b/src/qdoc/qdoc/doc/qdoc-manual-qdocconf.qdoc
@@ -1,33 +1,9 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the documentation of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:FDL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Free Documentation License Usage
-** Alternatively, this file may be used under the terms of the GNU Free
-** Documentation License version 1.3 as published by the Free Software
-** Foundation and appearing in the file included in the packaging of
-** this file. Please review the following information to ensure
-** the GNU Free Documentation License version 1.3 requirements
-** will be met: https://www.gnu.org/licenses/fdl-1.3.html.
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
\page 21-0-qdoc-configuration.html
- \previouspage Miscellaneous
+ \previouspage Miscellaneous Macros
\nextpage Generic Configuration Variables
\title The QDoc Configuration File
@@ -48,7 +24,7 @@
define where QDoc should find the various source files, images and
examples, where to put generated documentation etc. The
configuration file can also contain directives like \c
- include. For an example, see \l {minimal-qdocconf}{a minimal qdocconf file}.
+ include. For an example, see \l minimum.qdocconf.
You can also use configuration variables to get QDoc to support
\l{Supporting Derived Projects} {derived projects}, i.e QDoc can
@@ -164,13 +140,11 @@
\section1 Variable List
\list
- \li \l {alias-variable} {alias}
- \li \l {Cpp.ignoredirectives-variable} {Cpp.ignoredirectives}
- \li \l {Cpp.ignoretokens-variable} {Cpp.ignoretokens}
\li \l {defines-variable} {defines}
\li \l {depends-variable} {depends}
\li \l {exampledirs-variable} {exampledirs}
\li \l {examples-variable} {examples}
+ \li \l {examplesinstallpath-variable} {examplesinstallpath}
\li \l {examples.fileextensions-variable} {examples.fileextensions}
\li \l {excludedirs-variable} {excludedirs}
\li \l {excludefiles-variable} {excludefiles}
@@ -186,8 +160,6 @@
\li \l {ignorewords-variable} {ignorewords}
\li \l {ignoresince-variable} {ignoresince}
\li \l {imagedirs-variable} {imagedirs}
- \li \l {images-variable} {images}
- \li \l {images.fileextensions-variable} {images.fileextensions}
\li \l {indexes-variable} {indexes}
\li \l {language-variable} {language}
\li \l {locationinfo-variable} {locationinfo}
@@ -216,7 +188,6 @@
\list
\li \l {Generic Configuration Variables}
- \li \l {C++ Specific Configuration Variables}
\li \l {Format-specific Configuration Variables}
\endlist
@@ -242,23 +213,6 @@
documentation. You can also do some minor manipulation of QDoc
itself, controlling its output and processing behavior.
- \target alias-variable
- \section1 alias
-
- The \c alias variable renames a QDoc command.
-
- The general syntax is \tt {alias.\e{original-command-name} = \e
- temporary-command-name}.
-
- \badcode
- alias.e = i
- \endcode
-
- This renames the built-in command \\e (italics) to be \\i. The \c
- alias variable is often used for compatibility reasons.
-
- See also \l {macro-variable} {macro}.
-
\target codeindent-variable
\section1 codeindent
@@ -290,34 +244,19 @@
enclose documentation that only will be included if the
preprocessor symbol is defined.
- The values of the variable are regular expressions (see QRegularExpression
- for details). By default, no symbol is defined, meaning that code
- protected with #ifdef...#endif will be ignored.
-
\badcode
- defines = Q_QDOC \
- QT_.*_SUPPORT \
- QT_.*_LIB \
- QT_COMPAT \
- QT3_SUPPORT \
- Q_OS_.* \
- Q_BYTE_ORDER \
- __cplusplus
+ defines = QT_GUI_LIB
\endcode
This ensures that QDoc will process the code that requires these
symbols to be defined. For example:
\code
- #ifdef Q_OS_WIN
- HDC getDC() const;
- void releaseDC(HDC) const;
+ #ifdef Q_GUI_LIB
+ void keyClick(QWidget *widget, Qt::Key key, Qt::KeyboardModifiers modifier = Qt::NoModifier, int delay = -1)
#endif
\endcode
- Since the Q_OS_.* regular expression (specified using the \c
- defines variable) matches Q_OS_WIN, QDoc will process the code
- within #ifdef and #endif in our example.
You can also define preprocessor symbols manually on the command
line using the -D option. For example:
@@ -370,7 +309,7 @@
\endcode
With above, QDoc will search for a file
- \c {$T_INSTALL_DOCS/qtdoc/qtdoc.index} for a dependency to \c qtdoc.
+ \c {$QT_INSTALL_DOCS/qtdoc/qtdoc.index} for a dependency to \c qtdoc.
If an index file for a dependency is not found, QDoc will output a
warning.
@@ -466,6 +405,58 @@
See also \l {exampledirs-variable} {exampledirs}.
+ \target examplesinstallpath-variable
+ \section1 examplesinstallpath
+
+ The \c examplesinstallpath variable sets the root path for this
+ project's examples under the \e installed example directory.
+
+ Assuming a root install path of \c QT_INSTALL_EXAMPLES for all
+ examples, then the path
+
+ \badcode
+ <QT_INSTALL_EXAMPLES>/<examplesinstallpath>/<example_path>
+ \endcode
+
+ will be used to refer to a path of a single example within this
+ documentation project. These paths are recorded in the
+ \l {Example Manifest Files} {example manifest file}, read by Qt
+ Creator.
+
+ To ensure correct paths, \c examplesinstallpath must match with
+ one of the directories listed in \l {exampledirs-variable}
+ {exampledirs}. The path passed as an argument for each
+ \l {example-command} {\\example} command is relative to
+ the path in \e exampledirs.
+
+ For example:
+
+ \badcode
+ exampledirs = ./snippets \
+ ../../../examples/mymodule
+
+ examplesinstallpath = mymodule
+ \endcode
+
+ And given a following \\example command:
+
+ \badcode *
+ /*!
+ \example basic/hello
+ ...
+ \1/
+ \endcode
+
+ Then, the path \c {mymodule/basic/hello} is recorded in the manifest file
+ for this example.
+
+ \note It is possible to override \c examplesinstallpath for an individual
+ \l {example-command}{\\example} using the \l {meta-command}{\\meta}
+ command.
+
+ \b {See also}: \l {exampledirs}, \l {example-command} {\\example}, and
+ \l {meta-command}{\\meta}.
+
\target examples.fileextensions-variable
\section1 examples.fileextensions
@@ -536,23 +527,19 @@
The \c extraimages variable tells QDoc to incorporate specific
images in the generated documentation.
- QDoc will not recognize images used within HTML (or any other
- markup language). If we want the images to be copied from the
- directories specified by \l {imagedirs} {\c imagedirs} (the images
- in question must be located in these directories) to the output
- directory, we must specify the images using the \c extraimages
- variable.
+ QDoc automatically copies an image file from \l imagedirs to the output
+ directory if it's referenced by the \l {image-command} {\c{\image}} or
+ \l {inlineimage-command} {\c{\inlineimage}} command. If you want to copy
+ additional images, you must specify them using the \c extraimages variable.
- The general syntax is \tt {extraimages.\e{format} = \e image}. The
- file extension is optional.
+ The general syntax is \tt {\e{format}.extraimages = \e image}.
- For example, in \l qtgui.qdocconf we use a couple of images within
- the HTML.postheader variable which value is pure HTML. For that
- reason, these images are specified using the \c extraimages
- variable:
+ For a contextualized example, refer to the description of the
+ \l {HTML.postheader-variable}{HTML.postheader} variable.
+ Example:
\badcode
- extraimages.HTML = qt-logo
+ HTML.extraimages = images/qt-logo.png
\endcode
See also \l images and \l imagedirs.
@@ -647,8 +634,7 @@
In the specified directories, QDoc will only read the files with
the \c fileextensions specified in the \l {headers.fileextensions}
- {\c headers.fileextensions} variable. The default extensions are
- *.ch, *.h, *.h++, *.hh, *.hpp, and *.hxx". The files specified by
+ {\c headers.fileextensions} variable. The files specified by
\l {headers} {\c headers} will be read without taking into account
their fileextensions.
@@ -728,7 +714,7 @@
that QDoc will ignore when resolving hyperlink targets.
QDoc has an auto-linking feature, where linking is attempted for words
- that resemble C++, QML, or JavaScript entities. Specifically, a string
+ that resemble C++ or QML entities. Specifically, a string
qualifies for auto-linking if it is at least three characters in
length, has no whitespace, and it
@@ -829,89 +815,27 @@
$QTDIR/examples/calculator-example.png
\endcode
- You can filter the images in an image directory using the \l
- {images.fileextensions} {\c images.fileextensions} variable. The
- general idea behind the \l {images.fileextensions} {\c images.fileextensions}
- variable is to enable different image format for different output format.
-
- \warning The \l {images.fileextensions} {\c images.fileextensions}
- variable's functionality is preliminary since QDoc at this point
- only supports HTML.
-
- See also \l images and \l images.fileextensions.
-
- \target images-variable
- \section1 images
-
- The \c images variable allows you to specify individual image
- files in addition to those located in the directories specified by
- the \l {imagedirs} {\c imagedirs} variable.
-
- \badcode
- images = $QTDIR/doc/src/images/calculator-example.png
- \endcode
-
- When processing the \c images variable, QDoc behaves in the same
- way as it does when processing the \l {imagedirs} {\c imagedirs}
- variable. For more information, see the \l {imagedirs} {\c
- imagedirs} variable.
-
- See also \l imagedirs and \l images.fileextensions.
-
- \target images.fileextensions-variable
- \section1 images.fileextensions
-
- The images.fileextensions variable filters the files within an
- image directory.
-
- The variable's values (the extensions) are given as standard
- wildcard expressions. The general syntax is: \tt
- {images.fileextensions.\e{format} = *.\e{extension}}.
-
- The idea is to enable different image format for different output
- format.
-
- \badcode
- images.fileextensions.HTML = *.png
- images.fileextensions.LOUT = *.eps
- \endcode
-
- Then, when processing the \l {image-command} {\\image} and \l
- {inlineimage-command} {\\inlineimage} commands, QDoc will only
- search for files with extensions specified in the variable
- containing the list of output formats.
-
- \warning This is only a preliminary functionality since QDoc at this
- point only supports HTML.
-
- The default extensions for HTML are *.png, *.jpg, *.jpeg, and
- *.gif.
-
- You can add a file extension to the filter using '+='. For
- example:
-
- \badcode
- images.fileextensions.HTML += *.eps
- \endcode
-
- See also \l imagedirs and \l images.
-
\target language-variable
\section1 language
The \c language variable specifies the language of the source code
- that is used in the documentation.
+ that is used in the documentation. Specifically, it defines the
+ default language for parsing source code within \\code .. \\endcode
+ blocks.
+
+ \badcode
+ language = Cpp
+ \endcode
- Currently, C++ is the only language that QDoc understands. It is
- also the default language, and doesn't really need to be
- specified. However, a possible example of a language variable
- statement:
+ The default language is C++ (Cpp), and doesn't need to be explicitly
+ specified. If the code snippets in the documentation consist mainly
+ of QML code, set QML as the default:
\badcode
- language = Cpp
+ language = QML
\endcode
- This identifies C++ as the language of the Qt source code.
+ See also \l {code-command}[\\code}.
\target locationinfo-variable
\section1 locationinfo
@@ -947,11 +871,11 @@
example, the macro is only used when generating HTML output.
\badcode
- macro.gui = "\\b"
+ macro.key = "\\b"
macro.raisedaster.HTML = "<sup>*</sup>"
\endcode
- The first macro defines the \\gui command to render its argument
+ The first macro defines the \\key command to render its argument
using a bold font. The second macro defines the \\raisedaster
command to render a superscript asterisk, but only when generating
HTML.
@@ -996,7 +920,7 @@
(parentheses) concatenated together, or the exact matched string
if the pattern does not contain any capture groups.
- See also \l {alias-variable} {alias}.
+ For more information about pre-defined macros, see \l {Macros}.
\target manifestmeta-variable
\section1 manifestmeta
@@ -1116,7 +1040,14 @@
acts as a table of contents (TOC). QDoc generates navigation links
for pages listed in the TOC, without the need for
\l {nextpage-command}{\\nextpage} and \l {previouspage-command}
- {\\previouspage} commands.
+ {\\previouspage} commands, as well as a navigation hierarchy that's
+ visible in the navigation bar (breadcrumbs) for HTML output.
+ \row \li \c navigation.toctitles.inclusive (Since QDoc 6.3)
+ \li If set to \c true, page(s) listed in \c navigation.toctitles
+ will also appear in the navigation bar as a root item.
+ \row \li \c navigation.trademarkspage (Since QDoc 6.8)
+ \li Title of a page that documents trademarks mentioned in the
+ documentation. See also \qdoccmd tm command.
\endtable
For example:
@@ -1211,54 +1142,79 @@
\section1 outputprefixes
The \c outputprefixes variable specifies a mapping between types of files
- and the prefixes to prepend to the HTML file names in the generated
+ and the prefixes to prepend to the output file names in the generated
documentation.
+ QDoc supports adding an output prefix to the file names of QML type, C++
+ class, namespace, and header file reference pages.
+
\badcode
- outputprefixes = QML JS
+ outputprefixes = QML CPP
outputprefixes.QML = uicomponents-
- outputprefixes.JS = uicomponents-
+ outputprefixes.CPP = components-
\endcode
By default, files containing the API documentation for QML types
- are prefixed with "qml-", and javaScript types with "js-". In the
- above example, the prefix \c "uicomponents" is used instead for
- both.
+ are prefixed with \c {qml-}. In the above example, the prefix \c
+ {uicomponents-} is used instead.
- The output prefix is applied to file names for documentation on
- QML and JS types.
+ Likewise, C++ type documentation pages are prefixed with \c {components-}
+ in the above example. By default, C++ type pages have no prefix.
\target outputsuffixes-variable
\section1 outputsuffixes
The \c outputsuffixes variable specifies a mapping between types of
- files and module name suffixes to append to the HTML file names.
+ files and suffixes to apply to the module or type name as they appear
+ in the output file names.
+
+ QDoc supports adding an output suffix to the file names of module pages,
+ QML type, C++ class, namespace, and header file reference pages.
+
+ By default, no suffix is used. The QML output suffix, if defined, is
+ applied as a suffix to the module name as it appears in the file names
+ of QML type and QML module pages.
+
+ File names for C++ types do not include the module name. The CPP
+ output suffix, if defined, is applied as a suffix for the type name.
\badcode
- outputsuffixes = QML
- outputsuffixes.QML = -tp
+ outputsuffixes = QML CPP
+ {outputsuffixes.QML,outputsuffixes.CPP} = -tp
\endcode
- Given a QML module name \e FooBar and the default
- \l {outputprefixes-variable}{output prefix} ("qml-"), the file name of
- the generated HTML page for a QML type \e FooWidget would be
+ With the definitions above, given a QML module name \e FooBar and the default
+ \l {outputprefixes-variable}{output prefix} (\c {qml-}), the name of
+ the generated file for a QML type \e FooWidget is
\c qml-foobar-tp-foowidget.html.
- By default, no suffix is used. The output suffix, if defined, is applied
- to file names for documentation on QML and JS types, and their respective
- module pages.
+ Likewise, for a C++ class \e QFoobar, QDoc generates \c qfoobar-tp.html.
The \c outputsuffixes variable was introduced in QDoc 5.6.
\target qhp-variable
\section1 qhp
- The \c qhp variable is used to define the information to be
+ The \c qhp sub-variables are used to define the information to be
written out to Qt Help Project (\c{qhp}) files.
See the \l{Creating Help Project Files} chapter for information
about this process.
+ Since QDoc 6.6, setting the base \c qhp variable to \c true means
+ that a valid help project configuration is expected:
+
+ \badcode
+ qhp = true
+ \endcode
+
+ Then, if a project configuration did not define \c {qhp.projects},
+ QDoc issues a warning. This is useful for ensuring that all
+ documentation projects with a shared top-level \e .qdocconf file
+ (as in Qt) are configured correctly.
+
+ To turn off the warning, set the variable to \c false.
+
\target sourcedirs-variable
\section1 sourcedirs
@@ -1288,8 +1244,7 @@
In the specified directories, QDoc will only read the files with
the \c fileextensions specified in the \l {sources.fileextensions}
- {\c sources.fileextensions} variable. The default extensions are
- *.c++, *.cc, *.cpp and *.cxx. The files specified by \l {sources}
+ {\c sources.fileextensions} variable. The files specified by \l {sources}
{\c sources} will be read independent of their fileextensions.
See also \l {sources-variable} {sources} and
@@ -1350,7 +1305,8 @@
variable. In this way QDoc avoid spending time reading irrelevant
files.
- The default extensions are *.c++, *.cc, *.cpp and *.cxx.
+ The default extensions are *.c++, *.cc, *.cpp, *.cxx, *.mm, *.qml
+ and *.qdoc.
The extensions are given as standard wildcard expressions. You
can add a file extension to the filter using '+='. For example:
@@ -1362,7 +1318,7 @@
\warning The above assignment may not work as described.
See also \l {sourcedirs-variable} {sourcedirs} and \l
- (sources-variable} {sources}.
+ {sources-variable} {sources}.
\target spurious-variable
@@ -1492,7 +1448,7 @@
/*!
\page 22-creating-help-project-files.html
\previouspage Generic Configuration Variables
- \nextpage C++ Specific Configuration Variables
+ \nextpage Format-specific Configuration Variables
\title Creating Help Project Files
@@ -1619,118 +1575,8 @@
*/
/*!
- \page 23-qdoc-configuration-cppvariables.html
- \previouspage Creating Help Project Files
- \nextpage Format-specific Configuration Variables
-
- \title C++ Specific Configuration Variables
-
- The C++ specific configuration variables are provided to avoid
- erroneous documentation due to non-standard C++ constructs.
-
- \target Cpp.ignoredirectives-variable
- \section1 Cpp.ignoredirectives
- The \c Cpp.ignoredirectives variable makes QDoc ignore the
- specified non-standard constructs, within C++ source code.
-
- If not specified by the \tt {\l Cpp.ignoretokens} or \tt {\l
- Cpp.ignoredirectives} variables, non-standard constructs
- (typically macros) can result in erroneous documentation.
-
- \badcode
- Cpp.ignoredirectives = Q_DECLARE_INTERFACE \
- Q_DECLARE_OPERATORS_FOR_FLAGS \
- Q_DECLARE_PRIVATE \
- Q_DECLARE_PUBLIC \
- Q_DISABLE_COPY \
- Q_DUMMY_COMPARISON_OPERATOR \
- Q_ENUMS \
- Q_FLAGS \
- Q_INTERFACES \
- __attribute__
- \endcode
-
- makes sure that when processing the code below, for example, QDoc
- will simply ignore the 'Q_ENUMS' and 'Q_FLAGS' expressions:
-
- \code
- class Q_CORE_EXPORT Qt {
- Q_OBJECT
- Q_ENUMS(Orientation TextFormat BackgroundMode
- DateFormat ScrollBarPolicy FocusPolicy
- ContextMenuPolicy CaseSensitivity
- LayoutDirection ArrowType)
- Q_ENUMS(ToolButtonStyle)
- Q_FLAGS(Alignment)
- Q_FLAGS(Orientations)
- Q_FLAGS(DockWidgetAreas)
-
- public:
- ...
- };
- \endcode
-
- The Q_OBJECT macro, however, is an exception: QDoc recognizes this
- particular non-standard construct, so there is no need specifying
- it using the \tt {\l Cpp.ignoredirectives} variable.
-
- Regarding the Q_CORE_EXPORT macro; see the documentation of the
- \tt {\l Cpp.ignoretokens} variable.
-
- See also \l Cpp.ignoretokens.
-
- \target Cpp.ignoretokens-variable
- \section1 Cpp.ignoretokens
-
- The \c Cpp.ignoretokens variable makes QDoc ignore the specified
- non-standard constructs, within C++ source code.
-
- If not specified by the \tt {\l Cpp.ignoretokens} or \tt {\l
- Cpp.ignoredirectives} variables, non-standard constructs
- (typically macros) can result in erroneous documentation.
-
- In \l qtgui.qdocconf:
-
- \badcode
- Cpp.ignoretokens = QAXFACTORY_EXPORT \
- QM_EXPORT_CANVAS \
- ...
- Q_COMPAT_EXPORT \
- Q_CORE_EXPORT \
- Q_EXPLICIT \
- Q_EXPORT \
- ...
- Q_XML_EXPORT
- \endcode
-
- makes sure that when processing the code below, for example, QDoc
- will simply ignore the 'Q_CORE_EXPORT' expression:
-
- \code
- class Q_CORE_EXPORT Qt {
- Q_OBJECT
- Q_ENUMS(Orientation TextFormat BackgroundMode
- DateFormat ScrollBarPolicy FocusPolicy
- ContextMenuPolicy CaseSensitivity
- LayoutDirection ArrowType)
- Q_ENUMS(ToolButtonStyle)
- Q_FLAGS(Alignment)
- Q_FLAGS(Orientations)
- Q_FLAGS(DockWidgetAreas)
- public:
- ...
- };
- \endcode
-
- Regarding the Q_OBJECT, Q_ENUMS and Q_FLAGS macros; see the
- documentation of the \tt {\l Cpp.ignoredirectives} variable.
-
- See also \l Cpp.ignoredirectives.
-*/
-
-/*!
\page 24-qdoc-configuration-htmlvariables.html
- \previouspage C++ Specific Configuration Variables
+ \previouspage Creating Help Project Files
\nextpage Supporting Derived Projects
\keyword HTML Specific Configuration Variables
@@ -2051,10 +1897,9 @@
\title Example Manifest Files
- QDoc generates XML files that contain information about all documented
- examples and demos. These files, named \c {examples-manifest.xml} and
- \c {demos-manifest.xml}, are used by Qt Creator to present a list of
- examples in its welcome screen and to link to their documentation.
+ QDoc generates example-manifest.xml files that contain information about
+ all documented examples. These files are used by Qt Creator to present a
+ list of examples in its welcome screen and to link to their documentation.
\section1 Manifest XML Structure
@@ -2065,10 +1910,10 @@
<instructionals module="QtGui">
<examples>
<example
- name="Analog Clock Window Example"
+ name="Analog Clock Window"
docUrl="qthelp://org.qt-project.qtgui.502/qtgui/analogclock.html"
projectPath="gui/analogclock/analogclock.pro"
- imageUrl="qthelp://org.qt-project.qtgui.502/qtgui/images/analogclock-window-example.png">
+ imageUrl="qthelp://org.qt-project.qtgui.502/qtgui/images/analogclock-window.png">
<description><![CDATA[The Analog Clock Window example shows how
to draw the contents of a custom window.]]></description>
<tags>analog,clock,window</tags>
@@ -2105,8 +1950,8 @@
\badcode
manifestmeta.filters = highlighted sql webkit global
- manifestmeta.highlighted.names = "QtGui/Analog Clock Window Example" \
- "QtWidgets/Analog Clock Example"
+ manifestmeta.highlighted.names = "QtGui/Analog Clock Window" \
+ "QtWidgets/Analog Clock"
manifestmeta.highlighted.attributes = isHighlighted:true
manifestmeta.sql.names = "QtSql/*"
@@ -2116,7 +1961,7 @@
manifestmeta.webkit.tags = webkit
manifestmeta.global.names = *
- manifestmeta.global.tags = qt5
+ manifestmeta.global.tags = qt6
\endcode
Above, an \c isHighlighted attribute is added to two examples. If
diff --git a/src/qdoc/qdoc/doc/qdoc-manual-topiccmds.qdoc b/src/qdoc/qdoc/doc/qdoc-manual-topiccmds.qdoc
new file mode 100644
index 000000000..c1e6c2e5c
--- /dev/null
+++ b/src/qdoc/qdoc/doc/qdoc-manual-topiccmds.qdoc
@@ -0,0 +1,1049 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+
+/*!
+ \page 13-qdoc-commands-topics.html
+ \previouspage Command Index
+ \nextpage Context Commands
+
+ \title Topic Commands
+
+ A topic command tells QDoc which source code element is being
+ documented. Some topic commands allow you to create documentation
+ pages that aren't tied to any underlying source code element.
+
+ When QDoc processes a QDoc comment, it tries to connect the
+ comment to an element in the source code by first looking for a
+ topic command that names the source code element. If there is no
+ topic command, QDoc tries to connect the comment to the source
+ code element that immediately follows the comment. If it can't do
+ either of these and if there is no topic command that indicates
+ the comment does not have an underlying source code element (e.g.
+ \l{page-command} {\\page}), then the comment is discarded.
+
+ \target topic argument
+
+ The name of the entity being documented is usually the only
+ argument for a topic command. Use the complete name. Sometimes
+ there can be a second parameter in the argument. See e.g. \l
+ {page-command} {\\page}.
+
+ \code
+ \enum QComboBox::InsertPolicy
+ \endcode
+
+ The \l {fn-command} {\\fn} command is a special case. For the \l
+ {fn-command} {\\fn} command, use the function's signature
+ including the class qualifier.
+
+ \code
+ \fn void QGraphicsWidget::setWindowFlags(Qt::WindowFlags wFlags)
+ \endcode
+
+ A topic command can appear anywhere in a comment but must stand
+ alone on its own line. It is good practice is to let the topic command
+ be the first line of the comment. If the argument spans several
+ lines, make sure that each line (except the last one) is ended
+ with a backslash. Moreover, QDoc counts parentheses, which means
+ that if it encounters a '(' it considers everything until the
+ closing ')' as its argument.
+
+ If a topic command is repeated with different arguments, the
+ same documentation will appear for both the units.
+
+ \badcode *
+ /\1!
+ \fn void PreviewWindow::setWindowFlags()
+ \fn void ControllerWindow::setWindowFlags()
+
+ Sets the widgets flags using the QWidget::setWindowFlags()
+ function.
+
+ Then runs through the available window flags, creating a text
+ that contains the names of the flags that matches the flags
+ parameter, displaying the text in the widgets text editor.
+ \1/
+ \endcode
+
+ The \c PreviewWindow::setWindowFlags() and \c
+ ControllerWindow::setWindowFlags() functions will get the same
+ documentation.
+
+ \section2 Nomenclature for files generated by topic commands
+
+ For many topic commands, such as \l {page-command}{\\page}, QDoc
+ generates a file when processing the documentation.
+
+ QDoc normalizes the name of each file before writing it to disk.
+ The following operations are performed:
+
+ \list
+ \li All sequences of non alphanumeric characters are replaced with a hyphen, '-'.
+ \li All uppercase letters are replaced with their lowercase equivalent.
+ \li All trailing hyphens are removed.
+ \endlist
+
+ For example, the following command generates a file named
+ \c{this-generates-a-file-and-writes-it-to-disk.html}:
+
+ \badcode
+ \page this_generates_a_file_(and_writes_it_to_DISK)-.html
+ \endcode
+
+ As the example shows, the name that is given to the file in the
+ command might differ from the name of the actual file that is
+ written to disk.
+
+ \section3 Prefixes and Suffixes for generated files
+
+ When QDoc generates a file, it may add a prefix, a suffix, or both,
+ depending on the element that the file will document.
+
+ The table below shows what those prefixes and suffixes are for
+ various elements.
+
+ \table
+ \header
+ \li Element
+ \li Prefix
+ \li Suffix
+ \li Command
+ \row
+ \li QML Modules
+ \li None
+ \li "-qmlmodule"
+ \li \l {qmlmodule-command}{\\qmlmodule}
+ \row
+ \li Modules
+ \li None
+ \li "-module"
+ \li \l {module-command}{\\module}
+ \row
+ \li Examples
+ \li The project name, as given by the \l
+ {project-variable}{project configuration variable},
+ followed by a hyphen.
+ \li "-example"
+ \li \l {example-command}{\\example}
+ \row
+ \li QML Types
+ \li The output prefix for QML, as given by the \l
+ {outputprefixes-variable}{outputprefixes configuration
+ variable}.
+
+ If the module that contains this type is known to QDoc,
+ the module name is added as a prefix, followed by the QML
+ output suffix, as defined by the \l
+ {outputsuffixes-variable}{outputsuffixes configuration
+ variable} and a hyphen.
+ \li None
+ \li \l {qmltype-command}{\\qmltype}
+ \endtable
+
+ \target class-command
+ \section1 \\class
+
+ The \\class command is for documenting a C++ \e class, a C/C++
+ \e struct, or a \e union. The argument is the complete, qualified
+ name of the class. The command tells QDoc that a class is part of
+ the public API, and lets you enter a detailed description.
+
+ \badcode *
+ /\1!
+ \class QMap::iterator
+ \inmodule QtCore
+
+ \brief The QMap::iterator class provides an STL-style
+ non-const iterator for QMap and QMultiMap.
+
+ QMap features both \l{STL-style iterators} and
+ \l{Java-style iterators}. The STL-style iterators ...
+ \1/
+ \endcode
+
+ The HTML documentation for the named class is written to a
+ \c{.html} file named from the class name, in lower case, and with
+ the double colon qualifiers replaced with '-'. For example, the
+ documentation for the \c QMap::iterator class is written to \c
+ qmap-iterator.html.
+
+ The file contains the class description from the \\class comment,
+ plus the documentation generated from QDoc comments for all the
+ class members: a list of the class's types, properties,
+ functions, signals, and slots.
+
+ In addition to the detailed description of the class, the \\class
+ comment typically contains an \l {inmodule-command} {\\inmodule}
+ command, as well as a \l {brief-command} {\\brief} description.
+ Here is a very simple example:
+
+ \badcode *
+ /\1!
+ \class PreviewWindow
+ \inmodule CustomWidgets
+ \brief The PreviewWindow class is a custom widget.
+ displaying the names of its currently set
+ window flags in a read-only text editor.
+
+ \ingroup miscellaneous
+
+ The PreviewWindow class inherits QWidget. The widget
+ displays the names of its window flags set with the \l
+ {function} {setWindowFlags()} function. It is also
+ provided with a QPushButton that closes the window.
+
+ ...
+
+ \sa QWidget
+ \1/
+ \endcode
+
+ The way QDoc renders this \\class depends on your \c {style.css}
+ file.
+
+ \target enum-command
+ \section1 \\enum
+
+ The \\enum command is for documenting a C++ enum type. The
+ argument is the full name of the enum type.
+
+ The enum values are documented in the \\enum comment using the \l
+ {value-command} {\\value} command. If an enum value is not
+ documented with \\value, QDoc emits a warning. These warnings can
+ be avoided using the \l {omitvalue-command} {\\omitvalue} command
+ to tell QDoc that an enum value should not be documented. The enum
+ documentation will be included on the class reference page, header
+ file page, or namespace page where the enum type is defined. For
+ example, consider the enum type \c {Corner} in the Qt namespace:
+
+ \code
+ enum Corner {
+ TopLeftCorner = 0x00000,
+ TopRightCorner = 0x00001,
+ BottomLeftCorner = 0x00002,
+ BottomRightCorner = 0x00003
+ #if defined(QT3_SUPPORT) && !defined(Q_MOC_RUN)
+ ,TopLeft = TopLeftCorner,
+ TopRight = TopRightCorner,
+ BottomLeft = BottomLeftCorner,
+ BottomRight = BottomRightCorner
+ #endif
+ };
+ \endcode
+
+ This enum can be cocumented this way:
+
+ \badcode *
+ /\1!
+ \enum Qt::Corner
+
+ This enum type specifies a corner in a rectangle:
+
+ \value TopLeftCorner
+ The top-left corner of the rectangle.
+ \value TopRightCorner
+ The top-right corner of the rectangle.
+ \value BottomLeftCorner
+ The bottom-left corner of the rectangle.
+ \value BottomRightCorner
+ The bottom-right corner of the rectangle.
+
+ \omitvalue TopLeft
+ \omitvalue TopRight
+ \omitvalue BottomLeft
+ \omitvalue BottomRight
+ Bottom-right (omitted; not documented).
+ \1/
+ \endcode
+
+ Note the inclusion of the namespace qualifier.
+
+ See also \l {value-command} {\\value} and \l {omitvalue-command} {\\omitvalue}.
+
+ \target example-command
+ \section1 \\example
+
+ The \\example command is for documenting an example. The argument
+ is the example's path relative to one of the paths listed in the
+ \l {exampledirs-variable} {exampledirs} variable in the QDoc
+ configuration file.
+
+ The documentation page will be output to \c {modulename-path-to-example}.html.
+ QDoc will add a list of all the example's source and images files at the end
+ of the page, unless \l {noautolist-command}{\\noautolist} command is used or
+ the configuration variable \l {url.examples-variable}{url.examples} is defined
+ for the project.
+
+ For example, if \l {exampledirs-variable} {exampledirs} contains
+ \c $QTDIR/examples/widgets/imageviewer, then
+
+ \badcode *
+ /\1!
+ \example widgets/imageviewer
+ \title ImageViewer Example
+ \subtitle
+
+ The example shows how to combine QLabel and QScrollArea
+ to display an image.
+
+ ...
+ \1/
+ \endcode
+
+ \b {See also:} \l {noautolist-command}{\\noautolist},
+ \l {url.examples-variable}{url.examples},
+ \l {meta-command}{\\meta}
+
+ \target externalpage-command
+ \section1 \\externalpage
+
+ The \\externalpage command assigns a title to an external URL.
+
+ \badcode *
+ /\1!
+ \externalpage http://doc.qt.io/
+ \title Qt Documentation Site
+ \1/
+ \endcode
+
+ This allows you to include a link to the external page in your
+ documentation this way:
+
+ \badcode *
+ /\1!
+ At the \l {Qt Documentation Site} you can find the latest
+ documentation for Qt, Qt Creator, the Qt SDK and much more.
+ \1/
+ \endcode
+
+ To achieve the same result without using the \\externalpage
+ command, you would have to hard-code the address into your
+ documentation:
+
+ \badcode *
+ /\1!
+ At the \l {http://doc.qt.io/}{Qt Documentation Site}
+ you can find the latest documentation for Qt, Qt Creator, the Qt SDK
+ and much more.
+ \1/
+ \endcode
+
+ The \\externalpage command makes it easier to maintain the
+ documentation. If the address changes, you only need to change the
+ argument of the \\externalpage command.
+
+ \target fn-command
+ \section1 \\fn (function)
+
+ The \\fn command is for documenting a function. The argument is
+ the function's signature, including its template parameters (if
+ any), return type, const-ness, and list of formal arguments with
+ types. If the named function doesn't exist, QDoc emits a warning.
+
+ Since QDoc version 6.0, the \\fn command can be used for documenting
+ class members that are not explicitly declared in the header,
+ but are implicitly generated by the compiler; default constructor
+ and destructor, copy constructor and move-copy constructor,
+ assignment operator, and move-assignment operator.
+
+ When documenting an hidden friend, it is required to prepend the
+ enclosing class name to the function name.
+ For example, for:
+
+ \code
+ class Foo {
+ ...
+ friend bool operator==(const Foo&, const Foo&) { ... }
+ ...
+ }
+ \endcode
+
+ The command should be written as \c{"\fn Foo::operator==(const
+ Foo&, const Foo&)"} and not as the free function \c{"\fn
+ operator==(const Foo&, const Foo&)"}.
+
+ Failure to do so will have QDoc complaining about being unable to
+ resolve the function.
+
+ \note The \\fn command is QDoc's default command: when no
+ topic command can be found in a QDoc comment, QDoc tries to tie
+ the documentation to the following code as if it is the
+ documentation for a function. Hence, it is normally not necessary
+ to include this command when documenting a function, if the
+ function's QDoc comment is written immediately above the function
+ implementation in the \c .cpp file. But it must be present when
+ documenting an inline function in the \c .cpp file that is
+ implemented in the \c .h file.
+
+ \badcode *
+ /\1!
+ \fn bool QToolBar::isAreaAllowed(Qt::ToolBarArea area) const
+
+ Returns \c true if this toolbar is dockable in the given
+ \a area; otherwise returns \c false.
+ \1/
+ \endcode
+
+ \note Running in debug mode (pass the \c {-debug} command line option
+ or set the \c QDOC_DEBUG environment variable before invoking QDoc)
+ can help troubleshoot \\fn commands that QDoc fails to parse. In
+ debug mode, additional diagnostic information is available.
+
+ See also \l {overload-command} {\\overload}.
+
+ \target group-command
+ \section1 \\group
+
+ The \\group command creates a separate page that lists the classes,
+ pages, or other entities belonging to a named group. The argument
+ is the group name.
+
+ A class is included in a group by using the \l {ingroup-command}
+ {\\ingroup} command. Overview pages can also be related to a group
+ using the same command, but the list of overview pages must be
+ requested explicitly using the \l {generatelist-command}
+ {\\generatelist} command (see example below).
+
+ The \\group command is typically followed by a \l {title-command}
+ {\\title} command and a short introduction to the group. The
+ HTML page for the group is written to an \c {.html} file named
+ <lower-case-group-name>.html.
+
+ Each entity in the group is listed as a link (using page title
+ or class name), followed by a decription from the \l {brief-command}
+ {\\brief} command in the entity's documentation.
+
+ \badcode *
+ /\1!
+ \group io
+ \title Input/Output and Networking
+ \1/
+ \endcode
+
+ QDoc generates a group page \c{io.html}.
+
+ Note that overview pages related to the group must be listed
+ explicitly using the \l {generatelist-command} {\\generatelist}
+ command with the \c related argument.
+
+ \badcode *
+ /\1!
+ \group architecture
+
+ \title Architecture
+
+ These documents describe aspects of Qt's architecture
+ and design, including overviews of core Qt features and
+ technologies.
+
+ \generatelist{related}
+ \1/
+ \endcode
+
+ See also \l {ingroup-command} {\\ingroup}, \l {annotatedlist-command}
+ {\\annotatedlist}, \l {generatelist-command} {\\generatelist}, and
+ \l {noautolist-command}{\\noautolist}.
+
+ \target headerfile-command
+ \section1 \\headerfile
+
+ The \\headerfile command is for documenting the global functions,
+ types and macros that are declared in a header file, but not in a
+ namespace. The argument is the name of the header file. The HTML
+ page is written to a \c {.html} file constructed from the header
+ file argument.
+
+ The documentation for a function, type, or macro that is declared
+ in the header file being documented, is included in the header file
+ page using the \l {relates-command} {\\relates} command.
+
+ If the argument doesn't exist as a header file, the \\headerfile
+ command creates a documentation page for the header file anyway.
+
+ \badcode *
+ /\1!
+ \headerfile <QtAlgorithms>
+
+ \title Generic Algorithms
+
+ \brief The <QtAlgorithms> header file provides
+ generic template-based algorithms.
+
+ Qt provides a number of global template functions in \c
+ <QtAlgorithms> that work on containers and perform
+ well-know algorithms.
+ \1/
+ \endcode
+
+ QDoc generates a header file page, \c{qtalgorithms.html}.
+
+ See also \l {inheaderfile-command}{\\inheaderfile}.
+
+ \target macro-command
+ \section1 \\macro
+
+ The \\macro command is for documenting a C++ macro. The argument
+ is the macro in one of three styles: function-like macros like
+ Q_ASSERT(), declaration-style macros like Q_PROPERTY(), and macros
+ without parentheses like Q_OBJECT.
+
+ The \\macro comment must contain a \l {relates-command}
+ {\\relates} command that attaches the macro comment to a class,
+ header file, or namespace. Otherwise, the documentation will be
+ lost.
+
+ \target module-command
+ \section1 \\module
+
+ The \\module creates a page that lists the classes belonging to
+ the module specified by the command's argument. A class included
+ in the module by including the \l {inmodule-command} {\\inmodule}
+ command in the \\class comment.
+
+ The \\module command is typically followed by a \l {title-command}
+ {\\title} and a \l {brief-command} {\\brief} command. Each class
+ is listed as a link to the class reference page followed by the
+ text from the class's \l {brief-command} {\\brief} command. For
+ example:
+
+ \badcode *
+ /\1!
+ \module QtNetwork
+
+ \title Qt Network Module
+
+ \brief Contains classes for writing TCP/IP clients and servers.
+
+ The network module provides classes to make network
+ programming easier and portable. It offers both
+ high-level classes such as QNetworkAccessManager that
+ implements application-level protocols, and
+ lower-level classes such as QTcpSocket, QTcpServer, and
+ QUdpSocket.
+ \1/
+ \endcode
+
+ The \l {noautolist-command} {\\noautolist} command can be used here
+ to omit the automatically generated list of classes at the end.
+
+ See also \l {inmodule-command} {\\inmodule}
+
+ \target namespace-command
+ \section1 \\namespace
+
+ The \\namespace command is for documenting the contents of the C++
+ namespace named as its argument. The reference page QDoc generates
+ for a namespace is similar to the reference page it generates for a
+ C++ class.
+
+ \badcode *
+ /\1!
+ \namespace Qt
+
+ \brief Contains miscellaneous identifiers used throughout the Qt library.
+ \1/
+ \endcode
+
+ Note that in C++, a particular namespace can be used in more
+ than one module, but when C++ elements from different modules
+ are declared in the same namespace, the namespace itself must
+ be documented in one module only. For example, namespace Qt in
+ the example above contains types and functions from both QtCore
+ and QtGui, but it is documented with the \\namespace command
+ only in QtCore.
+
+ \target page-command
+ \section1 \\page
+
+ The \\page command is for creating a stand-alone documentation
+ page.
+
+ The \\page command expects a single argument that represents the
+ name of the file where QDoc should store the page.
+
+ The page title is set using the \l {title-command} {\\title}
+ command.
+
+ \badcode *
+ /\1!
+ \page aboutqt.html
+
+ \title About Qt
+
+ Qt is a C++ toolkit for cross-platform GUI
+ application development. Qt provides single-source
+ portability across Microsoft Windows, macOS, Linux,
+ and all major commercial Unix variants.
+
+ Qt provides application developers with all the
+ functionality needed to build applications with
+ state-of-the-art graphical user interfaces. Qt is fully
+ object-oriented, easily extensible, and allows true
+ component programming.
+
+ ...
+ \1/
+ \endcode
+
+ QDoc renders this page in \c {aboutqt.html}.
+
+ \target property-command
+ \section1 \\property
+
+ The \\property command is for documenting a Qt property. The
+ argument is the full property name.
+
+ A property is defined using the Q_PROPERTY() macro. The macro
+ takes as arguments the property's name and its set, reset and get
+ functions.
+
+ \badcode
+ Q_PROPERTY(QString state READ state WRITE setState)
+ \endcode
+
+ The set, reset and get functions don't need to be documented,
+ documenting the property is sufficient. QDoc will generate a list
+ of the access function that will appear in the property
+ documentation which in turn will be located in the documentation
+ of the class that defines the property.
+
+ The \\property command comment typically includes a \l
+ {brief-command} {\\brief} command. For properties the \l
+ {brief-command} {\\brief} command's argument is a sentence
+ fragment that will be included in a one line description of the
+ property. The command follows the same rules for the
+ description as the \l {variable-command} {\\variable} command.
+
+ \badcode *
+ /\1!
+ \property QPushButton::flat
+ \brief Whether the border is disabled.
+
+ This property's default is false.
+ \1/
+ \endcode
+
+ \target qmlattachedproperty-command
+ \section1 \\qmlattachedproperty
+
+ The \\qmlattachedproperty command is for documenting a QML
+ property that will be attached to some QML type. See
+ \l{Attached Properties and Attached Signal Handlers}
+ {Attached Properties}. The argument is the rest of the line.
+ It must start with the property type, followed by the QML
+ type name where the property is declared, the \c{::}
+ qualifier, and finally the property name.
+
+ For example, to document a boolean QML attached property named
+ \c isCurrentItem for the \c ListView type:
+
+ \badcode *
+ /\1!
+ \qmlattachedproperty bool ListView::isCurrentItem
+
+ This attached property is \c true if this delegate is the current
+ item; otherwise false.
+
+ It is attached to each instance of the delegate.
+
+ This property may be used to adjust the appearance of the current
+ item, for example:
+
+ \snippet doc/src/snippets/declarative/listview/listview.qml isCurrentItem
+ \1/
+ \endcode
+
+ QDoc includes this attached property on the QML reference page for the
+ \l [QML] {ListView} type.
+
+ \note Like \l{qmlproperty-command}{\\qmlproperty}, \\qmlattachedproperty
+ accepts a QML module identifier as part of its argument.
+
+ \target qmlattachedsignal-command
+ \section1 \\qmlattachedsignal
+
+ The \\qmlattachedsignal command is for documenting an attachable
+ \l{Signal and Handler Event System}{signal}. The \\qmlattachedsignal
+ command is used just like the \l{qmlsignal-command} {\\qmlsignal} command.
+
+ The argument is the rest of the line. It should be the name of the
+ QML type where the signal is declared, the \c{::}
+ qualifier, and finally the signal name. For example, a QML
+ attached signal named \c add() in the \c GridView
+ element is documented like this:
+
+ \badcode *
+ /\1!
+ \qmlattachedsignal GridView::add()
+ This attached signal is emitted immediately after an item is added to the view.
+ \1/
+ \endcode
+
+ QDoc includes this documentation on the QML reference page for the
+ \l GridView element.
+
+ \note Like \l{qmlproperty-command}{\\qmlproperty}, \\qmlattachedsignal accepts
+ a QML module identifier as part of its argument.
+
+ \target qmlvaluetype-command
+ \section1 \\qmlvaluetype
+
+ The \\qmlvaluetype command is for documenting a \l [QtQml]
+ {QML Value Types}{value type} for QML. The command takes
+ a type name as its only argument.
+
+ \\qmlvaluetype is functionally identical to the
+ \l {qmltype-command}{\\qmltype} command. The only difference
+ is that the type will be titled (and grouped) as a
+ \e {QML value type}.
+
+ \target qmlclass-command
+ \section1 \\qmlclass
+
+ This command is deprecated. Use \l{qmltype-command} {\\qmltype}
+ instead.
+
+ \target qmlmethod-command
+ \section1 \\qmlmethod
+
+ The \\qmlmethod command is for documenting a QML method. The
+ argument is the complete method signature, including return
+ type and parameter names and types.
+
+ \badcode *
+ /\1!
+ \qmlmethod void TextInput::select(int start, int end)
+
+ Causes the text from \a start to \a end to be selected.
+
+ If either start or end is out of range, the selection is not changed.
+
+ After having called this, selectionStart will become the lesser, and
+ selectionEnd the greater (regardless of the order passed to this method).
+
+ \sa selectionStart, selectionEnd
+ \1/
+ \endcode
+
+ QDoc includes this documentation on the element reference page for the
+ \l{http://doc.qt.io/qt-5/qml-qtquick-textinput.html#select-method}
+ {TextInput} element.
+
+ \target qmltype-command
+ \section1 \\qmltype
+
+ The \\qmltype command is for documenting a QML type. The command
+ has one argument, which is the name of the QML type.
+
+ If the QML type has an equivalent C++ class, you can specify that class
+ with the \qdoccmd nativetype context command.
+
+ The \l {inqmlmodule-command}{\\inqmlmodule} command documents the
+ QML module the type belongs to. The argument passed to this command
+ must match with a documented \l {qmlmodule-command}{\\qmlmodule}
+ page.
+
+ \badcode *
+ /\1!
+ \qmltype Transform
+ \nativetype QGraphicsTransform
+ \inqmlmodule QtQuick
+
+ \brief Provides a way to build advanced transformations on Items.
+
+ The Transform element is a base type which cannot be
+ instantiated directly.
+ \1/
+ \endcode
+
+ Here, the \e{\\qmltype} comment includes \qdoccmd nativetype
+ to specify that a Transform is the QML counterpart to the
+ C++ class QGraphicsTransform. A \\qmltype comment should
+ always include a \l {since-command} {\\since} command, because all
+ QML types are new. It should also include a \l{brief-command}
+ {\\brief} description. If a QML type is a member of a QML type group,
+ the \\qmltype comment should include one or more \l{ingroup-command}
+ {\\ingroup} commands.
+
+ \target qmlproperty-command
+ \section1 \\qmlproperty
+
+ The \\qmlproperty command is for documenting a QML property. The
+ argument is the rest of the line. The argument text should be the
+ property type, followed by the QML type name, the \c{::}
+ qualifier, and finally the property name. If we have a QML
+ property named \c x in QML type \c Translate, and the property
+ has type \c {real}, the \\qmlproperty for it would look like this:
+
+ \badcode *
+ /\1!
+ \qmlproperty real Translate::x
+
+ The translation along the X axis.
+ \1/
+ \endcode
+
+ QDoc includes this QML property on the QML reference page for the
+ \l [QML] {Translate} type.
+
+ If the QML property is of enumeration type, or it holds a bit-wise
+ combination of flags, the \l{value-command}{\\value} command can
+ be used to document the acceptable values.
+
+ QDoc accepts also a fully qualified property name, including the
+ QML module identifier:
+
+ \badcode
+ \qmlproperty bool QtQuick.Controls::Button::highlighted
+ \endcode
+
+ If specified, the module identifier (above, \c {QtQuick.Controls})
+ must match with value passed to \l {inqmlmodule-command}{\\inqmlmodule}
+ command in the associated \\qmltype documentation. If the name of
+ the QML type the property belongs to is unique across all types in
+ the documentation project, the module identifier can be omitted.
+
+ \target qmlsignal-command
+ \section1 \\qmlsignal
+
+ The \\qmlsignal command is for documenting a QML signal.
+ The argument is the rest of the line. The arguments should be: the QML type
+ where the signal is declared, the \c{::} qualifier, and finally the signal
+ name. If we have a QML signal named \c clicked(), the documentation for it
+ would look like this:
+
+ \badcode *
+ /\1!
+ \qmlsignal MouseArea::clicked(MouseEvent mouse)
+
+ This signal is emitted when there is a click. A click is defined as a
+ press followed by a release, both inside the MouseArea.
+ \1/
+ \endcode
+
+ QDoc includes this documentation on the QML reference page for the
+ \l [QML] {MouseArea} type.
+
+ \note Like \l{qmlproperty-command}{\\qmlproperty}, \\qmlsignal
+ accepts a QML module identifier as part of its argument.
+
+ \target qmlmodule-command
+ \section1 \\qmlmodule
+
+ Use the \c{\qmlmodule} command to create a \c QML module page. A QML
+ module page is a collection of QML types or any related material. The
+ command takes an optional \c <VERSION> number argument, and is similar
+ to the \l{group-command}.
+
+ A QML type is associated with a module by adding the
+ \l{inqmlmodule-command}{\\inqmlmodule} command to the comment-block that
+ documents the type. You can link to any member of a QML module using the
+ module name and two colons (\c{::}) prefix.
+
+ \badcode *
+ /\1!
+ A link to the TabWidget of the UI Component is \l {UIComponent::TabWidget}.
+ \1/
+ \endcode
+
+ QDoc generates a page for the module that lists all the members of the
+ module.
+
+ \badcode *
+ /\1!
+ \qmlmodule ClickableComponents
+
+ This is a list of the Clickable Components set. A Clickable component
+ responds to a \c clicked() event.
+ \1/
+ \endcode
+
+ \target inqmlmodule-command
+ \section1 \\inqmlmodule
+
+ A QML type is marked as being available under a specific QML module
+ import by inserting the \\inqmlmodule command in a
+ \l {qmltype-command}{\\qmltype} topic. The command takes the module
+ (import) name, without a version number, as the only argument.
+
+ The QML module name must match with a QML module documented with
+ the (\l{qmlmodule-command}{\\qmlmodule} command).
+
+ \badcode *
+ /\1!
+ \qmltype ClickableButton
+ \inqmlmodule ClickableComponents
+
+ A clickable button that responds to the \c click() event.
+ \1/
+ \endcode
+
+ QDoc outputs a row \e {Import statement: import <qmlmodule>}
+ in a table at the top of the QML type reference page.
+
+ When linking to QML types, the QML module identifier may appear in
+ the link target. For example:
+
+ \badcode
+ \l {ClickableComponents::}{ClickableButton}
+ \endcode
+
+ Links to the type reference page, with \e ClickableButton as the
+ link text.
+
+ \target instantiates-command
+ \section1 \\instantiates
+
+ The \\instantiates command is deprecated since Qt 6.8.
+ Use \qdoccmd nativetype instead.
+
+
+ \target nativetype-command
+ \section1 \\nativetype
+
+ The \\nativetype-command must be used in conjunction with the
+ \qdoccmd qmltype topic command. The command takes a C++ class as its
+ argument. If QDoc cannot find the C++ class, it issues a warning. This
+ command was introduced with Qt 6.8.
+
+ Use the \\nativetype-command to specify what the type is called in C++.
+ This ensures that the requisites block generated in the documentation for
+ the QML type contains an "In C++" entry. The C++ class will have a
+ corresponding "In QML" entry.
+
+ Any one QML type can only have one native type. QDoc issues a warning if
+ redefinition occurs. However, multiple QML types can have the same C++
+ class as their native type. The C++ class documentation will contain a list
+ of all corresponding types in QML.
+
+ \badcode *
+ /\1!
+ \qmltype Transform
+ \nativetype QGraphicsTransform
+ \inqmlmodule QtQuick
+
+ \brief Provides a way to build advanced transformations on Items.
+
+ The Transform element is a base type which cannot be
+ instantiated directly.
+ \1/
+ \endcode
+
+ Here, the \e{\\qmltype} topic includes \e{\\nativetype} to specify that a
+ Transform is called QGraphicsTransform in C++.
+
+
+ \target typealias-command
+ \section1 \\typealias
+
+ The \\typealias command is similar to \l {typedef-command}{\\typedef},
+ but specific to documenting a C++ type alias:
+
+ \code
+ class Foo
+ {
+ public:
+ using ptr = void*;
+ // ...
+ }
+ \endcode
+
+ This can be documented as
+
+ \badcode *
+ /\1!
+ \typealias Foo::ptr
+ \1/
+ \endcode
+
+ The \\typealias command was introduced in QDoc 5.15.
+
+ See also \l {typedef-command}{\\typedef}.
+
+ \target typedef-command
+ \section1 \\typedef
+
+ The \\typedef command is for documenting a C++ typedef. The
+ argument is the name of the typedef. The documentation for
+ the typedef will be included in the reference documentation
+ for the class, namespace, or header file in which the typedef
+ is declared. To relate the \\typedef to a class, namespace, or
+ header file, the \\typedef comment must contain a
+ \l {relates-command} {\\relates} command.
+
+ \badcode *
+ /\1!
+ \typedef QObjectList
+ \relates QObject
+
+ Synonym for QList<QObject>.
+ \1/
+ \endcode
+
+ Other typedefs are located on the reference page for the class
+ that defines them.
+
+ \badcode *
+ /\1!
+ \typedef QList::Iterator
+
+ Qt-style synonym for QList::iterator.
+ \1/
+ \endcode
+
+ See also \l {typealias-command}{\\typealias}.
+
+ \target variable-command
+ \section1 \\variable
+
+ The \\variable command is for documenting a class member variable
+ or a constant. The argument is the variable or constant name. The
+ \\variable command comment includes a \l {brief-command} {\\brief}
+ command. QDoc generates the documentation based on the text from
+ \\brief command.
+
+ The documentation will be located in the in the associated class,
+ header file, or namespace documentation.
+
+ In case of a member variable:
+
+ \badcode *
+ /\1!
+ \variable QStyleOption::palette
+ \brief The palette that should be used when painting
+ the control
+ \1/
+ \endcode
+
+ You can also document constants with the \\variable command. For
+ example, suppose you have the \c Type and \c UserType constants in
+ the QTreeWidgetItem class:
+
+ \code
+ enum { Type = 0, UserType = 1000 };
+ \endcode
+
+ For these, the \\variable command can be used this way:
+
+ \badcode *
+ /\1!
+ \variable QTreeWidgetItem::Type
+
+ The default type for tree widget items.
+
+ \sa UserType, type()
+ \1/
+ \endcode
+
+ \badcode *
+ /\1!
+ \variable QTreeWidgetItem::UserType
+
+ The minimum value for custom types. Values below
+ UserType are reserved by Qt.
+
+ \sa Type, type()
+ \1/
+ \endcode
+
+*/
diff --git a/src/qdoc/doc/qdoc-manual.qdoc b/src/qdoc/qdoc/doc/qdoc-manual.qdoc
index d8b9d35c7..da84fe430 100644
--- a/src/qdoc/doc/qdoc-manual.qdoc
+++ b/src/qdoc/qdoc/doc/qdoc-manual.qdoc
@@ -1,29 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the documentation of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:FDL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Free Documentation License Usage
-** Alternatively, this file may be used under the terms of the GNU Free
-** Documentation License version 1.3 as published by the Free Software
-** Foundation and appearing in the file included in the packaging of
-** this file. Please review the following information to ensure
-** the GNU Free Documentation License version 1.3 requirements
-** will be met: https://www.gnu.org/licenses/fdl-1.3.html.
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
\page qdoc-index.html
@@ -57,11 +33,20 @@
\li \l {Special Content}
\li \l {Miscellaneous}
\endlist
+ \li \l {Macros}
+ \list
+ \li \l {CMake Text Snippets}
+ \li \l {HTML and DocBook Formatting}
+ \li \l {Product Names}
+ \li \l {Product Versions}
+ \li \l {Tabbed Content}
+ \li \l {youtube-macro}{Linking to YouTube content}
+ \li \l {Miscellaneous Macros}
+ \endlist
\li \l {The QDoc Configuration File}
\list
\li \l {Generic Configuration Variables}
\li \l {Creating Help Project Files}
- \li \l {C++ Specific Configuration Variables}
\li \l {Format-specific Configuration Variables}
\li \l {Supporting Derived Projects}
\li \l {Example Manifest Files}
diff --git a/src/qdoc/doc/qdoc-warnings.qdoc b/src/qdoc/qdoc/doc/qdoc-warnings.qdoc
index d40ad6abd..d5d399a05 100644
--- a/src/qdoc/doc/qdoc-warnings.qdoc
+++ b/src/qdoc/qdoc/doc/qdoc-warnings.qdoc
@@ -1,29 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the documentation of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:FDL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Free Documentation License Usage
-** Alternatively, this file may be used under the terms of the GNU Free
-** Documentation License version 1.3 as published by the Free Software
-** Foundation and appearing in the file included in the packaging of
-** this file. Please review the following information to ensure
-** the GNU Free Documentation License version 1.3 requirements
-** will be met: https://www.gnu.org/licenses/fdl-1.3.html.
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
\page qdoc-warnings.html
@@ -58,6 +34,14 @@
text was not.
\endlist
+ \section1 Found a \\target command outside table item in a table
+
+ If QDoc encounters a \l {target-command}{\\target command} that is not
+ preceded by an \\li command within a \l {table-command}
+ {\\table ... \\endtable block}, it issues this warning. The warning is
+ followed by the text
+ \e {Move the \\target inside the \\li to resolve this warning}.
+
\section1 Cannot find snippets file to quote from
QDoc issues this warning if it is unable to find the file named
@@ -83,6 +67,30 @@
QDoc issues this warning if it is unable to locate the snippet file
quoted in a \l {snippet-command}{\\snippet} command.
+ \section1 Undocumented QML <module> referred by <type> or its members
+
+ QDoc issues this warning if it is unable to locate a QML module
+ based on the identifier passed to the
+ \l {inqmlmodule-command}{\\inqmlmodule} or \l {qmlproperty-command}
+ {\\qmlproperty} commands associated with a QML \e type.
+
+ This means that either documentation for the \l {qmlmodule-command}
+ {\\qmlmodule} is missing, or an incorrect module identifier was
+ used in \\qmlproperty, \\qmlmethod, or \\qmlsignal command(s).
+
+ \section1 No such <type> in QML <module>
+
+ QDoc issues this warning if a \l {qmlproperty-command} {\\qmlproperty},
+ \l {qmlmethod-command} {\\qmlmethod}, or \l {qmlsignal-command}
+ {\\qmlsignal} command argument uses a QML module identifier, but
+ the associated \l {qmltype-command}{\\qmltype} does not belong to
+ that module.
+
+ The QML module identifier, if defined, \b must match with the
+ \l {inqmlmodule-command} {\\inqmlmodule} argument in the QML type
+ documentation. In most cases, QDoc is able to locate the QML type
+ without a module identifier.
+
\section1 Undocumented return value
For functions whose return-type is not void, QDoc checks if the return
@@ -96,6 +104,10 @@
specified where the function or method is declared in a header file)
appearing after a \l {a-command}{\\a} command.
+ This requirement is not imposed for function overload documentation,
+ provided that the overload is marked with the \qdoccmd overload command
+ and a fully-documented function with the same name exists.
+
\section1 No such parameter
QDoc issues this warning when the parameter name given after an
@@ -106,26 +118,35 @@
\section1 Unknown macro
QDoc issues this warning when it sees a backslash, \c{\}, followed
- by a token it does not recognize as the name of a \l {macro-variable}{built-in command}
- or a \l {alias-variable}{user-defined macro}. When quoting code
+ by a token it doesn't recognize as the name of either a built-in command
+ or a \l {macro-variable}{user-defined macro}. When quoting code
that contains character escape sequences, you should enclose the
code in \\c{...} to prevent this warning against the escape sequences.
- \section1 Clang couldn't find function when parsing \\fn <signature>
+ \section1 Failed to find function when parsing \\fn <signature>
- When Clang parses a function statement after \l {fn-command}{\\fn}, it checks
- this against the declaration in the header file. If Clang
+ When Clang parses a function signature following an \qdoccmd fn command,
+ it checks this against the declaration in the header file. If Clang
discovers discrepancies, it issues this warning message.
- \section1 C++ class <ClassName> not found: \\instantiates <ClassName>
+ The signature must be fully qualified. Typical issues include missing or
+ incorrect template arguments, return type, or qualifiers such as
+ \e const.
+
+ \note The \c{\fn} signature for a \e {hidden friend} should be
+ fully qualified with the scope of the class that declares
+ the function.
+
+ \section1 QML type <TypeName> documented with <ClassName> as its native type. Replacing <ClassName> with <OtherClass>
+ If the \qdoccmd nativetype command is used with the same argument in
+ multiple QML type documentation comments that belong to the same
+ documentation project, QDoc issues this warning. To resolve this, ensure
+ that you use the \c {\nativetype} command only once for each C++ class.
- If you describe a QML type, you may specify the class it instantiates.
- Refer to the \l{instantiates-command}{\\instantiates} command in the
- \l {QDoc manual}.
\section1 Cannot tie this documentation to anything
- QDoc found a \\beginqdoc ... \\endqdoc comment, with no \l{Topic Commands}{topic command}, that was
+ QDoc found a \beginqdoc ... \endqdoc comment, with no \l{Topic Commands}{topic command}, that was
not immediately followed by a class, function or property definition.
It thus does not know what the comment documents.
@@ -136,11 +157,11 @@
Very similar to \l {Cannot tie this documentation to anything}, but
specific to comments that are not in C++ or QML files.
- \section1 <name> documented more than once (The previous doc is here)
+ \section1 <name> documented more than once
QDoc issues this warning when it finds two comments that
- document the same item. The warning comes in two parts,
- one giving the later occurrence, the other the first.
+ document the same item. The location of the previously seen
+ comment is provided in warning details.
For example, you see this warning when a function has a documentation
comment preceding its definition, and a separate \\fn comment
@@ -164,23 +185,6 @@
See also \l {depends-variable}{depends} and {indexes-variable}{indexes}.
- \section1 Clang couldn't find function when parsing \\fn <signature>
-
- When parsing a \l {fn-command}{\\fn} statement, Clang compares this with the
- function declaration in the header file. If the signature differs
- Clang issues this warning.
-
- \note
- \list
- \li A member of a class or namespace needs to include the class or namespace
- prefix on the member name.
- \li A \\fn with no return type matches, regardless of the actual return
- type, but if it specifies an incorrect return type, it doesn't.
- \li Differences in \\fn's parameter names don't preclude matching, although
- the \l {a-command}{\\a} commands in the comment must use the names in the declaration.
-
- \endlist
-
\section1 Has no \\inmodule command
QDoc issues this warning if the QDoc comments do not relate
@@ -251,7 +255,7 @@
the same QML property, either by appearing just before its definition,
or using the \l {qmlproperty-command}{\\qmlproperty} command.
- \section1 Command <command> not allowed with QML/JS property commands
+ \section1 Command <command> not allowed with QML property commands
Example:
@@ -266,11 +270,11 @@
Error message:
\badcode
- Command '\\qmlsignal' not allowed with QML/JS property commands
+ Command '\\qmlsignal' not allowed with QML property commands
\endcode
This warning is specific to property group documentation. QDoc
- allows multiple (qml|js)property or (qml|js)attachedproperty topic commands
+ allows multiple qmlproperty or qmlattachedproperty topic commands
in a single documentation comment to document a property group
where the last element in the path is <group>.<property>. Any other topic
commands triggers this warning.
@@ -305,10 +309,10 @@
\inherits Foo
\endcode
- \section1 \\instantiates is only allowed in \\qmltype
+ \section1 \\nativetype is only allowed in \\qmltype
- The \l{instantiates-command}{\\instantiates} command can only be used in a QDoc comment
- that documents a QML type.
+ The \l{nativetype-command}{\\nativetype} command can only be used in a QDoc
+ comment that documents a QML type.
\section1 All properties in a group must belong to the same type: <name>
@@ -322,11 +326,6 @@
\c{.pyproject} extension where the base name matches that of the example
directory. For example, \c {examples/mymodule/helloworld/helloworld.pro}.
- \section1 Command name <alias> cannot stand for both <first> and <later>
-
- QDoc issues this warning when one name is \l {alias-variable}{aliased} to more
- than one command when reading the configuration.
-
\section1 Cannot open file to quote from: <filename>
The search path for <filename> is defined by the following
@@ -352,8 +351,7 @@
The \l {raw-command}{\\raw} command and the corresponding \l {raw-command}{\\endraw}
command delimit a block of raw mark-up language code. The \\raw
- command must be followed by the format name. For now, this can
- only be HTML.
+ command must be followed by the format name.
\section1 Macro cannot have both format-specific and qdoc-syntax definitions
@@ -369,12 +367,11 @@
\section1 Unknown command <name>
- When a QDoc comment uses a backslash followed by a token that is not
- a QDoc built-in command and has not been defined as a custom command
- using \l {alias-variable}{\\alias} or \l {macro-variable}{macro}, QDoc produces this
- warning. Check the spelling of the command name and look to see if
- your QDoc configuration has neglected to include whatever would have
- defined it, if it is a custom command.
+ When a QDoc comment uses a backslash followed by a token that is not a QDoc
+ built-in command and has not been defined as a custom command
+ \l {macro-variable}{macro}, QDoc produces this warning. Check the spelling
+ of the command name, and check whether your QDoc configuration doesn't
+ include whatever would have defined it, if it's a custom command.
This may also be produced due to code being quoted in the QDoc comment,
for example the author may have referred to the C string termination
@@ -386,10 +383,11 @@
\section1 Duplicate target name <target>
- This means that there are two \l {target-command}{\\target} commands
- with the same parameter. They should be unique.
- This warning is followed by the warning "The previous
- occurrence is here".
+ This warning is issued if you define two targets with the same parameter,
+ using either the \l {target-command}{\\target} or the \l {keyword-command}
+ {\\keyword} command. The target name given as parameter to these commands
+ must be unique. The warning is followed by "The previous occurrence is
+ here: [location]", where location contains a file name and line number.
\section1 Cannot find qdoc include file <filename>
@@ -540,11 +538,6 @@
indexes += path/to/QtCreator/appmanplugin/manual.index
\endcode
- \section1 \\generatelist examplefiles can only be used with \\example topic command
-
- The command "\\generatelist examplefiles" can only be used in example
- documentation (i.e., when the topic command is \\example).
-
\section1 \\generatelist <group> is empty
Below a short overview of all possible arguments for \l {generatelist-command}{\\generatelist}:
@@ -554,9 +547,6 @@
\li \\generatelist classes <prefix>
\li \\generatelist classesbymodule <module name>
\li \\generatelist qmltypesbymodule <module name>
- \li \\generatelist jstypesbymodule <module_name>
- \li \\generatelist examplesfiles <regular expression>
- \li \\generatelist exampleimages <regular expression>
\li \\generatelist functionindex
\li \\generatelist legalese
\li \\generatelist overviews
@@ -621,14 +611,14 @@
Incorrect:
\badcode
\qmltype ItemSelectionModel
- \instantiates QItemSelectionModel
+ \nativetype QItemSelectionModel
\since 5.5
\ingroup qtquick-models
\endcode
Correct:
\badcode
\qmltype ItemSelectionModel
- \instantiates QItemSelectionModel
+ \nativetype QItemSelectionModel
\inqmlmodule QtQml.Models
\since 5.5
\ingroup qtquick-models
@@ -640,10 +630,10 @@
the topic documented, so should end in a full stop. It should
also be brief.
- \section1 QtDeclarative not installed; cannot parse QML or JS
+ \section1 QtDeclarative not installed; cannot parse QML
QDoc issues this warning if it has been compiled without support
- for QML/JS parsing. This should not happen unless you have a
+ for QML parsing. This should not happen unless you have a
custom build of QDoc.
\section1 Invalid regular expression <regex>
@@ -710,12 +700,11 @@
Without them, QDoc cannot locate the index files of any dependencies
defined with the 'depends' configuration variable.
- \section1 Overrides a previous doc (The previous doc is here)
+ \section1 Overrides a previous doc
- When QDoc finds two comments that appear to describe the same
- entity, it issues this warning and tells you where to find the
- other comment. The warning comes in two parts, one giving the
- later occurrence, the other the first.
+ QDoc issues this warning when it finds two comments that appear to
+ describe the same entity. The location of the previously seen
+ comment is provided in warning details.
\section1 Unrecognized list style <name>
@@ -810,4 +799,34 @@
\note Since content that is too long is not parsed in full, QDoc may
issue warnings that are false positives. Resolve all warnings of this type
before fixing other warnings.
+
+ \section1 No documentation generated for function <name> in global scope
+
+ QDoc was able to match the documentation for a function \e {<name>} to its
+ declaration, but no output was generated because the function is declared
+ in the global namespace.
+
+ Use the \l {relates-command}{\\relates} command to associate the function
+ with a documented type, namespace, or a header file. The function is then
+ listed as a \e {related non-member} on the associated reference page.
+
+ \section1 Documentation configuration for <project> doesn't define a help project (qhp)
+
+ A valid Qt help configuration was expected but not provided in the project's
+ .qdocconf file.
+
+ See also \l{Creating Help Project Files} and \l {qhp-variable}{qhp}.
+
+ \section1 Already generated FILE for this project
+
+ While generating the documentation for a project, QDoc keeps track of the
+ file names of the files it has generated. QDoc will issue a warning when it
+ opens a file for writing if that file is known to have been generated
+ previously, in the current execution. This can happen if a \qdoccmd page
+ command uses the same name as \qdoccmd group, for example.
+
+ You can set the environment variable \c QDOC_ALL_OVERWRITES_ARE_WARNINGS to
+ unconditionally warn about all such events. This may be useful when tracking
+ down the offending definitions.
+
*/
diff --git a/src/qdoc/doc/qtgui-qdocconf.qdoc b/src/qdoc/qdoc/doc/qtgui-qdocconf.qdoc
index 8602326bb..b72f97271 100644
--- a/src/qdoc/doc/qtgui-qdocconf.qdoc
+++ b/src/qdoc/qdoc/doc/qtgui-qdocconf.qdoc
@@ -1,29 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the documentation of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:FDL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Free Documentation License Usage
-** Alternatively, this file may be used under the terms of the GNU Free
-** Documentation License version 1.3 as published by the Free Software
-** Foundation and appearing in the file included in the packaging of
-** this file. Please review the following information to ensure
-** the GNU Free Documentation License version 1.3 requirements
-** will be met: https://www.gnu.org/licenses/fdl-1.3.html.
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
@@ -43,7 +19,7 @@ every statement in the qdocconf file.
project = QtGui
description = Qt GUI Reference Documentation
- url = http://doc.qt.io/qt-5
+ url = http://doc.qt.io/qt
version = $QT_VERSION
examplesinstallpath = gui
@@ -59,10 +35,10 @@ every statement in the qdocconf file.
qhp.QtGui.subprojects = classes
qhp.QtGui.subprojects.classes.title = C++ Classes
qhp.QtGui.subprojects.classes.indexTitle = Qt GUI C++ Classes
- qhp.QtGui.subprojects.classes.selectors = class fake:headerfile
+ qhp.QtGui.subprojects.classes.selectors = class headerfile
qhp.QtGui.subprojects.classes.sortPages = true
- tagfile = ../../../doc/qtgui/qtgui.tags
+ tagfile = qtgui.tags
depends += \
qtcore \
@@ -86,7 +62,7 @@ every statement in the qdocconf file.
imagedirs += images \
../../../examples/gui/doc/images \
- ../../../doc/src/images \
+ ../../../doc/src/images
\endcode
\title Qtgui.qdocconf with notes
@@ -133,24 +109,23 @@ to content listed in the index.
\note QDoc omits this value when the -installdir argument
is specified when running QDoc.
-\target examplesinstallpath
-
\badcode
examplesinstallpath = gui
\endcode
-This \c examplesinstallpath variable indicates that the examples will be
+The \c examplesinstallpath variable indicates that the examples will be
installed in the \e gui directory under the parent examples directory
(for Qt, this is $QT_INSTALL_EXAMPLES).
\note The examplepath variable has to match the example directory specified in
\c exampledirs.
-\note It is possible to override the \c exampleinstallpath for a specific
+\note It is possible to override \c examplesinstallpath for a specific
\l {example-command}{\\example} using the \l {meta-command}{\\meta}
command.
-\b {See also}: \l {exampledirs} and \l {meta-command}{\\meta}.
+\b {See also}: \l {examplesinstallpath}, \l {exampledirs}, and
+\l {meta-command}{\\meta}.
\badcode
qhp.projects = QtGui
diff --git a/src/qdoc/qdoc/src/qdoc/access.h b/src/qdoc/qdoc/src/qdoc/access.h
new file mode 100644
index 000000000..96cad686b
--- /dev/null
+++ b/src/qdoc/qdoc/src/qdoc/access.h
@@ -0,0 +1,15 @@
+// Copyright (C) 2020 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include <QtCore/qglobal.h>
+
+#ifndef ACCESS_H
+#define ACCESS_H
+
+QT_BEGIN_NAMESPACE
+
+enum class Access : unsigned char { Public, Protected, Private };
+
+QT_END_NAMESPACE
+
+#endif // ACCESS_H
diff --git a/src/qdoc/aggregate.cpp b/src/qdoc/qdoc/src/qdoc/aggregate.cpp
index 64ac0223e..bf210ba17 100644
--- a/src/qdoc/aggregate.cpp
+++ b/src/qdoc/qdoc/src/qdoc/aggregate.cpp
@@ -1,30 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the tools applications of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "aggregate.h"
@@ -35,6 +10,9 @@
#include "qmlpropertynode.h"
#include "qmltypenode.h"
#include "sharedcommentnode.h"
+#include <vector>
+
+using namespace Qt::Literals::StringLiterals;
QT_BEGIN_NAMESPACE
@@ -50,37 +28,36 @@ QT_BEGIN_NAMESPACE
*/
/*!
- The destructor calls delete for each child of this Aggregate
- that has this Aggregate as its parent. A child node that has
- some other Aggregate as its parent is deleted by that
- Aggregate's destructor. This is a fail-safe test.
-
- The destructor no longer deletes the collection of children
- by calling qDeleteAll() because the child list can contain
- pointers to children that have some other Aggregate as their
- parent. This is because of how the \e{\\relates} command is
- processed. An Aggregate can have a pointer to, for example,
- a FunctionNode in its child list, but that FunctionNode has
- a differen Aggregate as its parent because a \e{\\relates}
- command was used to relate it to that parent. In that case,
- the other Aggregate's destructor must delete that node.
-
- \note This function is the \b only place where delete is
- called to delete any subclass of Node.
-
- \note This strategy depends on the node tree being destroyed
- by calling delete on the root node of the tree. This happens
- in the destructor of class Tree.
+ Recursively set all non-related members in the list of children to
+ \nullptr, after which each aggregate can safely delete all children
+ in their list. Aggregate's destructor calls this only on the root
+ namespace node.
+ */
+void Aggregate::dropNonRelatedMembers()
+{
+ for (auto &child : m_children) {
+ if (!child)
+ continue;
+ if (child->parent() != this)
+ child = nullptr;
+ else if (child->isAggregate())
+ static_cast<Aggregate*>(child)->dropNonRelatedMembers();
+ }
+}
+
+/*!
+ Destroys this Aggregate; deletes each child.
*/
Aggregate::~Aggregate()
{
+ // If this is the root, clear non-related children first
+ if (isNamespace() && name().isEmpty())
+ dropNonRelatedMembers();
+
m_enumChildren.clear();
m_nonfunctionMap.clear();
m_functionMap.clear();
- for (const Node *child : m_children) {
- if ((child != nullptr) && (child->parent() == this))
- delete child;
- }
+ qDeleteAll(m_children.begin(), m_children.end());
m_children.clear();
}
@@ -105,9 +82,8 @@ Node *Aggregate::findChildNode(const QString &name, Node::Genus genus, int findF
for (auto *node : nodes) {
if (genus & node->genus()) {
if (findFlags & TypesOnly) {
- if (!node->isTypedef() && !node->isClassNode() && !node->isQmlType()
- && !node->isQmlBasicType() && !node->isJsType()
- && !node->isJsBasicType() && !node->isEnumType())
+ if (!node->isTypedef() && !node->isClassNode()
+ && !node->isQmlType() && !node->isEnumType())
continue;
} else if (findFlags & IgnoreModules && node->isModule())
continue;
@@ -117,7 +93,9 @@ Node *Aggregate::findChildNode(const QString &name, Node::Genus genus, int findF
}
if (genus != Node::DontCare && !(genus & this->genus()))
return nullptr;
- return m_functionMap.value(name);
+
+ auto it = m_functionMap.find(name);
+ return it != m_functionMap.end() ? (*(*it).begin()) : nullptr;
}
/*!
@@ -127,29 +105,14 @@ Node *Aggregate::findChildNode(const QString &name, Node::Genus genus, int findF
void Aggregate::findChildren(const QString &name, NodeVector &nodes) const
{
nodes.clear();
- int nonfunctionCount = m_nonfunctionMap.count(name);
- auto it = m_functionMap.find(name);
- if (it != m_functionMap.end()) {
- int functionCount = 0;
- FunctionNode *fn = it.value();
- while (fn != nullptr) {
- ++functionCount;
- fn = fn->nextOverload();
- }
- nodes.reserve(nonfunctionCount + functionCount);
- fn = it.value();
- while (fn != nullptr) {
- nodes.append(fn);
- fn = fn->nextOverload();
- }
- } else {
- nodes.reserve(nonfunctionCount);
- }
- if (nonfunctionCount > 0) {
- for (auto it = m_nonfunctionMap.find(name);
- it != m_nonfunctionMap.end() && it.key() == name; ++it) {
- nodes.append(it.value());
- }
+ const auto &functions = m_functionMap.value(name);
+ nodes.reserve(functions.size() + m_nonfunctionMap.count(name));
+ for (auto f : functions)
+ nodes.emplace_back(f);
+ auto [it, end] = m_nonfunctionMap.equal_range(name);
+ while (it != end) {
+ nodes.emplace_back(*it);
+ ++it;
}
}
@@ -176,57 +139,55 @@ Node *Aggregate::findNonfunctionChild(const QString &name, bool (Node::*isMatch)
If \a parameters is empty but no matching function is found
that has no parameters, return the first non-internal primary
function or overload, whether it has parameters or not.
+
+ \sa normalizeOverloads()
*/
FunctionNode *Aggregate::findFunctionChild(const QString &name, const Parameters &parameters)
{
- auto it = m_functionMap.find(name);
- if (it == m_functionMap.end())
+ auto map_it = m_functionMap.find(name);
+ if (map_it == m_functionMap.end())
return nullptr;
- FunctionNode *fn = it.value();
-
- if (parameters.isEmpty() && fn->parameters().isEmpty() && !fn->isInternal())
- return fn;
-
- while (fn != nullptr) {
- if (parameters.count() == fn->parameters().count() && !fn->isInternal()) {
- if (parameters.isEmpty())
- return fn;
- bool matched = true;
- for (int i = 0; i < parameters.count(); i++) {
- if (parameters.at(i).type() != fn->parameters().at(i).type()) {
- matched = false;
- break;
- }
- }
- if (matched)
- return fn;
- }
- fn = fn->nextOverload();
- }
- if (parameters.isEmpty()) {
- for (fn = it.value(); fn != nullptr; fn = fn->nextOverload())
- if (!fn->isInternal())
- return fn;
- return it.value();
- }
- return nullptr;
+ auto match_it = std::find_if((*map_it).begin(), (*map_it).end(),
+ [&parameters](const FunctionNode *fn) {
+ if (fn->isInternal())
+ return false;
+ if (parameters.count() != fn->parameters().count())
+ return false;
+ for (int i = 0; i < parameters.count(); ++i)
+ if (parameters.at(i).type() != fn->parameters().at(i).type())
+ return false;
+ return true;
+ });
+
+ if (match_it != (*map_it).end())
+ return *match_it;
+
+ // Assumes that overloads are already normalized; i.e, if there's
+ // an active function, it'll be found at the start of the list.
+ auto *fn = (*(*map_it).begin());
+ return (parameters.isEmpty() && !fn->isInternal()) ? fn : nullptr;
}
/*!
- Find the function node that is a child of this node, such
+ Returns the function node that is a child of this node, such
that the function described has the same name and signature
as the function described by the function node \a clone.
+
+ Returns \nullptr if no matching function was found.
*/
FunctionNode *Aggregate::findFunctionChild(const FunctionNode *clone)
{
- FunctionNode *fn = m_functionMap.value(clone->name());
- while (fn != nullptr) {
- if (isSameSignature(clone, fn))
- return fn;
- fn = fn->nextOverload();
- }
- return nullptr;
+ auto funcs_it = m_functionMap.find(clone->name());
+ if (funcs_it == m_functionMap.end())
+ return nullptr;
+
+ auto func_it = std::find_if((*funcs_it).begin(), (*funcs_it).end(),
+ [clone](const FunctionNode *fn) {
+ return compare(clone, fn) == 0;
+ });
+
+ return func_it != (*funcs_it).end() ? *func_it : nullptr;
}
/*!
@@ -236,8 +197,8 @@ FunctionNode *Aggregate::findFunctionChild(const FunctionNode *clone)
*/
void Aggregate::markUndocumentedChildrenInternal()
{
- for (auto *child : qAsConst(m_children)) {
- if (!child->isSharingComment() && !child->hasDoc() && !child->isDontDocument()) {
+ for (auto *child : std::as_const(m_children)) {
+ if (!child->hasDoc() && !child->isDontDocument()) {
if (!child->docMustBeGenerated()) {
if (child->isFunction()) {
if (static_cast<FunctionNode *>(child)->hasAssociatedProperties())
@@ -257,52 +218,73 @@ void Aggregate::markUndocumentedChildrenInternal()
}
/*!
- This is where we set the overload numbers for function nodes.
+ Adopts each non-aggregate C++ node (function/macro, typedef, enum, variable,
+ or a shared comment node with genus Node::CPP) in the global scope to the
+ aggregate specified in the node's documentation using the \\relates command.
+
+ If the target Aggregate is not found in the primary tree, creates a new
+ ProxyNode to use as the parent.
+*/
+void Aggregate::resolveRelates()
+{
+ Q_ASSERT(name().isEmpty()); // Must be called on the root namespace
+ auto *database = QDocDatabase::qdocDB();
+
+ for (auto *node : m_children) {
+ if (node->isRelatedNonmember() || node->isAggregate())
+ continue;
+ if (node->genus() != Node::CPP)
+ continue;
+
+ const auto &relates_args = node->doc().metaCommandArgs("relates"_L1);
+ if (relates_args.isEmpty())
+ continue;
+
+ auto *aggregate = database->findRelatesNode(relates_args[0].first.split("::"_L1));
+ if (!aggregate)
+ aggregate = new ProxyNode(this, relates_args[0].first);
+ else if (node->parent() == aggregate)
+ continue;
+
+ aggregate->adoptChild(node);
+ node->setRelatedNonmember(true);
+ }
+}
+
+/*!
+ Sorts the lists of overloads in the function map and assigns overload
+ numbers.
+
+ For sorting, active functions take precedence over internal ones, as well
+ as ones marked as \\overload - the latter ones typically do not contain
+ full documentation, so selecting them as the \e primary function
+ would cause unnecessary warnings to be generated.
+
+ Otherwise, the order is set as determined by FunctionNode::compare().
*/
void Aggregate::normalizeOverloads()
{
- /*
- Ensure that none of the primary functions is inactive, private,
- or marked \e {overload}.
- */
- for (auto it = m_functionMap.begin(); it != m_functionMap.end(); ++it) {
- FunctionNode *fn = it.value();
- if (fn->isOverload()) {
- FunctionNode *primary = fn->findPrimaryFunction();
- if (primary) {
- primary->setNextOverload(fn);
- it.value() = primary;
- fn = primary;
- }
- }
- int count = 0;
- fn->setOverloadNumber(0); // also clears the overload flag
- FunctionNode *internalFn = nullptr;
- while (fn != nullptr) {
- FunctionNode *next = fn->nextOverload();
- if (next) {
- if (next->isInternal()) {
- // internal overloads are moved to a separate list
- // and processed last
- fn->setNextOverload(next->nextOverload());
- next->setNextOverload(internalFn);
- internalFn = next;
- } else {
- next->setOverloadNumber(++count);
- }
- fn = fn->nextOverload();
- } else {
- fn->setNextOverload(internalFn);
- break;
- }
- }
- while (internalFn) {
- internalFn->setOverloadNumber(++count);
- internalFn = internalFn->nextOverload();
+ for (auto map_it = m_functionMap.begin(); map_it != m_functionMap.end(); ++map_it) {
+ if ((*map_it).size() > 1) {
+ std::sort((*map_it).begin(), (*map_it).end(),
+ [](const FunctionNode *f1, const FunctionNode *f2) -> bool {
+ if (f1->isInternal() != f2->isInternal())
+ return f2->isInternal();
+ if (f1->isOverload() != f2->isOverload())
+ return f2->isOverload();
+ // Prioritize documented over undocumented
+ if (f1->hasDoc() != f2->hasDoc())
+ return f1->hasDoc();
+ return (compare(f1, f2) < 0);
+ });
+ // Set overload numbers
+ signed short n{0};
+ for (auto *fn : (*map_it))
+ fn->setOverloadNumber(n++);
}
}
- for (auto *node : qAsConst(m_children)) {
+ for (auto *node : std::as_const(m_children)) {
if (node->isAggregate())
static_cast<Aggregate *>(node)->normalizeOverloads();
}
@@ -344,137 +326,6 @@ const EnumNode *Aggregate::findEnumNodeForValue(const QString &enumValue) const
}
/*!
- Appends \a includeFile file to the list of include files.
- */
-void Aggregate::addIncludeFile(const QString &includeFile)
-{
- m_includeFiles.append(includeFile);
-}
-
-/*!
- Sets the list of include files to \a includeFiles.
- */
-void Aggregate::setIncludeFiles(const QStringList &includeFiles)
-{
- m_includeFiles = includeFiles;
-}
-
-/*!
- Compare \a f1 to \a f2 and return \c true if they have the same
- signature. Otherwise return \c false. They must have the same
- number of parameters, and all the parameter types must be the
- same. The functions must have the same constness and refness.
- This is a private function.
- */
-bool Aggregate::isSameSignature(const FunctionNode *f1, const FunctionNode *f2)
-{
- if (f1->parameters().count() != f2->parameters().count())
- return false;
- if (f1->isConst() != f2->isConst())
- return false;
- if (f1->isRef() != f2->isRef())
- return false;
- if (f1->isRefRef() != f2->isRefRef())
- return false;
-
- const Parameters &p1 = f1->parameters();
- const Parameters &p2 = f2->parameters();
- for (int i = 0; i < p1.count(); i++) {
- if (p1.at(i).hasType() && p2.at(i).hasType()) {
- QString t1 = p1.at(i).type();
- QString t2 = p2.at(i).type();
-
- if (t1.length() < t2.length())
- qSwap(t1, t2);
-
- /*
- ### hack for C++ to handle superfluous
- "Foo::" prefixes gracefully
- */
- if (t1 != t2 && t1 != (f2->parent()->name() + "::" + t2)) {
- // Accept a difference in the template parametters of the type if one
- // is omited (eg. "QAtomicInteger" == "QAtomicInteger<T>")
- auto ltLoc = t1.indexOf('<');
- auto gtLoc = t1.indexOf('>', ltLoc);
- if (ltLoc < 0 || gtLoc < ltLoc)
- return false;
- t1.remove(ltLoc, gtLoc - ltLoc + 1);
- if (t1 != t2)
- return false;
- }
- }
- }
- return true;
-}
-
-/*!
- This function is only called by addChild(), when the child is a
- FunctionNode. If the function map does not contain a function with
- the name in \a fn, \a fn is inserted into the function map. If the
- map already contains a function by that name, \a fn is appended to
- that function's linked list of overloads.
-
- \note A function's overloads appear in the linked list in the same
- order they were created. The first overload in the list is the first
- overload created. This order is maintained in the numbering of
- overloads. In other words, the first overload in the linked list has
- overload number 1, and the last overload in the list has overload
- number n, where n is the number of overloads not including the
- function in the function map.
-
- \not Adding a function increments the aggregate's function count,
- which is the total number of function nodes in the function map,
- including the overloads. The overloads are not inserted into the map
- but are in a linked list using the FunctionNode's m_nextOverload
- pointer.
-
- \note The function's overload number and overload flag are set in
- normalizeOverloads().
-
- \note This is a private function.
-
- \sa normalizeOverloads()
- */
-void Aggregate::addFunction(FunctionNode *fn)
-{
- auto it = m_functionMap.find(fn->name());
- if (it == m_functionMap.end())
- m_functionMap.insert(fn->name(), fn);
- else
- it.value()->appendOverload(fn);
-}
-
-/*!
- When an Aggregate adopts a function \a fn that is a child of
- another Aggregate, the function is inserted into this
- Aggregate's function map.
-
- The function is also removed from the overload list
- that's relative to the the original parent \a firstParent.
-
- \note This is a private function.
- */
-void Aggregate::adoptFunction(FunctionNode *fn, Aggregate *firstParent)
-{
- auto *primary = firstParent->m_functionMap.value(fn->name());
- if (primary) {
- if (primary != fn)
- primary->removeOverload(fn);
- else if (primary->nextOverload())
- firstParent->m_functionMap.insert(primary->name(),
- primary->nextOverload());
- /* else...technically we should call
- firstParent->m_functionMap.remove(primary->name());
- but we want to be able to still find global functions
- from the global namespace, even after adopting them
- elsewhere.
- */
- }
- fn->setNextOverload(nullptr);
- addFunction(fn);
-}
-
-/*!
Adds the \a child to this node's child map using \a title
as the key. The \a child is not added to the child list
again, because it is presumed to already be there. We just
@@ -507,12 +358,11 @@ void Aggregate::addChild(Node *child)
{
m_children.append(child);
child->setParent(this);
- child->setOutputSubdirectory(this->outputSubdirectory());
child->setUrl(QString());
child->setIndexNodeFlag(isIndexNode());
if (child->isFunction()) {
- addFunction(static_cast<FunctionNode *>(child));
+ m_functionMap[child->name()].emplace_back(static_cast<FunctionNode *>(child));
} else if (!child->name().isEmpty()) {
m_nonfunctionMap.insert(child->name(), child);
if (child->isEnumType())
@@ -532,10 +382,9 @@ void Aggregate::adoptChild(Node *child)
{
if (child->parent() != this) {
m_children.append(child);
- auto firstParent = child->parent();
child->setParent(this);
if (child->isFunction()) {
- adoptFunction(static_cast<FunctionNode *>(child), firstParent);
+ m_functionMap[child->name()].emplace_back(static_cast<FunctionNode *>(child));
} else if (!child->name().isEmpty()) {
m_nonfunctionMap.insert(child->name(), child);
if (child->isEnumType())
@@ -550,25 +399,13 @@ void Aggregate::adoptChild(Node *child)
}
/*!
- Recursively sets the output subdirectory for children
- */
-void Aggregate::setOutputSubdirectory(const QString &t)
-{
- Node::setOutputSubdirectory(t);
- for (auto *node : qAsConst(m_children))
- node->setOutputSubdirectory(t);
-}
-
-/*!
- If this node has a child that is a QML property or JS property
- named \a n, return a pointer to that child. Otherwise, return \nullptr.
+ If this node has a child that is a QML property named \a n, return a
+ pointer to that child. Otherwise, return \nullptr.
*/
QmlPropertyNode *Aggregate::hasQmlProperty(const QString &n) const
{
NodeType goal = Node::QmlProperty;
- if (isJsNode())
- goal = Node::JsProperty;
- for (auto *child : qAsConst(m_children)) {
+ for (auto *child : std::as_const(m_children)) {
if (child->nodeType() == goal) {
if (child->name() == n)
return static_cast<QmlPropertyNode *>(child);
@@ -578,16 +415,13 @@ QmlPropertyNode *Aggregate::hasQmlProperty(const QString &n) const
}
/*!
- If this node has a child that is a QML property or JS property
- named \a n and that also matches \a attached, return a pointer
- to that child.
+ If this node has a child that is a QML property named \a n and that
+ also matches \a attached, return a pointer to that child.
*/
QmlPropertyNode *Aggregate::hasQmlProperty(const QString &n, bool attached) const
{
NodeType goal = Node::QmlProperty;
- if (isJsNode())
- goal = Node::JsProperty;
- for (auto *child : qAsConst(m_children)) {
+ for (auto *child : std::as_const(m_children)) {
if (child->nodeType() == goal) {
if (child->name() == n && child->isAttached() == attached)
return static_cast<QmlPropertyNode *>(child);
@@ -597,25 +431,15 @@ QmlPropertyNode *Aggregate::hasQmlProperty(const QString &n, bool attached) cons
}
/*!
- The FunctionNode \a fn is assumed to be a member function
- of this Aggregate. The function's name is looked up in the
- Aggregate's function map. It should be found because it is
- assumed that \a fn is in this Aggregate's function map. But
- in case it is not found, \c false is returned.
-
- Normally, the name will be found in the function map, and
- the value of the iterator is used to get the value, which
- is a pointer to another FunctionNode, which is not itself
- an overload. If that function has a non-null overload
- pointer, true is returned. Otherwise false is returned.
+ Returns \c true if this aggregate has multiple function
+ overloads matching the name of \a fn.
- This is a convenience function that you should not need to
- use.
- */
+ \note Assumes \a fn is a member of this aggregate.
+*/
bool Aggregate::hasOverloads(const FunctionNode *fn) const
{
auto it = m_functionMap.find(fn->name());
- return !(it == m_functionMap.end()) && (it.value()->nextOverload() != nullptr);
+ return !(it == m_functionMap.end()) && (it.value().size() > 1);
}
/*
@@ -643,18 +467,16 @@ static bool keep(FunctionNode *fn)
*/
void Aggregate::findAllFunctions(NodeMapMap &functionIndex)
{
- for (auto it = m_functionMap.constBegin(); it != m_functionMap.constEnd(); ++it) {
- FunctionNode *fn = it.value();
- if (keep(fn))
- functionIndex[fn->name()].insert(fn->parent()->fullDocumentName(), fn);
- fn = fn->nextOverload();
- while (fn != nullptr) {
- if (keep(fn))
- functionIndex[fn->name()].insert(fn->parent()->fullDocumentName(), fn);
- fn = fn->nextOverload();
- }
+ for (auto functions : m_functionMap) {
+ std::for_each(functions.begin(), functions.end(),
+ [&functionIndex](FunctionNode *fn) {
+ if (keep(fn))
+ functionIndex[fn->name()].insert(fn->parent()->fullDocumentName(), fn);
+ }
+ );
}
- for (Node *node : qAsConst(m_children)) {
+
+ for (Node *node : std::as_const(m_children)) {
if (node->isAggregate() && !node->isPrivate() && !node->isDontDocument())
static_cast<Aggregate *>(node)->findAllFunctions(functionIndex);
}
@@ -676,7 +498,7 @@ void Aggregate::findAllFunctions(NodeMapMap &functionIndex)
*/
void Aggregate::findAllNamespaces(NodeMultiMap &namespaces)
{
- for (auto *node : qAsConst(m_children)) {
+ for (auto *node : std::as_const(m_children)) {
if (node->isAggregate() && !node->isPrivate()) {
if (node->isNamespace() && !node->name().isEmpty())
namespaces.insert(node->name(), node);
@@ -694,8 +516,7 @@ bool Aggregate::hasObsoleteMembers() const
for (const auto *node : m_children)
if (!node->isPrivate() && node->isDeprecated()) {
if (node->isFunction() || node->isProperty() || node->isEnumType() || node->isTypedef()
- || node->isTypeAlias() || node->isVariable() || node->isQmlProperty()
- || node->isJsProperty())
+ || node->isTypeAlias() || node->isVariable() || node->isQmlProperty())
return true;
}
return false;
@@ -709,19 +530,18 @@ bool Aggregate::hasObsoleteMembers() const
*/
void Aggregate::findAllObsoleteThings()
{
- for (auto *node : qAsConst(m_children)) {
+ for (auto *node : std::as_const(m_children)) {
if (!node->isPrivate()) {
- QString name = node->name();
if (node->isDeprecated()) {
if (node->isClassNode())
QDocDatabase::obsoleteClasses().insert(node->qualifyCppName(), node);
- else if (node->isQmlType() || node->isJsType())
+ else if (node->isQmlType())
QDocDatabase::obsoleteQmlTypes().insert(node->qualifyQmlName(), node);
} else if (node->isClassNode()) {
auto *a = static_cast<Aggregate *>(node);
if (a->hasObsoleteMembers())
QDocDatabase::classesWithObsoleteMembers().insert(node->qualifyCppName(), node);
- } else if (node->isQmlType() || node->isJsType()) {
+ } else if (node->isQmlType()) {
auto *a = static_cast<Aggregate *>(node);
if (a->hasObsoleteMembers())
QDocDatabase::qmlTypesWithObsoleteMembers().insert(node->qualifyQmlName(),
@@ -734,23 +554,22 @@ void Aggregate::findAllObsoleteThings()
}
/*!
- Finds all the C++ classes, QML types, JS types, QML and JS
- basic types, and examples in this aggregate and inserts them
- into appropriate maps for later use in generating documentation.
+ Finds all the C++ classes, QML types, QML basic types, and examples
+ in this aggregate and inserts them into appropriate maps for later
+ use in generating documentation.
*/
void Aggregate::findAllClasses()
{
- for (auto *node : qAsConst(m_children)) {
+ for (auto *node : std::as_const(m_children)) {
if (!node->isPrivate() && !node->isInternal() && !node->isDontDocument()
&& node->tree()->camelCaseModuleName() != QString("QDoc")) {
if (node->isClassNode()) {
QDocDatabase::cppClasses().insert(node->qualifyCppName().toLower(), node);
- } else if (node->isQmlType() || node->isQmlBasicType() || node->isJsType()
- || node->isJsBasicType()) {
+ } else if (node->isQmlType()) {
QString name = node->name().toLower();
QDocDatabase::qmlTypes().insert(name, node);
// also add to the QML basic type map
- if (node->isQmlBasicType() || node->isJsBasicType())
+ if (node->isQmlBasicType())
QDocDatabase::qmlBasicTypes().insert(name, node);
} else if (node->isExample()) {
// use the module index title as key for the example map
@@ -770,9 +589,9 @@ void Aggregate::findAllClasses()
*/
void Aggregate::findAllAttributions(NodeMultiMap &attributions)
{
- for (auto *node : qAsConst(m_children)) {
+ for (auto *node : std::as_const(m_children)) {
if (!node->isPrivate()) {
- if (node->pageType() == Node::AttributionPage)
+ if (node->isPageNode() && static_cast<PageNode*>(node)->isAttribution())
attributions.insert(node->tree()->indexTitle(), node);
else if (node->isAggregate())
static_cast<Aggregate *>(node)->findAllAttributions(attributions);
@@ -790,48 +609,58 @@ void Aggregate::findAllAttributions(NodeMultiMap &attributions)
*/
void Aggregate::findAllSince()
{
- for (auto *node : qAsConst(m_children)) {
+ for (auto *node : std::as_const(m_children)) {
if (node->isRelatedNonmember() && node->parent() != this)
continue;
QString sinceString = node->since();
// Insert a new entry into each map for each new since string found.
- if (!node->isPrivate() && !sinceString.isEmpty()) {
- auto nsmap = QDocDatabase::newSinceMaps().find(sinceString);
- if (nsmap == QDocDatabase::newSinceMaps().end())
- nsmap = QDocDatabase::newSinceMaps().insert(sinceString, {});
-
- auto ncmap = QDocDatabase::newClassMaps().find(sinceString);
- if (ncmap == QDocDatabase::newClassMaps().end())
- ncmap = QDocDatabase::newClassMaps().insert(sinceString, {});
-
- auto nqcmap = QDocDatabase::newQmlTypeMaps().find(sinceString);
- if (nqcmap == QDocDatabase::newQmlTypeMaps().end())
- nqcmap = QDocDatabase::newQmlTypeMaps().insert(sinceString, {});
+ if (node->isInAPI() && !sinceString.isEmpty()) {
+ // operator[] will insert a default-constructed value into the
+ // map if key is not found, which is what we want here.
+ auto &nsmap = QDocDatabase::newSinceMaps()[sinceString];
+ auto &ncmap = QDocDatabase::newClassMaps()[sinceString];
+ auto &nqcmap = QDocDatabase::newQmlTypeMaps()[sinceString];
if (node->isFunction()) {
// Insert functions into the general since map.
auto *fn = static_cast<FunctionNode *>(node);
if (!fn->isDeprecated() && !fn->isSomeCtor() && !fn->isDtor())
- nsmap.value().insert(fn->name(), fn);
+ nsmap.insert(fn->name(), fn);
} else if (node->isClassNode()) {
// Insert classes into the since and class maps.
QString name = node->qualifyWithParentName();
- nsmap.value().insert(name, node);
- ncmap.value().insert(name, node);
- } else if (node->isQmlType() || node->isJsType()) {
+ nsmap.insert(name, node);
+ ncmap.insert(name, node);
+ } else if (node->isQmlType()) {
// Insert QML elements into the since and element maps.
QString name = node->qualifyWithParentName();
- nsmap.value().insert(name, node);
- nqcmap.value().insert(name, node);
- } else if (node->isQmlProperty() || node->isJsProperty()) {
+ nsmap.insert(name, node);
+ nqcmap.insert(name, node);
+ } else if (node->isQmlProperty()) {
// Insert QML properties into the since map.
- nsmap.value().insert(node->name(), node);
+ nsmap.insert(node->name(), node);
} else {
// Insert external documents into the general since map.
QString name = node->qualifyWithParentName();
- nsmap.value().insert(name, node);
+ nsmap.insert(name, node);
+ }
+ }
+ // Enum values - a special case as EnumItem is not a Node subclass
+ if (node->isInAPI() && node->isEnumType()) {
+ for (const auto &val : static_cast<EnumNode *>(node)->items()) {
+ sinceString = val.since();
+ if (sinceString.isEmpty())
+ continue;
+ // Insert to enum value map
+ QDocDatabase::newEnumValueMaps()[sinceString].insert(
+ node->name() + "::" + val.name(), node);
+ // Ugly hack: Insert into general map with an empty key -
+ // we need something in there to mark the corresponding
+ // section populated. See Sections class constructor.
+ QDocDatabase::newSinceMaps()[sinceString].replace(QString(), node);
}
}
+
// Recursively find child nodes with since commands.
if (node->isAggregate())
static_cast<Aggregate *>(node)->findAllSince();
@@ -845,8 +674,8 @@ void Aggregate::findAllSince()
void Aggregate::resolveQmlInheritance()
{
NodeMap previousSearches;
- for (auto *child : qAsConst(m_children)) {
- if (!child->isQmlType() && !child->isJsType())
+ for (auto *child : std::as_const(m_children)) {
+ if (!child->isQmlType())
continue;
static_cast<QmlTypeNode *>(child)->resolveInheritance(previousSearches);
}
@@ -901,11 +730,6 @@ QString Aggregate::typeWord(bool cap) const
Returns a const iterator pointing at the end of the child list.
*/
-/*! \fn const QStringList &Aggregate::includeFiles() const
- This function returns a const reference to a list of strings, but
- I no longer know what they are.
- */
-
/*! \fn QmlTypeNode *Aggregate::qmlBaseNode() const
If this Aggregate is a QmlTypeNode, this function returns a pointer to
the QmlTypeNode that is its base type. Otherwise it returns \c nullptr.
diff --git a/src/qdoc/aggregate.h b/src/qdoc/qdoc/src/qdoc/aggregate.h
index d50250f41..a02633e04 100644
--- a/src/qdoc/aggregate.h
+++ b/src/qdoc/qdoc/src/qdoc/aggregate.h
@@ -1,36 +1,14 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the tools applications of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#ifndef AGGREGATE_H
#define AGGREGATE_H
#include "pagenode.h"
+#include <optional>
+#include <vector>
+
#include <QtCore/qglobal.h>
#include <QtCore/qstringlist.h>
@@ -43,6 +21,8 @@ class QmlPropertyNode;
class Aggregate : public PageNode
{
public:
+ using FunctionMap = QMap<QString, std::vector<FunctionNode*>>;
+
[[nodiscard]] Node *findChildNode(const QString &name, Node::Genus genus,
int findFlags = 0) const;
Node *findNonfunctionChild(const QString &name, bool (Node::*)() const);
@@ -50,6 +30,7 @@ public:
FunctionNode *findFunctionChild(const QString &name, const Parameters &parameters);
FunctionNode *findFunctionChild(const FunctionNode *clone);
+ void resolveRelates();
void normalizeOverloads();
void markUndocumentedChildrenInternal();
@@ -62,9 +43,11 @@ public:
[[nodiscard]] NodeList::ConstIterator constBegin() const { return m_children.constBegin(); }
[[nodiscard]] NodeList::ConstIterator constEnd() const { return m_children.constEnd(); }
- void addIncludeFile(const QString &includeFile);
- void setIncludeFiles(const QStringList &includeFiles);
- [[nodiscard]] const QStringList &includeFiles() const { return m_includeFiles; }
+ inline void setIncludeFile(const QString& include) { m_includeFile.emplace(include); }
+ // REMARK: Albeit not enforced at the API boundaries,
+ // downstream-user can assume that if there is a QString that was
+ // set, then that string is not empty.
+ [[nodiscard]] inline const std::optional<QString>& includeFile() const { return m_includeFile; }
[[nodiscard]] QmlPropertyNode *hasQmlProperty(const QString &) const;
[[nodiscard]] QmlPropertyNode *hasQmlProperty(const QString &, bool attached) const;
@@ -72,7 +55,6 @@ public:
void addChildByTitle(Node *child, const QString &title);
void addChild(Node *child);
void adoptChild(Node *child);
- void setOutputSubdirectory(const QString &t) override;
FunctionMap &functionMap() { return m_functionMap; }
void findAllFunctions(NodeMapMap &functionIndex);
@@ -95,9 +77,7 @@ protected:
private:
friend class Node;
- void addFunction(FunctionNode *fn);
- void adoptFunction(FunctionNode *fn, Aggregate *firstParent);
- static bool isSameSignature(const FunctionNode *f1, const FunctionNode *f2);
+ void dropNonRelatedMembers();
protected:
NodeList m_children {};
@@ -105,7 +85,29 @@ protected:
FunctionMap m_functionMap {};
private:
- QStringList m_includeFiles {};
+ // REMARK: The member indicates the name of a file where the
+ // aggregate can be found from, for example, an header file that
+ // declares a class.
+ // For aggregates such as classes we expect this to always be set
+ // to a non-empty string after the code-parsing phase.
+ // Indeed, currently, by default, QDoc always generates such a
+ // string using the name of the aggregate if no include file can
+ // be propagated from some of the parents.
+ //
+ // Nonetheless, we are still forced to make this an optional, as
+ // this will not be true for all Aggregates.
+ //
+ // For example, for namespaces, we don't seem to set an include
+ // file and indeed doing so wouldn't be particularly meaningful.
+ //
+ // It is possible to assume in later code, especially the
+ // generation phase, that at least some classes of aggregates
+ // always have a value set here but we should, for the moment,
+ // still check for the possibility of something not to be there,
+ // or warn if we decide to ignore that, to be compliant with the
+ // current interface, whose change would require deep changes to
+ // QDoc internal structures.
+ std::optional<QString> m_includeFile{};
NodeList m_enumChildren {};
NodeMultiMap m_nonfunctionMap {};
NodeList m_nonfunctionList {};
diff --git a/src/qdoc/atom.cpp b/src/qdoc/qdoc/src/qdoc/atom.cpp
index eafaac764..f887c4ec5 100644
--- a/src/qdoc/atom.cpp
+++ b/src/qdoc/qdoc/src/qdoc/atom.cpp
@@ -1,30 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the tools applications of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "atom.h"
@@ -73,13 +48,12 @@ QT_BEGIN_NAMESPACE
\value CaptionRight
\value Code
\value CodeBad
- \value CodeNew
- \value CodeOld
\value CodeQuoteArgument
\value CodeQuoteCommand
+ \value DetailsLeft
+ \value DetailsRight
\value DivLeft
\value DivRight
- \value EndQmlText
\value ExampleFileLink
\value ExampleImageLink
\value FormatElse
@@ -94,8 +68,6 @@ QT_BEGIN_NAMESPACE
\value ImageText
\value ImportantNote
\value InlineImage
- \value JavaScript
- \value EndJavaScript
\value Keyword
\value LineBreak
\value Link
@@ -114,7 +86,6 @@ QT_BEGIN_NAMESPACE
\value ParaLeft
\value ParaRight
\value Qml
- \value QmlText
\value QuotationLeft
\value QuotationRight
\value RawString
@@ -142,8 +113,6 @@ QT_BEGIN_NAMESPACE
\value UnknownCommand
*/
-QString Atom::s_noError = QString();
-
static const struct
{
const char *english;
@@ -159,13 +128,14 @@ static const struct
{ "CaptionRight", Atom::CaptionRight },
{ "Code", Atom::Code },
{ "CodeBad", Atom::CodeBad },
- { "CodeNew", Atom::CodeNew },
- { "CodeOld", Atom::CodeOld },
{ "CodeQuoteArgument", Atom::CodeQuoteArgument },
{ "CodeQuoteCommand", Atom::CodeQuoteCommand },
+ { "ComparesLeft", Atom::ComparesLeft },
+ { "ComparesRight", Atom::ComparesRight },
+ { "DetailsLeft", Atom::DetailsLeft },
+ { "DetailsRight", Atom::DetailsRight },
{ "DivLeft", Atom::DivLeft },
{ "DivRight", Atom::DivRight },
- { "EndQmlText", Atom::EndQmlText },
{ "ExampleFileLink", Atom::ExampleFileLink },
{ "ExampleImageLink", Atom::ExampleImageLink },
{ "FootnoteLeft", Atom::FootnoteLeft },
@@ -182,8 +152,6 @@ static const struct
{ "ImportantLeft", Atom::ImportantLeft },
{ "ImportantRight", Atom::ImportantRight },
{ "InlineImage", Atom::InlineImage },
- { "JavaScript", Atom::JavaScript },
- { "EndJavaScript", Atom::EndJavaScript },
{ "Keyword", Atom::Keyword },
{ "LegaleseLeft", Atom::LegaleseLeft },
{ "LegaleseRight", Atom::LegaleseRight },
@@ -205,7 +173,6 @@ static const struct
{ "ParaLeft", Atom::ParaLeft },
{ "ParaRight", Atom::ParaRight },
{ "Qml", Atom::Qml },
- { "QmlText", Atom::QmlText },
{ "QuotationLeft", Atom::QuotationLeft },
{ "QuotationRight", Atom::QuotationRight },
{ "RawString", Atom::RawString },
@@ -272,7 +239,7 @@ static const struct
\also string()
*/
-/*! \fn void Atom::appendString(const QString &string)
+/*! \fn void Atom::concatenateString(const QString &string)
Appends \a string to the string parameter of this atom.
@@ -284,6 +251,32 @@ static const struct
\also string()
*/
+/*!
+ Starting from this Atom, searches the linked list for the
+ atom of specified type \a t and returns it. Returns \nullptr
+ if no such atom is found.
+*/
+const Atom *Atom::find(AtomType t) const
+{
+ const auto *a{this};
+ while (a && a->type() != t)
+ a = a->next();
+ return a;
+}
+
+/*!
+ Starting from this Atom, searches the linked list for the
+ atom of specified type \a t and string \a s, and returns it.
+ Returns \nullptr if no such atom is found.
+*/
+const Atom *Atom::find(AtomType t, const QString &s) const
+{
+ const auto *a{this};
+ while (a && (a->type() != t || a->string() != s))
+ a = a->next();
+ return a;
+}
+
/*! \fn Atom *Atom::next()
Return the next atom in the atom list.
\also type(), string()
@@ -333,8 +326,7 @@ QString Atom::typeString() const
int i = 0;
while (atms[i].english != nullptr) {
if (atms[i].no != i)
- Location::internalError(
- QCoreApplication::translate("QDoc::Atom", "atom %1 missing").arg(i));
+ Location::internalError(QStringLiteral("QDoc::Atom: atom %1 missing").arg(i));
++i;
}
deja = true;
@@ -355,21 +347,6 @@ QString Atom::typeString() const
*/
/*!
- Dumps this Atom to stderr in printer friendly form.
- */
-void Atom::dump() const
-{
- QString str = string();
- str.replace(QLatin1String("\\"), QLatin1String("\\\\"));
- str.replace(QLatin1String("\""), QLatin1String("\\\""));
- str.replace(QLatin1String("\n"), QLatin1String("\\n"));
- str.replace(QRegularExpression(QLatin1String(R"([^ -~])")), QLatin1String("?"));
- if (!str.isEmpty())
- str = QLatin1String(" \"") + str + QLatin1Char('"');
- fprintf(stderr, " %-15s%s\n", typeString().toLatin1().data(), str.toLatin1().data());
-}
-
-/*!
For a link atom, returns the string representing the link text
if one exist in the list of atoms.
*/
@@ -398,11 +375,11 @@ QString Atom::linkText() const
words separated by spaces. The constructor splits \a p2 on
the space character.
*/
-LinkAtom::LinkAtom(const QString &p1, const QString &p2)
+LinkAtom::LinkAtom(const QString &p1, const QString &p2, Location location)
: Atom(Atom::Link, p1),
+ location(location),
m_resolved(false),
m_genus(Node::DontCare),
- m_goal(Node::NoType),
m_domain(nullptr),
m_squareBracketParams(p2)
{
@@ -426,11 +403,7 @@ void LinkAtom::resolveSquareBracketParams()
continue;
}
}
- if (m_goal == Node::NoType) {
- m_goal = Node::goal(param);
- if (m_goal != Node::NoType)
- continue;
- }
+
if (param == "qml") {
m_genus = Node::QML;
continue;
@@ -447,7 +420,6 @@ void LinkAtom::resolveSquareBracketParams()
m_genus = Node::API;
continue;
}
- m_error = m_squareBracketParams;
break;
}
m_resolved = true;
@@ -458,11 +430,10 @@ void LinkAtom::resolveSquareBracketParams()
*/
LinkAtom::LinkAtom(const LinkAtom &t)
: Atom(Link, t.string()),
+ location(t.location),
m_resolved(t.m_resolved),
m_genus(t.m_genus),
- m_goal(t.m_goal),
m_domain(t.m_domain),
- m_error(t.m_error),
m_squareBracketParams(t.m_squareBracketParams)
{
// nothing
@@ -475,11 +446,10 @@ LinkAtom::LinkAtom(const LinkAtom &t)
*/
LinkAtom::LinkAtom(Atom *previous, const LinkAtom &t)
: Atom(previous, Link, t.string()),
+ location(t.location),
m_resolved(t.m_resolved),
m_genus(t.m_genus),
- m_goal(t.m_goal),
m_domain(t.m_domain),
- m_error(t.m_error),
m_squareBracketParams(t.m_squareBracketParams)
{
previous->m_next = this;
diff --git a/src/qdoc/atom.h b/src/qdoc/qdoc/src/qdoc/atom.h
index 544e8369b..7483c829e 100644
--- a/src/qdoc/atom.h
+++ b/src/qdoc/qdoc/src/qdoc/atom.h
@@ -1,30 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the tools applications of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#ifndef ATOM_H
#define ATOM_H
@@ -54,13 +29,14 @@ public:
CaptionRight,
Code,
CodeBad,
- CodeNew,
- CodeOld,
CodeQuoteArgument,
CodeQuoteCommand,
+ ComparesLeft,
+ ComparesRight,
+ DetailsLeft,
+ DetailsRight,
DivLeft,
DivRight,
- EndQmlText,
ExampleFileLink,
ExampleImageLink,
FootnoteLeft,
@@ -77,8 +53,6 @@ public:
ImportantLeft,
ImportantRight,
InlineImage,
- JavaScript,
- EndJavaScript,
Keyword,
LegaleseLeft,
LegaleseRight,
@@ -100,7 +74,6 @@ public:
ParaLeft,
ParaRight,
Qml,
- QmlText,
QuotationLeft,
QuotationRight,
RawString,
@@ -161,12 +134,15 @@ public:
virtual ~Atom() = default;
void appendChar(QChar ch) { m_strs[0] += ch; }
- void appendString(const QString &string) { m_strs[0] += string; }
+ void concatenateString(const QString &string) { m_strs[0] += string; }
+ void append(const QString &string) { m_strs << string; }
void chopString() { m_strs[0].chop(1); }
void setString(const QString &string) { m_strs[0] = string; }
Atom *next() { return m_next; }
void setNext(Atom *newNext) { m_next = newNext; }
+ [[nodiscard]] const Atom *find(AtomType t) const;
+ [[nodiscard]] const Atom *find(AtomType t, const QString &s) const;
[[nodiscard]] const Atom *next() const { return m_next; }
[[nodiscard]] const Atom *next(AtomType t) const;
[[nodiscard]] const Atom *next(AtomType t, const QString &s) const;
@@ -175,18 +151,15 @@ public:
[[nodiscard]] const QString &string() const { return m_strs[0]; }
[[nodiscard]] const QString &string(int i) const { return m_strs[i]; }
[[nodiscard]] qsizetype count() const { return m_strs.size(); }
- void dump() const;
[[nodiscard]] QString linkText() const;
[[nodiscard]] const QStringList &strings() const { return m_strs; }
[[nodiscard]] virtual bool isLinkAtom() const { return false; }
virtual Node::Genus genus() { return Node::DontCare; }
virtual Tree *domain() { return nullptr; }
- virtual const QString &error() { return s_noError; }
virtual void resolveSquareBracketParams() {}
protected:
- static QString s_noError;
Atom *m_next = nullptr;
AtomType m_type {};
QStringList m_strs {};
@@ -195,7 +168,7 @@ protected:
class LinkAtom : public Atom
{
public:
- LinkAtom(const QString &p1, const QString &p2);
+ LinkAtom(const QString &p1, const QString &p2, Location location = Location());
LinkAtom(const LinkAtom &t);
LinkAtom(Atom *previous, const LinkAtom &t);
~LinkAtom() override = default;
@@ -211,15 +184,15 @@ public:
resolveSquareBracketParams();
return m_domain;
}
- const QString &error() override { return m_error; }
void resolveSquareBracketParams() override;
+public:
+ Location location;
+
protected:
bool m_resolved {};
Node::Genus m_genus {};
- Node::NodeType m_goal {};
Tree *m_domain {};
- QString m_error {};
QString m_squareBracketParams {};
};
@@ -232,6 +205,7 @@ protected:
#define ATOM_FORMATTING_SUBSCRIPT "subscript"
#define ATOM_FORMATTING_SUPERSCRIPT "superscript"
#define ATOM_FORMATTING_TELETYPE "teletype"
+#define ATOM_FORMATTING_TRADEMARK "trademark"
#define ATOM_FORMATTING_UICONTROL "uicontrol"
#define ATOM_FORMATTING_UNDERLINE "underline"
diff --git a/src/qdoc/qdoc/src/qdoc/boundaries/filesystem/directorypath.cpp b/src/qdoc/qdoc/src/qdoc/boundaries/filesystem/directorypath.cpp
new file mode 100644
index 000000000..799582139
--- /dev/null
+++ b/src/qdoc/qdoc/src/qdoc/boundaries/filesystem/directorypath.cpp
@@ -0,0 +1,126 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "directorypath.h"
+
+/*!
+ * \class DirectoryPath
+ *
+ * \brief Represents a path to a directory that was known to exist on the
+ * filesystem.
+ *
+ * An instance of this type guarantees that, at the time of creation
+ * of the instance, the contained path represented an existing,
+ * readable, executable directory.
+ *
+ * The type is intended to be used whenever a user-provided path to a
+ * directory is encountered the first time, validating that it can be
+ * used later on for the duration of a QDoc execution and
+ * canonicalizing the original path.
+ *
+ * Such a usage example could be during the configuration process,
+ * when encountering the paths that defines where QDoc should search
+ * for images or other files.
+ *
+ * Similarly, it is intended to be used at the API boundaries,
+ * internally, to relieve the called element of the requirement to
+ * check the validity of a path when a directory is required and to
+ * ensure that a single format of the path is encountered.
+ *
+ * Do note that the guarantees provided by this type do not
+ * necessarily hold after the time of creation of an instance.
+ * Indeed, the underlying filesystem may have changed.
+ *
+ * It is possible to renew the contract by obtaining a new instance:
+ *
+ * \code
+ * DirectoryPath old...
+ *
+ * ...
+ *
+ * auto current{DirectoryPath:refine(old.value())};
+ * \endcode
+ *
+ * QDoc itself will not generally perform destructive operations on
+ * its input files during an execution and, as such, it is never
+ * required to renew a contract. Ensuring that the underlying input
+ * files are indeed immutable is out-of-scope for QDoc and it is
+ * allowed to consider a case where the contract was invalidated as
+ * undefined behavior.
+ */
+
+/*!
+ * \include boundaries/refined_typedef_members.qdocinc {wrapped_type_documentation} {DirectoryPath}
+ */
+
+/*!
+ * \include boundaries/refined_typedef_members.qdocinc {has_equality_operator_documentation} {DirectoryPath}
+ */
+
+/*!
+ * \include boundaries/refined_typedef_members.qdocinc {has_less_than_operator_documentation} {DirectoryPath}
+ */
+
+/*!
+ * \include boundaries/refined_typedef_members.qdocinc {has_strictly_less_than_operator_documentation} {DirectoryPath}
+ */
+
+/*!
+ * \include boundaries/refined_typedef_members.qdocinc {has_greater_than_operator_documentation} {DirectoryPath}
+ */
+
+/*!
+ * \include boundaries/refined_typedef_members.qdocinc {has_strictly_greater_than_operator_documentation} {DirectoryPath}
+ */
+
+/*!
+ * \include boundaries/refined_typedef_members.qdocinc {refine_documentation} {DirectoryPath}
+ */
+
+/*!
+ * \include boundaries/refined_typedef_members.qdocinc {value_documentation} {DirectoryPath}
+ */
+
+/*!
+ * \include boundaries/refined_typedef_members.qdocinc {copy_constructor_documentation} {DirectoryPath}
+ */
+
+/*!
+ * \include boundaries/refined_typedef_members.qdocinc {copy_assignment_documentation} {DirectoryPath}
+ */
+
+/*!
+ * \include boundaries/refined_typedef_members.qdocinc {move_constructor_documentation} {DirectoryPath}
+ */
+
+/*!
+ * \include boundaries/refined_typedef_members.qdocinc {move_assignment_documentation} {DirectoryPath}
+ */
+
+/*!
+ * \include boundaries/refined_typedef_members.qdocinc {conversion_documentation} {DirectoryPath}
+ */
+
+/*!
+ * \include boundaries/refined_typedef_members.qdocinc {operator_equal_documentation} {DirectoryPath}
+ */
+
+/*!
+ * \include boundaries/refined_typedef_members.qdocinc {operator_unequal_documentation} {DirectoryPath}
+ */
+
+/*!
+ * \include boundaries/refined_typedef_members.qdocinc {operator_less_than_documentation} {DirectoryPath}
+ */
+
+/*!
+ * \include boundaries/refined_typedef_members.qdocinc {operator_less_than_or_equal_documentation} {DirectoryPath}
+ */
+
+/*!
+ * \include boundaries/refined_typedef_members.qdocinc {operator_greater_than_documentation} {DirectoryPath}
+ */
+
+/*!
+ * \include boundaries/refined_typedef_members.qdocinc {operator_greater_than_or_equal_documentation} {DirectoryPath}
+ */
diff --git a/src/qdoc/qdoc/src/qdoc/boundaries/filesystem/directorypath.h b/src/qdoc/qdoc/src/qdoc/boundaries/filesystem/directorypath.h
new file mode 100644
index 000000000..7ec1dd415
--- /dev/null
+++ b/src/qdoc/qdoc/src/qdoc/boundaries/filesystem/directorypath.h
@@ -0,0 +1,17 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#pragma once
+
+#include "../refined_typedef.h"
+
+#include <optional>
+
+#include <QtCore/qstring.h>
+#include <QtCore/qfileinfo.h>
+
+QDOC_REFINED_TYPEDEF(QString, DirectoryPath) {
+ QFileInfo info{value};
+
+ return (info.isDir() && info.isReadable() && info.isExecutable()) ? std::optional(DirectoryPath{info.canonicalFilePath()}) : std::nullopt;
+}
diff --git a/src/qdoc/qdoc/src/qdoc/boundaries/filesystem/filepath.cpp b/src/qdoc/qdoc/src/qdoc/boundaries/filesystem/filepath.cpp
new file mode 100644
index 000000000..d8964b6a6
--- /dev/null
+++ b/src/qdoc/qdoc/src/qdoc/boundaries/filesystem/filepath.cpp
@@ -0,0 +1,125 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "filepath.h"
+
+/*!
+ * \class FilePath
+ *
+ * \brief Represents a path to a file that was known to exist on the
+ * filesystem.
+ *
+ * An instance of this type guarantees that, at the time of creation
+ * of the instance, the contained path represented an existing,
+ * readable file.
+ *
+ * The type is intended to be used whenever a user-provided path to a
+ * file is encountered the first time, validating that it can be
+ * used later on for the duration of a QDoc execution and
+ * canonicalizing the original path.
+ *
+ * Such a usage example could be when resolving a file whose path is
+ * provided by the user.
+ *
+ * Similarly, it is intended to be used at the API boundaries,
+ * internally, to relieve the called element of the requirement to
+ * check the validity of a path when a file is required and to
+ * ensure that a single format of the path is encountered.
+ *
+ * Do note that the guarantees provided by this type do not
+ * necessarily hold after the time of creation of an instance.
+ * Indeed, the underlying filesystem may have changed.
+ *
+ * It is possible to renew the contract by obtaining a new instance:
+ *
+ * \code
+ * FilePath old...
+ *
+ * ...
+ *
+ * auto current{FilePath::refine(old.value())};
+ * \endcode
+ *
+ * QDoc itself will not generally perform destructive operations on
+ * its input files during an execution and, as such, it is never
+ * required to renew a contract. Ensuring that the underlying input
+ * files are indeed immutable is out-of-scope for QDoc and it is
+ * allowed to consider a case where the contract was invalidated as
+ * undefined behavior.
+ */
+
+/*!
+ * \include boundaries/refined_typedef_members.qdocinc {wrapped_type_documentation} {FilePath}
+ */
+
+/*!
+ * \include boundaries/refined_typedef_members.qdocinc {has_equality_operator_documentation} {FilePath}
+ */
+
+/*!
+ * \include boundaries/refined_typedef_members.qdocinc {has_less_than_operator_documentation} {FilePath}
+ */
+
+/*!
+ * \include boundaries/refined_typedef_members.qdocinc {has_strictly_less_than_operator_documentation} {FilePath}
+ */
+
+/*!
+ * \include boundaries/refined_typedef_members.qdocinc {has_greater_than_operator_documentation} {FilePath}
+ */
+
+/*!
+ * \include boundaries/refined_typedef_members.qdocinc {has_strictly_greater_than_operator_documentation} {FilePath}
+ */
+
+/*!
+ * \include boundaries/refined_typedef_members.qdocinc {refine_documentation} {FilePath}
+ */
+
+/*!
+ * \include boundaries/refined_typedef_members.qdocinc {value_documentation} {FilePath}
+ */
+
+/*!
+ * \include boundaries/refined_typedef_members.qdocinc {copy_constructor_documentation} {FilePath}
+ */
+
+/*!
+ * \include boundaries/refined_typedef_members.qdocinc {copy_assignment_documentation} {FilePath}
+ */
+
+/*!
+ * \include boundaries/refined_typedef_members.qdocinc {move_constructor_documentation} {FilePath}
+ */
+
+/*!
+ * \include boundaries/refined_typedef_members.qdocinc {move_assignment_documentation} {FilePath}
+ */
+
+/*!
+ * \include boundaries/refined_typedef_members.qdocinc {conversion_documentation} {FilePath}
+ */
+
+/*!
+ * \include boundaries/refined_typedef_members.qdocinc {operator_equal_documentation} {FilePath}
+ */
+
+/*!
+ * \include boundaries/refined_typedef_members.qdocinc {operator_unequal_documentation} {FilePath}
+ */
+
+/*!
+ * \include boundaries/refined_typedef_members.qdocinc {operator_less_than_documentation} {FilePath}
+ */
+
+/*!
+ * \include boundaries/refined_typedef_members.qdocinc {operator_less_than_or_equal_documentation} {FilePath}
+ */
+
+/*!
+ * \include boundaries/refined_typedef_members.qdocinc {operator_greater_than_documentation} {FilePath}
+ */
+
+/*!
+ * \include boundaries/refined_typedef_members.qdocinc {operator_greater_than_or_equal_documentation} {FilePath}
+ */
diff --git a/src/qdoc/qdoc/src/qdoc/boundaries/filesystem/filepath.h b/src/qdoc/qdoc/src/qdoc/boundaries/filesystem/filepath.h
new file mode 100644
index 000000000..e8a9b2d35
--- /dev/null
+++ b/src/qdoc/qdoc/src/qdoc/boundaries/filesystem/filepath.h
@@ -0,0 +1,17 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#pragma once
+
+#include "qdoc/boundaries/refined_typedef.h"
+
+#include <optional>
+
+#include <QtCore/qstring.h>
+#include <QtCore/qfileinfo.h>
+
+QDOC_REFINED_TYPEDEF(QString, FilePath) {
+ QFileInfo info{value};
+
+ return (info.isFile() && info.isReadable()) ? std::optional(FilePath{info.canonicalFilePath()}) : std::nullopt;
+}
diff --git a/src/qdoc/qdoc/src/qdoc/boundaries/filesystem/resolvedfile.cpp b/src/qdoc/qdoc/src/qdoc/boundaries/filesystem/resolvedfile.cpp
new file mode 100644
index 000000000..4d5eca512
--- /dev/null
+++ b/src/qdoc/qdoc/src/qdoc/boundaries/filesystem/resolvedfile.cpp
@@ -0,0 +1,96 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "resolvedfile.h"
+
+/*!
+ * \class ResolvedFile
+ *
+ * \brief Represents a file that is reachable by QDoc based on its
+ * current configuration.
+ *
+ * Instances of this type are, generally, intended to be generated by
+ * any process that needs to query the filesystem for the presence of
+ * some files based on a user-inputted path to ensure their
+ * availability.
+ *
+ * Such an example might be when QDoc is searching for a file whose
+ * path is provided by the user, such as the one in a snippet command,
+ * that should represent a file that is reachable with the current
+ * configuration.
+ *
+ * On the other side, logic that requires access to files that are
+ * known to be user-provided, such as the quoting of snippets, can use
+ * this type at the API boundary to signal that the file should be
+ * accessible so that they avoid the need to search for the file
+ * themselves.
+ *
+ * Do note that, semantically, this type doesn't actually guarantee
+ * anything about its origin and only guarantees whatever its members
+ * guarantee.
+ *
+ * The reasoning behind this lack of enforcement is to allow for an
+ * easier testing.
+ * As many parts of QDoc might require the presence of an instance of
+ * this type, we want to be able to construct those instances without
+ * the need to pass trough whichever valid generator for them.
+ *
+ * Nonetheless, inside QDoc, any boundary that requires an instance of
+ * this type can consider it guaranteed that the instance was
+ * generated trough some appropriate logic, and consider it a bug if
+ * such is not the case.
+ *
+ * An instance of this type provides two pieces of information.
+ *
+ * The path to the file that is considered resolved, accessible trough
+ * the get_path() method and the string that was used to resolve the
+ * file in the first place, accessible trough the get_query() method.
+ *
+ * The first should be used by consumer who needs to interact with the
+ * file itself, such as reading from it or copying it.
+ *
+ * The second is provided for context and can be used when consumers
+ * need to know what the user-inputted path was in the first place,
+ * for example when presenting debug information.
+ *
+ * It is not semantically guaranteed that this two pieces of
+ * information are actually related. Any such instance for which this
+ * is true should be considered malformed. Inside QDoc, tough,
+ * consumer of this type can consider it guaranteed that no malformed
+ * instance will be passed to them, and consider it a bug if it
+ * happens otherwise.
+ */
+
+/*!
+ * \fn ResolvedFile::ResolvedFile(QString query, FilePath filepath)
+ *
+ * Constructs an instance of this type from \a query and \a filepath.
+ *
+ * \a query should represent the user-inputted path that was used to
+ * resolve the file that this instance represents.
+ *
+ * \a filepath should represent the file that is found querying the
+ * filesystem trough \a query using an appropriate logic for resolving
+ * files.
+ *
+ * An instance that is built from \a query and \a filepath is
+ * guaranteed to return a value that is equivalent to \a query when
+ * get_query() is called and a value that is equivalent to \a
+ * filepath.value() when get_path() is called.
+ */
+
+/*!
+ * \fn const QString& ResolvedFile::get_query() const
+ *
+ * Returns a string representing the user-inputted path that was used
+ * to resolve the file.
+ */
+
+/*!
+ * \fn const QString& ResolvedFile::get_path() const
+ *
+ * Returns a string representing the canonicalized path to the file
+ * that was resolved.
+ *
+ * Access to this file is to be considered guranteed to be available.
+ */
diff --git a/src/qdoc/qdoc/src/qdoc/boundaries/filesystem/resolvedfile.h b/src/qdoc/qdoc/src/qdoc/boundaries/filesystem/resolvedfile.h
new file mode 100644
index 000000000..e21120ab1
--- /dev/null
+++ b/src/qdoc/qdoc/src/qdoc/boundaries/filesystem/resolvedfile.h
@@ -0,0 +1,20 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#pragma once
+
+#include "qdoc/boundaries/filesystem/filepath.h"
+
+#include <QString>
+
+struct ResolvedFile {
+public:
+ ResolvedFile(QString query, FilePath filepath) : query{query}, filepath{filepath} {}
+
+ [[nodiscard]] const QString& get_query() const { return query; }
+ [[nodiscard]] const QString& get_path() const { return filepath.value(); }
+
+private:
+ QString query;
+ FilePath filepath;
+};
diff --git a/src/qdoc/qdoc/src/qdoc/boundaries/refined_typedef.h b/src/qdoc/qdoc/src/qdoc/boundaries/refined_typedef.h
new file mode 100644
index 000000000..4f3a13b7c
--- /dev/null
+++ b/src/qdoc/qdoc/src/qdoc/boundaries/refined_typedef.h
@@ -0,0 +1,207 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#pragma once
+
+#include <QtCore/qglobal.h>
+
+#include <functional>
+#include <optional>
+#include <type_traits>
+
+// TODO: Express the documentation such that QDoc would be able to see
+// it and process it correctly. This probably means that we would like
+// to associate the definition with a namespace, albeit we could use
+// the header file too, and put the documentation in an empty cpp
+// file. This is delayed as there currently isn't much namespacing for
+// anything in QDoc and such a namespacing should be added gradually
+// and attentively.
+
+// TODO: Review the semantics for construction and optmize it. Should we copy the
+// value? Should we only allow rvalues?
+
+// TODO: There is an high chance that we will need to "compose"
+// refinitions later on when dealing, for example, with the basics of
+// user-provided paths.
+// For example, when requiring that each user-inputted path is purged
+// as per QFileInfo definition of purging.
+// For example, it might be that instead of passing QString around we
+// might pass some Path type that is a purged QString.
+// Then, any other refinement over paths will need to use that as a
+// base type.
+// To avoid the clutter that comes from that, if such will be the
+// case, we will need to change the definition of refine and value if
+// the passed in type was refined already.
+// That is, such that if we have:
+//
+// QDOC_REFINE_TYPE(QString, Path) { ... }
+// QDOC_REFINE_TYPE(Path, Foo) { ... }
+//
+// Foo refines a QString and Foo.value returns a QString. This should
+// in general be trivial as long as we add a way to identify, such as
+// a tag that refinements derive from, what type was declared through
+// QDOC_REFINED_TYPEDEF and what type was not.
+
+// TODO: Provide a way to generate a standard documentation for all
+// members of a type generated by QDOC_REFINED_TYPEDEF without having
+// to copy-paste include command everywhere.
+// The main problem of doing this is that the preprocessor strips
+// comments away, making it impossible to generate comments, and hence
+// QDoc documentation, with the preprocessor.
+
+/*!
+ * \macro QDOC_REFINED_TYPEDEF(_type, _name)
+ * \relates refined_typedef.hpp
+ *
+ * Declares a wrapper type for \c {_type}, with identifier \c {_name},
+ * that represents a subset of \c {_type} for which some conditions
+ * hold.
+ *
+ * For example:
+ *
+ * \code
+ QDOC_REFINED_TYPEDEF(std::size_t, Fin5) {
+ return (value < 5) : std::make_optional<Fin5>{value} : std::nullopt;
+ }
+ * \endcode
+ *
+ * Represents the subset of \c {std::size_t} that contains the value 0, 1,
+ * 2, 3 and 4, that is, the general finite set of cardinality 5.
+ *
+ * As the example shows, usages of the macro require some type, an
+ * identifier and some code.
+ *
+ * The type that is provided is the type that will be wrapped.
+ * Do note that we expect a type with no-qualifiers and that is not a
+ * pointer type. Types passed with those kind of qualifiers will be
+ * simplified to their base type.
+ *
+ * That is, for example, \c {int*}, \c {const int}, \c {const int&},
+ * \c {int&} all counts as \c {int}.
+ *
+ * The identifier that is passed is used as the name for the newly
+ * declared type that wraps the original type.
+ *
+ * The code block that is passed will be run when an instance of the
+ * newly created wrapper type is being obtained.
+ * If the wrapper type is T, the codeblock must return a \c
+ * {std::optional<T>}.
+ * The code block should perform any check that ensures that the
+ * guarantees provided by the wrapper type holds and return a value if
+ * such is the case, otherwise returning \c {std::nullopt}.
+ *
+ * Inside the code block, the identifier \e {value} is implicitly
+ * bound to an element of the wrapped type that the instance is being
+ * built from and for which the guarantees provided by the wrapper
+ * type must hold.
+ *
+ * When a call to QDOC_REFINED_TYPEDEF is successful, a type with the
+ * provided identifier is declared.
+ *
+ * Let T be a type declared trough a call of QDOC_REFINED_TYPEDEF and
+ * W be the type that it wraps.
+ *
+ * An instance of T can be obtained by calling T::refine with an
+ * element of W.
+ *
+ * If the element of W respects the guarantees that T provides, then
+ * the call will return an optional that contains an instance of T,
+ * othewise it will return an empty optional.
+ *
+ * When an instance of T is obtained, it will wrap the element of W that
+ * was used to obtain it.
+ *
+ * The wrapped value can be accessed trough the \c {value} method.
+ *
+ * For example, considering \c {Fin5}, we could obtain an instance of
+ * it as follows:
+ *
+ * \code
+ * auto instance = *(Fin5::refine(std::size_t{1}));
+ * \endcode
+ *
+ * With that instance available we can retrieve the original value as
+ * follows:
+ *
+ * \code
+ * instance.value(); // The value 1
+ * \endcode
+ */
+
+#define QDOC_REFINED_TYPEDEF(_type, _name) \
+ struct _name { \
+ public: \
+ using wrapped_type = std::remove_reference_t<std::remove_cv_t<std::remove_pointer_t<_type>>>; \
+ \
+ inline static constexpr auto has_equality_operator_v = std::is_invocable_r_v<bool, std::equal_to<>, wrapped_type, wrapped_type>; \
+ inline static constexpr auto has_less_than_operator_v = std::is_invocable_r_v<bool, std::less_equal<>, wrapped_type, wrapped_type>; \
+ inline static constexpr auto has_strictly_less_than_operator_v = std::is_invocable_r_v<bool, std::less<>, wrapped_type, wrapped_type>; \
+ inline static constexpr auto has_greater_than_operator_v = std::is_invocable_r_v<bool, std::greater_equal<>, wrapped_type, wrapped_type>; \
+ inline static constexpr auto has_strictly_greater_than_operator_v = std::is_invocable_r_v<bool, std::greater<>, wrapped_type, wrapped_type>; \
+ \
+ public: \
+ static std::optional<_name> refine(wrapped_type value); \
+ \
+ [[nodiscard]] const wrapped_type& value() const noexcept { return _value; } \
+ \
+ _name(const _name&) = default; \
+ _name& operator=(const _name&) = default; \
+ \
+ _name(_name&&) = default; \
+ _name& operator=(_name&&) = default; \
+ \
+ operator wrapped_type() const { return _value; } \
+ \
+ public: \
+ \
+ template< \
+ typename = std::enable_if_t< \
+ has_equality_operator_v \
+ > \
+ > \
+ bool operator==(const _name& rhs) const noexcept { return _value == rhs._value; } \
+ \
+ template< \
+ typename = std::enable_if_t< \
+ has_equality_operator_v \
+ > \
+ > \
+ bool operator!=(const _name& rhs) const noexcept { return !(_value == rhs._value); } \
+ \
+ template< \
+ typename = std::enable_if_t< \
+ has_less_than_operator_v \
+ > \
+ > \
+ bool operator<=(const _name& rhs) const noexcept { return _value <= rhs._value; } \
+ \
+ \
+ template< \
+ typename = std::enable_if_t< \
+ has_strictly_less_than_operator_v \
+ > \
+ > \
+ bool operator<(const _name& rhs) const noexcept { return _value < rhs._value; } \
+ \
+ template< \
+ typename = std::enable_if_t< \
+ has_greater_than_operator_v \
+ > \
+ > \
+ bool operator>=(const _name& rhs) const noexcept { return _value >= rhs._value; } \
+ \
+ template< \
+ typename = std::enable_if_t< \
+ has_strictly_greater_than_operator_v \
+ > \
+ > \
+ bool operator>(const _name& rhs) const noexcept { return _value > rhs._value; } \
+ \
+ private: \
+ _name(wrapped_type value) : _value{std::move(value)} {} \
+ \
+ private: \
+ wrapped_type _value; \
+ }; \
+ \
+ inline std::optional<_name> _name::refine(wrapped_type value)
diff --git a/src/qdoc/qdoc/src/qdoc/boundaries/refined_typedef_members.qdocinc b/src/qdoc/qdoc/src/qdoc/boundaries/refined_typedef_members.qdocinc
new file mode 100644
index 000000000..ab956f49f
--- /dev/null
+++ b/src/qdoc/qdoc/src/qdoc/boundaries/refined_typedef_members.qdocinc
@@ -0,0 +1,162 @@
+//! [wrapped_type_documentation]
+\typealias \1::wrapped_type
+
+The type that is wrapped by this type.
+
+This type is always deprived of qualifiers and is never a pointer
+type.
+//! [wrapped_type_documentation]
+
+//! [has_equality_operator_documentation]
+\variable \1::has_equality_operator_v
+
+True when the wrapped_type can be compared for equality.
+
+When this is the case, \1 can be compared for equality and inequality.
+//! [has_equality_operator_documentation]
+
+//! [has_less_than_operator_documentation]
+\variable \1::has_less_than_operator_v
+
+True when the wrapped_type can be compared for lesserness.
+
+When this is the case, \1 can be compared for lesserness.
+//! [has_less_than_operator_documentation]
+
+//! [has_strictly_less_than_operator_documentation]
+\variable \1::has_strictly_less_than_operator_v
+
+True when the wrapped_type can be compared for strict lesserness.
+
+When this is the case, \1 can be compared for strict lesserness.
+//! [has_stricly_less_than_operator_documentation]
+
+//! [has_greater_than_operator_documentation]
+\variable \1::has_greater_than_operator_v
+
+True when the wrapped_type can be compared for greaterness.
+
+When this is the case, \1 can be compared for greaterness.
+//! [has_less_than_operator_documentation]
+
+//! [has_strictly_greater_than_operator_documentation]
+\variable \1::has_strictly_greater_than_operator_v
+
+True when the wrapped_type can be compared for strict greaterness.
+
+When this is the case, \1 can be compared for strict greaterness.
+//! [has_stricly_greater_than_operator_documentation]
+
+//! [refine_documentation]
+\fn static std::optional<\1> \1::refine(wrapped_type value)
+
+Returns an instance of \1 wrapping \a value if \a value respects the
+guarantees that are required by \1.
+
+If such is not the case, \c {std::nullopt} is returned instead.
+//! [refine_documentation]
+
+//! [value_documentation]
+\fn const wrapped_type& \1::value() const noexcept
+
+Returns a const reference to the value that is wrapped by this
+instance.
+//! [value_documentation]
+
+//! [copy_constructor_documentation]
+\fn \1::\1(const \1& other)
+
+Copy-constructs an instance of \1 from \a other.
+
+This constructor is generated by the compiler.
+//! [copy_constructor_documentation]
+
+//! [copy_assignment_documentation]
+\fn \1::operator=(const \1& other)
+
+Copy-assigns to this instance of \1 from \a other.
+
+This constructor is generated by the compiler.
+//! [copy_assignment_documentation]
+
+//! [move_constructor_documentation]
+\fn \1::\1(\1&& other)
+
+Move-constructs an instance of \1 from \a other.
+
+The only valid operations on an instance that was moved-from are
+destruction and reassignment.
+
+This constructor is generated by the compiler.
+//! [move_constructor_documentation]
+
+//! [move_assignment_documentation]
+\fn \1::operator=(\1&& other)
+
+Move-assigns to this instance of \1 from \a other.
+
+The only valid operations on an instance that was moved-from are
+destruction and reassignment.
+
+This constructor is generated by the compiler.
+//! [move_assignment_documentation]
+
+//! [conversion_documentation]
+\fn operator wrapped_type() const
+
+Converts this instance to its wrapped value.
+//! [conversion_documentation]
+
+//! [operator_equal_documentation]
+\fn bool operator=(const \1& rhs) const noexcept
+
+Returns true if the value wrapped by this instance and \a rhs compare
+equal.
+
+Returns false otherwise.
+//! [operator_equal_documentation]
+
+//! [operator_unequal_documentation]
+\fn bool operator!=(const \1& rhs) const noexcept
+
+Returns true if the value wrapped by this instance and \a rhs do not
+compare equal.
+
+Returns false otherwise.
+//! [operator_unequal_documentation]
+
+//! [operator_less_than_documentation]
+\fn bool operator<(const \1& rhs) const noexcept
+
+Returns true if the value wrapped by this instance compares less than
+the value wrapped by \a rhs.
+
+Returns false otherwise.
+//! [operator_less_than_documentation]
+
+//! [operator_less_than_or_equal_documentation]
+\fn bool operator<=(const \1& rhs) const noexcept
+
+Returns true if the value wrapped by this instance compares less than
+or equal than the value wrapped by \a rhs.
+
+Returns false otherwise.
+//! [operator_less_than_or_equal_documentation]
+
+//! [operator_greater_than_documentation]
+\fn bool operator>(const \1& rhs) const noexcept
+
+Returns true if the value wrapped by this instance compares greater
+than the value wrapped by \a rhs.
+
+Returns false otherwise.
+//! [operator_greater_than_documentation]
+
+//! [operator_greater_than_or_equal_documentation]
+\fn bool operator>=(const \1& rhs) const noexcept
+
+Returns true if the value wrapped by this instance compares greater
+than or equal or equal than the value wrapped by \a rhs.
+
+Returns false otherwise.
+//! [operator_greater_than_or_equal_documentation]
diff --git a/src/qdoc/qdoc/src/qdoc/clang/AST/LLVM_LICENSE.txt b/src/qdoc/qdoc/src/qdoc/clang/AST/LLVM_LICENSE.txt
new file mode 100644
index 000000000..fa6ac5400
--- /dev/null
+++ b/src/qdoc/qdoc/src/qdoc/clang/AST/LLVM_LICENSE.txt
@@ -0,0 +1,279 @@
+==============================================================================
+The LLVM Project is under the Apache License v2.0 with LLVM Exceptions:
+==============================================================================
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+
+---- LLVM Exceptions to the Apache 2.0 License ----
+
+As an exception, if, as a result of your compiling your source code, portions
+of this Software are embedded into an Object form of such source code, you
+may redistribute such embedded portions in such Object form without complying
+with the conditions of Sections 4(a), 4(b) and 4(d) of the License.
+
+In addition, if you combine or link compiled forms of this Software with
+software that is licensed under the GPLv2 ("Combined Software") and if a
+court of competent jurisdiction determines that the patent provision (Section
+3), the indemnity provision (Section 9) or other Section of the License
+conflicts with the conditions of the GPLv2, you may retroactively and
+prospectively choose to deem waived or otherwise exclude such Section(s) of
+the License, but only in their entirety and only with respect to the Combined
+Software.
+
+==============================================================================
+Software from third parties included in the LLVM Project:
+==============================================================================
+The LLVM Project contains third party software which is under different license
+terms. All such code will be identified clearly using at least one of two
+mechanisms:
+1) It will be in a separate directory tree with its own `LICENSE.txt` or
+ `LICENSE` file at the top containing the specific license and restrictions
+ which apply to that software, or
+2) It will contain specific license and restriction terms at the top of every
+ file.
+
+==============================================================================
+Legacy LLVM License (https://llvm.org/docs/DeveloperPolicy.html#legacy):
+==============================================================================
+University of Illinois/NCSA
+Open Source License
+
+Copyright (c) 2003-2019 University of Illinois at Urbana-Champaign.
+All rights reserved.
+
+Developed by:
+
+ LLVM Team
+
+ University of Illinois at Urbana-Champaign
+
+ http://llvm.org
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal with
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+of the Software, and to permit persons to whom the Software is furnished to do
+so, subject to the following conditions:
+
+ * Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimers.
+
+ * Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimers in the
+ documentation and/or other materials provided with the distribution.
+
+ * Neither the names of the LLVM Team, University of Illinois at
+ Urbana-Champaign, nor the names of its contributors may be used to
+ endorse or promote products derived from this Software without specific
+ prior written permission.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE
+SOFTWARE.
+
diff --git a/src/qdoc/qdoc/src/qdoc/clang/AST/QualTypeNames.h b/src/qdoc/qdoc/src/qdoc/clang/AST/QualTypeNames.h
new file mode 100644
index 000000000..c6d331ea8
--- /dev/null
+++ b/src/qdoc/qdoc/src/qdoc/clang/AST/QualTypeNames.h
@@ -0,0 +1,491 @@
+//===------- QualTypeNames.cpp - Generate Complete QualType Names ---------===//
+//
+// Part of the LLVM Project, 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
+//
+//===----------------------------------------------------------------------===//
+
+#pragma once
+
+// Those directives indirectly includes "clang/AST/Attrs.h" which
+// includes "clang/AST/Attrs.inc".
+// "clang/AST/Attrs.inc", produces some "C4267" warnings specifically
+// on MSVC 2019.
+// This in turn blocks CI integrations for configuration with that
+// compiler that treats warnings as errors.
+// As that header is not under our control, we disable the warning
+// completely when going through those includes.
+#include <QtCore/qcompilerdetection.h>
+
+QT_WARNING_PUSH
+QT_WARNING_DISABLE_MSVC(4267)
+
+#include "clang/AST/DeclTemplate.h"
+#include "clang/AST/DeclarationName.h"
+#include "clang/AST/GlobalDecl.h"
+#include "clang/AST/Mangle.h"
+
+QT_WARNING_POP
+
+#include <stdio.h>
+#include <memory>
+
+namespace clang {
+
+namespace TypeName {
+
+inline QualType getFullyQualifiedType(QualType QT, const ASTContext &Ctx,
+ bool WithGlobalNsPrefix);
+
+/// Create a NestedNameSpecifier for Namesp and its enclosing
+/// scopes.
+///
+/// \param[in] Ctx - the AST Context to be used.
+/// \param[in] Namesp - the NamespaceDecl for which a NestedNameSpecifier
+/// is requested.
+/// \param[in] WithGlobalNsPrefix - Indicate whether the global namespace
+/// specifier "::" should be prepended or not.
+static inline NestedNameSpecifier *createNestedNameSpecifier(
+ const ASTContext &Ctx,
+ const NamespaceDecl *Namesp,
+ bool WithGlobalNsPrefix);
+
+/// Create a NestedNameSpecifier for TagDecl and its enclosing
+/// scopes.
+///
+/// \param[in] Ctx - the AST Context to be used.
+/// \param[in] TD - the TagDecl for which a NestedNameSpecifier is
+/// requested.
+/// \param[in] FullyQualify - Convert all template arguments into fully
+/// qualified names.
+/// \param[in] WithGlobalNsPrefix - Indicate whether the global namespace
+/// specifier "::" should be prepended or not.
+static inline NestedNameSpecifier *createNestedNameSpecifier(
+ const ASTContext &Ctx, const TypeDecl *TD,
+ bool FullyQualify, bool WithGlobalNsPrefix);
+
+static inline NestedNameSpecifier *createNestedNameSpecifierForScopeOf(
+ const ASTContext &Ctx, const Decl *decl,
+ bool FullyQualified, bool WithGlobalNsPrefix);
+
+static inline NestedNameSpecifier *getFullyQualifiedNestedNameSpecifier(
+ const ASTContext &Ctx, NestedNameSpecifier *scope, bool WithGlobalNsPrefix);
+
+static inline bool getFullyQualifiedTemplateName(const ASTContext &Ctx,
+ TemplateName &TName,
+ bool WithGlobalNsPrefix) {
+ bool Changed = false;
+ NestedNameSpecifier *NNS = nullptr;
+
+ TemplateDecl *ArgTDecl = TName.getAsTemplateDecl();
+ // ArgTDecl won't be NULL because we asserted that this isn't a
+ // dependent context very early in the call chain.
+ assert(ArgTDecl != nullptr);
+ QualifiedTemplateName *QTName = TName.getAsQualifiedTemplateName();
+
+ if (QTName && !QTName->hasTemplateKeyword()) {
+ NNS = QTName->getQualifier();
+ NestedNameSpecifier *QNNS = getFullyQualifiedNestedNameSpecifier(
+ Ctx, NNS, WithGlobalNsPrefix);
+ if (QNNS != NNS) {
+ Changed = true;
+ NNS = QNNS;
+ } else {
+ NNS = nullptr;
+ }
+ } else {
+ NNS = createNestedNameSpecifierForScopeOf(
+ Ctx, ArgTDecl, true, WithGlobalNsPrefix);
+ }
+ if (NNS) {
+ TemplateName UnderlyingTN(ArgTDecl);
+ if (UsingShadowDecl *USD = TName.getAsUsingShadowDecl())
+ UnderlyingTN = TemplateName(USD);
+ TName =
+ Ctx.getQualifiedTemplateName(NNS,
+ /*TemplateKeyword=*/false, UnderlyingTN);
+ Changed = true;
+ }
+ return Changed;
+}
+
+static inline bool getFullyQualifiedTemplateArgument(const ASTContext &Ctx,
+ TemplateArgument &Arg,
+ bool WithGlobalNsPrefix) {
+ bool Changed = false;
+
+ // Note: we do not handle TemplateArgument::Expression, to replace it
+ // we need the information for the template instance decl.
+
+ if (Arg.getKind() == TemplateArgument::Template) {
+ TemplateName TName = Arg.getAsTemplate();
+ Changed = getFullyQualifiedTemplateName(Ctx, TName, WithGlobalNsPrefix);
+ if (Changed) {
+ Arg = TemplateArgument(TName);
+ }
+ } else if (Arg.getKind() == TemplateArgument::Type) {
+ QualType SubTy = Arg.getAsType();
+ // Check if the type needs more desugaring and recurse.
+ QualType QTFQ = getFullyQualifiedType(SubTy, Ctx, WithGlobalNsPrefix);
+ if (QTFQ != SubTy) {
+ Arg = TemplateArgument(QTFQ);
+ Changed = true;
+ }
+ }
+ return Changed;
+}
+
+static inline const Type *getFullyQualifiedTemplateType(const ASTContext &Ctx,
+ const Type *TypePtr,
+ bool WithGlobalNsPrefix) {
+ // DependentTemplateTypes exist within template declarations and
+ // definitions. Therefore we shouldn't encounter them at the end of
+ // a translation unit. If we do, the caller has made an error.
+ assert(!isa<DependentTemplateSpecializationType>(TypePtr));
+ // In case of template specializations, iterate over the arguments
+ // and fully qualify them as well.
+ if (const auto *TST = dyn_cast<const TemplateSpecializationType>(TypePtr)) {
+ bool MightHaveChanged = false;
+ SmallVector<TemplateArgument, 4> FQArgs;
+ // Cheap to copy and potentially modified by
+ // getFullyQualifedTemplateArgument.
+ for (TemplateArgument Arg : TST->template_arguments()) {
+ MightHaveChanged |= getFullyQualifiedTemplateArgument(
+ Ctx, Arg, WithGlobalNsPrefix);
+ FQArgs.push_back(Arg);
+ }
+
+ // If a fully qualified arg is different from the unqualified arg,
+ // allocate new type in the AST.
+ if (MightHaveChanged) {
+ QualType QT = Ctx.getTemplateSpecializationType(
+ TST->getTemplateName(), FQArgs,
+ TST->getCanonicalTypeInternal());
+ // getTemplateSpecializationType returns a fully qualified
+ // version of the specialization itself, so no need to qualify
+ // it.
+ return QT.getTypePtr();
+ }
+ } else if (const auto *TSTRecord = dyn_cast<const RecordType>(TypePtr)) {
+ // We are asked to fully qualify and we have a Record Type,
+ // which can point to a template instantiation with no sugar in any of
+ // its template argument, however we still need to fully qualify them.
+
+ if (const auto *TSTDecl =
+ dyn_cast<ClassTemplateSpecializationDecl>(TSTRecord->getDecl())) {
+ const TemplateArgumentList &TemplateArgs = TSTDecl->getTemplateArgs();
+
+ bool MightHaveChanged = false;
+ SmallVector<TemplateArgument, 4> FQArgs;
+ for (unsigned int I = 0, E = TemplateArgs.size(); I != E; ++I) {
+ // cheap to copy and potentially modified by
+ // getFullyQualifedTemplateArgument
+ TemplateArgument Arg(TemplateArgs[I]);
+ MightHaveChanged |= getFullyQualifiedTemplateArgument(
+ Ctx, Arg, WithGlobalNsPrefix);
+ FQArgs.push_back(Arg);
+ }
+
+ // If a fully qualified arg is different from the unqualified arg,
+ // allocate new type in the AST.
+ if (MightHaveChanged) {
+ TemplateName TN(TSTDecl->getSpecializedTemplate());
+ QualType QT = Ctx.getTemplateSpecializationType(
+ TN, FQArgs,
+ TSTRecord->getCanonicalTypeInternal());
+ // getTemplateSpecializationType returns a fully qualified
+ // version of the specialization itself, so no need to qualify
+ // it.
+ return QT.getTypePtr();
+ }
+ }
+ }
+ return TypePtr;
+}
+
+static inline NestedNameSpecifier *createOuterNNS(const ASTContext &Ctx, const Decl *D,
+ bool FullyQualify,
+ bool WithGlobalNsPrefix) {
+ const DeclContext *DC = D->getDeclContext();
+ if (const auto *NS = dyn_cast<NamespaceDecl>(DC)) {
+ while (NS && NS->isInline()) {
+ // Ignore inline namespace;
+ NS = dyn_cast<NamespaceDecl>(NS->getDeclContext());
+ }
+ if (NS && NS->getDeclName()) {
+ return createNestedNameSpecifier(Ctx, NS, WithGlobalNsPrefix);
+ }
+ return nullptr; // no starting '::', no anonymous
+ } else if (const auto *TD = dyn_cast<TagDecl>(DC)) {
+ return createNestedNameSpecifier(Ctx, TD, FullyQualify, WithGlobalNsPrefix);
+ } else if (const auto *TDD = dyn_cast<TypedefNameDecl>(DC)) {
+ return createNestedNameSpecifier(
+ Ctx, TDD, FullyQualify, WithGlobalNsPrefix);
+ } else if (WithGlobalNsPrefix && DC->isTranslationUnit()) {
+ return NestedNameSpecifier::GlobalSpecifier(Ctx);
+ }
+ return nullptr; // no starting '::' if |WithGlobalNsPrefix| is false
+}
+
+/// Return a fully qualified version of this name specifier.
+static inline NestedNameSpecifier *getFullyQualifiedNestedNameSpecifier(
+ const ASTContext &Ctx, NestedNameSpecifier *Scope,
+ bool WithGlobalNsPrefix) {
+ switch (Scope->getKind()) {
+ case NestedNameSpecifier::Global:
+ // Already fully qualified
+ return Scope;
+ case NestedNameSpecifier::Namespace:
+ return TypeName::createNestedNameSpecifier(
+ Ctx, Scope->getAsNamespace(), WithGlobalNsPrefix);
+ case NestedNameSpecifier::NamespaceAlias:
+ // Namespace aliases are only valid for the duration of the
+ // scope where they were introduced, and therefore are often
+ // invalid at the end of the TU. So use the namespace name more
+ // likely to be valid at the end of the TU.
+ return TypeName::createNestedNameSpecifier(
+ Ctx,
+ Scope->getAsNamespaceAlias()->getNamespace()->getCanonicalDecl(),
+ WithGlobalNsPrefix);
+ case NestedNameSpecifier::Identifier:
+ // A function or some other construct that makes it un-namable
+ // at the end of the TU. Skip the current component of the name,
+ // but use the name of it's prefix.
+ return getFullyQualifiedNestedNameSpecifier(
+ Ctx, Scope->getPrefix(), WithGlobalNsPrefix);
+ case NestedNameSpecifier::Super:
+ case NestedNameSpecifier::TypeSpec:
+ case NestedNameSpecifier::TypeSpecWithTemplate: {
+ const Type *Type = Scope->getAsType();
+ // Find decl context.
+ const TagDecl *TD = nullptr;
+ if (const TagType *TagDeclType = Type->getAs<TagType>()) {
+ TD = TagDeclType->getDecl();
+ } else {
+ TD = Type->getAsCXXRecordDecl();
+ }
+ if (TD) {
+ return TypeName::createNestedNameSpecifier(Ctx, TD,
+ true /*FullyQualified*/,
+ WithGlobalNsPrefix);
+ } else if (const auto *TDD = dyn_cast<TypedefType>(Type)) {
+ return TypeName::createNestedNameSpecifier(Ctx, TDD->getDecl(),
+ true /*FullyQualified*/,
+ WithGlobalNsPrefix);
+ }
+ return Scope;
+ }
+ }
+ llvm_unreachable("bad NNS kind");
+}
+
+/// Create a nested name specifier for the declaring context of
+/// the type.
+static inline NestedNameSpecifier *createNestedNameSpecifierForScopeOf(
+ const ASTContext &Ctx, const Decl *Decl,
+ bool FullyQualified, bool WithGlobalNsPrefix) {
+ assert(Decl);
+
+ const DeclContext *DC = Decl->getDeclContext()->getRedeclContext();
+ const auto *Outer = dyn_cast_or_null<NamedDecl>(DC);
+ const auto *OuterNS = dyn_cast_or_null<NamespaceDecl>(DC);
+ if (Outer && !(OuterNS && OuterNS->isAnonymousNamespace())) {
+ if (OuterNS) {
+ return createNestedNameSpecifier(Ctx, OuterNS, WithGlobalNsPrefix);
+ } else if (const auto *TD = dyn_cast<TagDecl>(Outer)) {
+ return createNestedNameSpecifier(
+ Ctx, TD, FullyQualified, WithGlobalNsPrefix);
+ } else if (isa<TranslationUnitDecl>(Outer)) {
+ // Context is the TU. Nothing needs to be done.
+ return nullptr;
+ } else {
+ // Decl's context was neither the TU, a namespace, nor a
+ // TagDecl, which means it is a type local to a scope, and not
+ // accessible at the end of the TU.
+ return nullptr;
+ }
+ } else if (WithGlobalNsPrefix && DC->isTranslationUnit()) {
+ return NestedNameSpecifier::GlobalSpecifier(Ctx);
+ }
+ return nullptr;
+}
+
+/// Create a nested name specifier for the declaring context of
+/// the type.
+static inline NestedNameSpecifier *createNestedNameSpecifierForScopeOf(
+ const ASTContext &Ctx, const Type *TypePtr,
+ bool FullyQualified, bool WithGlobalNsPrefix) {
+ if (!TypePtr) return nullptr;
+
+ Decl *Decl = nullptr;
+ // There are probably other cases ...
+ if (const auto *TDT = dyn_cast<TypedefType>(TypePtr)) {
+ Decl = TDT->getDecl();
+ } else if (const auto *TagDeclType = dyn_cast<TagType>(TypePtr)) {
+ Decl = TagDeclType->getDecl();
+ } else if (const auto *TST = dyn_cast<TemplateSpecializationType>(TypePtr)) {
+ Decl = TST->getTemplateName().getAsTemplateDecl();
+ } else {
+ Decl = TypePtr->getAsCXXRecordDecl();
+ }
+
+ if (!Decl) return nullptr;
+
+ return createNestedNameSpecifierForScopeOf(
+ Ctx, Decl, FullyQualified, WithGlobalNsPrefix);
+}
+
+inline NestedNameSpecifier *createNestedNameSpecifier(const ASTContext &Ctx,
+ const NamespaceDecl *Namespace,
+ bool WithGlobalNsPrefix) {
+ while (Namespace && Namespace->isInline()) {
+ // Ignore inline namespace;
+ Namespace = dyn_cast<NamespaceDecl>(Namespace->getDeclContext());
+ }
+ if (!Namespace) return nullptr;
+
+ bool FullyQualified = true; // doesn't matter, DeclContexts are namespaces
+ return NestedNameSpecifier::Create(
+ Ctx,
+ createOuterNNS(Ctx, Namespace, FullyQualified, WithGlobalNsPrefix),
+ Namespace);
+}
+
+inline NestedNameSpecifier *createNestedNameSpecifier(const ASTContext &Ctx,
+ const TypeDecl *TD,
+ bool FullyQualify,
+ bool WithGlobalNsPrefix) {
+ const Type *TypePtr = TD->getTypeForDecl();
+ if (isa<const TemplateSpecializationType>(TypePtr) ||
+ isa<const RecordType>(TypePtr)) {
+ // We are asked to fully qualify and we have a Record Type (which
+ // may point to a template specialization) or Template
+ // Specialization Type. We need to fully qualify their arguments.
+
+ TypePtr = getFullyQualifiedTemplateType(Ctx, TypePtr, WithGlobalNsPrefix);
+ }
+
+ return NestedNameSpecifier::Create(
+ Ctx, createOuterNNS(Ctx, TD, FullyQualify, WithGlobalNsPrefix),
+ false /*No TemplateKeyword*/, TypePtr);
+}
+
+/// Return the fully qualified type, including fully-qualified
+/// versions of any template parameters.
+inline QualType getFullyQualifiedType(QualType QT, const ASTContext &Ctx,
+ bool WithGlobalNsPrefix = false) {
+ // In case of myType* we need to strip the pointer first, fully
+ // qualify and attach the pointer once again.
+ if (isa<PointerType>(QT.getTypePtr())) {
+ // Get the qualifiers.
+ Qualifiers Quals = QT.getQualifiers();
+ QT = getFullyQualifiedType(QT->getPointeeType(), Ctx, WithGlobalNsPrefix);
+ QT = Ctx.getPointerType(QT);
+ // Add back the qualifiers.
+ QT = Ctx.getQualifiedType(QT, Quals);
+ return QT;
+ }
+
+ if (auto *MPT = dyn_cast<MemberPointerType>(QT.getTypePtr())) {
+ // Get the qualifiers.
+ Qualifiers Quals = QT.getQualifiers();
+ // Fully qualify the pointee and class types.
+ QT = getFullyQualifiedType(QT->getPointeeType(), Ctx, WithGlobalNsPrefix);
+ QualType Class = getFullyQualifiedType(QualType(MPT->getClass(), 0), Ctx,
+ WithGlobalNsPrefix);
+ QT = Ctx.getMemberPointerType(QT, Class.getTypePtr());
+ // Add back the qualifiers.
+ QT = Ctx.getQualifiedType(QT, Quals);
+ return QT;
+ }
+
+ // In case of myType& we need to strip the reference first, fully
+ // qualify and attach the reference once again.
+ if (isa<ReferenceType>(QT.getTypePtr())) {
+ // Get the qualifiers.
+ bool IsLValueRefTy = isa<LValueReferenceType>(QT.getTypePtr());
+ Qualifiers Quals = QT.getQualifiers();
+ QT = getFullyQualifiedType(QT->getPointeeType(), Ctx, WithGlobalNsPrefix);
+ // Add the r- or l-value reference type back to the fully
+ // qualified one.
+ if (IsLValueRefTy)
+ QT = Ctx.getLValueReferenceType(QT);
+ else
+ QT = Ctx.getRValueReferenceType(QT);
+ // Add back the qualifiers.
+ QT = Ctx.getQualifiedType(QT, Quals);
+ return QT;
+ }
+
+ // Remove the part of the type related to the type being a template
+ // parameter (we won't report it as part of the 'type name' and it
+ // is actually make the code below to be more complex (to handle
+ // those)
+ while (isa<SubstTemplateTypeParmType>(QT.getTypePtr())) {
+ // Get the qualifiers.
+ Qualifiers Quals = QT.getQualifiers();
+
+ QT = cast<SubstTemplateTypeParmType>(QT.getTypePtr())->desugar();
+
+ // Add back the qualifiers.
+ QT = Ctx.getQualifiedType(QT, Quals);
+ }
+
+ NestedNameSpecifier *Prefix = nullptr;
+ // Local qualifiers are attached to the QualType outside of the
+ // elaborated type. Retrieve them before descending into the
+ // elaborated type.
+ Qualifiers PrefixQualifiers = QT.getLocalQualifiers();
+ QT = QualType(QT.getTypePtr(), 0);
+#if LIBCLANG_VERSION_MAJOR >= 18
+ constexpr ElaboratedTypeKeyword ETK_None = ElaboratedTypeKeyword::None;
+#endif
+ ElaboratedTypeKeyword Keyword = ETK_None;
+ if (const auto *ETypeInput = dyn_cast<ElaboratedType>(QT.getTypePtr())) {
+ QT = ETypeInput->getNamedType();
+ assert(!QT.hasLocalQualifiers());
+ Keyword = ETypeInput->getKeyword();
+ }
+
+ // We don't consider the alias introduced by `using a::X` as a new type.
+ // The qualified name is still a::X.
+ if (const auto *UT = QT->getAs<UsingType>()) {
+ QT = Ctx.getQualifiedType(UT->getUnderlyingType(), PrefixQualifiers);
+ return getFullyQualifiedType(QT, Ctx, WithGlobalNsPrefix);
+ }
+
+ // Create a nested name specifier if needed.
+ Prefix = createNestedNameSpecifierForScopeOf(Ctx, QT.getTypePtr(),
+ true /*FullyQualified*/,
+ WithGlobalNsPrefix);
+
+ // In case of template specializations iterate over the arguments and
+ // fully qualify them as well.
+ if (isa<const TemplateSpecializationType>(QT.getTypePtr()) ||
+ isa<const RecordType>(QT.getTypePtr())) {
+ // We are asked to fully qualify and we have a Record Type (which
+ // may point to a template specialization) or Template
+ // Specialization Type. We need to fully qualify their arguments.
+
+ const Type *TypePtr = getFullyQualifiedTemplateType(
+ Ctx, QT.getTypePtr(), WithGlobalNsPrefix);
+ QT = QualType(TypePtr, 0);
+ }
+ if (Prefix || Keyword != ETK_None) {
+ QT = Ctx.getElaboratedType(Keyword, Prefix, QT);
+ }
+ QT = Ctx.getQualifiedType(QT, PrefixQualifiers);
+ return QT;
+}
+
+inline std::string getFullyQualifiedName(QualType QT,
+ const ASTContext &Ctx,
+ const PrintingPolicy &Policy,
+ bool WithGlobalNsPrefix = false) {
+ QualType FQQT = getFullyQualifiedType(QT, Ctx, WithGlobalNsPrefix);
+ return FQQT.getAsString(Policy);
+}
+
+} // end namespace TypeName
+} // end namespace clang
diff --git a/src/qdoc/qdoc/src/qdoc/clang/AST/qt_attribution.json b/src/qdoc/qdoc/src/qdoc/clang/AST/qt_attribution.json
new file mode 100644
index 000000000..13219767d
--- /dev/null
+++ b/src/qdoc/qdoc/src/qdoc/clang/AST/qt_attribution.json
@@ -0,0 +1,20 @@
+[
+ {
+ "Id": "llvm_clang_typename_namespace",
+ "Name": "QualTypeNames",
+ "QDocModule": "qdoc",
+ "QtUsage": "Used to have access to a version of clang::TypeName::getFullyQualifiedName that does not expand templates to an instance.",
+ "QtParts": [
+ "tools"
+ ],
+ "Files": "QualTypeNames.h",
+
+ "Description": "A part of Clang's C++ API that deals with fully qualifying types.",
+ "Homepage": "https://github.com/llvm/llvm-project",
+ "Version": "16.0",
+ "License": "Apache License 2.0",
+ "LicenseId": "Apache-2.0 WITH LLVM-exception",
+ "LicenseFile": "LLVM_LICENSE.txt",
+ "Copyright": "Copyright assigned to LLVM project contributors."
+ }
+]
diff --git a/src/qdoc/clangcodeparser.cpp b/src/qdoc/qdoc/src/qdoc/clangcodeparser.cpp
index d61b24270..a414b55a3 100644
--- a/src/qdoc/clangcodeparser.cpp
+++ b/src/qdoc/qdoc/src/qdoc/clangcodeparser.cpp
@@ -1,43 +1,8 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the tools applications of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "clangcodeparser.h"
+#include "cppcodeparser.h"
#include "access.h"
#include "classnode.h"
@@ -49,30 +14,72 @@
#include "propertynode.h"
#include "qdocdatabase.h"
#include "typedefnode.h"
-#include "utilities.h"
#include "variablenode.h"
+#include "utilities.h"
#include <QtCore/qdebug.h>
#include <QtCore/qelapsedtimer.h>
#include <QtCore/qfile.h>
#include <QtCore/qscopedvaluerollback.h>
#include <QtCore/qtemporarydir.h>
+#include <QtCore/qtextstream.h>
+#include <QtCore/qvarlengtharray.h>
#include <clang-c/Index.h>
+#include <clang/AST/Decl.h>
+#include <clang/AST/DeclFriend.h>
+#include <clang/AST/DeclTemplate.h>
+#include <clang/AST/Expr.h>
+#include <clang/AST/Type.h>
+#include <clang/AST/TypeLoc.h>
+#include <clang/Basic/SourceLocation.h>
+#include <clang/Frontend/ASTUnit.h>
+#include <clang/Lex/Lexer.h>
+#include <llvm/Support/Casting.h>
+
+#include "clang/AST/QualTypeNames.h"
+#include "template_declaration.h"
+
#include <cstdio>
QT_BEGIN_NAMESPACE
+struct CompilationIndex {
+ CXIndex index = nullptr;
+
+ operator CXIndex() {
+ return index;
+ }
+
+ ~CompilationIndex() {
+ clang_disposeIndex(index);
+ }
+};
+
+struct TranslationUnit {
+ CXTranslationUnit tu = nullptr;
+
+ operator CXTranslationUnit() {
+ return tu;
+ }
+
+ operator bool() {
+ return tu;
+ }
+
+ ~TranslationUnit() {
+ clang_disposeTranslationUnit(tu);
+ }
+};
+
// We're printing diagnostics in ClangCodeParser::printDiagnostics,
// so avoid clang itself printing them.
static const auto kClangDontDisplayDiagnostics = 0;
static CXTranslationUnit_Flags flags_ = static_cast<CXTranslationUnit_Flags>(0);
-static CXIndex index_ = nullptr;
-QByteArray ClangCodeParser::s_fn;
-constexpr const char *fnDummyFileName = "/fn_dummyfile.cpp";
+constexpr const char fnDummyFileName[] = "/fn_dummyfile.cpp";
#ifndef QT_NO_DEBUG_STREAM
template<class T>
@@ -93,6 +100,206 @@ static QDebug operator<<(QDebug debug, const std::vector<T> &v)
}
#endif // !QT_NO_DEBUG_STREAM
+static void printDiagnostics(const CXTranslationUnit &translationUnit)
+{
+ if (!lcQdocClang().isDebugEnabled())
+ return;
+
+ static const auto displayOptions = CXDiagnosticDisplayOptions::CXDiagnostic_DisplaySourceLocation
+ | CXDiagnosticDisplayOptions::CXDiagnostic_DisplayColumn
+ | CXDiagnosticDisplayOptions::CXDiagnostic_DisplayOption;
+
+ for (unsigned i = 0, numDiagnostics = clang_getNumDiagnostics(translationUnit); i < numDiagnostics; ++i) {
+ auto diagnostic = clang_getDiagnostic(translationUnit, i);
+ auto formattedDiagnostic = clang_formatDiagnostic(diagnostic, displayOptions);
+ qCDebug(lcQdocClang) << clang_getCString(formattedDiagnostic);
+ clang_disposeString(formattedDiagnostic);
+ clang_disposeDiagnostic(diagnostic);
+ }
+}
+
+/*!
+ * Returns the underlying Decl that \a cursor represents.
+ *
+ * This can be used to drop back down from a LibClang's CXCursor to
+ * the underlying C++ AST that Clang provides.
+ *
+ * It should be used when LibClang does not expose certain
+ * functionalities that are available in the C++ AST.
+ *
+ * The CXCursor should represent a declaration. Usages of this
+ * function on CXCursors that do not represent a declaration may
+ * produce undefined results.
+ */
+static const clang::Decl* get_cursor_declaration(CXCursor cursor) {
+ assert(clang_isDeclaration(clang_getCursorKind(cursor)));
+
+ return static_cast<const clang::Decl*>(cursor.data[0]);
+}
+
+
+/*!
+ * Returns a string representing the name of \a type as if it was
+ * referred to at the end of the translation unit that it was parsed
+ * from.
+ *
+ * For example, given the following code:
+ *
+ * \code
+ * namespace foo {
+ * template<typename T>
+ * struct Bar {
+ * using Baz = const T&;
+ *
+ * void bam(Baz);
+ * };
+ * }
+ * \endcode
+ *
+ * Given a parsed translation unit and an AST node, say \e {decl},
+ * representing the parameter declaration of the first argument of \c {bam},
+ * calling \c{get_fully_qualified_name(decl->getType(), * decl->getASTContext())}
+ * would result in the string \c {foo::Bar<T>::Baz}.
+ *
+ * This should generally be used every time the stringified
+ * representation of a type is acquired as part of parsing with Clang,
+ * so as to ensure a consistent behavior and output.
+ */
+static std::string get_fully_qualified_type_name(clang::QualType type, const clang::ASTContext& declaration_context) {
+ return clang::TypeName::getFullyQualifiedName(
+ type,
+ declaration_context,
+ declaration_context.getPrintingPolicy()
+ );
+}
+
+/*
+ * Retrieves expression as written in the original source code.
+ *
+ * declaration_context should be the ASTContext of the declaration
+ * from which the expression was extracted from.
+ *
+ * If the expression contains a leading equal sign it will be removed.
+ *
+ * Leading and trailing spaces will be similarly removed from the expression.
+ */
+static std::string get_expression_as_string(const clang::Expr* expression, const clang::ASTContext& declaration_context) {
+ QString default_value = QString::fromStdString(clang::Lexer::getSourceText(
+ clang::CharSourceRange::getTokenRange(expression->getSourceRange()),
+ declaration_context.getSourceManager(),
+ declaration_context.getLangOpts()
+ ).str());
+
+ if (default_value.startsWith("="))
+ default_value.remove(0, 1);
+
+ default_value = default_value.trimmed();
+
+ return default_value.toStdString();
+}
+
+/*
+ * Retrieves the default value of the passed in type template parameter as a string.
+ *
+ * The default value of a type template parameter is always a type,
+ * and its stringified representation will be return as the fully
+ * qualified version of the type.
+ *
+ * If the parameter has no default value the empty string will be returned.
+ */
+static std::string get_default_value_initializer_as_string(const clang::TemplateTypeParmDecl* parameter) {
+ return (parameter && parameter->hasDefaultArgument()) ?
+ get_fully_qualified_type_name(parameter->getDefaultArgument(), parameter->getASTContext()) :
+ "";
+
+}
+
+/*
+ * Retrieves the default value of the passed in non-type template parameter as a string.
+ *
+ * The default value of a non-type template parameter is an expression
+ * and its stringified representation will be return as it was written
+ * in the original code.
+ *
+ * If the parameter as no default value the empty string will be returned.
+ */
+static std::string get_default_value_initializer_as_string(const clang::NonTypeTemplateParmDecl* parameter) {
+ return (parameter && parameter->hasDefaultArgument()) ?
+ get_expression_as_string(parameter->getDefaultArgument(), parameter->getASTContext()) : "";
+
+}
+
+/*
+ * Retrieves the default value of the passed in template template parameter as a string.
+ *
+ * The default value of a template template parameter is a template
+ * name and its stringified representation will be returned as a fully
+ * qualified version of that name.
+ *
+ * If the parameter as no default value the empty string will be returned.
+ */
+static std::string get_default_value_initializer_as_string(const clang::TemplateTemplateParmDecl* parameter) {
+ std::string default_value{};
+
+ if (parameter && parameter->hasDefaultArgument()) {
+ const clang::TemplateName template_name = parameter->getDefaultArgument().getArgument().getAsTemplate();
+
+ llvm::raw_string_ostream ss{default_value};
+ template_name.print(ss, parameter->getASTContext().getPrintingPolicy(), clang::TemplateName::Qualified::Fully);
+ }
+
+ return default_value;
+}
+
+/*
+ * Retrieves the default value of the passed in function parameter as
+ * a string.
+ *
+ * The default value of a function parameter is an expression and its
+ * stringified representation will be returned as it was written in
+ * the original code.
+ *
+ * If the parameter as no default value or Clang was not able to yet
+ * parse it at this time the empty string will be returned.
+ */
+static std::string get_default_value_initializer_as_string(const clang::ParmVarDecl* parameter) {
+ if (!parameter || !parameter->hasDefaultArg() || parameter->hasUnparsedDefaultArg())
+ return "";
+
+ return get_expression_as_string(
+ parameter->hasUninstantiatedDefaultArg() ? parameter->getUninstantiatedDefaultArg() : parameter->getDefaultArg(),
+ parameter->getASTContext()
+ );
+}
+
+/*
+ * Retrieves the default value of the passed in declaration, based on
+ * its concrete type, as a string.
+ *
+ * If the declaration is a nullptr or the concrete type of the
+ * declaration is not a supported one, the returned string will be the
+ * empty string.
+ */
+static std::string get_default_value_initializer_as_string(const clang::NamedDecl* declaration) {
+ if (!declaration) return "";
+
+ if (auto type_template_parameter = llvm::dyn_cast<clang::TemplateTypeParmDecl>(declaration))
+ return get_default_value_initializer_as_string(type_template_parameter);
+
+ if (auto non_type_template_parameter = llvm::dyn_cast<clang::NonTypeTemplateParmDecl>(declaration))
+ return get_default_value_initializer_as_string(non_type_template_parameter);
+
+ if (auto template_template_parameter = llvm::dyn_cast<clang::TemplateTemplateParmDecl>(declaration)) {
+ return get_default_value_initializer_as_string(template_template_parameter);
+ }
+
+ if (auto function_parameter = llvm::dyn_cast<clang::ParmVarDecl>(declaration)) {
+ return get_default_value_initializer_as_string(function_parameter);
+ }
+
+ return "";
+}
+
/*!
Call clang_visitChildren on the given cursor with the lambda as a callback
T can be any functor that is callable with a CXCursor parameter and returns a CXChildVisitResult
@@ -118,52 +325,76 @@ static QString fromCXString(CXString &&string)
return ret;
}
-static QString templateDecl(CXCursor cursor);
-
-/*!
- Returns a list of template parameters at \a cursor.
-*/
-static QStringList getTemplateParameters(CXCursor cursor)
-{
- QStringList parameters;
- visitChildrenLambda(cursor, [&parameters](CXCursor cur) {
- QString name = fromCXString(clang_getCursorSpelling(cur));
- QString type;
-
- switch (clang_getCursorKind(cur)) {
- case CXCursor_TemplateTypeParameter:
- type = QStringLiteral("typename");
- break;
- case CXCursor_NonTypeTemplateParameter:
- type = fromCXString(clang_getTypeSpelling(clang_getCursorType(cur)));
- // Hack: Omit QtPrivate template parameters from public documentation
- if (type.startsWith(QLatin1String("QtPrivate")))
- return CXChildVisit_Continue;
- break;
- case CXCursor_TemplateTemplateParameter:
- type = templateDecl(cur) + QLatin1String(" class");
- break;
- default:
- return CXChildVisit_Continue;
+/*
+ * Returns an intermediate representation that models the the given
+ * template declaration.
+ */
+static RelaxedTemplateDeclaration get_template_declaration(const clang::TemplateDecl* template_declaration) {
+ assert(template_declaration);
+
+ RelaxedTemplateDeclaration template_declaration_ir{};
+
+ auto template_parameters = template_declaration->getTemplateParameters();
+ for (auto template_parameter : template_parameters->asArray()) {
+ auto kind{RelaxedTemplateParameter::Kind::TypeTemplateParameter};
+ std::string type{};
+
+ if (auto non_type_template_parameter = llvm::dyn_cast<clang::NonTypeTemplateParmDecl>(template_parameter)) {
+ kind = RelaxedTemplateParameter::Kind::NonTypeTemplateParameter;
+ type = get_fully_qualified_type_name(non_type_template_parameter->getType(), non_type_template_parameter->getASTContext());
+
+ // REMARK: QDoc uses this information to match a user
+ // provided documentation (for example from an "\fn"
+ // command) with a `Node` that was extracted from the
+ // code-base.
+ //
+ // Due to how QDoc obtains an AST for documentation that
+ // is provided by the user, there might be a mismatch in
+ // the type of certain non type template parameters.
+ //
+ // QDoc generally builds a fake out-of-line definition for
+ // a callable provided through an "\fn" command, when it
+ // needs to match it.
+ // In that context, certain type names may be dependent
+ // names, while they may not be when the element they
+ // represent is extracted from the code-base.
+ //
+ // This in turn makes their stringified representation
+ // different in the two contextes, as a dependent name may
+ // require the "typename" keyword to precede it.
+ //
+ // Since QDoc uses a very simplified model, and it
+ // generally doesn't need care about the exact name
+ // resolution rules for C++, since it passes by
+ // Clang-validated data, we remove the "typename" keyword
+ // if it prefixes the type representation, so that it
+ // doesn't impact the matching procedure..
+
+ // KLUDGE: Waiting for C++20 to avoid the conversion.
+ // Doesn't really impact performance in a
+ // meaningful way so it can be kept while waiting.
+ if (QString::fromStdString(type).startsWith("typename ")) type.erase(0, std::string("typename ").size());
}
- if (!name.isEmpty())
- name.prepend(QLatin1Char(' '));
-
- parameters << type + name;
- return CXChildVisit_Continue;
- });
+ auto template_template_parameter = llvm::dyn_cast<clang::TemplateTemplateParmDecl>(template_parameter);
+ if (template_template_parameter) kind = RelaxedTemplateParameter::Kind::TemplateTemplateParameter;
- return parameters;
-}
+ template_declaration_ir.parameters.push_back({
+ kind,
+ template_parameter->isTemplateParameterPack(),
+ {
+ type,
+ template_parameter->getNameAsString(),
+ get_default_value_initializer_as_string(template_parameter)
+ },
+ (template_template_parameter ?
+ std::optional<TemplateDeclarationStorage>(TemplateDeclarationStorage{
+ get_template_declaration(template_template_parameter).parameters
+ }) : std::nullopt)
+ });
+ }
-/*!
- Gets the template declaration at specified \a cursor.
- */
-static QString templateDecl(CXCursor cursor)
-{
- QStringList params = getTemplateParameters(cursor);
- return QLatin1String("template <") + params.join(QLatin1String(", ")) + QLatin1Char('>');
+ return template_declaration_ir;
}
/*!
@@ -200,6 +431,44 @@ static Access fromCX_CXXAccessSpecifier(CX_CXXAccessSpecifier spec)
/*!
Returns the spelling in the file for a source range
*/
+
+struct FileCacheEntry
+{
+ QByteArray fileName;
+ QByteArray content;
+};
+
+static inline QString fromCache(const QByteArray &cache,
+ unsigned int offset1, unsigned int offset2)
+{
+ return QString::fromUtf8(cache.mid(offset1, offset2 - offset1));
+}
+
+static QString readFile(CXFile cxFile, unsigned int offset1, unsigned int offset2)
+{
+ using FileCache = QList<FileCacheEntry>;
+ static FileCache cache;
+
+ CXString cxFileName = clang_getFileName(cxFile);
+ const QByteArray fileName = clang_getCString(cxFileName);
+ clang_disposeString(cxFileName);
+
+ for (const auto &entry : std::as_const(cache)) {
+ if (fileName == entry.fileName)
+ return fromCache(entry.content, offset1, offset2);
+ }
+
+ QFile file(QString::fromUtf8(fileName));
+ if (file.open(QIODeviceBase::ReadOnly)) { // binary to match clang offsets
+ FileCacheEntry entry{fileName, file.readAll()};
+ cache.prepend(entry);
+ while (cache.size() > 5)
+ cache.removeLast();
+ return fromCache(entry.content, offset1, offset2);
+ }
+ return {};
+}
+
static QString getSpelling(CXSourceRange range)
{
auto start = clang_getRangeStart(range);
@@ -211,14 +480,8 @@ static QString getSpelling(CXSourceRange range)
if (file1 != file2 || offset2 <= offset1)
return QString();
- QFile file(fromCXString(clang_getFileName(file1)));
- if (!file.open(QFile::ReadOnly)) {
- if (file.fileName() == fnDummyFileName)
- return QString::fromUtf8(ClangCodeParser::fn().mid(offset1, offset2 - offset1));
- return QString();
- }
- file.seek(offset1);
- return QString::fromUtf8(file.read(offset2 - offset1));
+
+ return readFile(file1, offset1, offset2);
}
/*!
@@ -232,10 +495,13 @@ QString functionName(CXCursor cursor)
// For a CXCursor_ConversionFunction we don't want the spelling which would be something
// like "operator type-parameter-0-0" or "operator unsigned int". we want the actual name as
// spelled;
- QString type = fromCXString(clang_getTypeSpelling(clang_getCursorResultType(cursor)));
- if (type.isEmpty())
- return fromCXString(clang_getCursorSpelling(cursor));
- return QLatin1String("operator ") + type;
+ auto conversion_declaration =
+ static_cast<const clang::CXXConversionDecl*>(get_cursor_declaration(cursor));
+
+ return QLatin1String("operator ") + QString::fromStdString(get_fully_qualified_type_name(
+ conversion_declaration->getConversionType(),
+ conversion_declaration->getASTContext()
+ ));
}
QString name = fromCXString(clang_getCursorSpelling(cursor));
@@ -320,35 +586,66 @@ static Node *findNodeForCursor(QDocDatabase *qdb, CXCursor cur)
parent->findChildren(functionName(cur), candidates);
if (candidates.isEmpty())
return nullptr;
+
CXType funcType = clang_getCursorType(cur);
auto numArg = clang_getNumArgTypes(funcType);
bool isVariadic = clang_isFunctionTypeVariadic(funcType);
QVarLengthArray<QString, 20> args;
- for (Node *candidate : qAsConst(candidates)) {
+
+ std::optional<RelaxedTemplateDeclaration> relaxed_template_declaration{std::nullopt};
+ if (kind == CXCursor_FunctionTemplate)
+ relaxed_template_declaration = get_template_declaration(
+ get_cursor_declaration(cur)->getAsFunction()->getDescribedFunctionTemplate()
+ );
+
+ for (Node *candidate : std::as_const(candidates)) {
if (!candidate->isFunction(Node::CPP))
continue;
+
auto fn = static_cast<FunctionNode *>(candidate);
+
+ if (!fn->templateDecl() && relaxed_template_declaration)
+ continue;
+
+ if (fn->templateDecl() && !relaxed_template_declaration)
+ continue;
+
+ if (fn->templateDecl() && relaxed_template_declaration &&
+ !are_template_declarations_substitutable(*fn->templateDecl(), *relaxed_template_declaration))
+ continue;
+
const Parameters &parameters = fn->parameters();
+
if (parameters.count() != numArg + isVariadic)
continue;
+
if (fn->isConst() != bool(clang_CXXMethod_isConst(cur)))
continue;
+
if (isVariadic && parameters.last().type() != QLatin1String("..."))
continue;
+
+ if (fn->isRef() != (clang_Type_getCXXRefQualifier(funcType) == CXRefQualifier_LValue))
+ continue;
+
+ if (fn->isRefRef() != (clang_Type_getCXXRefQualifier(funcType) == CXRefQualifier_RValue))
+ continue;
+
+ auto function_declaration = get_cursor_declaration(cur)->getAsFunction();
+
bool different = false;
for (int i = 0; i < numArg; ++i) {
CXType argType = clang_getArgType(funcType, i);
+
if (args.size() <= i)
- args.append(fromCXString(clang_getTypeSpelling(argType)));
+ args.append(QString::fromStdString(get_fully_qualified_type_name(
+ function_declaration->getParamDecl(i)->getOriginalType(),
+ function_declaration->getASTContext()
+ )));
+
QString recordedType = parameters.at(i).type();
QString typeSpelling = args.at(i);
- auto p = parent;
- while (p && recordedType != typeSpelling) {
- QString parentScope = p->name() + QLatin1String("::");
- recordedType.remove(parentScope);
- typeSpelling.remove(parentScope);
- p = p->parent();
- }
+
different = recordedType != typeSpelling;
// Retry with a canonical type spelling
@@ -356,13 +653,18 @@ static Node *findNodeForCursor(QDocDatabase *qdb, CXCursor cur)
QStringView canonicalType = parameters.at(i).canonicalType();
if (!canonicalType.isEmpty()) {
different = canonicalType !=
- fromCXString(clang_getTypeSpelling(clang_getCanonicalType(argType)));
+ QString::fromStdString(get_fully_qualified_type_name(
+ function_declaration->getParamDecl(i)->getOriginalType().getCanonicalType(),
+ function_declaration->getASTContext()
+ ));
}
}
+
if (different) {
break;
}
}
+
if (!different)
return fn;
}
@@ -399,9 +701,11 @@ static void setOverridesForFunction(FunctionNode *fn, CXCursor cursor)
class ClangVisitor
{
public:
- ClangVisitor(QDocDatabase *qdb, const QMultiHash<QString, QString> &allHeaders)
- : qdb_(qdb), parent_(qdb->primaryTreeRoot()), allHeaders_(allHeaders)
+ ClangVisitor(QDocDatabase *qdb, const std::set<Config::HeaderFilePath> &allHeaders)
+ : qdb_(qdb), parent_(qdb->primaryTreeRoot())
{
+ std::transform(allHeaders.cbegin(), allHeaders.cend(), std::inserter(allHeaders_, allHeaders_.begin()),
+ [](const auto& header_file_path) { return header_file_path.filename; });
}
QDocDatabase *qdocDB() { return qdb_; }
@@ -421,7 +725,7 @@ public:
} else {
QFileInfo fi(fromCXString(clang_getFileName(file)));
// Match by file name in case of PCH/installed headers
- isInteresting = allHeaders_.contains(fi.fileName());
+ isInteresting = allHeaders_.find(fi.fileName()) != allHeaders_.end();
isInterestingCache_[file] = isInteresting;
}
if (isInteresting) {
@@ -472,8 +776,7 @@ private:
QDocDatabase *qdb_;
Aggregate *parent_;
- bool m_friendDecl { false }; // true if currently visiting a friend declaration
- const QMultiHash<QString, QString> allHeaders_;
+ std::set<QString> allHeaders_;
QHash<CXFile, bool> isInterestingCache_; // doing a canonicalFilePath is slow, so keep a cache.
/*!
@@ -486,34 +789,12 @@ private:
// Ignore functions generated by property macros
if (symbolName.startsWith("_qt_property_"))
return true;
+ // Ignore template argument deduction guides
+ if (symbolName.startsWith("<deduction guide"))
+ return true;
return false;
}
- /*!
- The type parameters do not need to be fully qualified
- This function removes the ClassName:: if needed.
-
- example: 'QLinkedList::iterator' -> 'iterator'
- */
- QString adjustTypeName(const QString &typeName)
- {
- auto parent = parent_->parent();
- if (parent && parent->isClassNode()) {
- QStringView typeNameConstRemoved(typeName);
- if (typeNameConstRemoved.startsWith(QLatin1String("const ")))
- typeNameConstRemoved = typeNameConstRemoved.mid(6);
-
- auto parentName = parent->fullName();
- if (typeNameConstRemoved.startsWith(parentName)
- && typeNameConstRemoved.mid(parentName.size(), 2) == QLatin1String("::")) {
- QString result = typeName;
- result.remove(typeName.indexOf(typeNameConstRemoved), parentName.size() + 2);
- return result;
- }
- }
- return typeName;
- }
-
CXChildVisitResult visitSource(CXCursor cursor, CXSourceLocation loc);
CXChildVisitResult visitHeader(CXCursor cursor, CXSourceLocation loc);
CXChildVisitResult visitFnSignature(CXCursor cursor, CXSourceLocation loc, Node **fnNode,
@@ -609,7 +890,7 @@ CXChildVisitResult ClangVisitor::visitFnSignature(CXCursor cursor, CXSourceLocat
CXChildVisitResult ClangVisitor::visitHeader(CXCursor cursor, CXSourceLocation loc)
{
auto kind = clang_getCursorKind(cursor);
- QString templateString;
+
switch (kind) {
case CXCursor_TypeAliasTemplateDecl:
case CXCursor_TypeAliasDecl: {
@@ -620,15 +901,17 @@ CXChildVisitResult ClangVisitor::visitHeader(CXCursor cursor, CXSourceLocation l
const QLatin1String usingString("using ");
qsizetype usingPos = typeAlias[0].indexOf(usingString);
if (usingPos != -1) {
- if (kind == CXCursor_TypeAliasTemplateDecl)
- templateString = typeAlias[0].left(usingPos).trimmed();
typeAlias[0].remove(0, usingPos + usingString.size());
typeAlias[0] = typeAlias[0].split(QLatin1Char(' ')).first();
typeAlias[1] = typeAlias[1].trimmed();
auto *ta = new TypeAliasNode(parent_, typeAlias[0], typeAlias[1]);
ta->setAccess(fromCX_CXXAccessSpecifier(clang_getCXXAccessSpecifier(cursor)));
ta->setLocation(fromCXSourceLocation(clang_getCursorLocation(cursor)));
- ta->setTemplateDecl(templateString);
+
+ if (kind == CXCursor_TypeAliasTemplateDecl) {
+ auto template_decl = llvm::dyn_cast<clang::TemplateDecl>(get_cursor_declaration(cursor));
+ ta->setTemplateDecl(get_template_declaration(template_decl));
+ }
}
}
return CXChildVisit_Continue;
@@ -639,7 +922,6 @@ CXChildVisitResult ClangVisitor::visitHeader(CXCursor cursor, CXSourceLocation l
return CXChildVisit_Continue;
Q_FALLTHROUGH();
case CXCursor_ClassTemplate:
- templateString = templateDecl(cursor);
Q_FALLTHROUGH();
case CXCursor_ClassDecl: {
if (!clang_isCursorDefinition(cursor))
@@ -668,8 +950,10 @@ CXChildVisitResult ClangVisitor::visitHeader(CXCursor cursor, CXSourceLocation l
classe->setAccess(fromCX_CXXAccessSpecifier(clang_getCXXAccessSpecifier(cursor)));
classe->setLocation(fromCXSourceLocation(clang_getCursorLocation(cursor)));
- if (kind == CXCursor_ClassTemplate)
- classe->setTemplateDecl(templateString);
+ if (kind == CXCursor_ClassTemplate) {
+ auto template_declaration = llvm::dyn_cast<clang::TemplateDecl>(get_cursor_declaration(cursor));
+ classe->setTemplateDecl(get_template_declaration(template_declaration));
+ }
QScopedValueRollback<Aggregate *> setParent(parent_, classe);
return visitChildren(cursor);
@@ -707,7 +991,6 @@ CXChildVisitResult ClangVisitor::visitHeader(CXCursor cursor, CXSourceLocation l
return visitChildren(cursor);
}
case CXCursor_FunctionTemplate:
- templateString = templateDecl(cursor);
Q_FALLTHROUGH();
case CXCursor_FunctionDecl:
case CXCursor_CXXMethod:
@@ -719,6 +1002,9 @@ CXChildVisitResult ClangVisitor::visitHeader(CXCursor cursor, CXSourceLocation l
QString name = functionName(cursor);
if (ignoredSymbol(name))
return CXChildVisit_Continue;
+ // constexpr constructors generate also a global instance; ignore
+ if (kind == CXCursor_Constructor && parent_ == qdb_->primaryTreeRoot())
+ return CXChildVisit_Continue;
auto *fn = new FunctionNode(parent_, name);
CXSourceRange range = clang_Cursor_getCommentRange(cursor);
@@ -733,23 +1019,29 @@ CXChildVisitResult ClangVisitor::visitHeader(CXCursor cursor, CXSourceLocation l
}
}
}
+
processFunction(fn, cursor);
- fn->setTemplateDecl(templateString);
+
+ if (kind == CXCursor_FunctionTemplate) {
+ auto template_declaration = get_cursor_declaration(cursor)->getAsFunction()->getDescribedFunctionTemplate();
+ fn->setTemplateDecl(get_template_declaration(template_declaration));
+ }
+
return CXChildVisit_Continue;
}
#if CINDEX_VERSION >= 36
case CXCursor_FriendDecl: {
- QScopedValueRollback<bool> setFriend(m_friendDecl, true);
- // Visit the friend functions
return visitChildren(cursor);
}
#endif
case CXCursor_EnumDecl: {
auto *en = static_cast<EnumNode *>(findNodeForCursor(qdb_, cursor));
- if (en && en->items().count())
+ if (en && en->items().size())
return CXChildVisit_Continue; // Was already parsed, probably in another TU
+
QString enumTypeName = fromCXString(clang_getCursorSpelling(cursor));
- if (enumTypeName.isEmpty()) {
+
+ if (clang_Cursor_isAnonymous(cursor)) {
enumTypeName = "anonymous";
if (parent_ && (parent_->isClassNode() || parent_->isNamespace())) {
Node *n = parent_->findNonfunctionChild(enumTypeName, &Node::isEnumType);
@@ -795,12 +1087,21 @@ CXChildVisitResult ClangVisitor::visitHeader(CXCursor cursor, CXSourceLocation l
if (findNodeForCursor(qdb_, cursor)) // Was already parsed, probably in another TU
return CXChildVisit_Continue;
+ auto value_declaration =
+ llvm::dyn_cast<clang::ValueDecl>(get_cursor_declaration(cursor));
+ assert(value_declaration);
+
auto access = fromCX_CXXAccessSpecifier(clang_getCXXAccessSpecifier(cursor));
auto var = new VariableNode(parent_, fromCXString(clang_getCursorSpelling(cursor)));
+
var->setAccess(access);
var->setLocation(fromCXSourceLocation(clang_getCursorLocation(cursor)));
- var->setLeftType(fromCXString(clang_getTypeSpelling(clang_getCursorType(cursor))));
+ var->setLeftType(QString::fromStdString(get_fully_qualified_type_name(
+ value_declaration->getType(),
+ value_declaration->getASTContext()
+ )));
var->setStatic(kind == CXCursor_VarDecl && parent_->isClassNode());
+
return CXChildVisit_Continue;
}
case CXCursor_TypedefDecl: {
@@ -860,23 +1161,18 @@ void ClangVisitor::readParameterNamesAndAttributes(FunctionNode *fn, CXCursor cu
} else if (kind == CXCursor_ParmDecl) {
if (i >= parameters.count())
return CXChildVisit_Break; // Attributes comes before parameters so we can break.
- QString name = fromCXString(clang_getCursorSpelling(cur));
- if (!name.isEmpty()) {
+
+ if (QString name = fromCXString(clang_getCursorSpelling(cur)); !name.isEmpty())
parameters[i].setName(name);
- // Find the default value
- visitChildrenLambda(cur, [&](CXCursor cur) {
- if (clang_isExpression(clang_getCursorKind(cur))) {
- QString defaultValue = getSpelling(clang_getCursorExtent(cur));
- if (defaultValue.startsWith('=')) // In some cases, the = is part of the range.
- defaultValue = QStringView{defaultValue}.mid(1).trimmed().toString();
- if (defaultValue.isEmpty())
- defaultValue = QStringLiteral("...");
- parameters[i].setDefaultValue(defaultValue);
- return CXChildVisit_Break;
- }
- return CXChildVisit_Continue;
- });
- }
+
+ const clang::ParmVarDecl* parameter_declaration = llvm::dyn_cast<const clang::ParmVarDecl>(get_cursor_declaration(cur));
+ Q_ASSERT(parameter_declaration);
+
+ std::string default_value = get_default_value_initializer_as_string(parameter_declaration);
+
+ if (!default_value.empty())
+ parameters[i].setDefaultValue(QString::fromStdString(default_value));
+
++i;
}
return CXChildVisit_Continue;
@@ -889,6 +1185,29 @@ void ClangVisitor::processFunction(FunctionNode *fn, CXCursor cursor)
CXType funcType = clang_getCursorType(cursor);
fn->setAccess(fromCX_CXXAccessSpecifier(clang_getCXXAccessSpecifier(cursor)));
fn->setLocation(fromCXSourceLocation(clang_getCursorLocation(cursor)));
+ fn->setStatic(clang_CXXMethod_isStatic(cursor));
+ fn->setConst(clang_CXXMethod_isConst(cursor));
+ fn->setVirtualness(!clang_CXXMethod_isVirtual(cursor)
+ ? FunctionNode::NonVirtual
+ : clang_CXXMethod_isPureVirtual(cursor)
+ ? FunctionNode::PureVirtual
+ : FunctionNode::NormalVirtual);
+
+ // REMARK: We assume that the following operations and casts are
+ // generally safe.
+ // Callers of those methods will generally check at the LibClang
+ // level the kind of cursor we are dealing with and will pass on
+ // only valid cursors that are of a function kind and that are at
+ // least a declaration.
+ //
+ // Failure to do so implies a bug in the call chain and should be
+ // dealt with as such.
+ const clang::Decl* declaration = get_cursor_declaration(cursor);
+
+ assert(declaration);
+
+ const clang::FunctionDecl* function_declaration = declaration->getAsFunction();
+
if (kind == CXCursor_Constructor
// a constructor template is classified as CXCursor_FunctionTemplate
|| (kind == CXCursor_FunctionTemplate && fn->name() == parent_->name()))
@@ -896,16 +1215,47 @@ void ClangVisitor::processFunction(FunctionNode *fn, CXCursor cursor)
else if (kind == CXCursor_Destructor)
fn->setMetaness(FunctionNode::Dtor);
else
- fn->setReturnType(adjustTypeName(
- fromCXString(clang_getTypeSpelling(clang_getResultType(funcType)))));
+ fn->setReturnType(QString::fromStdString(get_fully_qualified_type_name(
+ function_declaration->getReturnType(),
+ function_declaration->getASTContext()
+ )));
+
+ const clang::CXXConstructorDecl* constructor_declaration = llvm::dyn_cast<const clang::CXXConstructorDecl>(function_declaration);
+
+ if (constructor_declaration && constructor_declaration->isCopyConstructor()) fn->setMetaness(FunctionNode::CCtor);
+ else if (constructor_declaration && constructor_declaration->isMoveConstructor()) fn->setMetaness(FunctionNode::MCtor);
+
+ const clang::CXXConversionDecl* conversion_declaration = llvm::dyn_cast<const clang::CXXConversionDecl>(function_declaration);
+
+ if (function_declaration->isConstexpr()) fn->markConstexpr();
+ if (
+ (constructor_declaration && constructor_declaration->isExplicit()) ||
+ (conversion_declaration && conversion_declaration->isExplicit())
+ ) fn->markExplicit();
+
+ const clang::CXXMethodDecl* method_declaration = llvm::dyn_cast<const clang::CXXMethodDecl>(function_declaration);
+
+ if (method_declaration && method_declaration->isCopyAssignmentOperator()) fn->setMetaness(FunctionNode::CAssign);
+ else if (method_declaration && method_declaration->isMoveAssignmentOperator()) fn->setMetaness(FunctionNode::MAssign);
+
+ const clang::FunctionType* function_type = function_declaration->getFunctionType();
+ const clang::FunctionProtoType* function_prototype = static_cast<const clang::FunctionProtoType*>(function_type);
+
+ if (function_prototype) {
+ clang::FunctionProtoType::ExceptionSpecInfo exception_specification = function_prototype->getExceptionSpecInfo();
+
+ if (exception_specification.Type != clang::ExceptionSpecificationType::EST_None) {
+ const std::string exception_specification_spelling =
+ exception_specification.NoexceptExpr ? get_expression_as_string(
+ exception_specification.NoexceptExpr,
+ function_declaration->getASTContext()
+ ) : "";
+
+ if (exception_specification_spelling != "false")
+ fn->markNoexcept(QString::fromStdString(exception_specification_spelling));
+ }
+ }
- fn->setStatic(clang_CXXMethod_isStatic(cursor));
- fn->setConst(clang_CXXMethod_isConst(cursor));
- fn->setVirtualness(!clang_CXXMethod_isVirtual(cursor)
- ? FunctionNode::NonVirtual
- : clang_CXXMethod_isPureVirtual(cursor)
- ? FunctionNode::PureVirtual
- : FunctionNode::NormalVirtual);
CXRefQualifierKind refQualKind = clang_Type_getCXXRefQualifier(funcType);
if (refQualKind == CXRefQualifier_LValue)
fn->setRef(true);
@@ -916,42 +1266,37 @@ void ClangVisitor::processFunction(FunctionNode *fn, CXCursor cursor)
if (!fn->isNonvirtual() && kind != CXCursor_Destructor)
setOverridesForFunction(fn, cursor);
- int numArg = clang_getNumArgTypes(funcType);
Parameters &parameters = fn->parameters();
parameters.clear();
- parameters.reserve(numArg);
- for (int i = 0; i < numArg; ++i) {
- CXType argType = clang_getArgType(funcType, i);
- if (fn->isCtor()) {
- if (fromCXString(clang_getTypeSpelling(clang_getPointeeType(argType))) == fn->name()) {
- if (argType.kind == CXType_RValueReference)
- fn->setMetaness(FunctionNode::MCtor);
- else if (argType.kind == CXType_LValueReference)
- fn->setMetaness(FunctionNode::CCtor);
- }
- } else if ((kind == CXCursor_CXXMethod) && (fn->name() == QLatin1String("operator="))) {
- if (argType.kind == CXType_RValueReference)
- fn->setMetaness(FunctionNode::MAssign);
- else if (argType.kind == CXType_LValueReference)
- fn->setMetaness(FunctionNode::CAssign);
- }
- parameters.append(adjustTypeName(fromCXString(clang_getTypeSpelling(argType))));
- if (argType.kind == CXType_Typedef || argType.kind == CXType_Elaborated) {
- parameters.last().setCanonicalType(fromCXString(
- clang_getTypeSpelling(clang_getCanonicalType(argType))));
- }
+ parameters.reserve(function_declaration->getNumParams());
+
+ for (clang::ParmVarDecl* const parameter_declaration : function_declaration->parameters()) {
+ clang::QualType parameter_type = parameter_declaration->getOriginalType();
+
+ parameters.append(QString::fromStdString(get_fully_qualified_type_name(
+ parameter_type,
+ parameter_declaration->getASTContext()
+ )));
+
+ if (!parameter_type.isCanonical())
+ parameters.last().setCanonicalType(QString::fromStdString(get_fully_qualified_type_name(
+ parameter_type.getCanonicalType(),
+ parameter_declaration->getASTContext()
+ )));
}
+
if (parameters.count() > 0) {
if (parameters.last().type().endsWith(QLatin1String("QPrivateSignal"))) {
parameters.pop_back(); // remove the QPrivateSignal argument
parameters.setPrivateSignal();
}
}
+
if (clang_isFunctionTypeVariadic(funcType))
parameters.append(QStringLiteral("..."));
readParameterNamesAndAttributes(fn, cursor);
- // Friend functions are not members
- if (m_friendDecl)
+
+ if (declaration->getFriendObjectKind() != clang::Decl::FOK_None)
fn->setRelatedNonmember(true);
}
@@ -969,32 +1314,44 @@ bool ClangVisitor::parseProperty(const QString &spelling, const Location &loc)
QString signature = spelling.mid(lpIdx + 1, rpIdx - lpIdx - 1);
signature = signature.simplified();
-
- QString type;
- QString name;
QStringList parts = signature.split(QChar(' '), Qt::SkipEmptyParts);
- if (parts.size() < 2)
- return false;
- if (parts.first() == QLatin1String("enum"))
- parts.removeFirst(); // QTBUG-80027
- type = parts.takeFirst();
- if (type == QLatin1String("const") && !parts.empty())
- type += " " + parts.takeFirst();
+ static const QStringList attrs =
+ QStringList() << "READ" << "MEMBER" << "WRITE"
+ << "NOTIFY" << "CONSTANT" << "FINAL"
+ << "REQUIRED" << "BINDABLE" << "DESIGNABLE"
+ << "RESET" << "REVISION" << "SCRIPTABLE"
+ << "STORED" << "USER";
+
+ // Find the location of the first attribute. All preceding parts
+ // represent the property type + name.
+ auto it = std::find_if(parts.cbegin(), parts.cend(),
+ [](const QString &attr) -> bool {
+ return attrs.contains(attr);
+ });
- if (!parts.empty())
- name = parts.takeFirst();
- else
+ if (it == parts.cend() || std::distance(parts.cbegin(), it) < 2)
return false;
- if (name.front() == QChar('*')) {
- type.append(QChar('*'));
- name.remove(0, 1);
+ QStringList typeParts;
+ std::copy(parts.cbegin(), it, std::back_inserter(typeParts));
+ parts.erase(parts.cbegin(), it);
+ QString name = typeParts.takeLast();
+
+ // Move the pointer operator(s) from name to type
+ while (!name.isEmpty() && name.front() == QChar('*')) {
+ typeParts.last().push_back(name.front());
+ name.removeFirst();
}
+
+ // Need at least READ or MEMBER + getter/member name
+ if (parts.size() < 2 || name.isEmpty())
+ return false;
+
auto *property = new PropertyNode(parent_, name);
property->setAccess(Access::Public);
property->setLocation(loc);
- property->setDataType(type);
+ property->setDataType(typeParts.join(QChar(' ')));
int i = 0;
while (i < parts.size()) {
@@ -1008,36 +1365,21 @@ bool ClangVisitor::parseProperty(const QString &spelling, const Location &loc)
if (i < parts.size()) {
QString value = parts.at(i++);
if (key == "READ") {
- qdb_->addPropertyFunction(property, value, PropertyNode::Getter);
+ qdb_->addPropertyFunction(property, value, PropertyNode::FunctionRole::Getter);
} else if (key == "WRITE") {
- qdb_->addPropertyFunction(property, value, PropertyNode::Setter);
+ qdb_->addPropertyFunction(property, value, PropertyNode::FunctionRole::Setter);
+ property->setWritable(true);
+ } else if (key == "MEMBER") {
property->setWritable(true);
} else if (key == "STORED") {
property->setStored(value.toLower() == "true");
- } else if (key == "DESIGNABLE") {
- QString v = value.toLower();
- if (v == "true")
- property->setDesignable(true);
- else if (v == "false")
- property->setDesignable(false);
- else {
- property->setDesignable(false);
- }
} else if (key == "BINDABLE") {
- property->setPropertyType(PropertyNode::Bindable);
+ property->setPropertyType(PropertyNode::PropertyType::BindableProperty);
+ qdb_->addPropertyFunction(property, value, PropertyNode::FunctionRole::Bindable);
} else if (key == "RESET") {
- qdb_->addPropertyFunction(property, value, PropertyNode::Resetter);
+ qdb_->addPropertyFunction(property, value, PropertyNode::FunctionRole::Resetter);
} else if (key == "NOTIFY") {
- qdb_->addPropertyFunction(property, value, PropertyNode::Notifier);
- } else if (key == "SCRIPTABLE") {
- QString v = value.toLower();
- if (v == "true")
- property->setScriptable(true);
- else if (v == "false")
- property->setScriptable(false);
- else {
- property->setScriptable(false);
- }
+ qdb_->addPropertyFunction(property, value, PropertyNode::FunctionRole::Notifier);
}
}
}
@@ -1084,121 +1426,32 @@ Node *ClangVisitor::nodeForCommentAtLocation(CXSourceLocation loc, CXSourceLocat
return node;
}
-/*!
- Get the include paths from the qdoc configuration database
- \a config. Call the initializeParser() in the base class.
- Get the defines list from the qdocconf database.
-
- \note on \macos, we try to also query the system/framework
- include paths from the compiler.
- */
-void ClangCodeParser::initializeParser()
-{
- Config &config = Config::instance();
- m_version = config.getString(CONFIG_VERSION);
- auto args = config.getCanonicalPathList(CONFIG_INCLUDEPATHS,
- Config::IncludePaths);
-#ifdef Q_OS_MACOS
- args.append(Utilities::getInternalIncludePaths(QStringLiteral("clang++")));
-#endif
- m_includePaths.clear();
- for (const auto &path : qAsConst(args)) {
- if (!path.isEmpty())
- m_includePaths.append(path.toUtf8());
- }
- m_includePaths.erase(std::unique(m_includePaths.begin(), m_includePaths.end()),
- m_includePaths.end());
- CppCodeParser::initializeParser();
- m_pchFileDir.reset(nullptr);
- m_allHeaders.clear();
- m_pchName.clear();
- m_defines.clear();
- QSet<QString> accepted;
- {
- const QStringList tmpDefines = config.getStringList(CONFIG_CLANGDEFINES);
- for (const QString &def : tmpDefines) {
- if (!accepted.contains(def)) {
- QByteArray tmp("-D");
- tmp.append(def.toUtf8());
- m_defines.append(tmp.constData());
- accepted.insert(def);
- }
- }
- }
- {
- const QStringList tmpDefines = config.getStringList(CONFIG_DEFINES);
- for (const QString &def : tmpDefines) {
- if (!accepted.contains(def) && !def.contains(QChar('*'))) {
- QByteArray tmp("-D");
- tmp.append(def.toUtf8());
- m_defines.append(tmp.constData());
- accepted.insert(def);
- }
- }
- }
- qCDebug(lcQdoc).nospace() << __FUNCTION__ << " Clang v" << CINDEX_VERSION_MAJOR << '.'
- << CINDEX_VERSION_MINOR;
-}
-
-/*!
- */
-void ClangCodeParser::terminateParser()
-{
- CppCodeParser::terminateParser();
-}
-
-/*!
- */
-QString ClangCodeParser::language()
-{
- return "Clang";
-}
-
-/*!
- Returns a list of extensions for header files.
- */
-QStringList ClangCodeParser::headerFileNameFilter()
-{
- return QStringList() << "*.ch"
- << "*.h"
- << "*.h++"
- << "*.hh"
- << "*.hpp"
- << "*.hxx";
-}
-
-/*!
- Returns a list of extensions for source files, i.e. not
- header files.
- */
-QStringList ClangCodeParser::sourceFileNameFilter()
+ClangCodeParser::ClangCodeParser(
+ QDocDatabase* qdb,
+ Config& config,
+ const std::vector<QByteArray>& include_paths,
+ const QList<QByteArray>& defines,
+ std::optional<std::reference_wrapper<const PCHFile>> pch
+) : m_qdb{qdb},
+ m_includePaths{include_paths},
+ m_defines{defines},
+ m_pch{pch}
{
- return QStringList() << "*.c++"
- << "*.cc"
- << "*.cpp"
- << "*.cxx"
- << "*.mm";
-}
-
-/*!
- Parse the C++ header file identified by \a filePath and add
- the parsed contents to the database. The \a location is used
- for reporting errors.
- */
-void ClangCodeParser::parseHeaderFile(const Location & /*location*/, const QString &filePath)
-{
- QFileInfo fi(filePath);
- const QString &fileName = fi.fileName();
- const QString &canonicalPath = fi.canonicalPath();
-
- if (m_allHeaders.contains(fileName, canonicalPath))
- return;
-
- m_allHeaders.insert(fileName, canonicalPath);
+ m_allHeaders = config.getHeaderFiles();
}
static const char *defaultArgs_[] = {
+/*
+ https://bugreports.qt.io/browse/QTBUG-94365
+ An unidentified bug in Clang 15.x causes parsing failures due to errors in
+ the AST. This replicates only with C++20 support enabled - avoid the issue
+ by using C++17 with Clang 15.
+ */
+#if LIBCLANG_VERSION_MAJOR == 15
+ "-std=c++17",
+#else
"-std=c++20",
+#endif
#ifndef Q_OS_WIN
"-fPIC",
#else
@@ -1206,7 +1459,7 @@ static const char *defaultArgs_[] = {
#endif
"-DQ_QDOC",
"-DQ_CLANG_QDOC",
- "-DQT_DISABLE_DEPRECATED_BEFORE=0",
+ "-DQT_DISABLE_DEPRECATED_UP_TO=0",
"-DQT_ANNOTATE_CLASS(type,...)=static_assert(sizeof(#__VA_ARGS__),#type);",
"-DQT_ANNOTATE_CLASS2(type,a1,a2)=static_assert(sizeof(#a1,#a2),#type);",
"-DQT_ANNOTATE_FUNCTION(a)=__attribute__((annotate(#a)))",
@@ -1216,34 +1469,32 @@ static const char *defaultArgs_[] = {
"-Wno-nullability-completeness",
"-fvisibility=default",
"-ferror-limit=0",
- "-I" CLANG_RESOURCE_DIR
+ ("-I" CLANG_RESOURCE_DIR)
};
/*!
Load the default arguments and the defines into \a args.
Clear \a args first.
*/
-void ClangCodeParser::getDefaultArgs()
+void getDefaultArgs(const QList<QByteArray>& defines, std::vector<const char*>& args)
{
- m_args.clear();
- m_args.insert(m_args.begin(), std::begin(defaultArgs_), std::end(defaultArgs_));
+ args.clear();
+ args.insert(args.begin(), std::begin(defaultArgs_), std::end(defaultArgs_));
+
// Add the defines from the qdocconf file.
- for (const auto &p : qAsConst(m_defines))
- m_args.push_back(p.constData());
+ for (const auto &p : std::as_const(defines))
+ args.push_back(p.constData());
}
-static QList<QByteArray> includePathsFromHeaders(const QMultiHash<QString, QString> &allHeaders)
+static QList<QByteArray> includePathsFromHeaders(const std::set<Config::HeaderFilePath> &allHeaders)
{
QList<QByteArray> result;
- for (auto it = allHeaders.cbegin(); it != allHeaders.cend(); ++it) {
- const QByteArray path = "-I" + it.value().toLatin1();
+ for (const auto& [header_path, _] : allHeaders) {
+ const QByteArray path = "-I" + header_path.toLatin1();
const QByteArray parent =
- "-I" + QDir::cleanPath(it.value() + QLatin1String("/../")).toLatin1();
- if (!result.contains(path))
- result.append(path);
- if (!result.contains(parent))
- result.append(parent);
+ "-I" + QDir::cleanPath(header_path + QLatin1String("/../")).toLatin1();
}
+
return result;
}
@@ -1251,9 +1502,12 @@ static QList<QByteArray> includePathsFromHeaders(const QMultiHash<QString, QStri
Load the include paths into \a moreArgs. If no include paths
were provided, try to guess reasonable include paths.
*/
-void ClangCodeParser::getMoreArgs()
-{
- if (m_includePaths.isEmpty()) {
+void getMoreArgs(
+ const std::vector<QByteArray>& include_paths,
+ const std::set<Config::HeaderFilePath>& all_headers,
+ std::vector<const char*>& args
+) {
+ if (include_paths.empty()) {
/*
The include paths provided are inadequate. Make a list
of reasonable places to look for include files and use
@@ -1262,10 +1516,12 @@ void ClangCodeParser::getMoreArgs()
qCWarning(lcQdoc) << "No include paths passed to qdoc; guessing reasonable include paths";
QString basicIncludeDir = QDir::cleanPath(QString(Config::installDir + "/../include"));
- m_moreArgs += "-I" + basicIncludeDir.toLatin1();
- m_moreArgs += includePathsFromHeaders(m_allHeaders);
+ args.emplace_back(QByteArray("-I" + basicIncludeDir.toLatin1()).constData());
+
+ auto include_paths_from_headers = includePathsFromHeaders(all_headers);
+ args.insert(args.end(), include_paths_from_headers.begin(), include_paths_from_headers.end());
} else {
- m_moreArgs = m_includePaths;
+ std::copy(include_paths.begin(), include_paths.end(), std::back_inserter(args));
}
}
@@ -1274,152 +1530,132 @@ void ClangCodeParser::getMoreArgs()
files, so it is moved here to its own member function, and
it is called after the list of header files is complete.
*/
-void ClangCodeParser::buildPCH()
-{
- if (!m_pchFileDir && !moduleHeader().isEmpty()) {
- m_pchFileDir.reset(new QTemporaryDir(QDir::tempPath() + QLatin1String("/qdoc_pch")));
- if (m_pchFileDir->isValid()) {
- const QByteArray module = moduleHeader().toUtf8();
- QByteArray header;
- QByteArray privateHeaderDir;
- qCDebug(lcQdoc) << "Build and visit PCH for" << moduleHeader();
- // A predicate for std::find_if() to locate a path to the module's header
- // (e.g. QtGui/QtGui) to be used as pre-compiled header
- struct FindPredicate
- {
- enum SearchType { Any, Module, Private };
- QByteArray &candidate_;
- const QByteArray &module_;
- SearchType type_;
- FindPredicate(QByteArray &candidate, const QByteArray &module,
- SearchType type = Any)
- : candidate_(candidate), module_(module), type_(type)
- {
- }
+std::optional<PCHFile> buildPCH(
+ QDocDatabase* qdb,
+ QString module_header,
+ const std::set<Config::HeaderFilePath>& all_headers,
+ const std::vector<QByteArray>& include_paths,
+ const QList<QByteArray>& defines
+) {
+ static std::vector<const char*> arguments{};
- bool operator()(const QByteArray &p) const
- {
- if (type_ != Any && !p.endsWith(module_))
- return false;
- candidate_ = p + "/";
- switch (type_) {
- case Any:
- case Module:
- candidate_.append(module_);
- break;
- case Private:
- candidate_.append("private");
- break;
- default:
- break;
- }
- if (p.startsWith("-I"))
- candidate_ = candidate_.mid(2);
- return QFile::exists(QString::fromUtf8(candidate_));
- }
- };
-
- // First, search for an include path that contains the module name, then any path
- QByteArray candidate;
- auto it = std::find_if(m_includePaths.begin(), m_includePaths.end(),
- FindPredicate(candidate, module, FindPredicate::Module));
- if (it == m_includePaths.end())
- it = std::find_if(m_includePaths.begin(), m_includePaths.end(),
- FindPredicate(candidate, module, FindPredicate::Any));
- if (it != m_includePaths.end())
- header = candidate;
-
- // Find the path to module's private headers - currently unused
- it = std::find_if(m_includePaths.begin(), m_includePaths.end(),
- FindPredicate(candidate, module, FindPredicate::Private));
- if (it != m_includePaths.end())
- privateHeaderDir = candidate;
-
- if (header.isEmpty()) {
- qWarning() << "(qdoc) Could not find the module header in include paths for module"
- << module << " (include paths: " << m_includePaths << ")";
- qWarning() << " Artificial module header built from header dirs in qdocconf "
- "file";
- }
- m_args.push_back("-xc++");
- CXTranslationUnit tu;
- QString tmpHeader = m_pchFileDir->path() + "/" + module;
- QFile tmpHeaderFile(tmpHeader);
- if (tmpHeaderFile.open(QIODevice::Text | QIODevice::WriteOnly)) {
- QTextStream out(&tmpHeaderFile);
- if (header.isEmpty()) {
- for (auto it = m_allHeaders.constKeyValueBegin();
- it != m_allHeaders.constKeyValueEnd(); ++it) {
- if (!(*it).first.endsWith(QLatin1String("_p.h"))
- && !(*it).first.startsWith(QLatin1String("moc_"))) {
- QString line = QLatin1String("#include \"") + (*it).second
- + QLatin1String("/") + (*it).first + QLatin1String("\"");
- out << line << "\n";
- }
- }
- } else {
- QFileInfo headerFile(header);
- if (!headerFile.exists()) {
- qWarning() << "Could not find module header file" << header;
- return;
- }
- out << QLatin1String("#include \"") + header + QLatin1String("\"");
- }
- tmpHeaderFile.close();
- }
+ if (module_header.isEmpty()) return std::nullopt;
+
+ getDefaultArgs(defines, arguments);
+ getMoreArgs(include_paths, all_headers, arguments);
+
+ flags_ = static_cast<CXTranslationUnit_Flags>(CXTranslationUnit_Incomplete
+ | CXTranslationUnit_SkipFunctionBodies
+ | CXTranslationUnit_KeepGoing);
+
+ CompilationIndex index{ clang_createIndex(1, kClangDontDisplayDiagnostics) };
+
+ QTemporaryDir pch_directory{QDir::tempPath() + QLatin1String("/qdoc_pch")};
+ if (!pch_directory.isValid()) return std::nullopt;
+
+ const QByteArray module = module_header.toUtf8();
+ QByteArray header;
+
+ qCDebug(lcQdoc) << "Build and visit PCH for" << module_header;
+ // A predicate for std::find_if() to locate a path to the module's header
+ // (e.g. QtGui/QtGui) to be used as pre-compiled header
+ struct FindPredicate
+ {
+ enum SearchType { Any, Module };
+ QByteArray &candidate_;
+ const QByteArray &module_;
+ SearchType type_;
+ FindPredicate(QByteArray &candidate, const QByteArray &module,
+ SearchType type = Any)
+ : candidate_(candidate), module_(module), type_(type)
+ {
+ }
+
+ bool operator()(const QByteArray &p) const
+ {
+ if (type_ != Any && !p.endsWith(module_))
+ return false;
+ candidate_ = p + "/";
+ candidate_.append(module_);
+ if (p.startsWith("-I"))
+ candidate_ = candidate_.mid(2);
+ return QFile::exists(QString::fromUtf8(candidate_));
+ }
+ };
+
+ // First, search for an include path that contains the module name, then any path
+ QByteArray candidate;
+ auto it = std::find_if(include_paths.begin(), include_paths.end(),
+ FindPredicate(candidate, module, FindPredicate::Module));
+ if (it == include_paths.end())
+ it = std::find_if(include_paths.begin(), include_paths.end(),
+ FindPredicate(candidate, module, FindPredicate::Any));
+ if (it != include_paths.end())
+ header = candidate;
+
+ if (header.isEmpty()) {
+ qWarning() << "(qdoc) Could not find the module header in include paths for module"
+ << module << " (include paths: " << include_paths << ")";
+ qWarning() << " Artificial module header built from header dirs in qdocconf "
+ "file";
+ }
+ arguments.push_back("-xc++");
+
+ TranslationUnit tu;
+
+ QString tmpHeader = pch_directory.path() + "/" + module;
+ if (QFile tmpHeaderFile(tmpHeader); tmpHeaderFile.open(QIODevice::Text | QIODevice::WriteOnly)) {
+ QTextStream out(&tmpHeaderFile);
+ if (header.isEmpty()) {
+ for (const auto& [header_path, header_name] : all_headers) {
+ if (!header_name.endsWith(QLatin1String("_p.h"))
+ && !header_name.startsWith(QLatin1String("moc_"))) {
+ QString line = QLatin1String("#include \"") + header_path
+ + QLatin1String("/") + header_name + QLatin1String("\"");
+ out << line << "\n";
- CXErrorCode err =
- clang_parseTranslationUnit2(index_, tmpHeader.toLatin1().data(), m_args.data(),
- static_cast<int>(m_args.size()), nullptr, 0,
- flags_ | CXTranslationUnit_ForSerialization, &tu);
- qCDebug(lcQdoc) << __FUNCTION__ << "clang_parseTranslationUnit2(" << tmpHeader << m_args
- << ") returns" << err;
-
- printDiagnostics(tu);
-
- if (!err && tu) {
- m_pchName = m_pchFileDir->path().toUtf8() + "/" + module + ".pch";
- auto error = clang_saveTranslationUnit(tu, m_pchName.constData(),
- clang_defaultSaveOptions(tu));
- if (error) {
- qCCritical(lcQdoc) << "Could not save PCH file for" << moduleHeader();
- m_pchName.clear();
- } else {
- // Visit the header now, as token from pre-compiled header won't be visited
- // later
- CXCursor cur = clang_getTranslationUnitCursor(tu);
- ClangVisitor visitor(m_qdb, m_allHeaders);
- visitor.visitChildren(cur);
- qCDebug(lcQdoc) << "PCH built and visited for" << moduleHeader();
}
- clang_disposeTranslationUnit(tu);
- } else {
- m_pchFileDir->remove();
- qCCritical(lcQdoc) << "Could not create PCH file for " << moduleHeader();
}
- m_args.pop_back(); // remove the "-xc++";
+ } else {
+ QFileInfo headerFile(header);
+ if (!headerFile.exists()) {
+ qWarning() << "Could not find module header file" << header;
+ return std::nullopt;
+ }
+ out << QLatin1String("#include \"") + header + QLatin1String("\"");
}
}
-}
-/*!
- Precompile the header files for the current module.
- */
-void ClangCodeParser::precompileHeaders()
-{
- getDefaultArgs();
- getMoreArgs();
- for (const auto &p : qAsConst(m_moreArgs))
- m_args.push_back(p.constData());
+ CXErrorCode err =
+ clang_parseTranslationUnit2(index, tmpHeader.toLatin1().data(), arguments.data(),
+ static_cast<int>(arguments.size()), nullptr, 0,
+ flags_ | CXTranslationUnit_ForSerialization, &tu.tu);
+ qCDebug(lcQdoc) << __FUNCTION__ << "clang_parseTranslationUnit2(" << tmpHeader << arguments
+ << ") returns" << err;
- flags_ = static_cast<CXTranslationUnit_Flags>(CXTranslationUnit_Incomplete
- | CXTranslationUnit_SkipFunctionBodies
- | CXTranslationUnit_KeepGoing);
+ printDiagnostics(tu);
- index_ = clang_createIndex(1, kClangDontDisplayDiagnostics);
+ if (err || !tu) {
+ qCCritical(lcQdoc) << "Could not create PCH file for " << module_header;
+ return std::nullopt;
+ }
+
+ QByteArray pch_name = pch_directory.path().toUtf8() + "/" + module + ".pch";
+ auto error = clang_saveTranslationUnit(tu, pch_name.constData(),
+ clang_defaultSaveOptions(tu));
+ if (error) {
+ qCCritical(lcQdoc) << "Could not save PCH file for" << module_header;
+ return std::nullopt;
+ }
- buildPCH();
- clang_disposeIndex(index_);
+ // Visit the header now, as token from pre-compiled header won't be visited
+ // later
+ CXCursor cur = clang_getTranslationUnitCursor(tu);
+ ClangVisitor visitor(qdb, all_headers);
+ visitor.visitChildren(cur);
+ qCDebug(lcQdoc) << "PCH built and visited for" << module_header;
+
+ return std::make_optional(PCHFile{std::move(pch_directory), pch_name});
}
static float getUnpatchedVersion(QString t)
@@ -1436,51 +1672,44 @@ static float getUnpatchedVersion(QString t)
Call matchDocsAndStuff() to do all the parsing and tree building.
*/
-void ClangCodeParser::parseSourceFile(const Location & /*location*/, const QString &filePath)
+ParsedCppFileIR ClangCodeParser::parse_cpp_file(const QString &filePath)
{
- /*
- The set of open namespaces is cleared before parsing
- each source file. The word "source" here means cpp file.
- */
- m_qdb->clearOpenNamespaces();
- m_currentFile = filePath;
flags_ = static_cast<CXTranslationUnit_Flags>(CXTranslationUnit_Incomplete
| CXTranslationUnit_SkipFunctionBodies
| CXTranslationUnit_KeepGoing);
- index_ = clang_createIndex(1, kClangDontDisplayDiagnostics);
+ CompilationIndex index{ clang_createIndex(1, kClangDontDisplayDiagnostics) };
- getDefaultArgs();
- if (!m_pchName.isEmpty() && !filePath.endsWith(".mm")) {
+ getDefaultArgs(m_defines, m_args);
+ if (m_pch && !filePath.endsWith(".mm")) {
m_args.push_back("-w");
m_args.push_back("-include-pch");
- m_args.push_back(m_pchName.constData());
+ m_args.push_back((*m_pch).get().name.constData());
}
- getMoreArgs();
- for (const auto &p : qAsConst(m_moreArgs))
- m_args.push_back(p.constData());
+ getMoreArgs(m_includePaths, m_allHeaders, m_args);
- CXTranslationUnit tu;
+ TranslationUnit tu;
CXErrorCode err =
- clang_parseTranslationUnit2(index_, filePath.toLocal8Bit(), m_args.data(),
- static_cast<int>(m_args.size()), nullptr, 0, flags_, &tu);
+ clang_parseTranslationUnit2(index, filePath.toLocal8Bit(), m_args.data(),
+ static_cast<int>(m_args.size()), nullptr, 0, flags_, &tu.tu);
qCDebug(lcQdoc) << __FUNCTION__ << "clang_parseTranslationUnit2(" << filePath << m_args
<< ") returns" << err;
printDiagnostics(tu);
if (err || !tu) {
qWarning() << "(qdoc) Could not parse source file" << filePath << " error code:" << err;
- clang_disposeIndex(index_);
- return;
+ return {};
}
+ ParsedCppFileIR parse_result{};
+
CXCursor tuCur = clang_getTranslationUnitCursor(tu);
ClangVisitor visitor(m_qdb, m_allHeaders);
visitor.visitChildren(tuCur);
CXToken *tokens;
unsigned int numTokens = 0;
- const QSet<QString> &commands = topicCommands() + metaCommands();
+ const QSet<QString> &commands = CppCodeParser::topic_commands + CppCodeParser::meta_commands;
clang_tokenize(tu, clang_getCursorExtent(tuCur), &tokens, &numTokens);
for (unsigned int i = 0; i < numTokens; ++i) {
@@ -1496,18 +1725,11 @@ void ClangCodeParser::parseSourceFile(const Location & /*location*/, const QStri
Doc::trimCStyleComment(loc, comment);
// Doc constructor parses the comment.
- Doc doc(loc, end_loc, comment, commands, topicCommands());
+ Doc doc(loc, end_loc, comment, commands, CppCodeParser::topic_commands);
if (hasTooManyTopics(doc))
continue;
- DocList docs;
- QString topic;
- NodeList nodes;
- const TopicList &topics = doc.topicsUsed();
- if (!topics.isEmpty())
- topic = topics[0].m_topic;
-
- if (topic.isEmpty()) {
+ if (doc.topicsUsed().isEmpty()) {
Node *n = nullptr;
if (i + 1 < numTokens) {
// Try to find the next declaration.
@@ -1519,13 +1741,13 @@ void ClangCodeParser::parseSourceFile(const Location & /*location*/, const QStri
}
if (n) {
- nodes.append(n);
- docs.append(doc);
+ parse_result.tied.emplace_back(TiedDocumentation{doc, n});
} else if (CodeParser::isWorthWarningAbout(doc)) {
bool future = false;
if (doc.metaCommandsUsed().contains(COMMAND_SINCE)) {
- QString sinceVersion = doc.metaCommandArgs(COMMAND_SINCE)[0].first;
- if (getUnpatchedVersion(sinceVersion) > getUnpatchedVersion(m_version))
+ QString sinceVersion = doc.metaCommandArgs(COMMAND_SINCE).at(0).first;
+ if (getUnpatchedVersion(sinceVersion) >
+ getUnpatchedVersion(Config::instance().get(CONFIG_VERSION).asString()))
future = true;
}
if (!future) {
@@ -1539,35 +1761,42 @@ void ClangCodeParser::parseSourceFile(const Location & /*location*/, const QStri
}
}
} else {
- // Store the namespace scope from lexical parents of the comment
- m_namespaceScope.clear();
+ parse_result.untied.emplace_back(UntiedDocumentation{doc, QStringList()});
+
CXCursor cur = clang_getCursor(tu, commentLoc);
while (true) {
CXCursorKind kind = clang_getCursorKind(cur);
if (clang_isTranslationUnit(kind) || clang_isInvalid(kind))
break;
- if (kind == CXCursor_Namespace)
- m_namespaceScope << fromCXString(clang_getCursorSpelling(cur));
+ if (kind == CXCursor_Namespace) {
+ parse_result.untied.back().context << fromCXString(clang_getCursorSpelling(cur));
+ }
cur = clang_getCursorLexicalParent(cur);
}
- processTopicArgs(doc, topic, nodes, docs);
}
- processMetaCommands(nodes, docs);
}
clang_disposeTokens(tu, tokens, numTokens);
- clang_disposeTranslationUnit(tu);
- clang_disposeIndex(index_);
m_namespaceScope.clear();
s_fn.clear();
+
+ return parse_result;
}
/*!
Use clang to parse the function signature from a function
command. \a location is used for reporting errors. \a fnSignature
is the string to parse. It is always a function decl.
+ \a idTag is the optional bracketed argument passed to \\fn, or
+ an empty string.
+ \a context is a string list representing the scope (namespaces)
+ under which the function is declared.
+
+ Returns a variant that's either a Node instance tied to the
+ function declaration, or a parsing failure for later processing.
*/
-Node *ClangCodeParser::parseFnArg(const Location &location, const QString &fnSignature, const QString &idTag)
+std::variant<Node*, FnMatchError> FnCommandParser::operator()(const Location &location, const QString &fnSignature,
+ const QString &idTag, QStringList context)
{
Node *fnNode = nullptr;
/*
@@ -1619,38 +1848,35 @@ Node *ClangCodeParser::parseFnArg(const Location &location, const QString &fnSig
| CXTranslationUnit_SkipFunctionBodies
| CXTranslationUnit_KeepGoing);
- CXIndex index = clang_createIndex(1, kClangDontDisplayDiagnostics);
+ CompilationIndex index{ clang_createIndex(1, kClangDontDisplayDiagnostics) };
- std::vector<const char *> args(std::begin(defaultArgs_), std::end(defaultArgs_));
- // Add the defines from the qdocconf file.
- for (const auto &p : qAsConst(m_defines))
- args.push_back(p.constData());
- if (!m_pchName.isEmpty()) {
- args.push_back("-w");
- args.push_back("-include-pch");
- args.push_back(m_pchName.constData());
+ getDefaultArgs(m_defines, m_args);
+
+ if (m_pch) {
+ m_args.push_back("-w");
+ m_args.push_back("-include-pch");
+ m_args.push_back((*m_pch).get().name.constData());
}
- CXTranslationUnit tu;
- s_fn.clear();
- for (const auto &ns : qAsConst(m_namespaceScope))
+
+ TranslationUnit tu;
+ QByteArray s_fn{};
+ for (const auto &ns : std::as_const(context))
s_fn.prepend("namespace " + ns.toUtf8() + " {");
s_fn += fnSignature.toUtf8();
if (!s_fn.endsWith(";"))
s_fn += "{ }";
- s_fn.append(m_namespaceScope.size(), '}');
+ s_fn.append(context.size(), '}');
const char *dummyFileName = fnDummyFileName;
CXUnsavedFile unsavedFile { dummyFileName, s_fn.constData(),
static_cast<unsigned long>(s_fn.size()) };
- CXErrorCode err = clang_parseTranslationUnit2(index, dummyFileName, args.data(),
- int(args.size()), &unsavedFile, 1, flags, &tu);
- qCDebug(lcQdoc) << __FUNCTION__ << "clang_parseTranslationUnit2(" << dummyFileName << args
+ CXErrorCode err = clang_parseTranslationUnit2(index, dummyFileName, m_args.data(),
+ int(m_args.size()), &unsavedFile, 1, flags, &tu.tu);
+ qCDebug(lcQdoc) << __FUNCTION__ << "clang_parseTranslationUnit2(" << dummyFileName << m_args
<< ") returns" << err;
printDiagnostics(tu);
if (err || !tu) {
location.error(QStringLiteral("clang could not parse \\fn %1").arg(fnSignature));
- clang_disposeTranslationUnit(tu);
- clang_disposeIndex(index);
return fnNode;
} else {
/*
@@ -1663,59 +1889,16 @@ Node *ClangCodeParser::parseFnArg(const Location &location, const QString &fnSig
ClangVisitor visitor(m_qdb, m_allHeaders);
bool ignoreSignature = false;
visitor.visitFnArg(cur, &fnNode, ignoreSignature);
- /*
- If the visitor couldn't find a FunctionNode for the
- signature, then print the clang diagnostics if there
- were any.
- */
- if (fnNode == nullptr) {
+
+ if (!fnNode) {
unsigned diagnosticCount = clang_getNumDiagnostics(tu);
const auto &config = Config::instance();
if (diagnosticCount > 0 && (!config.preparing() || config.singleExec())) {
- bool report = true;
- QStringList signature = fnSignature.split(QChar('('));
- if (signature.size() > 1) {
- QStringList qualifiedName = signature.at(0).split(QChar(' '));
- qualifiedName = qualifiedName.last().split(QLatin1String("::"));
- if (qualifiedName.size() > 1) {
- QString qualifier = qualifiedName.at(0);
- int i = 0;
- while (qualifier.size() > i && !qualifier.at(i).isLetter())
- qualifier[i++] = QChar(' ');
- if (i > 0)
- qualifier = qualifier.simplified();
- ClassNode *cn = m_qdb->findClassNode(QStringList(qualifier));
- if (cn && cn->isInternal())
- report = false;
- }
- }
- if (report) {
- location.warning(
- QStringLiteral("clang couldn't find function when parsing \\fn %1").arg(fnSignature));
- }
+ return FnMatchError{ fnSignature, location };
}
}
}
- clang_disposeTranslationUnit(tu);
- clang_disposeIndex(index);
return fnNode;
}
-void ClangCodeParser::printDiagnostics(const CXTranslationUnit &translationUnit) const
-{
- if (!lcQdocClang().isDebugEnabled())
- return;
-
- static const auto displayOptions = CXDiagnosticDisplayOptions::CXDiagnostic_DisplaySourceLocation
- | CXDiagnosticDisplayOptions::CXDiagnostic_DisplayColumn
- | CXDiagnosticDisplayOptions::CXDiagnostic_DisplayOption;
-
- for (unsigned i = 0, numDiagnostics = clang_getNumDiagnostics(translationUnit); i < numDiagnostics; ++i) {
- auto diagnostic = clang_getDiagnostic(translationUnit, i);
- auto formattedDiagnostic = clang_formatDiagnostic(diagnostic, displayOptions);
- qCDebug(lcQdocClang) << clang_getCString(formattedDiagnostic);
- clang_disposeString(formattedDiagnostic);
- }
-}
-
QT_END_NAMESPACE
diff --git a/src/qdoc/qdoc/src/qdoc/clangcodeparser.h b/src/qdoc/qdoc/src/qdoc/clangcodeparser.h
new file mode 100644
index 000000000..d7568f9a4
--- /dev/null
+++ b/src/qdoc/qdoc/src/qdoc/clangcodeparser.h
@@ -0,0 +1,94 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#ifndef CLANGCODEPARSER_H
+#define CLANGCODEPARSER_H
+
+#include "codeparser.h"
+#include "parsererror.h"
+#include "config.h"
+
+#include <QtCore/qtemporarydir.h>
+#include <QtCore/QStringList>
+
+#include <optional>
+
+typedef struct CXTranslationUnitImpl *CXTranslationUnit;
+
+class CppCodeParser;
+
+QT_BEGIN_NAMESPACE
+
+struct ParsedCppFileIR {
+ std::vector<UntiedDocumentation> untied;
+ std::vector<TiedDocumentation> tied;
+};
+
+struct PCHFile {
+ QTemporaryDir dir;
+ QByteArray name;
+};
+
+std::optional<PCHFile> buildPCH(
+ QDocDatabase* qdb,
+ QString module_header,
+ const std::set<Config::HeaderFilePath>& all_headers,
+ const std::vector<QByteArray>& include_paths,
+ const QList<QByteArray>& defines
+);
+
+struct FnCommandParser {
+ FnCommandParser(
+ QDocDatabase* qdb,
+ const std::set<Config::HeaderFilePath>& all_headers,
+ const QList<QByteArray>& defines,
+ std::optional<std::reference_wrapper<const PCHFile>> pch
+ ) : m_qdb{qdb},
+ m_allHeaders{all_headers},
+ m_defines{defines},
+ m_args{},
+ m_pch{pch}
+ {}
+
+ std::variant<Node*, FnMatchError> operator()(
+ const Location &location,
+ const QString &fnSignature,
+ const QString &idTag,
+ QStringList context
+ );
+
+private:
+ QDocDatabase* m_qdb;
+ const std::set<Config::HeaderFilePath>& m_allHeaders; // file name->path
+ QList<QByteArray> m_defines {};
+ std::vector<const char *> m_args {};
+ std::optional<std::reference_wrapper<const PCHFile>> m_pch;
+};
+
+class ClangCodeParser
+{
+public:
+ ClangCodeParser(
+ QDocDatabase* qdb,
+ Config&,
+ const std::vector<QByteArray>& include_paths,
+ const QList<QByteArray>& defines,
+ std::optional<std::reference_wrapper<const PCHFile>> pch
+ );
+
+ ParsedCppFileIR parse_cpp_file(const QString &filePath);
+
+private:
+ QDocDatabase* m_qdb{};
+ std::set<Config::HeaderFilePath> m_allHeaders {}; // file name->path
+ const std::vector<QByteArray>& m_includePaths;
+ QList<QByteArray> m_defines {};
+ std::vector<const char *> m_args {};
+ QStringList m_namespaceScope {};
+ QByteArray s_fn;
+ std::optional<std::reference_wrapper<const PCHFile>> m_pch;
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/qdoc/classnode.cpp b/src/qdoc/qdoc/src/qdoc/classnode.cpp
index f4ab72144..1b132f91e 100644
--- a/src/qdoc/classnode.cpp
+++ b/src/qdoc/qdoc/src/qdoc/classnode.cpp
@@ -1,30 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the tools applications of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "classnode.h"
@@ -115,37 +90,6 @@ PropertyNode *ClassNode::findPropertyNode(const QString &name)
}
/*!
- This function does a recursive search of this class node's
- base classes looking for one that has a QML element. If it
- finds one, it returns the pointer to that QML element. If
- it doesn't find one, it returns null.
- */
-QmlTypeNode *ClassNode::findQmlBaseNode()
-{
- QmlTypeNode *result = nullptr;
- const QList<RelatedClass> &bases = baseClasses();
-
- if (!bases.isEmpty()) {
- for (const RelatedClass &base : bases) {
- ClassNode *cn = base.m_node;
- if (cn && cn->qmlElement()) {
- return cn->qmlElement();
- }
- }
- for (const RelatedClass &base : bases) {
- ClassNode *cn = base.m_node;
- if (cn) {
- result = cn->findQmlBaseNode();
- if (result != nullptr) {
- return result;
- }
- }
- }
- }
- return result;
-}
-
-/*!
\a fn is an overriding function in this class or in a class
derived from this class. Find the node for the function that
\a fn overrides in this class's children or in one of this
@@ -299,7 +243,7 @@ void ClassNode::removePrivateAndInternalBases()
*/
void ClassNode::resolvePropertyOverriddenFromPtrs(PropertyNode *pn)
{
- for (const auto &baseClass : qAsConst(baseClasses())) {
+ for (const auto &baseClass : std::as_const(baseClasses())) {
ClassNode *cn = baseClass.m_node;
if (cn) {
Node *n = cn->findNonfunctionChild(pn->name(), &Node::isProperty);
diff --git a/src/qdoc/classnode.h b/src/qdoc/qdoc/src/qdoc/classnode.h
index 404ca1bfc..1ac944a34 100644
--- a/src/qdoc/classnode.h
+++ b/src/qdoc/qdoc/src/qdoc/classnode.h
@@ -1,37 +1,11 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the tools applications of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#ifndef CLASSNODE_H
#define CLASSNODE_H
#include "aggregate.h"
#include "relatedclass.h"
-#include "usingclause.h"
#include <QtCore/qglobal.h>
#include <QtCore/qlist.h>
@@ -53,8 +27,6 @@ public:
[[nodiscard]] bool isClassNode() const override { return true; }
[[nodiscard]] bool isRelatableType() const override { return true; }
[[nodiscard]] bool isWrapper() const override { return m_wrapper; }
- [[nodiscard]] QString obsoleteLink() const override { return m_obsoleteLink; }
- void setObsoleteLink(const QString &t) override { m_obsoleteLink = t; }
void setWrapper() override { m_wrapper = true; }
void addResolvedBaseClass(Access access, ClassNode *node);
@@ -66,20 +38,20 @@ public:
QList<RelatedClass> &baseClasses() { return m_bases; }
QList<RelatedClass> &derivedClasses() { return m_derived; }
QList<RelatedClass> &ignoredBaseClasses() { return m_ignoredBases; }
- QList<UsingClause> &usingClauses() { return m_usingClauses; }
[[nodiscard]] const QList<RelatedClass> &baseClasses() const { return m_bases; }
- QmlTypeNode *qmlElement() { return m_qmlElement; }
- void setQmlElement(QmlTypeNode *qcn) { m_qmlElement = qcn; }
[[nodiscard]] bool isAbstract() const override { return m_abstract; }
void setAbstract(bool b) override { m_abstract = b; }
PropertyNode *findPropertyNode(const QString &name);
- QmlTypeNode *findQmlBaseNode();
FunctionNode *findOverriddenFunction(const FunctionNode *fn);
PropertyNode *findOverriddenProperty(const FunctionNode *fn);
[[nodiscard]] bool docMustBeGenerated() const override;
+ void insertQmlNativeType(QmlTypeNode *qmlTypeNode) { m_nativeTypeForQml << qmlTypeNode; }
+ bool isQmlNativeType() { return !m_nativeTypeForQml.empty(); }
+ const QSet<QmlTypeNode *> &qmlNativeTypes() { return m_nativeTypeForQml; }
+
private:
void promotePublicBases(const QList<RelatedClass> &bases);
@@ -87,11 +59,9 @@ private:
QList<RelatedClass> m_bases {};
QList<RelatedClass> m_derived {};
QList<RelatedClass> m_ignoredBases {};
- QList<UsingClause> m_usingClauses {};
bool m_abstract { false };
bool m_wrapper { false };
- QString m_obsoleteLink {};
- QmlTypeNode *m_qmlElement { nullptr };
+ QSet<QmlTypeNode *> m_nativeTypeForQml;
};
QT_END_NAMESPACE
diff --git a/src/qdoc/codechunk.cpp b/src/qdoc/qdoc/src/qdoc/codechunk.cpp
index ed27d5f2a..889e091af 100644
--- a/src/qdoc/codechunk.cpp
+++ b/src/qdoc/qdoc/src/qdoc/codechunk.cpp
@@ -1,30 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2019 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the tools applications of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2019 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "codechunk.h"
diff --git a/src/qdoc/qdoc/src/qdoc/codechunk.h b/src/qdoc/qdoc/src/qdoc/codechunk.h
new file mode 100644
index 000000000..00ad26c6d
--- /dev/null
+++ b/src/qdoc/qdoc/src/qdoc/codechunk.h
@@ -0,0 +1,74 @@
+// Copyright (C) 2019 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#ifndef CODECHUNK_H
+#define CODECHUNK_H
+
+#include <QtCore/qstring.h>
+
+QT_BEGIN_NAMESPACE
+
+// ### get rid of that class
+
+class CodeChunk
+{
+public:
+ CodeChunk() : m_hotspot(-1) { }
+
+ void append(const QString &lexeme);
+ void appendHotspot()
+ {
+ if (m_hotspot == -1)
+ m_hotspot = m_str.size();
+ }
+
+ [[nodiscard]] bool isEmpty() const { return m_str.isEmpty(); }
+ void clear() { m_str.clear(); }
+ [[nodiscard]] QString toString() const { return m_str; }
+ [[nodiscard]] QString left() const
+ {
+ return m_str.left(m_hotspot == -1 ? m_str.size() : m_hotspot);
+ }
+ [[nodiscard]] QString right() const
+ {
+ return m_str.mid(m_hotspot == -1 ? m_str.size() : m_hotspot);
+ }
+
+private:
+ QString m_str {};
+ qsizetype m_hotspot {};
+};
+
+inline bool operator==(const CodeChunk &c, const CodeChunk &d)
+{
+ return c.toString() == d.toString();
+}
+
+inline bool operator!=(const CodeChunk &c, const CodeChunk &d)
+{
+ return !(c == d);
+}
+
+inline bool operator<(const CodeChunk &c, const CodeChunk &d)
+{
+ return c.toString() < d.toString();
+}
+
+inline bool operator>(const CodeChunk &c, const CodeChunk &d)
+{
+ return d < c;
+}
+
+inline bool operator<=(const CodeChunk &c, const CodeChunk &d)
+{
+ return !(c > d);
+}
+
+inline bool operator>=(const CodeChunk &c, const CodeChunk &d)
+{
+ return !(c < d);
+}
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/qdoc/codemarker.cpp b/src/qdoc/qdoc/src/qdoc/codemarker.cpp
index f42f50d62..28f84a946 100644
--- a/src/qdoc/codemarker.cpp
+++ b/src/qdoc/qdoc/src/qdoc/codemarker.cpp
@@ -1,30 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the tools applications of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "codemarker.h"
@@ -33,14 +8,18 @@
#include "functionnode.h"
#include "node.h"
#include "propertynode.h"
+#include "qmlpropertynode.h"
#include <QtCore/qobjectdefs.h>
QT_BEGIN_NAMESPACE
+using namespace Qt::StringLiterals;
+
QString CodeMarker::s_defaultLang;
QList<CodeMarker *> CodeMarker::s_markers;
+
/*!
When a code marker constructs itself, it puts itself into
the static list of code markers. All the code markers in
@@ -82,8 +61,8 @@ void CodeMarker::terminateMarker()
*/
void CodeMarker::initialize()
{
- s_defaultLang = Config::instance().getString(CONFIG_LANGUAGE);
- for (const auto &marker : qAsConst(s_markers))
+ s_defaultLang = Config::instance().get(CONFIG_LANGUAGE).asString();
+ for (const auto &marker : std::as_const(s_markers))
marker->initializeMarker();
}
@@ -92,7 +71,7 @@ void CodeMarker::initialize()
*/
void CodeMarker::terminate()
{
- for (const auto &marker : qAsConst(s_markers))
+ for (const auto &marker : std::as_const(s_markers))
marker->terminateMarker();
}
@@ -102,7 +81,7 @@ CodeMarker *CodeMarker::markerForCode(const QString &code)
if (defaultMarker != nullptr && defaultMarker->recognizeCode(code))
return defaultMarker;
- for (const auto &marker : qAsConst(s_markers)) {
+ for (const auto &marker : std::as_const(s_markers)) {
if (marker->recognizeCode(code))
return marker;
}
@@ -118,7 +97,7 @@ CodeMarker *CodeMarker::markerForFileName(const QString &fileName)
QString ext = fileName.mid(dot + 1);
if (defaultMarker != nullptr && defaultMarker->recognizeExtension(ext))
return defaultMarker;
- for (const auto &marker : qAsConst(s_markers)) {
+ for (const auto &marker : std::as_const(s_markers)) {
if (marker->recognizeExtension(ext))
return marker;
}
@@ -129,7 +108,7 @@ CodeMarker *CodeMarker::markerForFileName(const QString &fileName)
CodeMarker *CodeMarker::markerForLanguage(const QString &lang)
{
- for (const auto &marker : qAsConst(s_markers)) {
+ for (const auto &marker : std::as_const(s_markers)) {
if (marker->recognizeLanguage(lang))
return marker;
}
@@ -152,11 +131,52 @@ QString CodeMarker::stringForNode(const Node *node)
}
/*!
+ Returns a string representing the \a node status, set using \preliminary, \since,
+ and \deprecated commands.
+
+ If a string is returned, it is one of:
+ \list
+ \li \c {"preliminary"}
+ \li \c {"since <version_since>, deprecated in <version_deprecated>"}
+ \li \c {"since <version_since>, until <version_deprecated>"}
+ \li \c {"since <version_since>"}
+ \li \c {"since <version_since>, deprecated"}
+ \li \c {"deprecated in <version_deprecated>"}
+ \li \c {"until <version_deprecated>"}
+ \li \c {"deprecated"}
+ \endlist
+
+ If \a node has no related status information, returns std::nullopt.
+*/
+static std::optional<QString> nodeStatusAsString(const Node *node)
+{
+ if (node->isPreliminary())
+ return std::optional(u"preliminary"_s);
+
+ QStringList result;
+ if (const auto &since = node->since(); !since.isEmpty())
+ result << "since %1"_L1.arg(since);
+ if (const auto &deprecated = node->deprecatedSince(); !deprecated.isEmpty()) {
+ if (node->isDeprecated())
+ result << "deprecated in %1"_L1.arg(deprecated);
+ else
+ result << "until %1"_L1.arg(deprecated);
+ } else if (node->isDeprecated()) {
+ result << u"deprecated"_s;
+ }
+
+ return result.isEmpty() ? std::nullopt : std::optional(result.join(u", "_s));
+}
+
+/*!
Returns the 'extra' synopsis string for \a node with status information,
using a specified section \a style.
*/
QString CodeMarker::extraSynopsis(const Node *node, Section::Style style)
{
+ if (style != Section::Summary && style != Section::Details)
+ return {};
+
QStringList extra;
if (style == Section::Details) {
switch (node->nodeType()) {
@@ -174,6 +194,12 @@ QString CodeMarker::extraSynopsis(const Node *node, Section::Style style)
extra << "virtual";
}
+ if (func->isExplicit()) extra << "explicit";
+ if (func->isConstexpr()) extra << "constexpr";
+ if (auto noexcept_info = func->getNoexcept()) {
+ extra << (QString("noexcept") + (!(*noexcept_info).isEmpty() ? "(...)" : ""));
+ }
+
if (func->access() == Access::Protected)
extra << "protected";
else if (func->access() == Access::Private)
@@ -196,36 +222,43 @@ QString CodeMarker::extraSynopsis(const Node *node, Section::Style style)
break;
case Node::Property: {
auto propertyNode = static_cast<const PropertyNode *>(node);
- if (propertyNode->propertyType() == PropertyNode::Bindable)
+ if (propertyNode->propertyType() == PropertyNode::PropertyType::BindableProperty)
extra << "bindable";
if (!propertyNode->isWritable())
extra << "read-only";
}
break;
+ case Node::QmlProperty: {
+ auto qmlProperty = static_cast<const QmlPropertyNode *>(node);
+ if (qmlProperty->isDefault())
+ extra << u"default"_s;
+ // Call non-const overloads to ensure attributes are fetched from
+ // associated C++ properties
+ else if (const_cast<QmlPropertyNode *>(qmlProperty)->isReadOnly())
+ extra << u"read-only"_s;
+ else if (const_cast<QmlPropertyNode *>(qmlProperty)->isRequired())
+ extra << u"required"_s;
+ else if (!qmlProperty->defaultValue().isEmpty()) {
+ extra << u"default: "_s + qmlProperty->defaultValue();
+ }
+ break;
+ }
default:
break;
}
- } else if (style == Section::Summary) {
- if (node->isPreliminary())
- extra << "preliminary";
- else if (node->isDeprecated()) {
- extra << "deprecated";
- if (const QString &since = node->deprecatedSince(); !since.isEmpty())
- extra << QStringLiteral("(%1)").arg(since);
- }
}
- if (style == Section::Details && !node->since().isEmpty()) {
+ // Add status for both Summary and Details
+ if (auto status = nodeStatusAsString(node)) {
if (!extra.isEmpty())
- extra.last() += QLatin1Char(',');
- extra << "since" << node->since();
+ extra.last() += ','_L1;
+ extra << *status;
}
QString extraStr = extra.join(QLatin1Char(' '));
if (!extraStr.isEmpty()) {
extraStr.prepend(style == Section::Details ? '[' : '(');
extraStr.append(style == Section::Details ? ']' : ')');
- extraStr.append(' ');
}
return extraStr;
@@ -238,7 +271,7 @@ static const QString squot = QLatin1String("&quot;");
QString CodeMarker::protect(const QString &str)
{
- qsizetype n = str.length();
+ qsizetype n = str.size();
QString marked;
marked.reserve(n * 2 + 30);
const QChar *data = str.constData();
@@ -265,7 +298,7 @@ QString CodeMarker::protect(const QString &str)
void CodeMarker::appendProtectedString(QString *output, QStringView str)
{
- qsizetype n = str.length();
+ qsizetype n = str.size();
output->reserve(output->size() + n * 2 + 30);
const QChar *data = str.constData();
for (int i = 0; i != n; ++i) {
@@ -354,6 +387,7 @@ QString CodeMarker::taggedNode(const Node *node)
case Node::Enum:
tag = QLatin1String("@enum");
break;
+ case Node::TypeAlias:
case Node::Typedef:
tag = QLatin1String("@typedef");
break;
@@ -383,15 +417,12 @@ QString CodeMarker::taggedQmlNode(const Node *node)
if (node->isFunction()) {
const auto *fn = static_cast<const FunctionNode *>(node);
switch (fn->metaness()) {
- case FunctionNode::JsSignal:
case FunctionNode::QmlSignal:
tag = QLatin1String("@signal");
break;
- case FunctionNode::JsSignalHandler:
case FunctionNode::QmlSignalHandler:
tag = QLatin1String("@signalhandler");
break;
- case FunctionNode::JsMethod:
case FunctionNode::QmlMethod:
tag = QLatin1String("@method");
break;
@@ -399,7 +430,7 @@ QString CodeMarker::taggedQmlNode(const Node *node)
tag = QLatin1String("@unknown");
break;
}
- } else if (node->isQmlProperty() || node->isJsProperty()) {
+ } else if (node->isQmlProperty()) {
tag = QLatin1String("@property");
} else {
tag = QLatin1String("@unknown");
diff --git a/src/qdoc/codemarker.h b/src/qdoc/qdoc/src/qdoc/codemarker.h
index 3a1d8813e..af668b650 100644
--- a/src/qdoc/codemarker.h
+++ b/src/qdoc/qdoc/src/qdoc/codemarker.h
@@ -1,30 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the tools applications of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#ifndef CODEMARKER_H
#define CODEMARKER_H
@@ -62,9 +37,7 @@ public:
{
return QString();
}
- virtual QString markedUpIncludes(const QStringList & /*includes*/) { return QString(); }
- virtual QString functionBeginRegExp(const QString & /*funcName*/) { return QString(); }
- virtual QString functionEndRegExp(const QString & /*funcName*/) { return QString(); }
+ virtual QString markedUpInclude(const QString & /*include*/) { return QString(); }
static void initialize();
static void terminate();
diff --git a/src/qdoc/qdoc/src/qdoc/codeparser.cpp b/src/qdoc/qdoc/src/qdoc/codeparser.cpp
new file mode 100644
index 000000000..ad35e6d65
--- /dev/null
+++ b/src/qdoc/qdoc/src/qdoc/codeparser.cpp
@@ -0,0 +1,139 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "codeparser.h"
+
+#include "config.h"
+#include "generator.h"
+#include "node.h"
+#include "qdocdatabase.h"
+
+#include <QtCore/qregularexpression.h>
+
+QT_BEGIN_NAMESPACE
+
+QList<CodeParser *> CodeParser::s_parsers;
+
+/*!
+ The constructor adds this code parser to the static
+ list of code parsers.
+ */
+CodeParser::CodeParser()
+{
+ m_qdb = QDocDatabase::qdocDB();
+ s_parsers.prepend(this);
+}
+
+/*!
+ The destructor removes this code parser from the static
+ list of code parsers.
+ */
+CodeParser::~CodeParser()
+{
+ s_parsers.removeAll(this);
+}
+
+/*!
+ Terminating a code parser is trivial.
+ */
+void CodeParser::terminateParser()
+{
+ // nothing.
+}
+
+/*!
+ All the code parsers in the static list are initialized here,
+ after the qdoc configuration variables have been set.
+ */
+void CodeParser::initialize()
+{
+ for (const auto &parser : std::as_const(s_parsers))
+ parser->initializeParser();
+}
+
+/*!
+ All the code parsers in the static list are terminated here.
+ */
+void CodeParser::terminate()
+{
+ for (const auto parser : s_parsers)
+ parser->terminateParser();
+}
+
+CodeParser *CodeParser::parserForLanguage(const QString &language)
+{
+ for (const auto parser : std::as_const(s_parsers)) {
+ if (parser->language() == language)
+ return parser;
+ }
+ return nullptr;
+}
+
+CodeParser *CodeParser::parserForSourceFile(const QString &filePath)
+{
+ QString fileName = QFileInfo(filePath).fileName();
+
+ for (const auto &parser : s_parsers) {
+ const QStringList sourcePatterns = parser->sourceFileNameFilter();
+ for (const QString &pattern : sourcePatterns) {
+ auto re = QRegularExpression::fromWildcard(pattern, Qt::CaseInsensitive);
+ if (re.match(fileName).hasMatch())
+ return parser;
+ }
+ }
+ return nullptr;
+}
+
+/*!
+ \internal
+ */
+void CodeParser::extractPageLinkAndDesc(QStringView arg, QString *link, QString *desc)
+{
+ static const QRegularExpression bracedRegExp(
+ QRegularExpression::anchoredPattern(QLatin1String(R"(\{([^{}]*)\}(?:\{([^{}]*)\})?)")));
+ auto match = bracedRegExp.matchView(arg);
+ if (match.hasMatch()) {
+ *link = match.captured(1);
+ *desc = match.captured(2);
+ if (desc->isEmpty())
+ *desc = *link;
+ } else {
+ qsizetype spaceAt = arg.indexOf(QLatin1Char(' '));
+ if (arg.contains(QLatin1String(".html")) && spaceAt != -1) {
+ *link = arg.left(spaceAt).trimmed().toString();
+ *desc = arg.mid(spaceAt).trimmed().toString();
+ } else {
+ *link = arg.toString();
+ *desc = *link;
+ }
+ }
+}
+
+/*!
+ \internal
+ */
+void CodeParser::setLink(Node *node, Node::LinkType linkType, const QString &arg)
+{
+ QString link;
+ QString desc;
+ extractPageLinkAndDesc(arg, &link, &desc);
+ node->setLink(linkType, link, desc);
+}
+
+/*!
+ \brief Test for whether a doc comment warrants warnings.
+
+ Returns true if qdoc should report that it has found something
+ wrong with the qdoc comment in \a doc. Sometimes, qdoc should
+ not report the warning, for example, when the comment contains
+ the \c internal command, which normally means qdoc will not use
+ the comment in the documentation anyway, so there is no point
+ in reporting warnings about it.
+ */
+bool CodeParser::isWorthWarningAbout(const Doc &doc)
+{
+ return (Config::instance().showInternal()
+ || !doc.metaCommandsUsed().contains(QStringLiteral("internal")));
+}
+
+QT_END_NAMESPACE
diff --git a/src/qdoc/qdoc/src/qdoc/codeparser.h b/src/qdoc/qdoc/src/qdoc/codeparser.h
new file mode 100644
index 000000000..51d1ac2a4
--- /dev/null
+++ b/src/qdoc/qdoc/src/qdoc/codeparser.h
@@ -0,0 +1,142 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#ifndef CODEPARSER_H
+#define CODEPARSER_H
+
+#include "node.h"
+
+#include <QtCore/qset.h>
+
+QT_BEGIN_NAMESPACE
+
+#define COMMAND_ABSTRACT QLatin1String("abstract")
+#define COMMAND_CLASS QLatin1String("class")
+#define COMMAND_COMPARES QLatin1String("compares")
+#define COMMAND_COMPARESWITH QLatin1String("compareswith")
+#define COMMAND_DEFAULT QLatin1String("default")
+#define COMMAND_DEPRECATED QLatin1String("deprecated") // ### don't document
+#define COMMAND_DONTDOCUMENT QLatin1String("dontdocument")
+#define COMMAND_ENUM QLatin1String("enum")
+#define COMMAND_EXAMPLE QLatin1String("example")
+#define COMMAND_EXTERNALPAGE QLatin1String("externalpage")
+#define COMMAND_FN QLatin1String("fn")
+#define COMMAND_GROUP QLatin1String("group")
+#define COMMAND_HEADERFILE QLatin1String("headerfile")
+#define COMMAND_INGROUP QLatin1String("ingroup")
+#define COMMAND_INHEADERFILE QLatin1String("inheaderfile")
+#define COMMAND_INMODULE QLatin1String("inmodule") // ### don't document
+#define COMMAND_INPUBLICGROUP QLatin1String("inpublicgroup")
+#define COMMAND_INQMLMODULE QLatin1String("inqmlmodule")
+#define COMMAND_INTERNAL QLatin1String("internal")
+#define COMMAND_MACRO QLatin1String("macro")
+#define COMMAND_MODULE QLatin1String("module")
+#define COMMAND_MODULESTATE QLatin1String("modulestate")
+#define COMMAND_NAMESPACE QLatin1String("namespace")
+#define COMMAND_NEXTPAGE QLatin1String("nextpage")
+#define COMMAND_NOAUTOLIST QLatin1String("noautolist")
+#define COMMAND_NONREENTRANT QLatin1String("nonreentrant")
+#define COMMAND_OBSOLETE QLatin1String("obsolete")
+#define COMMAND_OVERLOAD QLatin1String("overload")
+#define COMMAND_PAGE QLatin1String("page")
+#define COMMAND_PRELIMINARY QLatin1String("preliminary")
+#define COMMAND_PREVIOUSPAGE QLatin1String("previouspage")
+#define COMMAND_PROPERTY QLatin1String("property")
+#define COMMAND_QMLABSTRACT QLatin1String("qmlabstract")
+#define COMMAND_QMLATTACHEDMETHOD QLatin1String("qmlattachedmethod")
+#define COMMAND_QMLATTACHEDPROPERTY QLatin1String("qmlattachedproperty")
+#define COMMAND_QMLATTACHEDSIGNAL QLatin1String("qmlattachedsignal")
+#define COMMAND_QMLVALUETYPE QLatin1String("qmlvaluetype")
+#define COMMAND_QMLCLASS QLatin1String("qmlclass")
+#define COMMAND_QMLDEFAULT QLatin1String("qmldefault")
+#define COMMAND_QMLENUMERATORSFROM QLatin1String("qmlenumeratorsfrom")
+#define COMMAND_QMLINHERITS QLatin1String("inherits")
+#define COMMAND_QMLINSTANTIATES QLatin1String("instantiates") // TODO Qt 7.0.0 - Remove: Deprecated since 6.8.
+#define COMMAND_QMLMETHOD QLatin1String("qmlmethod")
+#define COMMAND_QMLMODULE QLatin1String("qmlmodule")
+#define COMMAND_QMLNATIVETYPE QLatin1String("nativetype")
+#define COMMAND_QMLPROPERTY QLatin1String("qmlproperty")
+#define COMMAND_QMLPROPERTYGROUP QLatin1String("qmlpropertygroup")
+#define COMMAND_QMLREADONLY QLatin1String("readonly")
+#define COMMAND_QMLREQUIRED QLatin1String("required")
+#define COMMAND_QMLSIGNAL QLatin1String("qmlsignal")
+#define COMMAND_QMLTYPE QLatin1String("qmltype")
+#define COMMAND_QTCMAKEPACKAGE QLatin1String("qtcmakepackage")
+#define COMMAND_QTCMAKETARGETITEM QLatin1String("qtcmaketargetitem")
+#define COMMAND_QTVARIABLE QLatin1String("qtvariable")
+#define COMMAND_REENTRANT QLatin1String("reentrant")
+#define COMMAND_REIMP QLatin1String("reimp")
+#define COMMAND_RELATES QLatin1String("relates")
+#define COMMAND_SINCE QLatin1String("since")
+#define COMMAND_STRUCT QLatin1String("struct")
+#define COMMAND_SUBTITLE QLatin1String("subtitle")
+#define COMMAND_STARTPAGE QLatin1String("startpage")
+#define COMMAND_THREADSAFE QLatin1String("threadsafe")
+#define COMMAND_TITLE QLatin1String("title")
+#define COMMAND_TYPEALIAS QLatin1String("typealias")
+#define COMMAND_TYPEDEF QLatin1String("typedef")
+#define COMMAND_VARIABLE QLatin1String("variable")
+#define COMMAND_VERSION QLatin1String("version")
+#define COMMAND_UNION QLatin1String("union")
+#define COMMAND_WRAPPER QLatin1String("wrapper")
+#define COMMAND_ATTRIBUTION QLatin1String("attribution")
+
+// deprecated alias of qmlvaluetype
+#define COMMAND_QMLBASICTYPE QLatin1String("qmlbasictype")
+
+class Location;
+class QString;
+class QDocDatabase;
+class CppCodeParser;
+
+struct UntiedDocumentation {
+ Doc documentation;
+ QStringList context;
+};
+
+struct TiedDocumentation {
+ Doc documentation;
+ Node* node;
+};
+
+class CodeParser
+{
+public:
+ static inline const QSet<QString> common_meta_commands{
+ COMMAND_ABSTRACT, COMMAND_DEFAULT, COMMAND_DEPRECATED, COMMAND_INGROUP,
+ COMMAND_INMODULE, COMMAND_INPUBLICGROUP, COMMAND_INQMLMODULE, COMMAND_INTERNAL,
+ COMMAND_MODULESTATE, COMMAND_NOAUTOLIST, COMMAND_NONREENTRANT, COMMAND_OBSOLETE,
+ COMMAND_PRELIMINARY, COMMAND_QMLABSTRACT, COMMAND_QMLDEFAULT, COMMAND_QMLENUMERATORSFROM, COMMAND_QMLINHERITS,
+ COMMAND_QMLREADONLY, COMMAND_QMLREQUIRED, COMMAND_QTCMAKEPACKAGE, COMMAND_QTCMAKETARGETITEM,
+ COMMAND_QTVARIABLE, COMMAND_REENTRANT, COMMAND_SINCE, COMMAND_STARTPAGE, COMMAND_SUBTITLE,
+ COMMAND_THREADSAFE, COMMAND_TITLE, COMMAND_WRAPPER, COMMAND_ATTRIBUTION,
+ };
+
+public:
+ CodeParser();
+ virtual ~CodeParser();
+
+ virtual void initializeParser() = 0;
+ virtual void terminateParser();
+ virtual QString language() = 0;
+ virtual QStringList sourceFileNameFilter() = 0;
+ virtual void parseSourceFile(const Location &location, const QString &filePath, CppCodeParser& cpp_code_parser) = 0;
+
+ static void initialize();
+ static void terminate();
+ static CodeParser *parserForLanguage(const QString &language);
+ static CodeParser *parserForSourceFile(const QString &filePath);
+ static void setLink(Node *node, Node::LinkType linkType, const QString &arg);
+ static bool isWorthWarningAbout(const Doc &doc);
+
+protected:
+ static void extractPageLinkAndDesc(QStringView arg, QString *link, QString *desc);
+ QDocDatabase *m_qdb {};
+
+private:
+ static QList<CodeParser *> s_parsers;
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/qdoc/qdoc/src/qdoc/collectionnode.cpp b/src/qdoc/qdoc/src/qdoc/collectionnode.cpp
new file mode 100644
index 000000000..833a9d037
--- /dev/null
+++ b/src/qdoc/qdoc/src/qdoc/collectionnode.cpp
@@ -0,0 +1,104 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "collectionnode.h"
+
+#include <QtCore/qstringlist.h>
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class CollectionNode
+ \brief A class for holding the members of a collection of doc pages.
+ */
+
+/*!
+ Appends \a node to the collection node's member list, if
+ and only if it isn't already in the member list.
+ */
+void CollectionNode::addMember(Node *node)
+{
+ if (!m_members.contains(node))
+ m_members.append(node);
+}
+
+/*!
+ Returns \c true if this collection node contains at least
+ one namespace node.
+ */
+bool CollectionNode::hasNamespaces() const
+{
+ return std::any_of(m_members.cbegin(), m_members.cend(), [](const Node *member) {
+ return member->isClassNode() && member->isInAPI();
+ });
+}
+
+/*!
+ Returns \c true if this collection node contains at least
+ one class node.
+ */
+bool CollectionNode::hasClasses() const
+{
+ return std::any_of(m_members.cbegin(), m_members.cend(), [](const Node *member) {
+ return member->isClassNode() && member->isInAPI();
+ });
+}
+
+/*!
+ \fn template <typename F> NodeMap CollectionNode::getMembers(const F &&predicate) const
+
+ Returns a map containing this collection node's member nodes for which \c
+ predicate(node) returns \c true. The \a predicate is a function or a
+ lambda that takes a const Node pointer as an argument and returns a bool.
+*/
+
+/*!
+ \fn NodeMap CollectionNode::getMembers(Node::NodeType type) const
+
+ Returns a map containing this collection node's member nodes with
+ a specified node \a type.
+*/
+
+/*!
+ Returns the logical module version.
+*/
+QString CollectionNode::logicalModuleVersion() const
+{
+ QStringList version;
+ version << m_logicalModuleVersionMajor << m_logicalModuleVersionMinor;
+ version.removeAll(QString());
+ return version.join(".");
+}
+
+/*!
+ This function accepts the logical module \a info as a string
+ list. If the logical module info contains the version number,
+ it splits the version number on the '.' character to get the
+ major and minor version numbers. Both major and minor version
+ numbers should be provided, but the minor version number is
+ not strictly necessary.
+ */
+void CollectionNode::setLogicalModuleInfo(const QStringList &info)
+{
+ m_logicalModuleName = info[0];
+ if (info.size() > 1) {
+ QStringList dotSplit = info[1].split(QLatin1Char('.'));
+ m_logicalModuleVersionMajor = dotSplit[0];
+ if (dotSplit.size() > 1)
+ m_logicalModuleVersionMinor = dotSplit[1];
+ else
+ m_logicalModuleVersionMinor = "0";
+ }
+}
+
+/*!
+ \fn void CollectionNode::setState(const QString &state)
+ \fn QString CollectionNode::state()
+
+ Sets or gets a description of this module's state. For example,
+ \e {"Technical Preview"}. This string is used when generating the
+ module's documentation page and reference pages of the module's
+ members.
+*/
+
+QT_END_NAMESPACE
diff --git a/src/qdoc/qdoc/src/qdoc/collectionnode.h b/src/qdoc/qdoc/src/qdoc/collectionnode.h
new file mode 100644
index 000000000..3756d3534
--- /dev/null
+++ b/src/qdoc/qdoc/src/qdoc/collectionnode.h
@@ -0,0 +1,126 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#ifndef COLLECTIONNODE_H
+#define COLLECTIONNODE_H
+
+#include "pagenode.h"
+
+#include <QtCore/qglobal.h>
+#include <QtCore/qstring.h>
+
+QT_BEGIN_NAMESPACE
+
+class CollectionNode : public PageNode
+{
+public:
+ CollectionNode(NodeType type, Aggregate *parent, const QString &name)
+ : PageNode(type, parent, name)
+ {
+ }
+
+ [[nodiscard]] bool isCollectionNode() const override { return true; }
+ [[nodiscard]] QString qtVariable() const override { return m_qtVariable; }
+ void setQtVariable(const QString &v) override { m_qtVariable = v; }
+ [[nodiscard]] QString qtCMakeComponent() const override { return m_qtCMakeComponent; }
+ [[nodiscard]] QString qtCMakeTargetItem() const override { return m_qtCMakeTargetItem; }
+ void setQtCMakeComponent(const QString &target) override { m_qtCMakeComponent = target; }
+ void setQtCMakeTargetItem(const QString &target) override { m_qtCMakeTargetItem = target; }
+ void addMember(Node *node) override;
+ [[nodiscard]] bool hasNamespaces() const override;
+ [[nodiscard]] bool hasClasses() const override;
+ [[nodiscard]] bool wasSeen() const override { return m_seen; }
+
+ [[nodiscard]] QString fullTitle() const override { return title(); }
+ [[nodiscard]] QString logicalModuleName() const override { return m_logicalModuleName; }
+ [[nodiscard]] QString logicalModuleVersion() const override;
+ [[nodiscard]] QString logicalModuleIdentifier() const override
+ {
+ return m_logicalModuleName + m_logicalModuleVersionMajor;
+ }
+ [[nodiscard]] QString state() const { return m_state; }
+
+ template <typename F>
+ NodeMap getMembers(F &&predicate) const
+ {
+ NodeMap result;
+ for (const auto &member : std::as_const(m_members)) {
+ if (std::invoke(predicate, member) && member->isInAPI())
+ result.insert(member->name(), member);
+ }
+ return result;
+ }
+
+ NodeMap getMembers(Node::NodeType type) const
+ {
+ return getMembers([type](const Node *n) {
+ return n->nodeType() == type;
+ });
+ }
+
+ void setLogicalModuleInfo(const QStringList &info) override;
+ void setState(const QString &state) { m_state = state; }
+
+ // REMARK: Those methods are used by QDocDatabase as a performance
+ // detail to avoid merging a collection node multiple times. They
+ // should not be addressed in any other part of the code nor
+ // should their usage appear more than once in QDocDatabase,
+ // albeit this is not enforced.
+ // More information are provided in the comment for the definition
+ // of m_merged.
+ void markMerged() { m_merged = true; }
+ bool isMerged() { return m_merged; }
+
+ [[nodiscard]] const NodeList &members() const { return m_members; }
+
+ void markSeen() { m_seen = true; }
+ void markNotSeen() { m_seen = false; }
+
+private:
+ bool m_seen { false };
+ // REMARK: This is set by the database when merging the collection
+ // node and is later used to avoid merging the same collection
+ // multiple times.
+ // Currently, collection nodes may come from multiple projects,
+ // such that to have a complete overview of the members of a
+ // collection we need to rejoin all members for all instances of
+ // the "same" collection.
+ // This is done in QDocDatabase, generally through an external
+ // method call that is done ad-hoc when a source-of-truth
+ // collection node is needed.
+ // As each part of the code that need such a source-of-truth will
+ // need to merge the node, to avoid the overhead of a relatively
+ // expensive operation being performed multiple times, we expose
+ // this detail so that QDocDatabase can avoid performing the
+ // operation again.
+ // To avoid the coupling, QDocDatabase could keep track of the
+ // merged nodes itself, this is a bit less trivial that this
+ // implementation and doesn't address the source of the problem
+ // (the multiple merges themselves and the sequencing of the
+ // related operations) and as such was discarded in favor of this
+ // simpler implementation.
+ // Do note that outside the very specific purpose for which this
+ // member was made, no part of the code should refer to it and its
+ // associated methods.
+ // Should this start to be the case, we can switch to the more
+ // complex encapsulation into QDocDatabase without having to touch
+ // the outside user of the merges.
+ // Further down the line, this is expected to go away completely
+ // as other part of the code are streamlined.
+ // KLUDGE: Note that this whole exposure is done as a hackish
+ // solution to QTBUG-104237 and should not be considered final or
+ // dependable.
+ bool m_merged { false };
+ NodeList m_members {};
+ QString m_logicalModuleName {};
+ QString m_logicalModuleVersionMajor {};
+ QString m_logicalModuleVersionMinor {};
+ QString m_qtVariable {};
+ QString m_qtCMakeComponent {};
+ QString m_qtCMakeTargetItem {};
+ QString m_state {};
+};
+
+QT_END_NAMESPACE
+
+#endif // COLLECTIONNODE_H
diff --git a/src/qdoc/qdoc/src/qdoc/comparisoncategory.cpp b/src/qdoc/qdoc/src/qdoc/comparisoncategory.cpp
new file mode 100644
index 000000000..98f7aaf66
--- /dev/null
+++ b/src/qdoc/qdoc/src/qdoc/comparisoncategory.cpp
@@ -0,0 +1,28 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+/*!
+ \enum ComparisonCategory
+ \internal
+
+ \value None No comparison is defined.
+ \value Strong Strong comparison is defined, see std::strong_ordering.
+ \value Weak Weak comparison is defined, see std::weak_ordering.
+ \value Partial A partial ordering is defined, see std::partial_ordering.
+ \value Equality Only (in)equality comparison is defined.
+*/
+
+/*!
+ \fn static inline std::string comparisonCategoryAsString(const ComparisonCategory &category)
+ \internal
+
+ Returns a string representation of the comparison category \a category.
+*/
+
+/*!
+ \fn static ComparisonCategory comparisonCategoryFromString(const std::string &string)
+ \internal
+
+ Returns a matching comparison category for a \a string representation, or
+ ComparisonCategory::None for an unknown category string.
+*/
diff --git a/src/qdoc/qdoc/src/qdoc/comparisoncategory.h b/src/qdoc/qdoc/src/qdoc/comparisoncategory.h
new file mode 100644
index 000000000..d3f36654b
--- /dev/null
+++ b/src/qdoc/qdoc/src/qdoc/comparisoncategory.h
@@ -0,0 +1,54 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#ifndef COMPARISONCATEGORY_H
+#define COMPARISONCATEGORY_H
+
+#include <string>
+
+QT_BEGIN_NAMESPACE
+
+enum struct ComparisonCategory : unsigned char {
+ None,
+ Strong,
+ Weak,
+ Partial,
+ Equality
+};
+
+static inline std::string comparisonCategoryAsString(ComparisonCategory category)
+{
+ switch (category) {
+ case ComparisonCategory::Strong:
+ return "strong";
+ case ComparisonCategory::Weak:
+ return "weak";
+ case ComparisonCategory::Partial:
+ return "partial";
+ case ComparisonCategory::Equality:
+ return "equality";
+ case ComparisonCategory::None:
+ [[fallthrough]];
+ default:
+ break;
+ }
+ return {};
+}
+
+static inline ComparisonCategory comparisonCategoryFromString(const std::string &string)
+{
+ if (string == "strong")
+ return ComparisonCategory::Strong;
+ if (string == "weak")
+ return ComparisonCategory::Weak;
+ if (string == "partial")
+ return ComparisonCategory::Partial;
+ if (string == "equality")
+ return ComparisonCategory::Equality;
+
+ return ComparisonCategory::None;
+}
+
+QT_END_NAMESPACE
+
+#endif // COMPARISONCATEGORY_H
diff --git a/src/qdoc/config.cpp b/src/qdoc/qdoc/src/qdoc/config.cpp
index 41dc9b2c6..de987dae8 100644
--- a/src/qdoc/config.cpp
+++ b/src/qdoc/qdoc/src/qdoc/config.cpp
@@ -1,30 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the tools applications of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "config.h"
#include "utilities.h"
@@ -38,10 +13,8 @@
QT_BEGIN_NAMESPACE
-QString ConfigStrings::ALIAS = QStringLiteral("alias");
QString ConfigStrings::AUTOLINKERRORS = QStringLiteral("autolinkerrors");
QString ConfigStrings::BUILDVERSION = QStringLiteral("buildversion");
-QString ConfigStrings::CLANGDEFINES = QStringLiteral("clangdefines");
QString ConfigStrings::CODEINDENT = QStringLiteral("codeindent");
QString ConfigStrings::CODEPREFIX = QStringLiteral("codeprefix");
QString ConfigStrings::CODESUFFIX = QStringLiteral("codesuffix");
@@ -71,8 +44,8 @@ QString ConfigStrings::IGNORESINCE = QStringLiteral("ignoresince");
QString ConfigStrings::IGNORETOKENS = QStringLiteral("ignoretokens");
QString ConfigStrings::IGNOREWORDS = QStringLiteral("ignorewords");
QString ConfigStrings::IMAGEDIRS = QStringLiteral("imagedirs");
-QString ConfigStrings::IMAGES = QStringLiteral("images");
QString ConfigStrings::INCLUDEPATHS = QStringLiteral("includepaths");
+QString ConfigStrings::INCLUSIVE = QStringLiteral("inclusive");
QString ConfigStrings::INDEXES = QStringLiteral("indexes");
QString ConfigStrings::LANDINGPAGE = QStringLiteral("landingpage");
QString ConfigStrings::LANDINGTITLE = QStringLiteral("landingtitle");
@@ -107,7 +80,7 @@ QString ConfigStrings::TABSIZE = QStringLiteral("tabsize");
QString ConfigStrings::TAGFILE = QStringLiteral("tagfile");
QString ConfigStrings::TIMESTAMPS = QStringLiteral("timestamps");
QString ConfigStrings::TOCTITLES = QStringLiteral("toctitles");
-QString ConfigStrings::TRANSLATORS = QStringLiteral("translators");
+QString ConfigStrings::TRADEMARKSPAGE = QStringLiteral("trademarkspage");
QString ConfigStrings::URL = QStringLiteral("url");
QString ConfigStrings::VERSION = QStringLiteral("version");
QString ConfigStrings::VERSIONSYM = QStringLiteral("versionsym");
@@ -187,7 +160,7 @@ void MetaStack::process(QChar ch, const Location &location)
push(MetaStackEntry());
top().open();
} else if (ch == QLatin1Char('}')) {
- if (count() == 1)
+ if (size() == 1)
location.fatal(QStringLiteral("Unexpected '}'"));
top().close();
@@ -199,7 +172,7 @@ void MetaStack::process(QChar ch, const Location &location)
for (const auto &suffix : suffixes)
top().next << prefix + suffix;
}
- } else if (ch == QLatin1Char(',') && count() > 1) {
+ } else if (ch == QLatin1Char(',') && size() > 1) {
top().close();
top().open();
} else {
@@ -213,7 +186,7 @@ void MetaStack::process(QChar ch, const Location &location)
*/
QStringList MetaStack::getExpanded(const Location &location)
{
- if (count() > 1)
+ if (size() > 1)
location.fatal(QStringLiteral("Missing '}'"));
top().close();
@@ -222,6 +195,7 @@ QStringList MetaStack::getExpanded(const Location &location)
const QString Config::dot = QLatin1String(".");
bool Config::m_debug = false;
+bool Config::m_atomsDump = false;
bool Config::generateExamples = true;
QString Config::overrideOutputDir;
QString Config::installDir;
@@ -231,6 +205,100 @@ QStack<QString> Config::m_workingDirs;
QMap<QString, QStringList> Config::m_includeFilesMap;
/*!
+ \class ConfigVar
+ \brief contains all the information for a single config variable in a
+ .qdocconf file.
+*/
+
+/*!
+ Returns this configuration variable as a string.
+
+ If the variable is not defined, returns \a defaultString.
+
+ \note By default, \a defaultString is a null string.
+ This allows determining whether a configuration variable is
+ undefined (returns a null string) or defined as empty
+ (returns a non-null, empty string).
+*/
+QString ConfigVar::asString(const QString defaultString) const
+{
+ if (m_name.isEmpty())
+ return defaultString;
+
+ QString result(""); // an empty but non-null string
+ for (const auto &value : std::as_const(m_values)) {
+ if (!result.isEmpty() && !result.endsWith(QChar('\n')))
+ result.append(QChar(' '));
+ result.append(value.m_value);
+ }
+ return result;
+}
+
+/*!
+ Returns this config variable as a string list.
+*/
+QStringList ConfigVar::asStringList() const
+{
+ QStringList result;
+ for (const auto &value : std::as_const(m_values))
+ result << value.m_value;
+ return result;
+}
+
+/*!
+ Returns this config variable as a string set.
+*/
+QSet<QString> ConfigVar::asStringSet() const
+{
+ const auto &stringList = asStringList();
+ return QSet<QString>(stringList.cbegin(), stringList.cend());
+}
+
+/*!
+ Returns this config variable as a boolean.
+*/
+bool ConfigVar::asBool() const
+{
+ return QVariant(asString()).toBool();
+}
+
+/*!
+ Returns this configuration variable as an integer; iterates
+ through the string list, interpreting each
+ string in the list as an integer and adding it to a total sum.
+
+ Returns 0 if this variable is defined as empty, and
+ -1 if it's is not defined.
+ */
+int ConfigVar::asInt() const
+{
+ const QStringList strs = asStringList();
+ if (strs.isEmpty())
+ return -1;
+
+ int sum = 0;
+ for (const auto &str : strs)
+ sum += str.toInt();
+ return sum;
+}
+
+/*!
+ Appends values to this ConfigVar, and adjusts the ExpandVar
+ parameters so that they continue to refer to the correct values.
+*/
+void ConfigVar::append(const ConfigVar &other)
+{
+ m_expandVars << other.m_expandVars;
+ QList<ExpandVar>::Iterator it = m_expandVars.end();
+ it -= other.m_expandVars.size();
+ std::for_each(it, m_expandVars.end(), [this](ExpandVar &v) {
+ v.m_valueIndex += m_values.size();
+ });
+ m_values << other.m_values;
+ m_location = other.m_location;
+}
+
+/*!
\class Config
\brief The Config class contains the configuration variables
for controlling how qdoc produces documentation.
@@ -279,9 +347,10 @@ Config::~Config()
*/
void Config::clear()
{
- m_location = m_lastLocation = Location();
+ m_location = Location();
m_configVars.clear();
m_includeFilesMap.clear();
+ m_excludedPaths.reset();
}
/*!
@@ -294,7 +363,8 @@ void Config::reset()
// Default values
setStringList(CONFIG_CODEINDENT, QStringList("0"));
setStringList(CONFIG_FALSEHOODS, QStringList("0"));
- setStringList(CONFIG_FILEEXTENSIONS, QStringList("*.cpp *.h *.qdoc *.qml"));
+ setStringList(CONFIG_HEADERS + dot + CONFIG_FILEEXTENSIONS, QStringList("*.ch *.h *.h++ *.hh *.hpp *.hxx"));
+ setStringList(CONFIG_SOURCES + dot + CONFIG_FILEEXTENSIONS, QStringList("*.c++ *.cc *.cpp *.cxx *.mm *.qml *.qdoc"));
setStringList(CONFIG_LANGUAGE, QStringList("Cpp")); // i.e. C++
setStringList(CONFIG_OUTPUTFORMATS, QStringList("HTML"));
setStringList(CONFIG_TABSIZE, QStringList("8"));
@@ -311,7 +381,7 @@ void Config::reset()
SET(CONFIG_REDIRECTDOCUMENTATIONTODEVNULL, redirectDocumentationToDevNullOption);
SET(CONFIG_AUTOLINKERRORS, autoLinkErrorsOption);
#undef SET
- m_showInternal = getBool(CONFIG_SHOWINTERNAL);
+ m_showInternal = m_configVars.value(CONFIG_SHOWINTERNAL).asBool();
setListFlag(CONFIG_NOLINKERRORS,
m_parser.isSet(m_parser.noLinkErrorsOption)
|| qEnvironmentVariableIsSet("QDOC_NOLINKERRORS"));
@@ -336,7 +406,6 @@ void Config::load(const QString &fileName)
m_location = Location(fileName);
else
m_location.setEtc(true);
- m_lastLocation = Location();
expandVariables();
@@ -374,9 +443,9 @@ void Config::expandVariables()
}
QString expanded;
if (it->m_delim.isNull())
- expanded = getStringList(key).join(QString());
+ expanded = m_configVars.value(key).asStringList().join(QString());
else
- expanded = getStringList(key).join(it->m_delim);
+ expanded = m_configVars.value(key).asStringList().join(it->m_delim);
configVar.m_values[it->m_valueIndex].m_value.insert(it->m_index, expanded);
}
configVar.m_expandVars.clear();
@@ -416,12 +485,13 @@ void Config::processCommandLineOptions(const QStringList &args)
if (m_parser.isSet(m_parser.installDirOption))
installDir = m_parser.value(m_parser.installDirOption);
if (m_parser.isSet(m_parser.outputDirOption))
- overrideOutputDir = m_parser.value(m_parser.outputDirOption);
+ overrideOutputDir = QDir(m_parser.value(m_parser.outputDirOption)).absolutePath();
const auto outputFormats = m_parser.values(m_parser.outputFormatOption);
for (const auto &format : outputFormats)
overrideOutputFormats.insert(format);
m_debug = m_parser.isSet(m_parser.debugOption) || qEnvironmentVariableIsSet("QDOC_DEBUG");
+ m_atomsDump = m_parser.isSet(m_parser.atomsDumpOption);
m_showInternal = m_parser.isSet(m_parser.showInternalOption)
|| qEnvironmentVariableIsSet("QDOC_SHOW_INTERNAL");
@@ -429,7 +499,7 @@ void Config::processCommandLineOptions(const QStringList &args)
m_qdocPass = Prepare;
if (m_parser.isSet(m_parser.generateOption))
m_qdocPass = Generate;
- if (m_parser.isSet(m_parser.logProgressOption))
+ if (m_debug || m_parser.isSet(m_parser.logProgressOption))
setStringList(CONFIG_LOGPROGRESS, QStringList("true"));
if (m_parser.isSet(m_parser.timestampsOption))
setStringList(CONFIG_TIMESTAMPS, QStringList("true"));
@@ -468,33 +538,6 @@ void Config::setIndexDirs()
}
/*!
- Looks up the configuration variable \a var in the string
- map and returns the boolean value.
- */
-bool Config::getBool(const QString &var) const
-{
- return QVariant(getString(var)).toBool();
-}
-
-/*!
- Looks up the configuration variable \a var in the string list
- map. Iterates through the string list found, interpreting each
- string in the list as an integer and adding it to a total sum.
- Returns the sum or \c -1 if \a var is not set.
- */
-int Config::getInt(const QString &var) const
-{
- const QStringList strs = getStringList(var);
- if (strs.isEmpty())
- return -1;
-
- int sum = 0;
- for (const auto &str : strs)
- sum += str.toInt();
- return sum;
-}
-
-/*!
Function to return the correct outputdir for the output \a format.
If \a format is not specified, defaults to 'HTML'.
outputdir can be set using the qdocconf or the command-line
@@ -504,21 +547,20 @@ QString Config::getOutputDir(const QString &format) const
{
QString t;
if (overrideOutputDir.isNull())
- t = getString(CONFIG_OUTPUTDIR);
+ t = m_configVars.value(CONFIG_OUTPUTDIR).asString();
else
t = overrideOutputDir;
- if (getBool(CONFIG_SINGLEEXEC)) {
- QString project = getString(CONFIG_PROJECT);
+ if (m_configVars.value(CONFIG_SINGLEEXEC).asBool()) {
+ QString project = m_configVars.value(CONFIG_PROJECT).asString();
t += QLatin1Char('/') + project.toLower();
}
- if (getBool(format + Config::dot + "nosubdirs")) {
- t = t.left(t.lastIndexOf('/'));
- QString singleOutputSubdir = getString(format + Config::dot + "outputsubdir");
+ if (m_configVars.value(format + Config::dot + "nosubdirs").asBool()) {
+ QString singleOutputSubdir = m_configVars.value(format + Config::dot + "outputsubdir").asString();
if (singleOutputSubdir.isEmpty())
singleOutputSubdir = "html";
t += QLatin1Char('/') + singleOutputSubdir;
}
- return t;
+ return QDir::cleanPath(t);
}
/*!
@@ -529,67 +571,24 @@ QString Config::getOutputDir(const QString &format) const
QSet<QString> Config::getOutputFormats() const
{
if (overrideOutputFormats.isEmpty())
- return getStringSet(CONFIG_OUTPUTFORMATS);
+ return m_configVars.value(CONFIG_OUTPUTFORMATS).asStringSet();
else
return overrideOutputFormats;
}
-/*!
- Returns the value of a configuration variable \a var
- as a string. If \a var is defined, updates the internal
- location to the location of \a var for the purposes of
- error reporting.
-
- If \a var is not defined, returns \a defaultString.
-
- \note By default, \a defaultString is a null string. If \a var
- is found but contains an empty string, that is returned instead.
- This allows determining whether a configuration variable is
- undefined (null string) or defined as empty (empty string).
- */
-QString Config::getString(const QString &var, const QString &defaultString) const
-{
- const auto &configVar = m_configVars.value(var);
-
- if (configVar.m_name.isEmpty())
- return defaultString;
- updateLocation(configVar);
-
- QString result(""); // an empty but non-null string
- for (const auto &value : configVar.m_values) {
- if (!result.isEmpty() && !result.endsWith(QChar('\n')))
- result.append(QChar(' '));
- result.append(value.m_value);
- }
- return result;
-}
-
-/*!
- Looks up the configuration variable \a var in the string
- list map, converts the string list it maps to into a set
- of strings, and returns the set.
- */
-QSet<QString> Config::getStringSet(const QString &var) const
-{
- const auto &stringList = getStringList(var);
- return QSet<QString>(stringList.cbegin(), stringList.cend());
-}
-
-/*!
- Returns the string list contained in the configuration variable
- \a var. If \a var is defined, updates the internal location
- to the location of \a var for the purposes of error reporting.
- */
-QStringList Config::getStringList(const QString &var) const
-{
- const auto &configVar = m_configVars.value(var);
- updateLocation(configVar);
-
- QStringList result;
- for (const auto &value : configVar.m_values)
- result << value.m_value;
- return result;
-}
+// TODO: [late-canonicalization][pod-configuration]
+// The canonicalization for paths is done at the time where they are
+// required, and done each time they are requested.
+// Instead, config should be parsed to an intermediate format that is
+// a POD type that already contains canonicalized representations for
+// each element.
+// Those representations should provide specific guarantees about
+// their format and be representable at the API boundaries.
+//
+// This would ensure that the correct canonicalization is always
+// applied, is applied only once and that dependent sub-logics can be
+// written in a way that doesn't require branching or futher
+// canonicalization.
/*!
Returns a path list where all paths from the config variable \a var
@@ -597,15 +596,11 @@ QStringList Config::getStringList(const QString &var) const
for invalid paths. The \c IncludePaths flag is used as a hint to strip
away potential prefixes found in include paths before attempting to
canonicalize.
-
- \note The internal location is updated to the location of \a var for
- the purposes of error reporting.
*/
QStringList Config::getCanonicalPathList(const QString &var, PathFlags flags) const
{
QStringList result;
const auto &configVar = m_configVars.value(var);
- updateLocation(configVar);
for (const auto &value : configVar.m_values) {
const QString &currentPath = value.m_path;
@@ -639,7 +634,7 @@ QStringList Config::getCanonicalPathList(const QString &var, PathFlags flags) co
if (dir.isRelative())
dir.setPath(currentPath + QLatin1Char('/') + path);
if ((flags & Validate) && !QFileInfo::exists(dir.path()))
- m_lastLocation.warning(QStringLiteral("Cannot find file or directory: %1").arg(path));
+ configVar.m_location.warning(QStringLiteral("Cannot find file or directory: %1").arg(path));
else {
const QString canonicalPath = dir.canonicalPath();
if (!canonicalPath.isEmpty())
@@ -649,7 +644,7 @@ QStringList Config::getCanonicalPathList(const QString &var, PathFlags flags) co
else
qCDebug(lcQdoc) <<
qUtf8Printable(QStringLiteral("%1: Ignored nonexistent path \'%2\'")
- .arg(m_lastLocation.toString()).arg(rawValue));
+ .arg(configVar.m_location.toString(), rawValue));
}
}
return result;
@@ -687,7 +682,7 @@ QRegularExpression Config::getRegExp(const QString &var) const
*/
QList<QRegularExpression> Config::getRegExpList(const QString &var) const
{
- const QStringList strs = getStringList(var);
+ const QStringList strs = m_configVars.value(var).asStringList();
QList<QRegularExpression> regExps;
for (const auto &str : strs)
regExps += QRegularExpression(str);
@@ -706,7 +701,7 @@ QSet<QString> Config::subVars(const QString &var) const
QString varDot = var + QLatin1Char('.');
for (auto it = m_configVars.constBegin(); it != m_configVars.constEnd(); ++it) {
if (it.key().startsWith(varDot)) {
- QString subVar = it.key().mid(varDot.length());
+ QString subVar = it.key().mid(varDot.size());
int dot = subVar.indexOf(QLatin1Char('.'));
if (dot != -1)
subVar.truncate(dot);
@@ -766,7 +761,7 @@ QStringList Config::getAllFiles(const QString &filesVar, const QString &dirsVar,
QStringList result = getCanonicalPathList(filesVar, Validate);
const QStringList dirs = getCanonicalPathList(dirsVar, Validate);
- const QString nameFilter = getString(filesVar + dot + CONFIG_FILEEXTENSIONS);
+ const QString nameFilter = m_configVars.value(filesVar + dot + CONFIG_FILEEXTENSIONS).asString();
for (const auto &dir : dirs)
result += getFilesHere(dir, nameFilter, location(), excludedDirs, excludedFiles);
@@ -790,13 +785,20 @@ QStringList Config::getExampleImageFiles(const QSet<QString> &excludedDirs,
{
QStringList result;
const QStringList dirs = getCanonicalPathList("exampledirs");
- const QString nameFilter = getString(CONFIG_EXAMPLES + dot + CONFIG_IMAGEEXTENSIONS);
+ const QString nameFilter = m_configVars.value(CONFIG_EXAMPLES + dot + CONFIG_IMAGEEXTENSIONS).asString();
for (const auto &dir : dirs)
result += getFilesHere(dir, nameFilter, location(), excludedDirs, excludedFiles);
return result;
}
+// TODO: [misplaced-logic][examples][pod-configuration]
+// The definition of how an example is structured and how to find its
+// components should not be part of Config or, for that matter,
+// CppCodeParser, which is the actual caller of this method.
+// Move this method to a more appropriate place as soon as a suitable
+// place is available for it.
+
/*!
Returns the path to the project file for \a examplePath, or an empty string
if no project file was found.
@@ -805,15 +807,15 @@ QString Config::getExampleProjectFile(const QString &examplePath)
{
QFileInfo fileInfo(examplePath);
QStringList validNames;
- validNames << fileInfo.fileName() + QLatin1String(".pro")
+ validNames << QLatin1String("CMakeLists.txt")
+ << fileInfo.fileName() + QLatin1String(".pro")
<< fileInfo.fileName() + QLatin1String(".qmlproject")
<< fileInfo.fileName() + QLatin1String(".pyproject")
- << QLatin1String("CMakeLists.txt")
<< QLatin1String("qbuild.pro"); // legacy
QString projectFile;
- for (const auto &name : qAsConst(validNames)) {
+ for (const auto &name : std::as_const(validNames)) {
projectFile = Config::findFile(Location(), m_exampleFiles, m_exampleDirs,
examplePath + QLatin1Char('/') + name);
if (!projectFile.isEmpty())
@@ -823,6 +825,24 @@ QString Config::getExampleProjectFile(const QString &examplePath)
return projectFile;
}
+// TODO: [pod-configuration]
+// Remove findFile completely from the configuration.
+// External usages of findFile were already removed but a last caller
+// of this method exists internally to Config in
+// `getExampleProjectFile`.
+// That method has to be removed at some point and this method should
+// go with it.
+// Do notice that FileResolver is the replacement for findFile but it
+// is designed, for now, with a scope that does only care about the
+// usages of findFile that are outside the Config class.
+// More specifically, it was designed to replace only the uses of
+// findFile that deal with user provided queries or queries related to
+// that.
+// The logic that is used internally in Config is the same, but has a
+// different conceptual meaning.
+// When findFile is permanently removed, it must be considered whether
+// FileResolver itself should be used for the same logic or not.
+
/*!
\a fileName is the path of the file to find.
@@ -868,6 +888,13 @@ QString Config::findFile(const Location &location, const QStringList &files,
if (!fileInfo.exists())
return QString();
+ // <<REMARK: This is actually dead code. It is unclear what it tries
+ // to do and why but its usage is unnecessary in the current
+ // codebase.
+ // Indeed, the whole concept of the "userFriendlyFilePath" is
+ // removed for file searching.
+ // It will be removed directly with the whole of findFile, but it
+ // should not be considered anymore until then.
if (userFriendlyFilePath) {
for (auto c = components.constBegin();;) {
bool isArchive = (c != components.constEnd() - 1);
@@ -875,6 +902,7 @@ QString Config::findFile(const Location &location, const QStringList &files,
if (isArchive) {
QString extracted = m_extractedDirs[fileInfo.filePath()];
+
++c;
fileInfo.setFile(QDir(extracted), *c);
} else {
@@ -884,23 +912,22 @@ QString Config::findFile(const Location &location, const QStringList &files,
userFriendlyFilePath->append(QLatin1Char('?'));
}
}
+ // REMARK>>
+
return fileInfo.filePath();
}
-/*!
- */
-QString Config::findFile(const Location &location, const QStringList &files,
- const QStringList &dirs, const QString &fileBase,
- const QStringList &fileExtensions, QString *userFriendlyFilePath)
-{
- for (const auto &extension : fileExtensions) {
- QString filePath = findFile(location, files, dirs, fileBase + QLatin1Char('.') + extension,
- userFriendlyFilePath);
- if (!filePath.isEmpty())
- return filePath;
- }
- return findFile(location, files, dirs, fileBase, userFriendlyFilePath);
-}
+// TODO: [pod-configuration]
+// An intermediate representation for the configuration should only
+// contain data that will later be destructured into subsystem that
+// care about specific subsets of the configuration and can carry that
+// information with them, uniquely.
+// Remove copyFile, moving it into whatever will have the unique
+// resposability of knowing how to build an output directory for a
+// QDoc execution.
+// Should copy file being used for not only copying file to the build
+// output directory, split its responsabilities into smaller elements
+// instead of forcing the logic together.
/*!
Copies the \a sourceFilePath to the file name constructed by
@@ -912,6 +939,15 @@ QString Config::findFile(const Location &location, const QStringList &files,
QString Config::copyFile(const Location &location, const QString &sourceFilePath,
const QString &userFriendlySourceFilePath, const QString &targetDirPath)
{
+ // TODO: A copying operation should only be performed on files
+ // that we assume to be available. Ensure that this is true at the
+ // API boundary and bubble up the error checking and reporting to
+ // call-site users. Possibly this will be as simple as
+ // ResolvedFile, but could not be done at the time of the introduction of
+ // that type as we first need to encapsulate the logic for
+ // copying files into an appropriate subsystem and have a better
+ // understanding of call-site usages.
+
QFile inFile(sourceFilePath);
if (!inFile.open(QFile::ReadOnly)) {
location.warning(QStringLiteral("Cannot open input file for copy: '%1': %2")
@@ -919,21 +955,64 @@ QString Config::copyFile(const Location &location, const QString &sourceFilePath
return QString();
}
- QString outFileName = userFriendlySourceFilePath;
- qsizetype slash = outFileName.lastIndexOf(QLatin1Char('/'));
- if (slash != -1)
- outFileName = outFileName.mid(slash);
- if ((outFileName.size()) > 0 && (outFileName[0] != '/'))
- outFileName = targetDirPath + QLatin1Char('/') + outFileName;
- else
- outFileName = targetDirPath + outFileName;
+ // TODO: [non-canonical-representation]
+ // Similar to other part of QDoc, we do a series of non-intuitive
+ // checks to canonicalize some multi-format parameter into
+ // something we can use.
+ // Understand which of those formats are actually in use and
+ // provide a canonicalized version that can be requested at the
+ // API boundary to ensure that correct formatting is used.
+ // If possible, gradually bubble up the canonicalization until a
+ // single entry-point in the program exists where the
+ // canonicalization can be processed to avoid complicating
+ // intermediate steps.
+ // ADDENDUM 1: At least one usage of this seems to depend on the
+ // processing done for files coming from
+ // Generator::copyTemplateFile, which are expressed as absolute
+ // paths. This seems to be the only usage that is currently
+ // needed, hence a temporary new implementation is provided that
+ // only takes this case into account.
+ // Do notice that we assume that in this case we always want a
+ // flat structure, that is, we are copying the file as a direct
+ // child of the target directory.
+ // Nonetheless, it is possible that this case will not be needed,
+ // such that it can be removed later on, or that it will be nedeed
+ // in multiple places such that an higher level interface for it
+ // should be provided.
+ // Furthermoe, it might be possible that there is an edge case
+ // that is now not considered, as it is unknown, that was
+ // considered before.
+ // As it is now unclear what kind of paths are used here, what
+ // format they have, why they are used and why they have some
+ // specific format, further processing is avoided but a more
+ // torough overview of what should is needed must be done when
+ // more information are gathered and this function is extracted
+ // away from config.
+
+ QString outFileName{userFriendlySourceFilePath};
+ QFileInfo outFileNameInfo{userFriendlySourceFilePath};
+ if (outFileNameInfo.isAbsolute())
+ outFileName = outFileNameInfo.fileName();
+
+ outFileName = targetDirPath + "/" + outFileName;
+ QDir targetDir(targetDirPath);
+ if (!targetDir.exists())
+ targetDir.mkpath(".");
+
QFile outFile(outFileName);
if (!outFile.open(QFile::WriteOnly)) {
+ // TODO: [uncrentralized-warning]
location.warning(QStringLiteral("Cannot open output file for copy: '%1': %2")
.arg(outFileName, outFile.errorString()));
return QString();
}
+ // TODO: There shouldn't be any particular advantage to copying
+ // the file by readying its content and writing it compared to
+ // asking the underlying system to do the copy for us.
+ // Consider simplifying this part by avoiding doing the manual
+ // work ourselves.
+
char buffer[1024];
qsizetype len;
while ((len = inFile.read(buffer, sizeof(buffer))) > 0)
@@ -948,7 +1027,7 @@ QString Config::copyFile(const Location &location, const QString &sourceFilePath
int Config::numParams(const QString &value)
{
int max = 0;
- for (int i = 0; i != value.length(); ++i) {
+ for (int i = 0; i != value.size(); ++i) {
uint c = value[i].unicode();
if (c > 0 && c < 8)
max = qMax(max, static_cast<int>(c));
@@ -976,9 +1055,9 @@ QStringList Config::loadMaster(const QString &fileName)
QFile fin(fileName);
if (!fin.open(QFile::ReadOnly | QFile::Text)) {
if (!Config::installDir.isEmpty()) {
- qsizetype prefix = location.filePath().length() - location.fileName().length();
+ qsizetype prefix = location.filePath().size() - location.fileName().size();
fin.setFileName(Config::installDir + QLatin1Char('/')
- + fileName.right(fileName.length() - prefix));
+ + fileName.right(fileName.size() - prefix));
}
if (!fin.open(QFile::ReadOnly | QFile::Text))
location.fatal(QStringLiteral("Cannot open master qdocconf file '%1': %2")
@@ -989,7 +1068,8 @@ QStringList Config::loadMaster(const QString &fileName)
QDir configDir(QFileInfo(fileName).canonicalPath());
QString line = stream.readLine();
while (!line.isNull()) {
- qdocFiles.append(QFileInfo(configDir, line).filePath());
+ if (!line.isEmpty())
+ qdocFiles.append(QFileInfo(configDir, line).filePath());
line = stream.readLine();
}
fin.close();
@@ -1005,10 +1085,8 @@ QStringList Config::loadMaster(const QString &fileName)
void Config::load(Location location, const QString &fileName)
{
QFileInfo fileInfo(fileName);
- QString path = fileInfo.canonicalPath();
- pushWorkingDir(path);
- QDir::setCurrent(path);
- QRegularExpression keySyntax(QRegularExpression::anchoredPattern(QLatin1String("\\w+(?:\\.\\w+)*")));
+ pushWorkingDir(fileInfo.canonicalPath());
+ static const QRegularExpression keySyntax(QRegularExpression::anchoredPattern(QLatin1String("\\w+(?:\\.\\w+)*")));
#define SKIP_CHAR() \
do { \
@@ -1032,9 +1110,9 @@ void Config::load(Location location, const QString &fileName)
QFile fin(fileInfo.fileName());
if (!fin.open(QFile::ReadOnly | QFile::Text)) {
if (!Config::installDir.isEmpty()) {
- qsizetype prefix = location.filePath().length() - location.fileName().length();
+ qsizetype prefix = location.filePath().size() - location.fileName().size();
fin.setFileName(Config::installDir + QLatin1Char('/')
- + fileName.right(fileName.length() - prefix));
+ + fileName.right(fileName.size() - prefix));
}
if (!fin.open(QFile::ReadOnly | QFile::Text))
location.fatal(
@@ -1053,7 +1131,7 @@ void Config::load(Location location, const QString &fileName)
int i = 0;
QChar c = text.at(0);
uint cc = c.unicode();
- while (i < text.length()) {
+ while (i < text.size()) {
if (cc == 0) {
++i;
} else if (c.isSpace()) {
@@ -1080,7 +1158,7 @@ void Config::load(Location location, const QString &fileName)
const QStringList keys = stack.getExpanded(location);
SKIP_SPACES();
- if (keys.count() == 1 && keys.first() == QLatin1String("include")) {
+ if (keys.size() == 1 && keys.first() == QLatin1String("include")) {
QString includeFile;
if (cc != '(')
@@ -1122,7 +1200,7 @@ void Config::load(Location location, const QString &fileName)
/*
Here is the recursive call.
*/
- load(location, QFileInfo(QDir(path), includeFile).filePath());
+ load(location, QFileInfo(QDir(m_workingDirs.top()), includeFile).filePath());
} else {
/*
It wasn't an include statement, so it's something else.
@@ -1240,8 +1318,10 @@ void Config::load(Location location, const QString &fileName)
}
}
popWorkingDir();
- if (!m_workingDirs.isEmpty())
- QDir::setCurrent(m_workingDirs.top());
+
+#undef SKIP_CHAR
+#undef SKIP_SPACES
+#undef PUT_CHAR
}
bool Config::isFileExcluded(const QString &fileName, const QSet<QString> &excludedFiles)
@@ -1260,6 +1340,8 @@ QStringList Config::getFilesHere(const QString &uncleanDir, const QString &nameF
const Location &location, const QSet<QString> &excludedDirs,
const QSet<QString> &excludedFiles)
{
+ // TODO: Understand why location is used to branch the
+ // canonicalization and why the two different methods are used.
QString dir =
location.isEmpty() ? QDir::cleanPath(uncleanDir) : QDir(uncleanDir).canonicalPath();
QStringList result;
@@ -1272,7 +1354,9 @@ QStringList Config::getFilesHere(const QString &uncleanDir, const QString &nameF
dirInfo.setSorting(QDir::Name);
dirInfo.setFilter(QDir::Files);
QStringList fileNames = dirInfo.entryList();
- for (const auto &file : qAsConst(fileNames)) {
+ for (const auto &file : std::as_const(fileNames)) {
+ // TODO: Understand if this is needed and, should it be, if it
+ // is indeed the only case that should be considered.
if (!file.startsWith(QLatin1Char('~'))) {
QString s = dirInfo.filePath(file);
QString c = QDir::cleanPath(s);
@@ -1291,24 +1375,65 @@ QStringList Config::getFilesHere(const QString &uncleanDir, const QString &nameF
}
/*!
- Push \a dir onto the stack of working directories.
+ Set \a dir as the working directory and push it onto the
+ stack of working directories.
*/
void Config::pushWorkingDir(const QString &dir)
{
m_workingDirs.push(dir);
+ QDir::setCurrent(dir);
}
/*!
- If the stack of working directories is not empty, pop the
- top entry and return it. Otherwise return an empty string.
+ Pop the top entry from the stack of working directories.
+ Set the working directory to the next one on the stack,
+ if one exists.
*/
-QString Config::popWorkingDir()
+void Config::popWorkingDir()
{
+ Q_ASSERT(!m_workingDirs.isEmpty());
+ m_workingDirs.pop();
if (!m_workingDirs.isEmpty())
- return m_workingDirs.pop();
+ QDir::setCurrent(m_workingDirs.top());
+}
- qDebug() << "RETURNED EMPTY WORKING DIR";
- return QString();
+const Config::ExcludedPaths& Config::getExcludedPaths() {
+ if (m_excludedPaths)
+ return *m_excludedPaths;
+
+ const auto &excludedDirList = getCanonicalPathList(CONFIG_EXCLUDEDIRS);
+ const auto &excludedFilesList = getCanonicalPathList(CONFIG_EXCLUDEFILES);
+
+ QSet<QString> excludedDirs = QSet<QString>(excludedDirList.cbegin(), excludedDirList.cend());
+ QSet<QString> excludedFiles = QSet<QString>(excludedFilesList.cbegin(), excludedFilesList.cend());
+
+ m_excludedPaths.emplace(ExcludedPaths{excludedDirs, excludedFiles});
+
+ return *m_excludedPaths;
+}
+
+std::set<Config::HeaderFilePath> Config::getHeaderFiles() {
+ static QStringList accepted_header_file_extensions{
+ "ch", "h", "h++", "hh", "hpp", "hxx"
+ };
+
+ const auto& [excludedDirs, excludedFiles] = getExcludedPaths();
+
+ QStringList headerList =
+ getAllFiles(CONFIG_HEADERS, CONFIG_HEADERDIRS, excludedDirs, excludedFiles);
+
+ std::set<HeaderFilePath> headers{};
+
+ for (const auto& header : headerList) {
+ if (header.contains("doc/snippets")) continue;
+
+ if (!accepted_header_file_extensions.contains(QFileInfo{header}.suffix()))
+ continue;
+
+ headers.insert(HeaderFilePath{QFileInfo{header}.canonicalPath(), QFileInfo{header}.fileName()});
+ }
+
+ return headers;
}
QT_END_NAMESPACE
diff --git a/src/qdoc/config.h b/src/qdoc/qdoc/src/qdoc/config.h
index e3b8bf9d8..30dad4746 100644
--- a/src/qdoc/config.h
+++ b/src/qdoc/qdoc/src/qdoc/config.h
@@ -1,30 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the tools applications of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#ifndef CONFIG_H
#define CONFIG_H
@@ -34,15 +9,17 @@
#include "singleton.h"
#include <QtCore/qmap.h>
-#include <QtCore/qpair.h>
#include <QtCore/qset.h>
#include <QtCore/qstack.h>
#include <QtCore/qstringlist.h>
+#include <set>
#include <utility>
QT_BEGIN_NAMESPACE
+class Config;
+
/*
Contains information about a location
where a ConfigVar string needs to be expanded
@@ -61,25 +38,22 @@ struct ExpandVar
}
};
-/*
- This struct contains all the information for
- one config variable found in a qdocconf file.
- */
-struct ConfigVar
+class ConfigVar
{
- QString m_name {};
-
+public:
struct ConfigValue {
QString m_value;
QString m_path;
};
- QList<ConfigValue> m_values {};
- Location m_location {};
- QList<ExpandVar> m_expandVars {};
+ [[nodiscard]] QString asString(const QString defaultString = QString()) const;
+ [[nodiscard]] QStringList asStringList() const;
+ [[nodiscard]] QSet<QString> asStringSet() const;
+ [[nodiscard]] bool asBool() const;
+ [[nodiscard]] int asInt() const;
+ [[nodiscard]] const Location &location() const { return m_location; }
ConfigVar() = default;
-
ConfigVar(QString name, const QStringList &values, const QString &dir,
const Location &loc = Location(),
const QList<ExpandVar> &expandVars = QList<ExpandVar>())
@@ -89,21 +63,16 @@ struct ConfigVar
m_values << ConfigValue {v, dir};
}
- /*
- Appends values to this ConfigVar, and adjusts the ExpandVar
- parameters so they continue to refer to the correct values.
- */
- void append(const ConfigVar &other)
- {
- m_expandVars << other.m_expandVars;
- QList<ExpandVar>::Iterator it = m_expandVars.end();
- it -= other.m_expandVars.size();
- std::for_each(it, m_expandVars.end(), [this](ExpandVar &v) {
- v.m_valueIndex += m_values.size();
- });
- m_values << other.m_values;
- m_location = other.m_location;
- }
+private:
+ void append(const ConfigVar &other);
+
+private:
+ QString m_name {};
+ QList<ConfigValue> m_values {};
+ Location m_location {};
+ QList<ExpandVar> m_expandVars {};
+
+ friend class Config;
};
/*
@@ -120,12 +89,24 @@ public:
enum PathFlags : unsigned char {
None = 0x0,
+ // TODO: [unenforced-unclear-validation]
+ // The Validate flag is used, for example, during the retrival
+ // of paths in getCanonicalPathList.
+ // It is unclear what kind of validation it performs, if any,
+ // and when this validation is required.
+ // Instead, remove this kind of flag and ensure that any
+ // amount of required validation is performed during the
+ // parsing step, if possilbe, and only once.
+ // Furthemore, ensure any such validation removes some
+ // uncertainty on dependent subsystems, moving constraints to
+ // preconditions and expressing them at the API boundaries.
Validate = 0x1,
IncludePaths = 0x2
};
void init(const QString &programName, const QStringList &args);
[[nodiscard]] bool getDebug() const { return m_debug; }
+ [[nodiscard]] bool getAtomsDump() const { return m_atomsDump; }
[[nodiscard]] bool showInternal() const { return m_showInternal; }
void clear();
@@ -138,16 +119,15 @@ public:
[[nodiscard]] QStringList qdocFiles() const { return m_parser.positionalArguments(); }
[[nodiscard]] const QString &programName() const { return m_prog; }
[[nodiscard]] const Location &location() const { return m_location; }
- [[nodiscard]] const Location &lastLocation() const { return m_lastLocation; }
- [[nodiscard]] bool getBool(const QString &var) const;
- [[nodiscard]] int getInt(const QString &var) const;
-
+ [[nodiscard]] const ConfigVar &get(const QString &var) const
+ {
+ // Avoid injecting default-constructed values to map if var doesn't exist
+ static ConfigVar empty;
+ auto it = m_configVars.constFind(var);
+ return (it != m_configVars.constEnd()) ? *it : empty;
+ }
[[nodiscard]] QString getOutputDir(const QString &format = QString("HTML")) const;
[[nodiscard]] QSet<QString> getOutputFormats() const;
- [[nodiscard]] QString getString(const QString &var,
- const QString &defaultString = QString()) const;
- [[nodiscard]] QSet<QString> getStringSet(const QString &var) const;
- [[nodiscard]] QStringList getStringList(const QString &var) const;
[[nodiscard]] QStringList getCanonicalPathList(const QString &var,
PathFlags flags = None) const;
[[nodiscard]] QRegularExpression getRegExp(const QString &var) const;
@@ -172,16 +152,12 @@ public:
static QString findFile(const Location &location, const QStringList &files,
const QStringList &dirs, const QString &fileName,
QString *userFriendlyFilePath = nullptr);
- static QString findFile(const Location &location, const QStringList &files,
- const QStringList &dirs, const QString &fileBase,
- const QStringList &fileExtensions,
- QString *userFriendlyFilePath = nullptr);
static QString copyFile(const Location &location, const QString &sourceFilePath,
const QString &userFriendlySourceFilePath,
const QString &targetDirPath);
static int numParams(const QString &value);
static void pushWorkingDir(const QString &dir);
- static QString popWorkingDir();
+ static void popWorkingDir();
static const QString dot;
@@ -205,16 +181,27 @@ public:
[[nodiscard]] bool preparing() const { return (m_qdocPass == Prepare); }
[[nodiscard]] bool generating() const { return (m_qdocPass == Generate); }
+ struct ExcludedPaths {
+ QSet<QString> excluded_directories;
+ QSet<QString> excluded_files;
+ };
+ const ExcludedPaths& getExcludedPaths();
+
+ struct HeaderFilePath {
+ QString path;
+ QString filename;
+
+ friend bool operator<(const HeaderFilePath& lhs, const HeaderFilePath& rhs) {
+ return std::tie(lhs.path, lhs.filename) < std::tie(rhs.path, rhs.filename);
+ }
+ };
+ std::set<HeaderFilePath> getHeaderFiles();
+
private:
void processCommandLineOptions(const QStringList &args);
void setIncludePaths();
void setIndexDirs();
void expandVariables();
- inline void updateLocation(const ConfigVar &cv) const
- {
- if (!cv.m_location.isEmpty())
- const_cast<Config *>(this)->m_lastLocation = cv.m_location;
- }
QStringList m_dependModules {};
QStringList m_defines {};
@@ -224,15 +211,22 @@ private:
QStringList m_exampleDirs {};
QString m_currentDir {};
QString m_previousCurrentDir {};
+ std::optional<ExcludedPaths> m_excludedPaths{};
bool m_showInternal { false };
static bool m_debug;
+
+ // An option that can be set trough a similarly named command-line option.
+ // When this is set, every time QDoc parses a block-comment, a
+ // human-readable presentation of the `Atom`s structure for that
+ // block will shown to the user.
+ static bool m_atomsDump;
+
static bool isMetaKeyChar(QChar ch);
void load(Location location, const QString &fileName);
QString m_prog {};
Location m_location {};
- Location m_lastLocation {};
ConfigVarMap m_configVars {};
static QMap<QString, QString> m_extractedDirs;
@@ -248,7 +242,6 @@ struct ConfigStrings
static QString ALIAS;
static QString AUTOLINKERRORS;
static QString BUILDVERSION;
- static QString CLANGDEFINES;
static QString CODEINDENT;
static QString CODEPREFIX;
static QString CODESUFFIX;
@@ -280,6 +273,7 @@ struct ConfigStrings
static QString IMAGEDIRS;
static QString IMAGES;
static QString INCLUDEPATHS;
+ static QString INCLUSIVE;
static QString INDEXES;
static QString LANDINGPAGE;
static QString LANDINGTITLE;
@@ -313,7 +307,7 @@ struct ConfigStrings
static QString TAGFILE;
static QString TIMESTAMPS;
static QString TOCTITLES;
- static QString TRANSLATORS;
+ static QString TRADEMARKSPAGE;
static QString URL;
static QString VERSION;
static QString VERSIONSYM;
@@ -324,10 +318,8 @@ struct ConfigStrings
static QString WARNINGLIMIT;
};
-#define CONFIG_ALIAS ConfigStrings::ALIAS
#define CONFIG_AUTOLINKERRORS ConfigStrings::AUTOLINKERRORS
#define CONFIG_BUILDVERSION ConfigStrings::BUILDVERSION
-#define CONFIG_CLANGDEFINES ConfigStrings::CLANGDEFINES
#define CONFIG_CODEINDENT ConfigStrings::CODEINDENT
#define CONFIG_CODEPREFIX ConfigStrings::CODEPREFIX
#define CONFIG_CODESUFFIX ConfigStrings::CODESUFFIX
@@ -357,8 +349,8 @@ struct ConfigStrings
#define CONFIG_IGNORETOKENS ConfigStrings::IGNORETOKENS
#define CONFIG_IGNOREWORDS ConfigStrings::IGNOREWORDS
#define CONFIG_IMAGEDIRS ConfigStrings::IMAGEDIRS
-#define CONFIG_IMAGES ConfigStrings::IMAGES
#define CONFIG_INCLUDEPATHS ConfigStrings::INCLUDEPATHS
+#define CONFIG_INCLUSIVE ConfigStrings::INCLUSIVE
#define CONFIG_INDEXES ConfigStrings::INDEXES
#define CONFIG_LANDINGPAGE ConfigStrings::LANDINGPAGE
#define CONFIG_LANDINGTITLE ConfigStrings::LANDINGTITLE
@@ -392,7 +384,7 @@ struct ConfigStrings
#define CONFIG_TAGFILE ConfigStrings::TAGFILE
#define CONFIG_TIMESTAMPS ConfigStrings::TIMESTAMPS
#define CONFIG_TOCTITLES ConfigStrings::TOCTITLES
-#define CONFIG_TRANSLATORS ConfigStrings::TRANSLATORS
+#define CONFIG_TRADEMARKSPAGE ConfigStrings::TRADEMARKSPAGE
#define CONFIG_URL ConfigStrings::URL
#define CONFIG_VERSION ConfigStrings::VERSION
#define CONFIG_VERSIONSYM ConfigStrings::VERSIONSYM
@@ -404,12 +396,12 @@ struct ConfigStrings
inline bool Config::singleExec() const
{
- return getBool(CONFIG_SINGLEEXEC);
+ return m_configVars.value(CONFIG_SINGLEEXEC).asBool();
}
inline bool Config::dualExec() const
{
- return !getBool(CONFIG_SINGLEEXEC);
+ return !m_configVars.value(CONFIG_SINGLEEXEC).asBool();
}
QT_END_NAMESPACE
diff --git a/src/qdoc/cppcodemarker.cpp b/src/qdoc/qdoc/src/qdoc/cppcodemarker.cpp
index 3b0a9a771..7fb26db0c 100644
--- a/src/qdoc/cppcodemarker.cpp
+++ b/src/qdoc/qdoc/src/qdoc/cppcodemarker.cpp
@@ -1,30 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the tools applications of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "cppcodemarker.h"
@@ -44,6 +19,8 @@
QT_BEGIN_NAMESPACE
+using namespace Qt::StringLiterals;
+
/*!
Returns \c true.
*/
@@ -91,7 +68,6 @@ QString CppCodeMarker::markedUpSynopsis(const Node *node, const Node * /* relati
{
const int MaxEnumValues = 6;
const FunctionNode *func;
- const PropertyNode *property;
const VariableNode *variable;
const EnumNode *enume;
QString synopsis;
@@ -104,8 +80,7 @@ QString CppCodeMarker::markedUpSynopsis(const Node *node, const Node * /* relati
if (style == Section::Details) {
if (!node->isRelatedNonmember() && !node->isProxyNode() && !node->parent()->name().isEmpty()
- && !node->parent()->isHeader() && !node->isProperty() && !node->isQmlNode()
- && !node->isJsNode()) {
+ && !node->parent()->isHeader() && !node->isProperty() && !node->isQmlNode()) {
name.prepend(taggedNode(node->parent()) + "::");
}
}
@@ -121,9 +96,9 @@ QString CppCodeMarker::markedUpSynopsis(const Node *node, const Node * /* relati
case Node::Function:
func = (const FunctionNode *)node;
if (style == Section::Details) {
- const QString &templateDecl = node->templateDecl();
- if (!templateDecl.isEmpty())
- synopsis = templateDecl + QLatin1Char(' ');
+ auto templateDecl = node->templateDecl();
+ if (templateDecl)
+ synopsis = protect((*templateDecl).to_qstring()) + QLatin1Char(' ');
}
if (style != Section::AllMembers && !func->returnType().isEmpty())
synopsis += typified(func->returnType(), true);
@@ -209,9 +184,9 @@ QString CppCodeMarker::markedUpSynopsis(const Node *node, const Node * /* relati
break;
case Node::TypeAlias:
if (style == Section::Details) {
- const QString &templateDecl = node->templateDecl();
- if (!templateDecl.isEmpty())
- synopsis += templateDecl + QLatin1Char(' ');
+ auto templateDecl = node->templateDecl();
+ if (templateDecl)
+ synopsis += protect((*templateDecl).to_qstring()) + QLatin1Char(' ');
}
synopsis += name;
break;
@@ -220,10 +195,16 @@ QString CppCodeMarker::markedUpSynopsis(const Node *node, const Node * /* relati
synopsis = "flags ";
synopsis += name;
break;
- case Node::Property:
- property = static_cast<const PropertyNode *>(node);
+ case Node::Property: {
+ auto property = static_cast<const PropertyNode *>(node);
synopsis = name + " : " + typified(property->qualifiedDataType());
break;
+ }
+ case Node::QmlProperty: {
+ auto property = static_cast<const QmlPropertyNode *>(node);
+ synopsis = name + " : " + typified(property->dataType());
+ break;
+ }
case Node::Variable:
variable = static_cast<const VariableNode *>(node);
if (style == Section::AllMembers) {
@@ -238,8 +219,8 @@ QString CppCodeMarker::markedUpSynopsis(const Node *node, const Node * /* relati
QString extra = CodeMarker::extraSynopsis(node, style);
if (!extra.isEmpty()) {
- extra.prepend("<@extra>");
- extra.append("</@extra>");
+ extra.prepend(u"<@extra>"_s);
+ extra.append(u"</@extra> "_s);
}
return extra + synopsis;
@@ -250,19 +231,20 @@ QString CppCodeMarker::markedUpSynopsis(const Node *node, const Node * /* relati
QString CppCodeMarker::markedUpQmlItem(const Node *node, bool summary)
{
QString name = taggedQmlNode(node);
+ QString synopsis;
+
if (summary) {
name = linkTag(node, name);
- } else if (node->isQmlProperty() || node->isJsProperty()) {
+ } else if (node->isQmlProperty()) {
const auto *pn = static_cast<const QmlPropertyNode *>(node);
if (pn->isAttached())
name.prepend(pn->element() + QLatin1Char('.'));
}
name = "<@name>" + name + "</@name>";
- QString synopsis;
- if (node->isQmlProperty() || node->isJsProperty()) {
+ if (node->isQmlProperty()) {
const auto *pn = static_cast<const QmlPropertyNode *>(node);
synopsis = name + " : " + typified(pn->dataType());
- } else if (node->isFunction(Node::QML) || node->isFunction(Node::JS)) {
+ } else if (node->isFunction(Node::QML)) {
const auto *func = static_cast<const FunctionNode *>(node);
if (!func->returnType().isEmpty())
synopsis = typified(func->returnType(), true) + name;
@@ -291,22 +273,12 @@ QString CppCodeMarker::markedUpQmlItem(const Node *node, bool summary)
synopsis = name;
}
- QString extra;
- if (summary) {
- if (node->isPreliminary())
- extra += " (preliminary)";
- else if (node->isDeprecated()) {
- if (const QString &version = node->deprecatedSince(); !version.isEmpty())
- extra += " (deprecated since " + version + ")";
- else
- extra += " (deprecated)";
- }
- }
-
+ QString extra = CodeMarker::extraSynopsis(node, summary ? Section::Summary : Section::Details);
if (!extra.isEmpty()) {
- extra.prepend("<@extra>");
- extra.append("</@extra>");
+ extra.prepend(u" <@extra>"_s);
+ extra.append(u"</@extra>"_s);
}
+
return synopsis + extra;
}
@@ -320,10 +292,18 @@ QString CppCodeMarker::markedUpName(const Node *node)
QString CppCodeMarker::markedUpEnumValue(const QString &enumValue, const Node *relative)
{
- if (!relative->isEnumType())
+ const auto *node = relative->parent();
+
+ if (relative->isQmlProperty()) {
+ const auto *qpn = static_cast<const QmlPropertyNode*>(relative);
+ if (qpn->enumNode() && !enumValue.startsWith("%1."_L1.arg(qpn->enumPrefix())))
+ return "%1<@op>.</@op>%2"_L1.arg(qpn->enumPrefix(), enumValue);
+ }
+
+ if (!relative->isEnumType()) {
return enumValue;
+ }
- const Node *node = relative->parent();
QStringList parts;
while (!node->isHeader() && node->parent()) {
parts.prepend(markedUpName(node));
@@ -338,24 +318,9 @@ QString CppCodeMarker::markedUpEnumValue(const QString &enumValue, const Node *r
return parts.join(QLatin1String("<@op>::</@op>"));
}
-QString CppCodeMarker::markedUpIncludes(const QStringList &includes)
+QString CppCodeMarker::markedUpInclude(const QString &include)
{
- QString code;
-
- for (const auto &include : includes)
- code += "<@preprocessor>#include &lt;<@headerfile>" + include
- + "</@headerfile>&gt;</@preprocessor>\n";
- return code;
-}
-
-QString CppCodeMarker::functionBeginRegExp(const QString &funcName)
-{
- return QLatin1Char('^') + QRegularExpression::escape(funcName) + QLatin1Char('$');
-}
-
-QString CppCodeMarker::functionEndRegExp(const QString & /* funcName */)
-{
- return "^\\}$";
+ return "<@preprocessor>#include &lt;<@headerfile>" + include + "</@headerfile>&gt;</@preprocessor>";
}
/*
@@ -374,61 +339,45 @@ QString CppCodeMarker::functionEndRegExp(const QString & /* funcName */)
QString CppCodeMarker::addMarkUp(const QString &in, const Node * /* relative */,
const Location & /* location */)
{
- static QSet<QString> types;
- static QSet<QString> keywords;
-
- if (types.isEmpty()) {
- // initialize statics
- Q_ASSERT(keywords.isEmpty());
- static const QString typeTable[] = {
- QLatin1String("bool"), QLatin1String("char"), QLatin1String("double"),
- QLatin1String("float"), QLatin1String("int"), QLatin1String("long"),
- QLatin1String("short"), QLatin1String("signed"), QLatin1String("unsigned"),
- QLatin1String("uint"), QLatin1String("ulong"), QLatin1String("ushort"),
- QLatin1String("uchar"), QLatin1String("void"), QLatin1String("qlonglong"),
- QLatin1String("qulonglong"), QLatin1String("qint"), QLatin1String("qint8"),
- QLatin1String("qint16"), QLatin1String("qint32"), QLatin1String("qint64"),
- QLatin1String("quint"), QLatin1String("quint8"), QLatin1String("quint16"),
- QLatin1String("quint32"), QLatin1String("quint64"), QLatin1String("qreal"),
- QLatin1String("cond")
- };
-
- static const QString keywordTable[] = {
- QLatin1String("and"), QLatin1String("and_eq"), QLatin1String("asm"),
- QLatin1String("auto"), QLatin1String("bitand"), QLatin1String("bitor"),
- QLatin1String("break"), QLatin1String("case"), QLatin1String("catch"),
- QLatin1String("class"), QLatin1String("compl"), QLatin1String("const"),
- QLatin1String("const_cast"), QLatin1String("continue"), QLatin1String("default"),
- QLatin1String("delete"), QLatin1String("do"), QLatin1String("dynamic_cast"),
- QLatin1String("else"), QLatin1String("enum"), QLatin1String("explicit"),
- QLatin1String("export"), QLatin1String("extern"), QLatin1String("false"),
- QLatin1String("for"), QLatin1String("friend"), QLatin1String("goto"),
- QLatin1String("if"), QLatin1String("include"), QLatin1String("inline"),
- QLatin1String("monitor"), QLatin1String("mutable"), QLatin1String("namespace"),
- QLatin1String("new"), QLatin1String("not"), QLatin1String("not_eq"),
- QLatin1String("operator"), QLatin1String("or"), QLatin1String("or_eq"),
- QLatin1String("private"), QLatin1String("protected"), QLatin1String("public"),
- QLatin1String("register"), QLatin1String("reinterpret_cast"), QLatin1String("return"),
- QLatin1String("sizeof"), QLatin1String("static"), QLatin1String("static_cast"),
- QLatin1String("struct"), QLatin1String("switch"), QLatin1String("template"),
- QLatin1String("this"), QLatin1String("throw"), QLatin1String("true"),
- QLatin1String("try"), QLatin1String("typedef"), QLatin1String("typeid"),
- QLatin1String("typename"), QLatin1String("union"), QLatin1String("using"),
- QLatin1String("virtual"), QLatin1String("volatile"), QLatin1String("wchar_t"),
- QLatin1String("while"), QLatin1String("xor"), QLatin1String("xor_eq"),
- QLatin1String("synchronized"),
- // Qt specific
- QLatin1String("signals"), QLatin1String("slots"), QLatin1String("emit")
- };
-
- types.reserve(sizeof(typeTable) / sizeof(QString));
- for (int j = sizeof(typeTable) / sizeof(QString) - 1; j; --j)
- types.insert(typeTable[j]);
-
- keywords.reserve(sizeof(keywordTable) / sizeof(QString));
- for (int j = sizeof(keywordTable) / sizeof(QString) - 1; j; --j)
- keywords.insert(keywordTable[j]);
- }
+ static QSet<QString> types{
+ QLatin1String("bool"), QLatin1String("char"), QLatin1String("double"),
+ QLatin1String("float"), QLatin1String("int"), QLatin1String("long"),
+ QLatin1String("short"), QLatin1String("signed"), QLatin1String("unsigned"),
+ QLatin1String("uint"), QLatin1String("ulong"), QLatin1String("ushort"),
+ QLatin1String("uchar"), QLatin1String("void"), QLatin1String("qlonglong"),
+ QLatin1String("qulonglong"), QLatin1String("qint"), QLatin1String("qint8"),
+ QLatin1String("qint16"), QLatin1String("qint32"), QLatin1String("qint64"),
+ QLatin1String("quint"), QLatin1String("quint8"), QLatin1String("quint16"),
+ QLatin1String("quint32"), QLatin1String("quint64"), QLatin1String("qreal"),
+ QLatin1String("cond")
+ };
+
+ static QSet<QString> keywords{
+ QLatin1String("and"), QLatin1String("and_eq"), QLatin1String("asm"), QLatin1String("auto"),
+ QLatin1String("bitand"), QLatin1String("bitor"), QLatin1String("break"),
+ QLatin1String("case"), QLatin1String("catch"), QLatin1String("class"),
+ QLatin1String("compl"), QLatin1String("const"), QLatin1String("const_cast"),
+ QLatin1String("continue"), QLatin1String("default"), QLatin1String("delete"),
+ QLatin1String("do"), QLatin1String("dynamic_cast"), QLatin1String("else"),
+ QLatin1String("enum"), QLatin1String("explicit"), QLatin1String("export"),
+ QLatin1String("extern"), QLatin1String("false"), QLatin1String("for"),
+ QLatin1String("friend"), QLatin1String("goto"), QLatin1String("if"),
+ QLatin1String("include"), QLatin1String("inline"), QLatin1String("monitor"),
+ QLatin1String("mutable"), QLatin1String("namespace"), QLatin1String("new"),
+ QLatin1String("not"), QLatin1String("not_eq"), QLatin1String("operator"),
+ QLatin1String("or"), QLatin1String("or_eq"), QLatin1String("private"),
+ QLatin1String("protected"), QLatin1String("public"), QLatin1String("register"),
+ QLatin1String("reinterpret_cast"), QLatin1String("return"), QLatin1String("sizeof"),
+ QLatin1String("static"), QLatin1String("static_cast"), QLatin1String("struct"),
+ QLatin1String("switch"), QLatin1String("template"), QLatin1String("this"),
+ QLatin1String("throw"), QLatin1String("true"), QLatin1String("try"),
+ QLatin1String("typedef"), QLatin1String("typeid"), QLatin1String("typename"),
+ QLatin1String("union"), QLatin1String("using"), QLatin1String("virtual"),
+ QLatin1String("volatile"), QLatin1String("wchar_t"), QLatin1String("while"),
+ QLatin1String("xor"), QLatin1String("xor_eq"), QLatin1String("synchronized"),
+ // Qt specific
+ QLatin1String("signals"), QLatin1String("slots"), QLatin1String("emit")
+ };
QString code = in;
QString out;
@@ -439,13 +388,13 @@ QString CppCodeMarker::addMarkUp(const QString &in, const Node * /* relative */,
int start = 0;
int finish = 0;
QChar ch;
- QRegularExpression classRegExp(QRegularExpression::anchoredPattern("Qt?(?:[A-Z3]+[a-z][A-Za-z]*|t)"));
- QRegularExpression functionRegExp(QRegularExpression::anchoredPattern("q([A-Z][a-z]+)+"));
- QRegularExpression findFunctionRegExp(QStringLiteral("^\\s*\\("));
+ static const QRegularExpression classRegExp(QRegularExpression::anchoredPattern("Qt?(?:[A-Z3]+[a-z][A-Za-z]*|t)"));
+ static const QRegularExpression functionRegExp(QRegularExpression::anchoredPattern("q([A-Z][a-z]+)+"));
+ static const QRegularExpression findFunctionRegExp(QStringLiteral("^\\s*\\("));
bool atEOF = false;
auto readChar = [&]() {
- if (i < code.length())
+ if (i < code.size())
ch = code[i++];
else
atEOF = true;
@@ -482,7 +431,7 @@ QString CppCodeMarker::addMarkUp(const QString &in, const Node * /* relative */,
do {
finish = i;
readChar();
- } while (!atEOF && (ch.isLetterOrNumber() || ch == '.'));
+ } while (!atEOF && (ch.isLetterOrNumber() || ch == '.' || ch == '\''));
tag = QStringLiteral("number");
} else {
switch (ch.unicode()) {
@@ -635,7 +584,7 @@ QString CppCodeMarker::addMarkUp(const QString &in, const Node * /* relative */,
}
}
- if (start < code.length()) {
+ if (start < code.size()) {
appendProtectedString(&out, QStringView{code}.mid(start));
}
diff --git a/src/qdoc/qdoc/src/qdoc/cppcodemarker.h b/src/qdoc/qdoc/src/qdoc/cppcodemarker.h
new file mode 100644
index 000000000..b0c5f3615
--- /dev/null
+++ b/src/qdoc/qdoc/src/qdoc/cppcodemarker.h
@@ -0,0 +1,35 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#ifndef CPPCODEMARKER_H
+#define CPPCODEMARKER_H
+
+#include "codemarker.h"
+
+QT_BEGIN_NAMESPACE
+
+class CppCodeMarker : public CodeMarker
+{
+public:
+ CppCodeMarker() = default;
+ ~CppCodeMarker() override = default;
+
+ bool recognizeCode(const QString &code) override;
+ bool recognizeExtension(const QString &ext) override;
+ bool recognizeLanguage(const QString &lang) override;
+ [[nodiscard]] Atom::AtomType atomType() const override;
+ QString markedUpCode(const QString &code, const Node *relative,
+ const Location &location) override;
+ QString markedUpSynopsis(const Node *node, const Node *relative, Section::Style style) override;
+ QString markedUpQmlItem(const Node *node, bool summary) override;
+ QString markedUpName(const Node *node) override;
+ QString markedUpEnumValue(const QString &enumValue, const Node *relative) override;
+ QString markedUpInclude(const QString &include) override;
+
+private:
+ QString addMarkUp(const QString &protectedCode, const Node *relative, const Location &location);
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/qdoc/cppcodeparser.cpp b/src/qdoc/qdoc/src/qdoc/cppcodeparser.cpp
index bf155c520..d2e8d7c63 100644
--- a/src/qdoc/cppcodeparser.cpp
+++ b/src/qdoc/qdoc/src/qdoc/cppcodeparser.cpp
@@ -1,36 +1,13 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the tools applications of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "cppcodeparser.h"
#include "access.h"
#include "classnode.h"
+#include "clangcodeparser.h"
#include "collectionnode.h"
+#include "comparisoncategory.h"
#include "config.h"
#include "examplenode.h"
#include "externalpagenode.h"
@@ -42,146 +19,60 @@
#include "qmltypenode.h"
#include "qmlpropertynode.h"
#include "sharedcommentnode.h"
-#include "utilities.h"
#include <QtCore/qdebug.h>
#include <QtCore/qmap.h>
#include <algorithm>
-QT_BEGIN_NAMESPACE
-
-/* qmake ignore Q_OBJECT */
-
-QSet<QString> CppCodeParser::m_excludeDirs;
-QSet<QString> CppCodeParser::m_excludeFiles;
+using namespace Qt::Literals::StringLiterals;
-static QSet<QString> topicCommands_;
-static QSet<QString> metaCommands_;
-
-/*!
- The constructor initializes some regular expressions
- and initializes the tokenizer variables.
- */
-CppCodeParser::CppCodeParser()
-{
- if (topicCommands_.isEmpty()) {
- topicCommands_ << COMMAND_CLASS << COMMAND_DONTDOCUMENT << COMMAND_ENUM << COMMAND_EXAMPLE
- << COMMAND_EXTERNALPAGE << COMMAND_FN << COMMAND_GROUP << COMMAND_HEADERFILE
- << COMMAND_MACRO << COMMAND_MODULE << COMMAND_NAMESPACE << COMMAND_PAGE
- << COMMAND_PROPERTY << COMMAND_TYPEALIAS << COMMAND_TYPEDEF
- << COMMAND_VARIABLE << COMMAND_QMLTYPE << COMMAND_QMLPROPERTY
- << COMMAND_QMLPROPERTYGROUP // mws 13/03/2019
- << COMMAND_QMLATTACHEDPROPERTY << COMMAND_QMLSIGNAL
- << COMMAND_QMLATTACHEDSIGNAL << COMMAND_QMLMETHOD
- << COMMAND_QMLATTACHEDMETHOD << COMMAND_QMLBASICTYPE << COMMAND_QMLMODULE
- << COMMAND_JSTYPE << COMMAND_JSPROPERTY
- << COMMAND_JSPROPERTYGROUP // mws 13/03/2019
- << COMMAND_JSATTACHEDPROPERTY << COMMAND_JSSIGNAL << COMMAND_JSATTACHEDSIGNAL
- << COMMAND_JSMETHOD << COMMAND_JSATTACHEDMETHOD << COMMAND_JSBASICTYPE
- << COMMAND_JSMODULE << COMMAND_STRUCT << COMMAND_UNION;
- }
- if (metaCommands_.isEmpty()) {
- metaCommands_ = commonMetaCommands();
- metaCommands_ << COMMAND_INHEADERFILE << COMMAND_NEXTPAGE
- << COMMAND_OVERLOAD << COMMAND_PREVIOUSPAGE << COMMAND_QMLINSTANTIATES
- << COMMAND_REIMP << COMMAND_RELATES;
- }
-}
+QT_BEGIN_NAMESPACE
-/*!
- The constructor initializes a map of special node types
- for identifying important nodes. And it initializes
- some filters for identifying and excluding certain kinds of files.
+/*
+ All these can appear in a C++ namespace. Don't add
+ anything that can't be in a C++ namespace.
*/
-void CppCodeParser::initializeParser()
+static const QMap<QString, Node::NodeType> s_nodeTypeMap{
+ { COMMAND_NAMESPACE, Node::Namespace }, { COMMAND_NAMESPACE, Node::Namespace },
+ { COMMAND_CLASS, Node::Class }, { COMMAND_STRUCT, Node::Struct },
+ { COMMAND_UNION, Node::Union }, { COMMAND_ENUM, Node::Enum },
+ { COMMAND_TYPEALIAS, Node::TypeAlias }, { COMMAND_TYPEDEF, Node::Typedef },
+ { COMMAND_PROPERTY, Node::Property }, { COMMAND_VARIABLE, Node::Variable }
+};
+
+typedef bool (Node::*NodeTypeTestFunc)() const;
+static const QMap<QString, NodeTypeTestFunc> s_nodeTypeTestFuncMap{
+ { COMMAND_NAMESPACE, &Node::isNamespace }, { COMMAND_CLASS, &Node::isClassNode },
+ { COMMAND_STRUCT, &Node::isStruct }, { COMMAND_UNION, &Node::isUnion },
+ { COMMAND_ENUM, &Node::isEnumType }, { COMMAND_TYPEALIAS, &Node::isTypeAlias },
+ { COMMAND_TYPEDEF, &Node::isTypedef }, { COMMAND_PROPERTY, &Node::isProperty },
+ { COMMAND_VARIABLE, &Node::isVariable },
+};
+
+CppCodeParser::CppCodeParser(FnCommandParser&& parser)
+ : fn_parser{parser}
{
- CodeParser::initializeParser();
-
- /*
- All these can appear in a C++ namespace. Don't add
- anything that can't be in a C++ namespace.
- */
- m_nodeTypeMap.insert(COMMAND_NAMESPACE, Node::Namespace);
- m_nodeTypeMap.insert(COMMAND_CLASS, Node::Class);
- m_nodeTypeMap.insert(COMMAND_STRUCT, Node::Struct);
- m_nodeTypeMap.insert(COMMAND_UNION, Node::Union);
- m_nodeTypeMap.insert(COMMAND_ENUM, Node::Enum);
- m_nodeTypeMap.insert(COMMAND_TYPEALIAS, Node::TypeAlias);
- m_nodeTypeMap.insert(COMMAND_TYPEDEF, Node::Typedef);
- m_nodeTypeMap.insert(COMMAND_PROPERTY, Node::Property);
- m_nodeTypeMap.insert(COMMAND_VARIABLE, Node::Variable);
-
- m_nodeTypeTestFuncMap.insert(COMMAND_NAMESPACE, &Node::isNamespace);
- m_nodeTypeTestFuncMap.insert(COMMAND_CLASS, &Node::isClassNode);
- m_nodeTypeTestFuncMap.insert(COMMAND_STRUCT, &Node::isStruct);
- m_nodeTypeTestFuncMap.insert(COMMAND_UNION, &Node::isUnion);
- m_nodeTypeTestFuncMap.insert(COMMAND_ENUM, &Node::isEnumType);
- m_nodeTypeTestFuncMap.insert(COMMAND_TYPEALIAS, &Node::isTypeAlias);
- m_nodeTypeTestFuncMap.insert(COMMAND_TYPEDEF, &Node::isTypedef);
- m_nodeTypeTestFuncMap.insert(COMMAND_PROPERTY, &Node::isProperty);
- m_nodeTypeTestFuncMap.insert(COMMAND_VARIABLE, &Node::isVariable);
-
Config &config = Config::instance();
- QStringList exampleFilePatterns =
- config.getStringList(CONFIG_EXAMPLES + Config::dot + CONFIG_FILEEXTENSIONS);
-
- // Used for excluding dirs and files from the list of example files
- const auto &excludeDirsList = config.getCanonicalPathList(CONFIG_EXCLUDEDIRS);
- m_excludeDirs = QSet<QString>(excludeDirsList.cbegin(), excludeDirsList.cend());
- const auto &excludeFilesList = config.getCanonicalPathList(CONFIG_EXCLUDEDIRS);
- m_excludeFiles = QSet<QString>(excludeFilesList.cbegin(), excludeFilesList.cend());
+ QStringList exampleFilePatterns{config.get(CONFIG_EXAMPLES
+ + Config::dot
+ + CONFIG_FILEEXTENSIONS).asStringList()};
if (!exampleFilePatterns.isEmpty())
m_exampleNameFilter = exampleFilePatterns.join(' ');
else
m_exampleNameFilter = "*.cpp *.h *.js *.xq *.svg *.xml *.ui";
- QStringList exampleImagePatterns =
- config.getStringList(CONFIG_EXAMPLES + Config::dot + CONFIG_IMAGEEXTENSIONS);
+ QStringList exampleImagePatterns{config.get(CONFIG_EXAMPLES
+ + Config::dot
+ + CONFIG_IMAGEEXTENSIONS).asStringList()};
if (!exampleImagePatterns.isEmpty())
m_exampleImageFilter = exampleImagePatterns.join(' ');
else
m_exampleImageFilter = "*.png";
-}
-/*!
- Clear the map of common node types and call
- the same function in the base class.
- */
-void CppCodeParser::terminateParser()
-{
- m_nodeTypeMap.clear();
- m_nodeTypeTestFuncMap.clear();
- m_excludeDirs.clear();
- m_excludeFiles.clear();
- CodeParser::terminateParser();
-}
-
-/*!
- Returns a list of extensions for header files.
- */
-QStringList CppCodeParser::headerFileNameFilter()
-{
- return QStringList();
-}
-
-/*!
- Returns a list of extensions for source files, i.e. not
- header files.
- */
-QStringList CppCodeParser::sourceFileNameFilter()
-{
- return QStringList();
-}
-
-/*!
- Returns the set of strings representing the topic commands.
- */
-const QSet<QString> &CppCodeParser::topicCommands()
-{
- return topicCommands_;
+ m_showLinkErrors = !config.get(CONFIG_NOLINKERRORS).asBool();
}
/*!
@@ -190,10 +81,11 @@ const QSet<QString> &CppCodeParser::topicCommands()
Node *CppCodeParser::processTopicCommand(const Doc &doc, const QString &command,
const ArgPair &arg)
{
- ExtraFuncData extra;
+ QDocDatabase* database = QDocDatabase::qdocDB();
+
if (command == COMMAND_FN) {
Q_UNREACHABLE();
- } else if (m_nodeTypeMap.contains(command)) {
+ } else if (s_nodeTypeMap.contains(command)) {
/*
We should only get in here if the command refers to
something that can appear in a C++ namespace,
@@ -202,7 +94,7 @@ Node *CppCodeParser::processTopicCommand(const Doc &doc, const QString &command,
this way to allow the writer to refer to the entity
without including the namespace qualifier.
*/
- Node::NodeType type = m_nodeTypeMap[command];
+ Node::NodeType type = s_nodeTypeMap[command];
QStringList words = arg.first.split(QLatin1Char(' '));
QStringList path;
qsizetype idx = 0;
@@ -212,12 +104,10 @@ Node *CppCodeParser::processTopicCommand(const Doc &doc, const QString &command,
idx = words.size() - 1;
path = words[idx].split("::");
- node = m_qdb->findNodeInOpenNamespace(path, m_nodeTypeTestFuncMap[command]);
- if (node == nullptr)
- node = m_qdb->findNodeByNameAndType(path, m_nodeTypeTestFuncMap[command]);
+ node = database->findNodeByNameAndType(path, s_nodeTypeTestFuncMap[command]);
// Allow representing a type alias as a class
if (node == nullptr && command == COMMAND_CLASS) {
- node = m_qdb->findNodeByNameAndType(path, &Node::isTypeAlias);
+ node = database->findNodeByNameAndType(path, &Node::isTypeAlias);
if (node) {
auto access = node->access();
auto loc = node->location();
@@ -229,7 +119,7 @@ Node *CppCodeParser::processTopicCommand(const Doc &doc, const QString &command,
}
}
if (node == nullptr) {
- if (isWorthWarningAbout(doc)) {
+ if (CodeParser::isWorthWarningAbout(doc)) {
doc.location().warning(
QStringLiteral("Cannot find '%1' specified with '\\%2' in any header file")
.arg(arg.first, command));
@@ -240,111 +130,64 @@ Node *CppCodeParser::processTopicCommand(const Doc &doc, const QString &command,
ns->markSeen();
ns->setWhereDocumented(ns->tree()->camelCaseModuleName());
}
- /*
- This treats a class as a namespace.
- */
- if ((type == Node::Class) || (type == Node::Namespace) || (type == Node::Struct)
- || (type == Node::Union)) {
- if (path.size() > 1) {
- path.pop_back();
- QString ns = path.join(QLatin1String("::"));
- m_qdb->insertOpenNamespace(ns);
- }
- }
}
return node;
} else if (command == COMMAND_EXAMPLE) {
if (Config::generateExamples) {
- auto *en = new ExampleNode(m_qdb->primaryTreeRoot(), arg.first);
+ auto *en = new ExampleNode(database->primaryTreeRoot(), arg.first);
en->setLocation(doc.startLocation());
setExampleFileLists(en);
return en;
}
} else if (command == COMMAND_EXTERNALPAGE) {
- auto *epn = new ExternalPageNode(m_qdb->primaryTreeRoot(), arg.first);
+ auto *epn = new ExternalPageNode(database->primaryTreeRoot(), arg.first);
epn->setLocation(doc.startLocation());
return epn;
} else if (command == COMMAND_HEADERFILE) {
- auto *hn = new HeaderNode(m_qdb->primaryTreeRoot(), arg.first);
+ auto *hn = new HeaderNode(database->primaryTreeRoot(), arg.first);
hn->setLocation(doc.startLocation());
return hn;
} else if (command == COMMAND_GROUP) {
- CollectionNode *cn = m_qdb->addGroup(arg.first);
+ CollectionNode *cn = database->addGroup(arg.first);
cn->setLocation(doc.startLocation());
cn->markSeen();
return cn;
} else if (command == COMMAND_MODULE) {
- CollectionNode *cn = m_qdb->addModule(arg.first);
+ CollectionNode *cn = database->addModule(arg.first);
cn->setLocation(doc.startLocation());
cn->markSeen();
return cn;
} else if (command == COMMAND_QMLMODULE) {
QStringList blankSplit = arg.first.split(QLatin1Char(' '));
- CollectionNode *cn = m_qdb->addQmlModule(blankSplit[0]);
- cn->setLogicalModuleInfo(blankSplit);
- cn->setLocation(doc.startLocation());
- cn->markSeen();
- return cn;
- } else if (command == COMMAND_JSMODULE) {
- QStringList blankSplit = arg.first.split(QLatin1Char(' '));
- CollectionNode *cn = m_qdb->addJsModule(blankSplit[0]);
+ CollectionNode *cn = database->addQmlModule(blankSplit[0]);
cn->setLogicalModuleInfo(blankSplit);
cn->setLocation(doc.startLocation());
cn->markSeen();
return cn;
} else if (command == COMMAND_PAGE) {
- Node::PageType ptype = Node::ArticlePage;
- QStringList args = arg.first.split(QLatin1Char(' '));
- if (args.size() > 1) {
- QString t = args[1].toLower();
- if (t == "howto")
- ptype = Node::HowToPage;
- else if (t == "api")
- ptype = Node::ApiPage;
- else if (t == "example")
- ptype = Node::ExamplePage;
- else if (t == "overview")
- ptype = Node::OverviewPage;
- else if (t == "tutorial")
- ptype = Node::TutorialPage;
- else if (t == "faq")
- ptype = Node::FAQPage;
- else if (t == "attribution")
- ptype = Node::AttributionPage;
- }
- auto *pn = new PageNode(m_qdb->primaryTreeRoot(), args[0], ptype);
+ auto *pn = new PageNode(database->primaryTreeRoot(), arg.first.split(' ').front());
pn->setLocation(doc.startLocation());
return pn;
- } else if (command == COMMAND_QMLTYPE) {
- QmlTypeNode *qcn = nullptr;
- Node *candidate = m_qdb->primaryTreeRoot()->findChildNode(arg.first, Node::QML);
- if (candidate != nullptr && candidate->isQmlType())
- qcn = static_cast<QmlTypeNode *>(candidate);
- else
- qcn = new QmlTypeNode(m_qdb->primaryTreeRoot(), arg.first);
- qcn->setLocation(doc.startLocation());
- return qcn;
- } else if (command == COMMAND_JSTYPE) {
- QmlTypeNode *qcn = nullptr;
- Node *candidate = m_qdb->primaryTreeRoot()->findChildNode(arg.first, Node::JS);
- if (candidate != nullptr && candidate->isJsType())
- qcn = static_cast<QmlTypeNode *>(candidate);
- else
- qcn = new QmlTypeNode(m_qdb->primaryTreeRoot(), arg.first, Node::JsType);
+ } else if (command == COMMAND_QMLTYPE ||
+ command == COMMAND_QMLVALUETYPE ||
+ command == COMMAND_QMLBASICTYPE) {
+ auto nodeType = (command == COMMAND_QMLTYPE) ? Node::QmlType : Node::QmlValueType;
+ QString qmid;
+ if (auto args = doc.metaCommandArgs(COMMAND_INQMLMODULE); !args.isEmpty())
+ qmid = args.first().first;
+ auto *qcn = database->findQmlTypeInPrimaryTree(qmid, arg.first);
+ // A \qmlproperty may have already constructed a placeholder type
+ // without providing a module identifier; allow such cases
+ if (!qcn && !qmid.isEmpty())
+ qcn = database->findQmlTypeInPrimaryTree(QString(), arg.first);
+ if (!qcn || qcn->nodeType() != nodeType)
+ qcn = new QmlTypeNode(database->primaryTreeRoot(), arg.first, nodeType);
+ if (!qmid.isEmpty())
+ database->addToQmlModule(qmid, qcn);
qcn->setLocation(doc.startLocation());
return qcn;
- } else if (command == COMMAND_QMLBASICTYPE) {
- auto *node = new QmlBasicTypeNode(m_qdb->primaryTreeRoot(), arg.first);
- node->setLocation(doc.startLocation());
- return node;
- } else if (command == COMMAND_JSBASICTYPE) {
- auto *node = new QmlBasicTypeNode(m_qdb->primaryTreeRoot(), arg.first, Node::JsBasicType);
- node->setLocation(doc.startLocation());
- return node;
} else if ((command == COMMAND_QMLSIGNAL) || (command == COMMAND_QMLMETHOD)
- || (command == COMMAND_QMLATTACHEDSIGNAL) || (command == COMMAND_QMLATTACHEDMETHOD)
- || (command == COMMAND_JSSIGNAL) || (command == COMMAND_JSMETHOD)
- || (command == COMMAND_JSATTACHEDSIGNAL) || (command == COMMAND_JSATTACHEDMETHOD)) {
+ || (command == COMMAND_QMLATTACHEDSIGNAL) || (command == COMMAND_QMLATTACHEDMETHOD)) {
Q_UNREACHABLE();
}
return nullptr;
@@ -397,43 +240,51 @@ bool CppCodeParser::splitQmlPropertyArg(const QString &arg, QString &type, QStri
return false;
}
-/*!
- */
-void CppCodeParser::processQmlProperties(const Doc &doc, NodeList &nodes, DocList &docs)
+std::vector<TiedDocumentation> CppCodeParser::processQmlProperties(const UntiedDocumentation &untied)
{
+ const Doc &doc = untied.documentation;
const TopicList &topics = doc.topicsUsed();
if (topics.isEmpty())
- return;
+ return {};
QString arg;
QString type;
QString group;
- QString module;
+ QString qmlModule;
QString property;
QString qmlTypeName;
+ std::vector<TiedDocumentation> tied{};
+
Topic topic = topics.at(0);
- bool jsProps = isJSPropertyTopic(topic.m_topic);
arg = topic.m_args;
- if (splitQmlPropertyArg(arg, type, module, qmlTypeName, property, doc.location())) {
+ if (splitQmlPropertyArg(arg, type, qmlModule, qmlTypeName, property, doc.location())) {
qsizetype i = property.indexOf('.');
if (i != -1)
group = property.left(i);
}
+ QDocDatabase *database = QDocDatabase::qdocDB();
+
NodeList sharedNodes;
- QmlTypeNode *qmlType = m_qdb->findQmlType(module, qmlTypeName);
- if (qmlType == nullptr)
- qmlType = new QmlTypeNode(m_qdb->primaryTreeRoot(), qmlTypeName);
+ QmlTypeNode *qmlType = database->findQmlTypeInPrimaryTree(qmlModule, qmlTypeName);
+ // Note: Constructing a QmlType node by default, as opposed to QmlValueType.
+ // This may lead to unexpected behavior if documenting \qmlvaluetype's properties
+ // before the type itself.
+ if (qmlType == nullptr) {
+ qmlType = new QmlTypeNode(database->primaryTreeRoot(), qmlTypeName, Node::QmlType);
+ qmlType->setLocation(doc.startLocation());
+ if (!qmlModule.isEmpty())
+ database->addToQmlModule(qmlModule, qmlType);
+ }
for (const auto &topicCommand : topics) {
QString cmd = topicCommand.m_topic;
arg = topicCommand.m_args;
- if ((cmd == COMMAND_QMLPROPERTY) || (cmd == COMMAND_QMLATTACHEDPROPERTY)
- || (cmd == COMMAND_JSPROPERTY) || (cmd == COMMAND_JSATTACHEDPROPERTY)) {
+ if ((cmd == COMMAND_QMLPROPERTY) || (cmd == COMMAND_QMLATTACHEDPROPERTY)) {
bool attached = cmd.contains(QLatin1String("attached"));
- if (splitQmlPropertyArg(arg, type, module, qmlTypeName, property, doc.location())) {
- if (qmlType != m_qdb->findQmlType(module, qmlTypeName)) {
+ if (splitQmlPropertyArg(arg, type, qmlModule, qmlTypeName, property, doc.location())) {
+ if (qmlType != database->findQmlTypeInPrimaryTree(qmlModule, qmlTypeName)) {
doc.startLocation().warning(
QStringLiteral(
"All properties in a group must belong to the same type: '%1'")
@@ -446,20 +297,22 @@ void CppCodeParser::processQmlProperties(const Doc &doc, NodeList &nodes, DocLis
if (!doc.body().isEmpty()) {
doc.startLocation().warning(
QStringLiteral("QML property documented multiple times: '%1'")
- .arg(arg));
+ .arg(arg), QStringLiteral("also seen here: %1")
+ .arg(existingProperty->location().toString()));
}
continue;
}
auto *qpn = new QmlPropertyNode(qmlType, property, type, attached);
qpn->setLocation(doc.startLocation());
- qpn->setGenus(jsProps ? Node::JS : Node::QML);
- nodes.append(qpn);
- docs.append(doc);
+ qpn->setGenus(Node::QML);
+
+ tied.emplace_back(TiedDocumentation{doc, qpn});
+
sharedNodes << qpn;
}
} else {
doc.startLocation().warning(
- QStringLiteral("Command '\\%1'; not allowed with QML/JS property commands")
+ QStringLiteral("Command '\\%1'; not allowed with QML property commands")
.arg(cmd));
}
}
@@ -468,24 +321,18 @@ void CppCodeParser::processQmlProperties(const Doc &doc, NodeList &nodes, DocLis
// valid nodes. Note that it's important to do this *after* constructing
// the topic nodes - which need to be written to index before the related
// scn.
- if (sharedNodes.count() > 1) {
- auto *scn = new SharedCommentNode(qmlType, sharedNodes.count(), group);
+ if (sharedNodes.size() > 1) {
+ auto *scn = new SharedCommentNode(qmlType, sharedNodes.size(), group);
scn->setLocation(doc.startLocation());
- nodes.append(scn);
- docs.append(doc);
+
+ tied.emplace_back(TiedDocumentation{doc, scn});
+
for (const auto n : sharedNodes)
scn->append(n);
scn->sort();
}
-}
-/*!
- Returns the set of strings representing the common metacommands
- plus some other metacommands.
- */
-const QSet<QString> &CppCodeParser::metaCommands()
-{
- return metaCommands_;
+ return tied;
}
/*!
@@ -498,12 +345,34 @@ const QSet<QString> &CppCodeParser::metaCommands()
void CppCodeParser::processMetaCommand(const Doc &doc, const QString &command,
const ArgPair &argPair, Node *node)
{
+ QDocDatabase* database = QDocDatabase::qdocDB();
+
QString arg = argPair.first;
if (command == COMMAND_INHEADERFILE) {
- if (node->isAggregate())
- static_cast<Aggregate *>(node)->addIncludeFile(arg);
+ // TODO: [incorrect-constructs][header-arg]
+ // The emptiness check for arg is required as,
+ // currently, DocParser fancies passing (without any warning)
+ // incorrect constructs doen the chain, such as an
+ // "\inheaderfile" command with no argument.
+ //
+ // As it is the case here, we require further sanity checks to
+ // preserve some of the semantic for the later phases.
+ // This generally has a ripple effect on the whole codebase,
+ // making it more complex and increasesing the surface of bugs.
+ //
+ // The following emptiness check should be removed as soon as
+ // DocParser is enhanced with correct semantics.
+ if (node->isAggregate() && !arg.isEmpty())
+ static_cast<Aggregate *>(node)->setIncludeFile(arg);
else
doc.location().warning(QStringLiteral("Ignored '\\%1'").arg(COMMAND_INHEADERFILE));
+ } else if (command == COMMAND_COMPARES) {
+ processComparesCommand(node, arg, doc.location());
+ } else if (command == COMMAND_COMPARESWITH) {
+ if (!node->isClassNode())
+ doc.location().warning(
+ u"Found \\%1 command outside of \\%2 context."_s
+ .arg(COMMAND_COMPARESWITH, COMMAND_CLASS));
} else if (command == COMMAND_OVERLOAD) {
/*
Note that this might set the overload flag of the
@@ -525,7 +394,7 @@ void CppCodeParser::processMetaCommand(const Doc &doc, const QString &command,
// qualified name of the overridden function.
// If the name of the overridden function isn't
// set, issue a warning.
- if (fn->overridesThis().isEmpty() && isWorthWarningAbout(doc)) {
+ if (fn->overridesThis().isEmpty() && CodeParser::isWorthWarningAbout(doc)) {
doc.location().warning(
QStringLiteral("Cannot find base function for '\\%1' in %2()")
.arg(COMMAND_REIMP, node->name()),
@@ -540,66 +409,37 @@ void CppCodeParser::processMetaCommand(const Doc &doc, const QString &command,
}
}
} else if (command == COMMAND_RELATES) {
- QStringList path = arg.split("::");
- Aggregate *aggregate = m_qdb->findRelatesNode(path);
- if (aggregate == nullptr)
- aggregate = new ProxyNode(node->root(), arg);
-
- if (node->parent() == aggregate) { // node is already a child of aggregate
- doc.location().warning(QStringLiteral("Invalid '\\%1' (already a member of '%2')")
- .arg(COMMAND_RELATES, arg));
- } else {
- if (node->isAggregate()) {
- doc.location().warning(QStringLiteral("Invalid '\\%1' not allowed in '\\%2'")
- .arg(COMMAND_RELATES, node->nodeTypeString()));
- } else if (!node->isRelatedNonmember() &&
- !node->parent()->isNamespace() && !node->parent()->isHeader()) {
- if (!doc.isInternal()) {
- doc.location().warning(QStringLiteral("Invalid '\\%1' ('%2' must be global)")
- .arg(COMMAND_RELATES, node->name()));
- }
- } else if (!node->isRelatedNonmember() && !node->parent()->isHeader()) {
- aggregate->adoptChild(node);
- node->setRelatedNonmember(true);
- } else {
- /*
- There are multiple \relates commands. This
- one is not the first, so clone the node as
- a child of aggregate.
- */
- Node *clone = node->clone(aggregate);
- if (clone == nullptr) {
- doc.location().warning(
- QStringLiteral("Invalid '\\%1' (multiple uses not allowed in '%2')")
- .arg(COMMAND_RELATES, node->nodeTypeString()));
- } else {
- clone->setRelatedNonmember(true);
- }
+ // REMARK: Generates warnings only; Node instances are
+ // adopted from the root namespace to other Aggregates
+ // in a post-processing step, Aggregate::resolveRelates(),
+ // after all topic commands are processed.
+ if (node->isAggregate()) {
+ doc.location().warning("Invalid '\\%1' not allowed in '\\%2'"_L1
+ .arg(COMMAND_RELATES, node->nodeTypeString()));
+ } else if (!node->isRelatedNonmember() && node->parent()->isClassNode()) {
+ if (!doc.isInternal()) {
+ doc.location().warning("Invalid '\\%1' ('%2' must be global)"_L1
+ .arg(COMMAND_RELATES, node->name()));
}
}
} else if (command == COMMAND_NEXTPAGE) {
- setLink(node, Node::NextLink, arg);
+ CodeParser::setLink(node, Node::NextLink, arg);
} else if (command == COMMAND_PREVIOUSPAGE) {
- setLink(node, Node::PreviousLink, arg);
+ CodeParser::setLink(node, Node::PreviousLink, arg);
} else if (command == COMMAND_STARTPAGE) {
- setLink(node, Node::StartLink, arg);
+ CodeParser::setLink(node, Node::StartLink, arg);
} else if (command == COMMAND_QMLINHERITS) {
if (node->name() == arg)
doc.location().warning(QStringLiteral("%1 tries to inherit itself").arg(arg));
- else if (node->isQmlType() || node->isJsType()) {
+ else if (node->isQmlType()) {
auto *qmlType = static_cast<QmlTypeNode *>(node);
qmlType->setQmlBaseName(arg);
}
- } else if (command == COMMAND_QMLINSTANTIATES) {
- if (node->isQmlType() || node->isJsType()) {
- ClassNode *classNode = m_qdb->findClassNode(arg.split("::"));
- if (classNode)
- node->setClassNode(classNode);
- else
- doc.location().warning(
- QStringLiteral("C++ class %1 not found: \\instantiates %1").arg(arg));
- } else
- doc.location().warning(QStringLiteral("\\instantiates is only allowed in \\qmltype"));
+ } else if (command == COMMAND_QMLNATIVETYPE || command == COMMAND_QMLINSTANTIATES) {
+ if (command == COMMAND_QMLINSTANTIATES)
+ doc.location().report(u"\\instantiates is deprected and will be removed in a future version. Use \\nativetype instead."_s);
+ // TODO: COMMAND_QMLINSTANTIATES is deprecated since 6.8. Its remains should be removed no later than Qt 7.0.0.
+ processQmlNativeTypeCommand(node, arg, doc.location());
} else if (command == COMMAND_DEFAULT) {
if (!node->isQmlProperty()) {
doc.location().warning(QStringLiteral("Ignored '\\%1', applies only to '\\%2'")
@@ -612,6 +452,14 @@ void CppCodeParser::processMetaCommand(const Doc &doc, const QString &command,
}
} else if (command == COMMAND_QMLDEFAULT) {
node->markDefault();
+ } else if (command == COMMAND_QMLENUMERATORSFROM) {
+ if (!node->isQmlProperty()) {
+ doc.location().warning("Ignored '\\%1', applies only to '\\%2'"_L1
+ .arg(command, COMMAND_QMLPROPERTY));
+ } else if (!static_cast<QmlPropertyNode*>(node)->setEnumNode(argPair.first, argPair.second)) {
+ doc.location().warning("Failed to find C++ enumeration '%2' passed to \\%1"_L1
+ .arg(command, arg), "Use \\value commands instead"_L1);
+ }
} else if (command == COMMAND_QMLREADONLY) {
node->markReadOnly(true);
} else if (command == COMMAND_QMLREQUIRED) {
@@ -620,21 +468,17 @@ void CppCodeParser::processMetaCommand(const Doc &doc, const QString &command,
else
static_cast<QmlPropertyNode *>(node)->setRequired();
} else if ((command == COMMAND_QMLABSTRACT) || (command == COMMAND_ABSTRACT)) {
- if (node->isQmlType() || node->isJsType())
+ if (node->isQmlType())
node->setAbstract(true);
} else if (command == COMMAND_DEPRECATED) {
- node->setStatus(Node::Deprecated);
- if (!argPair.second.isEmpty())
- node->setDeprecatedSince(argPair.second);
+ node->setDeprecated(argPair.second);
} else if (command == COMMAND_INGROUP || command == COMMAND_INPUBLICGROUP) {
// Note: \ingroup and \inpublicgroup are the same (and now recognized as such).
- m_qdb->addToGroup(arg, node);
+ database->addToGroup(arg, node);
} else if (command == COMMAND_INMODULE) {
- m_qdb->addToModule(arg, node);
+ database->addToModule(arg, node);
} else if (command == COMMAND_INQMLMODULE) {
- m_qdb->addToQmlModule(arg, node);
- } else if (command == COMMAND_INJSMODULE) {
- m_qdb->addToJsModule(arg, node);
+ // Handled when parsing topic commands
} else if (command == COMMAND_OBSOLETE) {
node->setStatus(Node::Deprecated);
} else if (command == COMMAND_NONREENTRANT) {
@@ -644,7 +488,7 @@ void CppCodeParser::processMetaCommand(const Doc &doc, const QString &command,
if (!node->isInternal())
node->setStatus(Node::Preliminary);
} else if (command == COMMAND_INTERNAL) {
- if (!showInternal())
+ if (!Config::instance().showInternal())
node->markInternal();
} else if (command == COMMAND_REENTRANT) {
node->setThreadSafeness(Node::Reentrant);
@@ -658,7 +502,7 @@ void CppCodeParser::processMetaCommand(const Doc &doc, const QString &command,
if (!node->setTitle(arg))
doc.location().warning(QStringLiteral("Ignored '\\%1'").arg(COMMAND_TITLE));
else if (node->isExample())
- m_qdb->addExampleNode(static_cast<ExampleNode *>(node));
+ database->addExampleNode(static_cast<ExampleNode *>(node));
} else if (command == COMMAND_SUBTITLE) {
if (!node->setSubtitle(arg))
doc.location().warning(QStringLiteral("Ignored '\\%1'").arg(COMMAND_SUBTITLE));
@@ -670,13 +514,83 @@ void CppCodeParser::processMetaCommand(const Doc &doc, const QString &command,
"Command '\\%1' is only meaningful in '\\module' and '\\qmlmodule'.")
.arg(COMMAND_QTVARIABLE));
} else if (command == COMMAND_QTCMAKEPACKAGE) {
- node->setQtCMakeComponent(arg);
- if (!node->isModule())
+ if (node->isModule())
+ node->setQtCMakeComponent(arg);
+ else
doc.location().warning(
QStringLiteral("Command '\\%1' is only meaningful in '\\module'.")
.arg(COMMAND_QTCMAKEPACKAGE));
+ } else if (command == COMMAND_QTCMAKETARGETITEM) {
+ if (node->isModule())
+ node->setQtCMakeTargetItem(arg);
+ else
+ doc.location().warning(
+ QStringLiteral("Command '\\%1' is only meaningful in '\\module'.")
+ .arg(COMMAND_QTCMAKETARGETITEM));
+ } else if (command == COMMAND_MODULESTATE ) {
+ if (!node->isModule() && !node->isQmlModule()) {
+ doc.location().warning(
+ QStringLiteral(
+ "Command '\\%1' is only meaningful in '\\module' and '\\qmlmodule'.")
+ .arg(COMMAND_MODULESTATE));
+ } else {
+ static_cast<CollectionNode*>(node)->setState(arg);
+ }
} else if (command == COMMAND_NOAUTOLIST) {
- node->setNoAutoList(true);
+ if (!node->isCollectionNode() && !node->isExample()) {
+ doc.location().warning(
+ QStringLiteral(
+ "Command '\\%1' is only meaningful in '\\module', '\\qmlmodule', `\\group` and `\\example`.")
+ .arg(COMMAND_NOAUTOLIST));
+ } else {
+ static_cast<PageNode*>(node)->setNoAutoList(true);
+ }
+ } else if (command == COMMAND_ATTRIBUTION) {
+ // TODO: This condition is not currently exact enough, as it
+ // will allow any non-aggregate `PageNode` to use the command,
+ // For example, an `ExampleNode`.
+ //
+ // The command is intended only for internal usage by
+ // "qattributionscanner" and should only work on `PageNode`s
+ // that are generated from a "\page" command.
+ //
+ // It is already possible to provide a more restricted check,
+ // albeit in a somewhat dirty way. It is not expected that
+ // this warning will have any particular use.
+ // If it so happens that a case where the too-broad scope of
+ // the warning is a problem or hides a bug, modify the
+ // condition to be restrictive enough.
+ // Otherwise, wait until a more torough look at QDoc's
+ // internal representations an way to enable "Attribution
+ // Pages" is performed before looking at the issue again.
+ if (!node->isTextPageNode()) {
+ doc.location().warning(u"Command '\\%1' is only meaningful in '\\%2'"_s.arg(COMMAND_ATTRIBUTION, COMMAND_PAGE));
+ } else { static_cast<PageNode*>(node)->markAttribution(); }
+ }
+}
+
+/*!
+ \internal
+ Processes the argument \a arg that's passed to the \\compares command,
+ and sets the comparison category of the \a node accordingly.
+
+ If the argument is invalid, issue a warning at the location the command
+ appears through \a loc.
+*/
+void CppCodeParser::processComparesCommand(Node *node, const QString &arg, const Location &loc)
+{
+ if (!node->isClassNode()) {
+ loc.warning(u"Found \\%1 command outside of \\%2 context."_s.arg(COMMAND_COMPARES,
+ COMMAND_CLASS));
+ return;
+ }
+
+ if (auto category = comparisonCategoryFromString(arg.toStdString());
+ category != ComparisonCategory::None) {
+ node->setComparisonCategory(category);
+ } else {
+ loc.warning(u"Invalid argument to \\%1 command: `%2`"_s.arg(COMMAND_COMPARES, arg),
+ u"Valid arguments are `strong`, `weak`, `partial`, or `equality`."_s);
}
}
@@ -688,16 +602,39 @@ void CppCodeParser::processMetaCommand(const Doc &doc, const QString &command,
*/
void CppCodeParser::processMetaCommands(const Doc &doc, Node *node)
{
+ std::vector<Node*> nodes_to_process{};
+ if (node->isSharedCommentNode()) {
+ auto scn = static_cast<SharedCommentNode*>(node);
+
+ nodes_to_process.reserve(scn->count() + 1);
+ std::copy(scn->collective().cbegin(), scn->collective().cend(), std::back_inserter(nodes_to_process));
+ }
+
+ // REMARK: Ordering is important here. If node is a
+ // SharedCommentNode it MUST be processed after all its child
+ // nodes.
+ // Failure to do so can incur in incorrect warnings.
+ // For example, if a shared documentation has a "\relates" command.
+ // When the command is processed for the SharedCommentNode it will
+ // apply to all its child nodes.
+ // If a child node is processed after the SharedCommentNode that
+ // contains it, that "\relates" command will be considered applied
+ // already, resulting in a warning.
+ nodes_to_process.push_back(node);
+
const QStringList metaCommandsUsed = doc.metaCommandsUsed().values();
for (const auto &command : metaCommandsUsed) {
const ArgList args = doc.metaCommandArgs(command);
- for (const auto &arg : args)
- processMetaCommand(doc, command, arg, node);
+ for (const auto &arg : args) {
+ std::for_each(nodes_to_process.cbegin(), nodes_to_process.cend(), [this, doc, command, arg](auto node){
+ processMetaCommand(doc, command, arg, node);
+ });
+ }
}
}
/*!
- Parse QML/JS signal/method topic commands.
+ Parse QML signal/method topic commands.
*/
FunctionNode *CppCodeParser::parseOtherFuncArg(const QString &topic, const Location &location,
const QString &funcArg)
@@ -713,7 +650,7 @@ FunctionNode *CppCodeParser::parseOtherFuncArg(const QString &topic, const Locat
qsizetype firstBlank = funcName.indexOf(QChar(' '));
if (firstBlank > 0) {
returnType = funcName.left(firstBlank);
- funcName = funcName.right(funcName.length() - firstBlank - 1);
+ funcName = funcName.right(funcName.size() - firstBlank - 1);
}
QStringList colonSplit(funcName.split("::"));
@@ -732,11 +669,18 @@ FunctionNode *CppCodeParser::parseOtherFuncArg(const QString &topic, const Locat
}
funcName = colonSplit.last();
- Aggregate *aggregate = m_qdb->findQmlType(moduleName, elementName);
- if (aggregate == nullptr)
- aggregate = m_qdb->findQmlBasicType(moduleName, elementName);
- if (aggregate == nullptr)
- return nullptr;
+ QDocDatabase* database = QDocDatabase::qdocDB();
+
+ auto *aggregate = database->findQmlTypeInPrimaryTree(moduleName, elementName);
+ // Note: Constructing a QmlType node by default, as opposed to QmlValueType.
+ // This may lead to unexpected behavior if documenting \qmlvaluetype's methods
+ // before the type itself.
+ if (!aggregate) {
+ aggregate = new QmlTypeNode(database->primaryTreeRoot(), elementName, Node::QmlType);
+ aggregate->setLocation(location);
+ if (!moduleName.isEmpty())
+ database->addToQmlModule(moduleName, aggregate);
+ }
QString params;
QStringList leftParenSplit = funcArg.split('(');
@@ -764,6 +708,8 @@ FunctionNode *CppCodeParser::parseOtherFuncArg(const QString &topic, const Locat
*/
FunctionNode *CppCodeParser::parseMacroArg(const Location &location, const QString &macroArg)
{
+ QDocDatabase* database = QDocDatabase::qdocDB();
+
QStringList leftParenSplit = macroArg.split('(');
if (leftParenSplit.isEmpty())
return nullptr;
@@ -772,7 +718,7 @@ FunctionNode *CppCodeParser::parseMacroArg(const Location &location, const QStri
QStringList blankSplit = leftParenSplit[0].split(' ');
if (!blankSplit.empty()) {
macroName = blankSplit.last();
- oldMacroNode = m_qdb->findMacroNode(macroName);
+ oldMacroNode = database->findMacroNode(macroName);
}
QString returnType;
if (blankSplit.size() > 1) {
@@ -787,7 +733,7 @@ FunctionNode *CppCodeParser::parseMacroArg(const Location &location, const QStri
params = afterParen.left(rightParen);
}
int i = 0;
- while (i < macroName.length() && !macroName.at(i).isLetter())
+ while (i < macroName.size() && !macroName.at(i).isLetter())
i++;
if (i > 0) {
returnType += QChar(' ') + macroName.left(i);
@@ -796,14 +742,16 @@ FunctionNode *CppCodeParser::parseMacroArg(const Location &location, const QStri
FunctionNode::Metaness metaness = FunctionNode::MacroWithParams;
if (params.isEmpty())
metaness = FunctionNode::MacroWithoutParams;
- auto *macro = new FunctionNode(metaness, m_qdb->primaryTreeRoot(), macroName);
+ auto *macro = new FunctionNode(metaness, database->primaryTreeRoot(), macroName);
macro->setAccess(Access::Public);
macro->setLocation(location);
macro->setReturnType(returnType);
macro->setParameters(params);
- if (macro->compare(oldMacroNode)) {
- location.warning(QStringLiteral("\\macro %1 documented more than once").arg(macroArg));
- oldMacroNode->doc().location().warning(QStringLiteral("(The previous doc is here)"));
+ if (oldMacroNode && macro->parent() == oldMacroNode->parent()
+ && compare(macro, oldMacroNode) == 0) {
+ location.warning(QStringLiteral("\\macro %1 documented more than once")
+ .arg(macroArg), QStringLiteral("also seen here: %1")
+ .arg(oldMacroNode->doc().location().toString()));
}
return macro;
}
@@ -823,13 +771,15 @@ void CppCodeParser::setExampleFileLists(ExampleNode *en)
QDir exampleDir(QFileInfo(fullPath).dir());
+ const auto& [excludeDirs, excludeFiles] = config.getExcludedPaths();
+
QStringList exampleFiles = Config::getFilesHere(exampleDir.path(), m_exampleNameFilter,
- Location(), m_excludeDirs, m_excludeFiles);
+ Location(), excludeDirs, excludeFiles);
// Search for all image files under the example project, excluding doc/images directory.
- QSet<QString> excludeDocDirs(m_excludeDirs);
+ QSet<QString> excludeDocDirs(excludeDirs);
excludeDocDirs.insert(exampleDir.path() + QLatin1String("/doc/images"));
QStringList imageFiles = Config::getFilesHere(exampleDir.path(), m_exampleImageFilter,
- Location(), excludeDocDirs, m_excludeFiles);
+ Location(), excludeDocDirs, excludeFiles);
if (!exampleFiles.isEmpty()) {
// move main.cpp to the end, if it exists
QString mainCpp;
@@ -853,7 +803,8 @@ void CppCodeParser::setExampleFileLists(ExampleNode *en)
// Add any resource and project files
exampleFiles += Config::getFilesHere(exampleDir.path(),
- QLatin1String("*.qrc *.pro *.qmlproject *.pyproject CMakeLists.txt qmldir"));
+ QLatin1String("*.qrc *.pro *.qmlproject *.pyproject CMakeLists.txt qmldir"),
+ Location(), excludeDirs, excludeFiles);
}
const qsizetype pathLen = exampleDir.path().size() - en->name().size();
@@ -867,16 +818,6 @@ void CppCodeParser::setExampleFileLists(ExampleNode *en)
}
/*!
- returns true if \a t is \e {jssignal}, \e {jsmethod},
- \e {jsattachedsignal}, or \e {jsattachedmethod}.
- */
-bool CppCodeParser::isJSMethodTopic(const QString &t)
-{
- return (t == COMMAND_JSSIGNAL || t == COMMAND_JSMETHOD || t == COMMAND_JSATTACHEDSIGNAL
- || t == COMMAND_JSATTACHEDMETHOD);
-}
-
-/*!
returns true if \a t is \e {qmlsignal}, \e {qmlmethod},
\e {qmlattachedsignal}, or \e {qmlattachedmethod}.
*/
@@ -887,15 +828,6 @@ bool CppCodeParser::isQMLMethodTopic(const QString &t)
}
/*!
- Returns true if \a t is \e {jsproperty}, \e {jspropertygroup},
- or \e {jsattachedproperty}.
- */
-bool CppCodeParser::isJSPropertyTopic(const QString &t)
-{
- return (t == COMMAND_JSPROPERTY || t == COMMAND_JSATTACHEDPROPERTY);
-}
-
-/*!
Returns true if \a t is \e {qmlproperty}, \e {qmlpropertygroup},
or \e {qmlattachedproperty}.
*/
@@ -904,41 +836,63 @@ bool CppCodeParser::isQMLPropertyTopic(const QString &t)
return (t == COMMAND_QMLPROPERTY || t == COMMAND_QMLATTACHEDPROPERTY);
}
-void CppCodeParser::processTopicArgs(const Doc &doc, const QString &topic, NodeList &nodes,
- DocList &docs)
+std::pair<std::vector<TiedDocumentation>, std::vector<FnMatchError>>
+CppCodeParser::processTopicArgs(const UntiedDocumentation &untied)
{
- if (isQMLPropertyTopic(topic) || isJSPropertyTopic(topic)) {
- processQmlProperties(doc, nodes, docs);
+ const Doc &doc = untied.documentation;
+
+ if (doc.topicsUsed().isEmpty())
+ return {};
+
+ QDocDatabase *database = QDocDatabase::qdocDB();
+
+ const QString topic = doc.topicsUsed().first().m_topic;
+
+ std::vector<TiedDocumentation> tied{};
+ std::vector<FnMatchError> errors{};
+
+ if (isQMLPropertyTopic(topic)) {
+ auto tied_qml = processQmlProperties(untied);
+ tied.insert(tied.end(), tied_qml.begin(), tied_qml.end());
} else {
ArgList args = doc.metaCommandArgs(topic);
Node *node = nullptr;
if (args.size() == 1) {
if (topic == COMMAND_FN) {
- if (showInternal() || !doc.isInternal())
- node = parserForLanguage("Clang")->parseFnArg(doc.location(), args[0].first, args[0].second);
+ if (Config::instance().showInternal() || !doc.isInternal()) {
+ auto result = fn_parser(doc.location(), args[0].first, args[0].second, untied.context);
+ if (auto *error = std::get_if<FnMatchError>(&result))
+ errors.emplace_back(*error);
+ else
+ node = std::get<Node*>(result);
+ }
} else if (topic == COMMAND_MACRO) {
node = parseMacroArg(doc.location(), args[0].first);
- } else if (isQMLMethodTopic(topic) || isJSMethodTopic(topic)) {
+ } else if (isQMLMethodTopic(topic)) {
node = parseOtherFuncArg(topic, doc.location(), args[0].first);
} else if (topic == COMMAND_DONTDOCUMENT) {
- m_qdb->primaryTree()->addToDontDocumentMap(args[0].first);
+ database->primaryTree()->addToDontDocumentMap(args[0].first);
} else {
node = processTopicCommand(doc, topic, args[0]);
}
if (node != nullptr) {
- nodes.append(node);
- docs.append(doc);
+ tied.emplace_back(TiedDocumentation{doc, node});
}
} else if (args.size() > 1) {
QList<SharedCommentNode *> sharedCommentNodes;
- for (const auto &arg : qAsConst(args)) {
+ for (const auto &arg : std::as_const(args)) {
node = nullptr;
if (topic == COMMAND_FN) {
- if (showInternal() || !doc.isInternal())
- node = parserForLanguage("Clang")->parseFnArg(doc.location(), arg.first, arg.second);
+ if (Config::instance().showInternal() || !doc.isInternal()) {
+ auto result = fn_parser(doc.location(), arg.first, arg.second, untied.context);
+ if (auto *error = std::get_if<FnMatchError>(&result))
+ errors.emplace_back(*error);
+ else
+ node = std::get<Node*>(result);
+ }
} else if (topic == COMMAND_MACRO) {
node = parseMacroArg(doc.location(), arg.first);
- } else if (isQMLMethodTopic(topic) || isJSMethodTopic(topic)) {
+ } else if (isQMLMethodTopic(topic)) {
node = parseOtherFuncArg(topic, doc.location(), arg.first);
} else {
node = processTopicCommand(doc, topic, arg);
@@ -955,67 +909,113 @@ void CppCodeParser::processTopicArgs(const Doc &doc, const QString &topic, NodeL
if (!found) {
auto *scn = new SharedCommentNode(node);
sharedCommentNodes.append(scn);
- nodes.append(scn);
- docs.append(doc);
+ tied.emplace_back(TiedDocumentation{doc, scn});
}
- processMetaCommands(doc, node);
}
}
for (auto *scn : sharedCommentNodes)
scn->sort();
}
}
+ return std::make_pair(tied, errors);
}
-void CppCodeParser::processMetaCommands(NodeList &nodes, DocList &docs)
+/*!
+ For each node that is part of C++ API and produces a documentation
+ page, this function ensures that the node belongs to a module.
+ */
+static void checkModuleInclusion(Node *n)
{
- QList<Doc>::Iterator d = docs.begin();
- for (const auto &node : nodes) {
- if (node != nullptr) {
- processMetaCommands(*d, node);
- node->setDoc(*d);
- checkModuleInclusion(node);
- if (node->isAggregate()) {
- auto *aggregate = static_cast<Aggregate *>(node);
- if (aggregate->includeFiles().isEmpty()) {
- Aggregate *parent = aggregate;
- while (parent->physicalModuleName().isEmpty() && (parent->parent() != nullptr))
- parent = parent->parent();
- if (parent == aggregate)
- aggregate->addIncludeFile(aggregate->name());
- else
- aggregate->setIncludeFiles(parent->includeFiles());
- }
+ if (n->physicalModuleName().isEmpty()) {
+ if (n->isInAPI() && !n->name().isEmpty()) {
+ switch (n->nodeType()) {
+ case Node::Class:
+ case Node::Struct:
+ case Node::Union:
+ case Node::Namespace:
+ case Node::HeaderFile:
+ break;
+ default:
+ return;
}
+ n->setPhysicalModuleName(Generator::defaultModuleName());
+ QDocDatabase::qdocDB()->addToModule(Generator::defaultModuleName(), n);
+ n->doc().location().warning(
+ QStringLiteral("Documentation for %1 '%2' has no \\inmodule command; "
+ "using project name by default: %3")
+ .arg(Node::nodeTypeString(n->nodeType()), n->name(),
+ n->physicalModuleName()));
}
- ++d;
}
}
-bool CppCodeParser::hasTooManyTopics(const Doc &doc) const
+void CppCodeParser::processMetaCommands(const std::vector<TiedDocumentation> &tied)
{
- const QSet<QString> topicCommandsUsed = topicCommands() & doc.metaCommandsUsed();
- if (topicCommandsUsed.count() > 1) {
- bool ok = true;
- for (const auto &t : topicCommandsUsed) {
- if (!t.startsWith(QLatin1String("qml")) && !t.startsWith(QLatin1String("js")))
- ok = false;
+ for (auto [doc, node] : tied) {
+ processMetaCommands(doc, node);
+ node->setDoc(doc);
+ checkModuleInclusion(node);
+ if (node->isAggregate()) {
+ auto *aggregate = static_cast<Aggregate *>(node);
+
+ if (!aggregate->includeFile()) {
+ Aggregate *parent = aggregate;
+ while (parent->physicalModuleName().isEmpty() && (parent->parent() != nullptr))
+ parent = parent->parent();
+
+ if (parent == aggregate)
+ // TODO: Understand if the name can be empty.
+ // In theory it should not be possible as
+ // there would be no aggregate to refer to
+ // such that this code is never reached.
+ //
+ // If the name can be empty, this would
+ // endanger users of the include file down the
+ // line, forcing them to ensure that, further
+ // to there being an actual include file, that
+ // include file is not an empty string, such
+ // that we would require a different way to
+ // generate the include file here.
+ aggregate->setIncludeFile(aggregate->name());
+ else if (aggregate->includeFile())
+ aggregate->setIncludeFile(*parent->includeFile());
+ }
}
- if (ok)
- return false;
- QString topicList;
- for (const auto &t : topicCommandsUsed)
- topicList += QLatin1String(" \\") + t + QLatin1Char(',');
- topicList[topicList.lastIndexOf(',')] = '.';
- qsizetype i = topicList.lastIndexOf(',');
- Q_ASSERT(i >= 0); // we had at least two commas
- topicList[i] = ' ';
- topicList.insert(i + 1, "and");
- doc.location().warning(
- QStringLiteral("Multiple topic commands found in comment:%1").arg(topicList));
- return true;
}
- return false;
+}
+
+void CppCodeParser::processQmlNativeTypeCommand(Node *node, const QString &arg, const Location &location)
+{
+ Q_ASSERT(node);
+ if (!node->isQmlNode()) {
+ location.warning(
+ QStringLiteral("Command '\\%1' is only meaningful in '\\%2'")
+ .arg(COMMAND_QMLNATIVETYPE, COMMAND_QMLTYPE));
+ return;
+ }
+
+ auto qmlNode = static_cast<QmlTypeNode *>(node);
+
+ QDocDatabase *database = QDocDatabase::qdocDB();
+ auto classNode = database->findClassNode(arg.split(u"::"_s));
+
+ if (!classNode) {
+ if (m_showLinkErrors) {
+ location.warning(
+ QStringLiteral("C++ class %2 not found: \\%1 %2")
+ .arg(COMMAND_QMLNATIVETYPE, arg));
+ }
+ return;
+ }
+
+ if (qmlNode->classNode()) {
+ location.warning(
+ QStringLiteral("QML type %1 documented with %2 as its native type. Replacing %2 with %3")
+ .arg(qmlNode->name(), qmlNode->classNode()->name(), arg));
+ }
+
+ qmlNode->setClassNode(classNode);
+ classNode->insertQmlNativeType(qmlNode);
}
QT_END_NAMESPACE
diff --git a/src/qdoc/qdoc/src/qdoc/cppcodeparser.h b/src/qdoc/qdoc/src/qdoc/cppcodeparser.h
new file mode 100644
index 000000000..32e11d05b
--- /dev/null
+++ b/src/qdoc/qdoc/src/qdoc/cppcodeparser.h
@@ -0,0 +1,111 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#ifndef CPPCODEPARSER_H
+#define CPPCODEPARSER_H
+
+#include "clangcodeparser.h"
+#include "codeparser.h"
+#include "parsererror.h"
+#include "utilities.h"
+
+QT_BEGIN_NAMESPACE
+
+class ClassNode;
+class ExampleNode;
+class FunctionNode;
+class Aggregate;
+
+class CppCodeParser
+{
+public:
+ static inline const QSet<QString> topic_commands{
+ COMMAND_CLASS, COMMAND_DONTDOCUMENT, COMMAND_ENUM, COMMAND_EXAMPLE,
+ COMMAND_EXTERNALPAGE, COMMAND_FN, COMMAND_GROUP, COMMAND_HEADERFILE,
+ COMMAND_MACRO, COMMAND_MODULE, COMMAND_NAMESPACE, COMMAND_PAGE,
+ COMMAND_PROPERTY, COMMAND_TYPEALIAS, COMMAND_TYPEDEF, COMMAND_VARIABLE,
+ COMMAND_QMLTYPE, COMMAND_QMLPROPERTY, COMMAND_QMLPROPERTYGROUP,
+ COMMAND_QMLATTACHEDPROPERTY, COMMAND_QMLSIGNAL, COMMAND_QMLATTACHEDSIGNAL,
+ COMMAND_QMLMETHOD, COMMAND_QMLATTACHEDMETHOD, COMMAND_QMLVALUETYPE, COMMAND_QMLBASICTYPE,
+ COMMAND_QMLMODULE, COMMAND_STRUCT, COMMAND_UNION,
+ };
+
+ static inline const QSet<QString> meta_commands = QSet<QString>(CodeParser::common_meta_commands)
+ << COMMAND_COMPARES << COMMAND_COMPARESWITH << COMMAND_INHEADERFILE
+ << COMMAND_NEXTPAGE << COMMAND_OVERLOAD << COMMAND_PREVIOUSPAGE
+ << COMMAND_QMLINSTANTIATES << COMMAND_QMLNATIVETYPE << COMMAND_REIMP << COMMAND_RELATES;
+
+public:
+ explicit CppCodeParser(FnCommandParser&& parser);
+
+ FunctionNode *parseMacroArg(const Location &location, const QString &macroArg);
+ FunctionNode *parseOtherFuncArg(const QString &topic, const Location &location,
+ const QString &funcArg);
+ static bool isQMLMethodTopic(const QString &t);
+ static bool isQMLPropertyTopic(const QString &t);
+
+ std::pair<std::vector<TiedDocumentation>, std::vector<FnMatchError>>
+ processTopicArgs(const UntiedDocumentation &untied);
+
+ void processMetaCommand(const Doc &doc, const QString &command, const ArgPair &argLocPair,
+ Node *node);
+ void processMetaCommands(const Doc &doc, Node *node);
+ void processMetaCommands(const std::vector<TiedDocumentation> &tied);
+
+protected:
+ virtual Node *processTopicCommand(const Doc &doc, const QString &command,
+ const ArgPair &arg);
+ std::vector<TiedDocumentation> processQmlProperties(const UntiedDocumentation& untied);
+ bool splitQmlPropertyArg(const QString &arg, QString &type, QString &module, QString &element,
+ QString &name, const Location &location);
+
+private:
+ void setExampleFileLists(ExampleNode *en);
+ static void processComparesCommand(Node *node, const QString &arg, const Location &loc);
+ void processQmlNativeTypeCommand(Node *node, const QString &arg, const Location &loc);
+
+private:
+ FnCommandParser fn_parser;
+ QString m_exampleNameFilter;
+ QString m_exampleImageFilter;
+ bool m_showLinkErrors { false };
+};
+
+/*!
+ * \internal
+ * \brief Checks if there are too many topic commands in \a doc.
+ *
+ * This method compares the commands used in \a doc with the set of topic
+ * commands. If zero or one topic command is found, or if all found topic
+ * commands are {\\qml*}-commands, the method returns \c false.
+ *
+ * If more than one topic command is found, QDoc issues a warning and the list
+ * of topic commands used in \a doc, and the method returns \c true.
+ */
+[[nodiscard]] inline bool hasTooManyTopics(const Doc &doc)
+{
+ const QSet<QString> topicCommandsUsed = CppCodeParser::topic_commands & doc.metaCommandsUsed();
+
+ if (topicCommandsUsed.empty() || topicCommandsUsed.size() == 1)
+ return false;
+ if (std::all_of(topicCommandsUsed.cbegin(), topicCommandsUsed.cend(),
+ [](const auto &cmd) { return cmd.startsWith(QLatin1String("qml")); }))
+ return false;
+
+ const QStringList commands = topicCommandsUsed.values();
+ const QString topicCommands{ std::accumulate(
+ commands.cbegin(), commands.cend(), QString{},
+ [index = qsizetype{ 0 }, numberOfCommands = commands.size()](
+ const QString &accumulator, const QString &topic) mutable -> QString {
+ return accumulator + QLatin1String("\\") + topic
+ + Utilities::separator(index++, numberOfCommands);
+ }) };
+
+ doc.location().warning(
+ QStringLiteral("Multiple topic commands found in comment: %1").arg(topicCommands));
+ return true;
+}
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/qdoc/doc.cpp b/src/qdoc/qdoc/src/qdoc/doc.cpp
index 4a4c90357..c994dc807 100644
--- a/src/qdoc/doc.cpp
+++ b/src/qdoc/qdoc/src/qdoc/doc.cpp
@@ -1,30 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the tools applications of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "doc.h"
@@ -37,9 +12,14 @@
#include "qmltypenode.h"
#include "quoter.h"
#include "text.h"
+#include "utilities.h"
+
+#include <qcryptographichash.h>
QT_BEGIN_NAMESPACE
+using namespace Qt::StringLiterals;
+
DocUtilities &Doc::m_utilities = DocUtilities::instance();
/*!
@@ -70,6 +50,15 @@ Doc::Doc(const Location &start_loc, const Location &end_loc, const QString &sour
m_priv = new DocPrivate(start_loc, end_loc, source);
DocParser parser;
parser.parse(source, m_priv, metaCommandSet, topics);
+
+ if (Config::instance().getAtomsDump()) {
+ start_loc.information(u"==== Atoms Structure for block comment starting at %1 ===="_s.arg(
+ start_loc.toString()));
+ body().dump();
+ end_loc.information(
+ u"==== Ending atoms Structure for block comment ending at %1 ===="_s.arg(
+ end_loc.toString()));
+ }
}
Doc::Doc(const Doc &doc) : m_priv(nullptr)
@@ -85,6 +74,8 @@ Doc::~Doc()
Doc &Doc::operator=(const Doc &doc)
{
+ if (&doc == this)
+ return *this;
if (doc.m_priv)
doc.m_priv->ref();
if (m_priv && m_priv->deref())
@@ -182,7 +173,7 @@ Text Doc::trimmedBriefText(const QString &className) const
whats = w.join(' ');
if (whats.endsWith(QLatin1Char('.')))
- whats.truncate(whats.length() - 1);
+ whats.truncate(whats.size() - 1);
if (!whats.isEmpty())
whats[0] = whats[0].toUpper();
@@ -303,53 +294,49 @@ QStringMultiMap *Doc::metaTagMap() const
return m_priv && m_priv->extra ? &m_priv->extra->m_metaMap : nullptr;
}
-void Doc::initialize()
+QMultiMap<ComparisonCategory, Text> *Doc::comparesWithMap() const
{
- Config &config = Config::instance();
- DocParser::initialize(config);
-
- QStringMap reverseAliasMap;
+ return m_priv && m_priv->extra ? &m_priv->extra->m_comparesWithMap : nullptr;
+}
- for (const auto &a : config.subVars(CONFIG_ALIAS)) {
- QString alias = config.getString(CONFIG_ALIAS + Config::dot + a);
- if (reverseAliasMap.contains(alias)) {
- config.lastLocation().warning(QStringLiteral("Command name '\\%1' cannot stand"
- " for both '\\%2' and '\\%3'")
- .arg(alias, reverseAliasMap[alias], a));
- } else {
- reverseAliasMap.insert(alias, a);
- }
- m_utilities.aliasMap.insert(a, alias);
- }
+void Doc::initialize(FileResolver& file_resolver)
+{
+ Config &config = Config::instance();
+ DocParser::initialize(config, file_resolver);
- for (const auto &macroName : config.subVars(CONFIG_MACRO)) {
+ const auto &configMacros = config.subVars(CONFIG_MACRO);
+ for (const auto &macroName : configMacros) {
QString macroDotName = CONFIG_MACRO + Config::dot + macroName;
Macro macro;
macro.numParams = -1;
- macro.m_defaultDef = config.getString(macroDotName);
+ const auto &macroConfigVar = config.get(macroDotName);
+ macro.m_defaultDef = macroConfigVar.asString();
if (!macro.m_defaultDef.isEmpty()) {
- macro.m_defaultDefLocation = config.lastLocation();
+ macro.m_defaultDefLocation = macroConfigVar.location();
macro.numParams = Config::numParams(macro.m_defaultDef);
}
bool silent = false;
- for (const auto &f : config.subVars(macroDotName)) {
- QString def = config.getString(macroDotName + Config::dot + f);
+ const auto &macroDotNames = config.subVars(macroDotName);
+ for (const auto &f : macroDotNames) {
+ const auto &macroSubVar = config.get(macroDotName + Config::dot + f);
+ QString def{macroSubVar.asString()};
if (!def.isEmpty()) {
macro.m_otherDefs.insert(f, def);
int m = Config::numParams(def);
if (macro.numParams == -1)
macro.numParams = m;
- else if (macro.numParams != m) {
+ // .match definition is a regular expression that contains no params
+ else if (macro.numParams != m && f != QLatin1String("match")) {
if (!silent) {
QString other = QStringLiteral("default");
if (macro.m_defaultDef.isEmpty())
other = macro.m_otherDefs.constBegin().key();
- config.lastLocation().warning(
+ macroSubVar.location().warning(
QStringLiteral("Macro '\\%1' takes inconsistent number of "
"arguments (%2 %3, %4 %5)")
- .arg(macroName, f, QString::number(m), other,
- QString::number(macro.numParams)));
+ .arg(macroName, f, QString::number(m), other,
+ QString::number(macro.numParams)));
silent = true;
}
if (macro.numParams < m)
@@ -367,15 +354,8 @@ void Doc::initialize()
*/
void Doc::terminate()
{
- m_utilities.aliasMap.clear();
m_utilities.cmdHash.clear();
m_utilities.macroHash.clear();
- DocParser::terminate();
-}
-
-QString Doc::alias(const QString &english)
-{
- return m_utilities.aliasMap.value(english, english);
}
/*!
@@ -390,7 +370,7 @@ void Doc::trimCStyleComment(Location &location, QString &str)
int asterColumn = location.columnNo() + 1;
int i;
- for (i = 0; i < str.length(); ++i) {
+ for (i = 0; i < str.size(); ++i) {
if (m.columnNo() == asterColumn) {
if (str[i] != '*')
break;
@@ -406,112 +386,35 @@ void Doc::trimCStyleComment(Location &location, QString &str)
}
m.advance(str[i]);
}
- if (cleaned.length() == str.length())
+ if (cleaned.size() == str.size())
str = cleaned;
for (int i = 0; i < 3; ++i)
location.advance(str[i]);
- str = str.mid(3, str.length() - 5);
-}
-
-QString Doc::resolveFile(const Location &location, const QString &fileName,
- QString *userFriendlyFilePath)
-{
- const QString result =
- Config::findFile(location, DocParser::s_exampleFiles, DocParser::s_exampleDirs,
- fileName, userFriendlyFilePath);
- qCDebug(lcQdoc).noquote().nospace()
- << __FUNCTION__ << "(location=" << location.fileName() << ':' << location.lineNo()
- << ", fileName=\"" << fileName << "\"), resolved to \"" << result;
- return result;
+ str = str.mid(3, str.size() - 5);
}
-CodeMarker *Doc::quoteFromFile(const Location &location, Quoter &quoter, const QString &fileName)
+void Doc::quoteFromFile(const Location &location, Quoter &quoter, ResolvedFile resolved_file)
{
+ // TODO: quoteFromFile should not care about modifying a stateful
+ // quoter from the outside, instead, it should produce a quoter
+ // that allows the caller to retrieve the required information
+ // about the quoted file.
+ //
+ // When changing the way in which quoting works, this kind of
+ // spread resposability should be removed, together with quoteFromFile.
quoter.reset();
QString code;
-
- QString userFriendlyFilePath;
- const QString filePath = resolveFile(location, fileName, &userFriendlyFilePath);
- if (filePath.isEmpty()) {
- QString details = QLatin1String("Example directories: ")
- + DocParser::s_exampleDirs.join(QLatin1Char(' '));
- if (!DocParser::s_exampleFiles.isEmpty())
- details += QLatin1String(", example files: ")
- + DocParser::s_exampleFiles.join(QLatin1Char(' '));
- location.warning(QStringLiteral("Cannot find file to quote from: '%1'").arg(fileName),
- details);
- } else {
- QFile inFile(filePath);
- if (!inFile.open(QFile::ReadOnly)) {
- location.warning(QStringLiteral("Cannot open file to quote from: '%1'")
- .arg(userFriendlyFilePath));
- } else {
- QTextStream inStream(&inFile);
- code = DocParser::untabifyEtc(inStream.readAll());
- }
+ {
+ QFile input_file{resolved_file.get_path()};
+ if (!input_file.open(QFile::ReadOnly))
+ return;
+ code = DocParser::untabifyEtc(QTextStream{&input_file}.readAll());
}
- CodeMarker *marker = CodeMarker::markerForFileName(fileName);
- quoter.quoteFromFile(userFriendlyFilePath, code, marker->markedUpCode(code, nullptr, location));
- return marker;
-}
-
-QString Doc::canonicalTitle(const QString &title)
-{
- // The code below is equivalent to the following chunk, but _much_
- // faster (accounts for ~10% of total running time)
- //
- // QRegularExpression attributeExpr("[^A-Za-z0-9]+");
- // QString result = title.toLower();
- // result.replace(attributeExpr, " ");
- // result = result.simplified();
- // result.replace(QLatin1Char(' '), QLatin1Char('-'));
-
- QString result;
- result.reserve(title.size());
-
- bool dashAppended = false;
- bool begun = false;
- qsizetype lastAlnum = 0;
- for (int i = 0; i != title.size(); ++i) {
- uint c = title.at(i).unicode();
- if (c >= 'A' && c <= 'Z')
- c += 'a' - 'A';
- bool alnum = (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9');
- if (alnum) {
- result += QLatin1Char(c);
- begun = true;
- dashAppended = false;
- lastAlnum = result.size();
- } else if (!dashAppended) {
- if (begun)
- result += QLatin1Char('-');
- dashAppended = true;
- }
- }
- result.truncate(lastAlnum);
- return result;
-}
-
-void Doc::detach()
-{
- if (m_priv == nullptr) {
- m_priv = new DocPrivate;
- return;
- }
- if (m_priv->count == 1)
- return;
-
- --m_priv->count;
-
- auto *newPriv = new DocPrivate(*m_priv);
- newPriv->count = 1;
- if (m_priv->extra)
- newPriv->extra = new DocPrivateExtra(*m_priv->extra);
-
- m_priv = newPriv;
+ CodeMarker *marker = CodeMarker::markerForFileName(resolved_file.get_path());
+ quoter.quoteFromFile(resolved_file.get_path(), code, marker->markedUpCode(code, nullptr, location));
}
QT_END_NAMESPACE
diff --git a/src/qdoc/doc.h b/src/qdoc/qdoc/src/qdoc/doc.h
index 8008025c7..50e88c72f 100644
--- a/src/qdoc/doc.h
+++ b/src/qdoc/qdoc/src/qdoc/doc.h
@@ -1,39 +1,17 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the tools applications of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#ifndef DOC_H
#define DOC_H
#include "location.h"
-
+#include "comparisoncategory.h"
#include "docutilities.h"
#include "topic.h"
+#include "filesystem/fileresolver.h"
+#include "boundaries/filesystem/resolvedfile.h"
+
#include <QtCore/qmap.h>
#include <QtCore/qset.h>
#include <QtCore/qstring.h>
@@ -41,12 +19,11 @@
QT_BEGIN_NAMESPACE
class Atom;
-class CodeMarker;
class DocPrivate;
class Quoter;
class Text;
-typedef QPair<QString, QString> ArgPair;
+typedef std::pair<QString, QString> ArgPair;
typedef QList<ArgPair> ArgList;
typedef QMultiMap<QString, QString> QStringMultiMap;
@@ -95,19 +72,15 @@ public:
[[nodiscard]] const QList<Atom *> &keywords() const;
[[nodiscard]] const QList<Atom *> &targets() const;
[[nodiscard]] QStringMultiMap *metaTagMap() const;
+ [[nodiscard]] QMultiMap<ComparisonCategory, Text> *comparesWithMap() const;
- static void initialize();
+ static void initialize(FileResolver& file_resolver);
static void terminate();
- static QString alias(const QString &english);
static void trimCStyleComment(Location &location, QString &str);
- static QString resolveFile(const Location &location, const QString &fileName,
- QString *userFriendlyFilePath = nullptr);
- static CodeMarker *quoteFromFile(const Location &location, Quoter &quoter,
- const QString &fileName);
- static QString canonicalTitle(const QString &title);
+ static void quoteFromFile(const Location &location, Quoter &quoter,
+ ResolvedFile resolved_file);
private:
- void detach();
DocPrivate *m_priv { nullptr };
static DocUtilities &m_utilities;
};
diff --git a/src/qdoc/docbookgenerator.cpp b/src/qdoc/qdoc/src/qdoc/docbookgenerator.cpp
index 2318cb963..aefb70766 100644
--- a/src/qdoc/docbookgenerator.cpp
+++ b/src/qdoc/qdoc/src/qdoc/docbookgenerator.cpp
@@ -1,31 +1,6 @@
-/****************************************************************************
-**
-** Copyright (C) 2019 Thibaut Cuvelier
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the tools applications of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2019 Thibaut Cuvelier
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "docbookgenerator.h"
@@ -34,6 +9,7 @@
#include "classnode.h"
#include "codemarker.h"
#include "collectionnode.h"
+#include "comparisoncategory.h"
#include "config.h"
#include "enumnode.h"
#include "examplenode.h"
@@ -59,25 +35,56 @@
QT_BEGIN_NAMESPACE
+using namespace Qt::StringLiterals;
+
static const char dbNamespace[] = "http://docbook.org/ns/docbook";
static const char xlinkNamespace[] = "http://www.w3.org/1999/xlink";
+static const char itsNamespace[] = "http://www.w3.org/2005/11/its";
+
+DocBookGenerator::DocBookGenerator(FileResolver& file_resolver) : XmlGenerator(file_resolver) {}
inline void DocBookGenerator::newLine()
{
m_writer->writeCharacters("\n");
}
-void DocBookGenerator::startSectionBegin()
+void DocBookGenerator::writeXmlId(const QString &id)
+{
+ if (id.isEmpty())
+ return;
+
+ m_writer->writeAttribute("xml:id", registerRef(id, true));
+}
+
+void DocBookGenerator::writeXmlId(const Node *node)
{
+ if (!node)
+ return;
+
+ // Specifically for nodes, do not use the same code path as for QString
+ // inputs, as refForNode calls registerRef in all cases. Calling
+ // registerRef a second time adds a character to "disambiguate" the two IDs
+ // (the one returned by refForNode, then the one that is written as
+ // xml:id).
+ QString id = Generator::cleanRef(refForNode(node), true);
+ if (!id.isEmpty())
+ m_writer->writeAttribute("xml:id", id);
+}
+
+void DocBookGenerator::startSectionBegin(const QString &id)
+{
+ m_hasSection = true;
+
m_writer->writeStartElement(dbNamespace, "section");
+ writeXmlId(id);
newLine();
m_writer->writeStartElement(dbNamespace, "title");
}
-void DocBookGenerator::startSectionBegin(const QString &id)
+void DocBookGenerator::startSectionBegin(const Node *node)
{
m_writer->writeStartElement(dbNamespace, "section");
- m_writer->writeAttribute("xml:id", id);
+ writeXmlId(node);
newLine();
m_writer->writeStartElement(dbNamespace, "title");
}
@@ -95,6 +102,19 @@ void DocBookGenerator::startSection(const QString &id, const QString &title)
startSectionEnd();
}
+void DocBookGenerator::startSection(const Node *node, const QString &title)
+{
+ startSectionBegin(node);
+ m_writer->writeCharacters(title);
+ startSectionEnd();
+}
+
+void DocBookGenerator::startSection(const QString &title)
+{
+ // No xml:id given: down the calls, "" is interpreted as "no ID".
+ startSection("", title);
+}
+
void DocBookGenerator::endSection()
{
m_writer->writeEndElement(); // section
@@ -103,8 +123,11 @@ void DocBookGenerator::endSection()
void DocBookGenerator::writeAnchor(const QString &id)
{
+ if (id.isEmpty())
+ return;
+
m_writer->writeEmptyElement(dbNamespace, "anchor");
- m_writer->writeAttribute("xml:id", id);
+ writeXmlId(id);
newLine();
}
@@ -118,22 +141,25 @@ void DocBookGenerator::initializeGenerator()
Generator::initializeGenerator();
m_config = &Config::instance();
- m_project = m_config->getString(CONFIG_PROJECT);
+ m_project = m_config->get(CONFIG_PROJECT).asString();
- m_projectDescription = m_config->getString(CONFIG_DESCRIPTION);
+ m_projectDescription = m_config->get(CONFIG_DESCRIPTION).asString();
if (m_projectDescription.isEmpty() && !m_project.isEmpty())
m_projectDescription = m_project + QLatin1String(" Reference Documentation");
- m_naturalLanguage = m_config->getString(CONFIG_NATURALLANGUAGE);
+ m_naturalLanguage = m_config->get(CONFIG_NATURALLANGUAGE).asString();
if (m_naturalLanguage.isEmpty())
m_naturalLanguage = QLatin1String("en");
- m_buildVersion = m_config->getString(CONFIG_BUILDVERSION);
+ m_buildVersion = m_config->get(CONFIG_BUILDVERSION).asString();
+ m_useDocBook52 = m_config->get(CONFIG_DOCBOOKEXTENSIONS).asBool() ||
+ m_config->get(format() + Config::dot + "usedocbookextensions").asBool();
+ m_useITS = m_config->get(format() + Config::dot + "its").asBool();
}
QString DocBookGenerator::format()
{
- return QStringLiteral("DocBook");
+ return "DocBook";
}
/*!
@@ -141,7 +167,7 @@ QString DocBookGenerator::format()
*/
QString DocBookGenerator::fileExtension() const
{
- return QStringLiteral("xml");
+ return "xml";
}
/*!
@@ -157,74 +183,27 @@ bool DocBookGenerator::generateText(const Text &text, const Node *relative)
int numAtoms = 0;
initializeTextOutput();
- generateAtomList(text.firstAtom(), relative, true, numAtoms);
+ generateAtomList(text.firstAtom(), relative, nullptr, true, numAtoms);
closeTextSections();
return true;
}
-/*!
- Generate the text for \a atom relatively to \a relative.
- \a generate indicates if output to \a writer is expected.
- The number of generated atoms is returned in the argument
- \a numAtoms. The returned value is the first atom that was not
- generated.
- */
-const Atom *DocBookGenerator::generateAtomList(const Atom *atom, const Node *relative,
- bool generate, int &numAtoms)
-{
- Q_ASSERT(m_writer);
- // From Generator::generateAtomList.
- while (atom) {
- switch (atom->type()) {
- case Atom::FormatIf: {
- int numAtoms0 = numAtoms;
- atom = generateAtomList(atom->next(), relative, generate, numAtoms);
- if (!atom)
- return nullptr;
-
- if (atom->type() == Atom::FormatElse) {
- ++numAtoms;
- atom = generateAtomList(atom->next(), relative, false, numAtoms);
- if (!atom)
- return nullptr;
- }
-
- if (atom->type() == Atom::FormatEndif) {
- if (generate && numAtoms0 == numAtoms) {
- relative->location().warning(QStringLiteral("Output format %1 not handled %2")
- .arg(format(), outFileName()));
- Atom unhandledFormatAtom(Atom::UnhandledFormat, format());
- generateAtomList(&unhandledFormatAtom, relative, generate, numAtoms);
- }
- atom = atom->next();
- }
- } break;
- case Atom::FormatElse:
- case Atom::FormatEndif:
- return atom;
- default:
- int n = 1;
- if (generate) {
- n += generateAtom(atom, relative);
- numAtoms += n;
- }
- while (n-- > 0)
- atom = atom->next();
- }
- }
- return nullptr;
+QString removeCodeMarkers(const QString& code) {
+ QString rewritten = code;
+ static const QRegularExpression re("(<@[^>&]*>)|(<\\/@[^&>]*>)");
+ rewritten.replace(re, "");
+ return rewritten;
}
/*!
Generate DocBook from an instance of Atom.
*/
-qsizetype DocBookGenerator::generateAtom(const Atom *atom, const Node *relative)
+qsizetype DocBookGenerator::generateAtom(const Atom *atom, const Node *relative, CodeMarker*)
{
Q_ASSERT(m_writer);
// From HtmlGenerator::generateAtom, without warning generation.
int idx = 0;
int skipAhead = 0;
- static bool inPara = false;
Node::Genus genus = Node::DontCare;
switch (atom->type()) {
@@ -259,11 +238,13 @@ qsizetype DocBookGenerator::generateAtom(const Atom *atom, const Node *relative)
break;
}
m_writer->writeStartElement(dbNamespace, "para");
+ m_inPara = true;
rewritePropertyBrief(atom, relative);
break;
case Atom::BriefRight:
if (hasBrief(relative)) {
m_writer->writeEndElement(); // para
+ m_inPara = false;
newLine();
}
break;
@@ -271,7 +252,10 @@ qsizetype DocBookGenerator::generateAtom(const Atom *atom, const Node *relative)
// This may at one time have been used to mark up C++ code but it is
// now widely used to write teletype text. As a result, text marked
// with the \c command is not passed to a code marker.
- m_writer->writeTextElement(dbNamespace, "code", plainCode(atom->string()));
+ if (m_inTeletype)
+ m_writer->writeCharacters(plainCode(atom->string()));
+ else
+ m_writer->writeTextElement(dbNamespace, "code", plainCode(atom->string()));
break;
case Atom::CaptionLeft:
m_writer->writeStartElement(dbNamespace, "title");
@@ -284,46 +268,34 @@ qsizetype DocBookGenerator::generateAtom(const Atom *atom, const Node *relative)
case Atom::Qml:
m_writer->writeStartElement(dbNamespace, "programlisting");
m_writer->writeAttribute("language", "qml");
- m_writer->writeCharacters(atom->string());
- m_writer->writeEndElement(); // programlisting
- newLine();
- break;
- case Atom::JavaScript:
- m_writer->writeStartElement(dbNamespace, "programlisting");
- m_writer->writeAttribute("language", "js");
- m_writer->writeCharacters(atom->string());
- m_writer->writeEndElement(); // programlisting
- newLine();
- break;
- case Atom::CodeNew:
- m_writer->writeTextElement(dbNamespace, "para", "you can rewrite it as");
- newLine();
- m_writer->writeStartElement(dbNamespace, "programlisting");
- m_writer->writeAttribute("language", "cpp");
- m_writer->writeAttribute("role", "new");
- m_writer->writeCharacters(atom->string());
+ if (m_useITS)
+ m_writer->writeAttribute(itsNamespace, "translate", "no");
+ m_writer->writeCharacters(removeCodeMarkers(atom->string()));
m_writer->writeEndElement(); // programlisting
newLine();
break;
case Atom::Code:
m_writer->writeStartElement(dbNamespace, "programlisting");
m_writer->writeAttribute("language", "cpp");
- m_writer->writeCharacters(atom->string());
+ if (m_useITS)
+ m_writer->writeAttribute(itsNamespace, "translate", "no");
+ m_writer->writeCharacters(removeCodeMarkers(atom->string()));
m_writer->writeEndElement(); // programlisting
newLine();
break;
- case Atom::CodeOld:
- m_writer->writeTextElement(dbNamespace, "para", "For example, if you have code like");
- newLine();
- Q_FALLTHROUGH();
case Atom::CodeBad:
m_writer->writeStartElement(dbNamespace, "programlisting");
m_writer->writeAttribute("language", "cpp");
m_writer->writeAttribute("role", "bad");
- m_writer->writeCharacters(atom->string());
+ if (m_useITS)
+ m_writer->writeAttribute(itsNamespace, "translate", "no");
+ m_writer->writeCharacters(removeCodeMarkers(atom->string()));
m_writer->writeEndElement(); // programlisting
newLine();
break;
+ case Atom::DetailsLeft:
+ case Atom::DetailsRight:
+ break;
case Atom::DivLeft:
case Atom::DivRight:
break;
@@ -331,9 +303,11 @@ qsizetype DocBookGenerator::generateAtom(const Atom *atom, const Node *relative)
m_writer->writeStartElement(dbNamespace, "footnote");
newLine();
m_writer->writeStartElement(dbNamespace, "para");
+ m_inPara = true;
break;
case Atom::FootnoteRight:
m_writer->writeEndElement(); // para
+ m_inPara = false;
newLine();
m_writer->writeEndElement(); // footnote
break;
@@ -351,15 +325,31 @@ qsizetype DocBookGenerator::generateAtom(const Atom *atom, const Node *relative)
m_writer->writeStartElement(dbNamespace, "emphasis");
m_writer->writeAttribute("role", "underline");
} else if (atom->string() == ATOM_FORMATTING_SUBSCRIPT) {
- m_writer->writeStartElement(dbNamespace, "sub");
+ m_writer->writeStartElement(dbNamespace, "subscript");
} else if (atom->string() == ATOM_FORMATTING_SUPERSCRIPT) {
- m_writer->writeStartElement(dbNamespace, "sup");
+ m_writer->writeStartElement(dbNamespace, "superscript");
} else if (atom->string() == ATOM_FORMATTING_TELETYPE
|| atom->string() == ATOM_FORMATTING_PARAMETER) {
m_writer->writeStartElement(dbNamespace, "code");
+ if (m_useITS)
+ m_writer->writeAttribute(itsNamespace, "translate", "no");
if (atom->string() == ATOM_FORMATTING_PARAMETER)
m_writer->writeAttribute("role", "parameter");
+ else // atom->string() == ATOM_FORMATTING_TELETYPE
+ m_inTeletype = true;
+ } else if (atom->string() == ATOM_FORMATTING_UICONTROL) {
+ m_writer->writeStartElement(dbNamespace, "guilabel");
+ if (m_useITS)
+ m_writer->writeAttribute(itsNamespace, "translate", "no");
+ } else if (atom->string() == ATOM_FORMATTING_TRADEMARK) {
+ m_writer->writeStartElement(dbNamespace,
+ appendTrademark(atom->find(Atom::FormattingRight)) ?
+ "trademark" : "phrase");
+ if (m_useITS)
+ m_writer->writeAttribute(itsNamespace, "translate", "no");
+ } else {
+ relative->location().warning(QStringLiteral("Unsupported formatting: %1").arg(atom->string()));
}
break;
case Atom::FormattingRight:
@@ -368,17 +358,24 @@ qsizetype DocBookGenerator::generateAtom(const Atom *atom, const Node *relative)
|| atom->string() == ATOM_FORMATTING_SUBSCRIPT
|| atom->string() == ATOM_FORMATTING_SUPERSCRIPT
|| atom->string() == ATOM_FORMATTING_TELETYPE
- || atom->string() == ATOM_FORMATTING_PARAMETER) {
+ || atom->string() == ATOM_FORMATTING_PARAMETER
+ || atom->string() == ATOM_FORMATTING_UICONTROL
+ || atom->string() == ATOM_FORMATTING_TRADEMARK) {
m_writer->writeEndElement();
- }
- if (atom->string() == ATOM_FORMATTING_LINK)
+ } else if (atom->string() == ATOM_FORMATTING_LINK) {
+ if (atom->string() == ATOM_FORMATTING_TELETYPE)
+ m_inTeletype = false;
endLink();
+ } else {
+ relative->location().warning(QStringLiteral("Unsupported formatting: %1").arg(atom->string()));
+ }
break;
case Atom::AnnotatedList:
if (const CollectionNode *cn = m_qdb->getCollectionNode(atom->string(), Node::Group))
generateList(cn, atom->string());
break;
- case Atom::GeneratedList:
+ case Atom::GeneratedList: {
+ bool hasGeneratedSomething = false;
if (atom->string() == QLatin1String("annotatedclasses")
|| atom->string() == QLatin1String("attributions")
|| atom->string() == QLatin1String("namespaces")) {
@@ -387,82 +384,212 @@ qsizetype DocBookGenerator::generateAtom(const Atom *atom, const Node *relative)
: atom->string() == QLatin1String("attributions") ? m_qdb->getAttributions()
: m_qdb->getNamespaces();
generateAnnotatedList(relative, things.values(), atom->string());
+ hasGeneratedSomething = !things.isEmpty();
} else if (atom->string() == QLatin1String("annotatedexamples")
- || atom->string() == QLatin1String("annotatedattributions")) {
+ || atom->string() == QLatin1String("annotatedattributions")) {
const NodeMultiMap things = atom->string() == QLatin1String("annotatedexamples")
- ? m_qdb->getAttributions()
- : m_qdb->getExamples();
+ ? m_qdb->getAttributions()
+ : m_qdb->getExamples();
generateAnnotatedLists(relative, things, atom->string());
+ hasGeneratedSomething = !things.isEmpty();
} else if (atom->string() == QLatin1String("classes")
- || atom->string() == QLatin1String("qmlbasictypes")
+ || atom->string() == QLatin1String("qmlbasictypes") // deprecated!
+ || atom->string() == QLatin1String("qmlvaluetypes")
|| atom->string() == QLatin1String("qmltypes")) {
const NodeMultiMap things = atom->string() == QLatin1String("classes")
? m_qdb->getCppClasses()
- : atom->string() == QLatin1String("qmlbasictypes") ? m_qdb->getQmlBasicTypes()
- : m_qdb->getQmlTypes();
- generateCompactList(Generic, relative, things, QString(), atom->string());
+ : (atom->string() == QLatin1String("qmlvaluetypes")
+ || atom->string() == QLatin1String("qmlbasictypes"))
+ ? m_qdb->getQmlValueTypes()
+ : m_qdb->getQmlTypes();
+ generateCompactList(relative, things, true, QString(), atom->string());
+ hasGeneratedSomething = !things.isEmpty();
} else if (atom->string().contains("classes ")) {
QString rootName = atom->string().mid(atom->string().indexOf("classes") + 7).trimmed();
- generateCompactList(Generic, relative, m_qdb->getCppClasses(), rootName,
- atom->string());
+ NodeMultiMap things = m_qdb->getCppClasses();
+
+ hasGeneratedSomething = !things.isEmpty();
+ generateCompactList(relative, things, true, rootName, atom->string());
} else if ((idx = atom->string().indexOf(QStringLiteral("bymodule"))) != -1) {
QString moduleName = atom->string().mid(idx + 8).trimmed();
- Node::NodeType type = typeFromString(atom);
+ Node::NodeType moduleType = typeFromString(atom);
QDocDatabase *qdb = QDocDatabase::qdocDB();
- if (const CollectionNode *cn = qdb->getCollectionNode(moduleName, type)) {
- if (type == Node::Module) {
- NodeMap m;
- cn->getMemberClasses(m);
- if (!m.isEmpty())
- generateAnnotatedList(relative, m.values(), atom->string());
- } else {
+ if (const CollectionNode *cn = qdb->getCollectionNode(moduleName, moduleType)) {
+ NodeMap map;
+ switch (moduleType) {
+ case Node::Module:
+ // classesbymodule <module_name>
+ map = cn->getMembers([](const Node *n){ return n->isClassNode(); });
+ break;
+ case Node::QmlModule:
+ if (atom->string().contains(QLatin1String("qmlvaluetypes")))
+ map = cn->getMembers(Node::QmlValueType); // qmlvaluetypesbymodule <module_name>
+ else
+ map = cn->getMembers(Node::QmlType); // qmltypesbymodule <module_name>
+ break;
+ default: // fall back to generating all members
generateAnnotatedList(relative, cn->members(), atom->string());
+ hasGeneratedSomething = !cn->members().isEmpty();
+ break;
+ }
+ if (!map.isEmpty()) {
+ generateAnnotatedList(relative, map.values(), atom->string());
+ hasGeneratedSomething = true;
}
}
- } else if (atom->string().startsWith("examplefiles")
- || atom->string().startsWith("exampleimages")) {
- if (relative->isExample())
- qDebug() << "GENERATE FILE LIST CALLED" << relative->name() << atom->string();
} else if (atom->string() == QLatin1String("classhierarchy")) {
generateClassHierarchy(relative, m_qdb->getCppClasses());
+ hasGeneratedSomething = !m_qdb->getCppClasses().isEmpty();
} else if (atom->string().startsWith("obsolete")) {
- ListType type = atom->string().endsWith("members") ? Obsolete : Generic;
QString prefix = atom->string().contains("cpp") ? QStringLiteral("Q") : QString();
const NodeMultiMap &things = atom->string() == QLatin1String("obsoleteclasses")
? m_qdb->getObsoleteClasses()
: atom->string() == QLatin1String("obsoleteqmltypes")
- ? m_qdb->getObsoleteQmlTypes()
- : atom->string() == QLatin1String("obsoletecppmembers")
- ? m_qdb->getClassesWithObsoleteMembers()
- : m_qdb->getQmlTypesWithObsoleteMembers();
- generateCompactList(type, relative, things, prefix, atom->string());
+ ? m_qdb->getObsoleteQmlTypes()
+ : atom->string() == QLatin1String("obsoletecppmembers")
+ ? m_qdb->getClassesWithObsoleteMembers()
+ : m_qdb->getQmlTypesWithObsoleteMembers();
+ generateCompactList(relative, things, false, prefix, atom->string());
+ hasGeneratedSomething = !things.isEmpty();
} else if (atom->string() == QLatin1String("functionindex")) {
generateFunctionIndex(relative);
+ hasGeneratedSomething = !m_qdb->getFunctionIndex().isEmpty();
} else if (atom->string() == QLatin1String("legalese")) {
generateLegaleseList(relative);
+ hasGeneratedSomething = !m_qdb->getLegaleseTexts().isEmpty();
} else if (atom->string() == QLatin1String("overviews")
|| atom->string() == QLatin1String("cpp-modules")
|| atom->string() == QLatin1String("qml-modules")
|| atom->string() == QLatin1String("related")) {
generateList(relative, atom->string());
+ hasGeneratedSomething = true; // Approximation, because there is
+ // some nontrivial logic in generateList.
+ } else if (const auto *cn = m_qdb->getCollectionNode(atom->string(), Node::Group); cn) {
+ generateAnnotatedList(cn, cn->members(), atom->string(), ItemizedList);
+ hasGeneratedSomething = true; // Approximation
+ }
+
+ // There must still be some content generated for the DocBook document
+ // to be valid (except if already in a paragraph).
+ if (!hasGeneratedSomething && !m_inPara) {
+ m_writer->writeEmptyElement(dbNamespace, "para");
+ newLine();
}
+ }
break;
case Atom::SinceList:
// Table of contents, should automatically be generated by the DocBook processor.
- break;
+ Q_FALLTHROUGH();
case Atom::LineBreak:
case Atom::BR:
case Atom::HR:
// Not supported in DocBook.
break;
case Atom::Image: // mediaobject
+ // An Image atom is always followed by an ImageText atom,
+ // containing the alternative text.
+ // If no caption is present, we just output a <db:mediaobject>,
+ // avoiding the wrapper as it is not required.
+ // For bordered images, there is another atom before the
+ // caption, DivRight (the corresponding DivLeft being just
+ // before the image).
+
+ if (atom->next() && matchAhead(atom->next(), Atom::DivRight) && atom->next()->next()
+ && matchAhead(atom->next()->next(), Atom::CaptionLeft)) {
+ // If there is a caption, there must be a <db:figure>
+ // wrapper starting with the caption.
+ Q_ASSERT(atom->next());
+ Q_ASSERT(atom->next()->next());
+ Q_ASSERT(atom->next()->next()->next());
+ Q_ASSERT(atom->next()->next()->next()->next());
+ Q_ASSERT(atom->next()->next()->next()->next()->next());
+
+ m_writer->writeStartElement(dbNamespace, "figure");
+ newLine();
+
+ const Atom *current = atom->next()->next()->next();
+ skipAhead += 2;
+
+ Q_ASSERT(current->type() == Atom::CaptionLeft);
+ generateAtom(current, relative, nullptr);
+ current = current->next();
+ ++skipAhead;
+
+ while (current->type() != Atom::CaptionRight) { // The actual caption.
+ generateAtom(current, relative, nullptr);
+ current = current->next();
+ ++skipAhead;
+ }
+
+ Q_ASSERT(current->type() == Atom::CaptionRight);
+ generateAtom(current, relative, nullptr);
+ current = current->next();
+ ++skipAhead;
+
+ m_closeFigureWrapper = true;
+ }
+
+ if (atom->next() && matchAhead(atom->next(), Atom::CaptionLeft)) {
+ // If there is a caption, there must be a <db:figure>
+ // wrapper starting with the caption.
+ Q_ASSERT(atom->next());
+ Q_ASSERT(atom->next()->next());
+ Q_ASSERT(atom->next()->next()->next());
+ Q_ASSERT(atom->next()->next()->next()->next());
+
+ m_writer->writeStartElement(dbNamespace, "figure");
+ newLine();
+
+ const Atom *current = atom->next()->next();
+ ++skipAhead;
+
+ Q_ASSERT(current->type() == Atom::CaptionLeft);
+ generateAtom(current, relative, nullptr);
+ current = current->next();
+ ++skipAhead;
+
+ while (current->type() != Atom::CaptionRight) { // The actual caption.
+ generateAtom(current, relative, nullptr);
+ current = current->next();
+ ++skipAhead;
+ }
+
+ Q_ASSERT(current->type() == Atom::CaptionRight);
+ generateAtom(current, relative, nullptr);
+ current = current->next();
+ ++skipAhead;
+
+ m_closeFigureWrapper = true;
+ }
+
+ Q_FALLTHROUGH();
case Atom::InlineImage: { // inlinemediaobject
+ // TODO: [generator-insufficient-structural-abstraction]
+ // The structure of the computations for this part of the
+ // docbook generation and the same parts in other format
+ // generators is the same.
+ //
+ // The difference, instead, lies in what the generated output
+ // is like. A correct abstraction for a generator would take
+ // this structural equivalence into account and encapsulate it
+ // into a driver for the format generators.
+ //
+ // This would avoid the replication of content, and the
+ // subsequent friction for changes and desynchronization
+ // between generators.
+ //
+ // Review all the generators routines and find the actual
+ // skeleton that is shared between them, then consider it when
+ // extracting the logic for the generation phase.
QString tag = atom->type() == Atom::Image ? "mediaobject" : "inlinemediaobject";
m_writer->writeStartElement(dbNamespace, tag);
newLine();
- QString fileName = imageFileName(relative, atom->string());
- if (fileName.isEmpty()) {
+ auto maybe_resolved_file{file_resolver.resolve(atom->string())};
+ if (!maybe_resolved_file) {
+ // TODO: [uncetnralized-admonition][failed-resolve-file]
+ relative->location().warning(QStringLiteral("Missing image: %1").arg(atom->string()));
+
m_writer->writeStartElement(dbNamespace, "textobject");
newLine();
m_writer->writeStartElement(dbNamespace, "para");
@@ -473,23 +600,40 @@ qsizetype DocBookGenerator::generateAtom(const Atom *atom, const Node *relative)
m_writer->writeEndElement(); // textobject
newLine();
} else {
- if (atom->next() && !atom->next()->string().isEmpty())
+ ResolvedFile file{*maybe_resolved_file};
+ QString file_name{QFileInfo{file.get_path()}.fileName()};
+
+ // TODO: [uncentralized-output-directory-structure]
+ Config::copyFile(relative->doc().location(), file.get_path(), file_name, outputDir() + QLatin1String("/images"));
+
+ if (atom->next() && !atom->next()->string().isEmpty()
+ && atom->next()->type() == Atom::ImageText) {
m_writer->writeTextElement(dbNamespace, "alt", atom->next()->string());
+ newLine();
+ }
m_writer->writeStartElement(dbNamespace, "imageobject");
newLine();
m_writer->writeEmptyElement(dbNamespace, "imagedata");
- m_writer->writeAttribute("fileref", fileName);
+ // TODO: [uncentralized-output-directory-structure]
+ m_writer->writeAttribute("fileref", "images/" + file_name);
newLine();
m_writer->writeEndElement(); // imageobject
newLine();
- setImageFileName(relative, fileName);
+ // TODO: [uncentralized-output-directory-structure]
+ setImageFileName(relative, "images/" + file_name);
}
m_writer->writeEndElement(); // [inline]mediaobject
if (atom->type() == Atom::Image)
newLine();
+
+ if (m_closeFigureWrapper) {
+ m_writer->writeEndElement(); // figure
+ newLine();
+ m_closeFigureWrapper = false;
+ }
} break;
case Atom::ImageText:
break;
@@ -502,11 +646,13 @@ qsizetype DocBookGenerator::generateAtom(const Atom *atom, const Node *relative)
m_writer->writeStartElement(dbNamespace, admonType);
newLine();
m_writer->writeStartElement(dbNamespace, "para");
+ m_inPara = true;
} break;
case Atom::ImportantRight:
case Atom::NoteRight:
case Atom::WarningRight:
m_writer->writeEndElement(); // para
+ m_inPara = false;
newLine();
m_writer->writeEndElement(); // note/important
newLine();
@@ -527,11 +673,17 @@ qsizetype DocBookGenerator::generateAtom(const Atom *atom, const Node *relative)
skipAhead = 1;
} break;
case Atom::ListLeft:
- if (inPara) {
+ if (m_inPara) {
+ // The variable m_inPara is not set in a very smart way, because
+ // it ignores nesting. This might in theory create false positives
+ // here. A better solution would be to track the depth of
+ // paragraphs the generator is in, but determining the right check
+ // for this condition is far from trivial (think of nested lists).
m_writer->writeEndElement(); // para
newLine();
- inPara = false;
+ m_inPara = false;
}
+
if (atom->string() == ATOM_LIST_BULLET) {
m_writer->writeStartElement(dbNamespace, "itemizedlist");
newLine();
@@ -550,19 +702,21 @@ qsizetype DocBookGenerator::generateAtom(const Atom *atom, const Node *relative)
m_threeColumnEnumValueTable = isThreeColumnEnumValueTable(atom);
if (m_threeColumnEnumValueTable && relative->nodeType() == Node::Enum) {
- // If not in \enum topic, skip the value column
+ // With three columns, if not in \enum topic, skip the value column
m_writer->writeTextElement(dbNamespace, "th", "Value");
newLine();
}
- m_writer->writeTextElement(dbNamespace, "th", "Description");
- newLine();
+ if (!isOneColumnValueTable(atom)) {
+ m_writer->writeTextElement(dbNamespace, "th", "Description");
+ newLine();
+ }
m_writer->writeEndElement(); // tr
newLine();
m_writer->writeEndElement(); // thead
newLine();
- } else {
+ } else { // No recognized list type.
m_writer->writeStartElement(dbNamespace, "orderedlist");
if (atom->next() != nullptr && atom->next()->string().toInt() > 1)
@@ -581,6 +735,7 @@ qsizetype DocBookGenerator::generateAtom(const Atom *atom, const Node *relative)
newLine();
}
+ m_inList++;
break;
case Atom::ListItemNumber:
break;
@@ -590,7 +745,7 @@ qsizetype DocBookGenerator::generateAtom(const Atom *atom, const Node *relative)
newLine();
m_writer->writeStartElement(dbNamespace, "item");
} else { // (atom->string() == ATOM_LIST_VALUE)
- QPair<QString, int> pair = getAtomListValue(atom);
+ std::pair<QString, int> pair = getAtomListValue(atom);
skipAhead = pair.second;
m_writer->writeStartElement(dbNamespace, "tr");
@@ -598,6 +753,8 @@ qsizetype DocBookGenerator::generateAtom(const Atom *atom, const Node *relative)
m_writer->writeStartElement(dbNamespace, "td");
newLine();
m_writer->writeStartElement(dbNamespace, "para");
+ if (m_useITS)
+ m_writer->writeAttribute(itsNamespace, "translate", "no");
generateEnumValue(pair.first, relative);
m_writer->writeEndElement(); // para
newLine();
@@ -611,63 +768,81 @@ qsizetype DocBookGenerator::generateAtom(const Atom *atom, const Node *relative)
m_writer->writeStartElement(dbNamespace, "td");
if (itemValue.isEmpty())
m_writer->writeCharacters("?");
- else
- m_writer->writeTextElement(dbNamespace, "code", itemValue);
+ else {
+ m_writer->writeStartElement(dbNamespace, "code");
+ if (m_useITS)
+ m_writer->writeAttribute(itsNamespace, "translate", "no");
+ m_writer->writeCharacters(itemValue);
+ m_writer->writeEndElement(); // code
+ }
m_writer->writeEndElement(); // td
newLine();
}
}
+ m_inList++;
break;
case Atom::SinceTagRight:
- case Atom::ListTagRight:
if (atom->string() == ATOM_LIST_TAG) {
m_writer->writeEndElement(); // item
newLine();
}
break;
- case Atom::ListItemLeft:
- m_inListItemLineOpen = false;
- if (atom->string() == ATOM_LIST_TAG) {
- m_writer->writeStartElement(dbNamespace, "listitem");
+ case Atom::ListTagRight:
+ if (m_inList > 0 && atom->string() == ATOM_LIST_TAG) {
+ m_writer->writeEndElement(); // item
newLine();
- m_writer->writeStartElement(dbNamespace, "para");
- } else if (atom->string() == ATOM_LIST_VALUE) {
- if (m_threeColumnEnumValueTable) {
- if (matchAhead(atom, Atom::ListItemRight)) {
- m_writer->writeEmptyElement(dbNamespace, "td");
- newLine();
- m_inListItemLineOpen = false;
- } else {
- m_writer->writeStartElement(dbNamespace, "td");
- newLine();
- m_inListItemLineOpen = true;
+ m_inList = false;
+ }
+ break;
+ case Atom::ListItemLeft:
+ if (m_inList > 0) {
+ m_inListItemLineOpen = false;
+ if (atom->string() == ATOM_LIST_TAG) {
+ m_writer->writeStartElement(dbNamespace, "listitem");
+ newLine();
+ m_writer->writeStartElement(dbNamespace, "para");
+ m_inPara = true;
+ } else if (atom->string() == ATOM_LIST_VALUE) {
+ if (m_threeColumnEnumValueTable) {
+ if (matchAhead(atom, Atom::ListItemRight)) {
+ m_writer->writeEmptyElement(dbNamespace, "td");
+ newLine();
+ m_inListItemLineOpen = false;
+ } else {
+ m_writer->writeStartElement(dbNamespace, "td");
+ newLine();
+ m_inListItemLineOpen = true;
+ }
}
+ } else {
+ m_writer->writeStartElement(dbNamespace, "listitem");
+ newLine();
}
- } else {
- m_writer->writeStartElement(dbNamespace, "listitem");
- newLine();
+ // Don't skip a paragraph, DocBook requires them within list items.
}
- // Don't skip a paragraph, DocBook requires them within list items.
break;
case Atom::ListItemRight:
- if (atom->string() == ATOM_LIST_TAG) {
- m_writer->writeEndElement(); // para
- newLine();
- m_writer->writeEndElement(); // listitem
- newLine();
- m_writer->writeEndElement(); // varlistentry
- newLine();
- } else if (atom->string() == ATOM_LIST_VALUE) {
- if (m_inListItemLineOpen) {
- m_writer->writeEndElement(); // td
+ if (m_inList > 0) {
+ if (atom->string() == ATOM_LIST_TAG) {
+ m_writer->writeEndElement(); // para
+ m_inPara = false;
+ newLine();
+ m_writer->writeEndElement(); // listitem
+ newLine();
+ m_writer->writeEndElement(); // varlistentry
+ newLine();
+ } else if (atom->string() == ATOM_LIST_VALUE) {
+ if (m_inListItemLineOpen) {
+ m_writer->writeEndElement(); // td
+ newLine();
+ m_inListItemLineOpen = false;
+ }
+ m_writer->writeEndElement(); // tr
+ newLine();
+ } else {
+ m_writer->writeEndElement(); // listitem
newLine();
- m_inListItemLineOpen = false;
}
- m_writer->writeEndElement(); // tr
- newLine();
- } else {
- m_writer->writeEndElement(); // listitem
- newLine();
}
break;
case Atom::ListRight:
@@ -678,33 +853,38 @@ qsizetype DocBookGenerator::generateAtom(const Atom *atom, const Node *relative)
// - ATOM_LIST_NUMERIC: orderedlist
m_writer->writeEndElement();
newLine();
+ m_inList--;
break;
case Atom::Nop:
break;
case Atom::ParaLeft:
m_writer->writeStartElement(dbNamespace, "para");
- inPara = true;
+ m_inPara = true;
break;
case Atom::ParaRight:
endLink();
- if (inPara) {
+ if (m_inPara) {
m_writer->writeEndElement(); // para
newLine();
- inPara = false;
+ m_inPara = false;
}
break;
case Atom::QuotationLeft:
m_writer->writeStartElement(dbNamespace, "blockquote");
- inPara = true;
+ m_inBlockquote = true;
break;
case Atom::QuotationRight:
m_writer->writeEndElement(); // blockquote
newLine();
+ m_inBlockquote = false;
break;
- case Atom::RawString:
- m_writer->writeCharacters(atom->string());
+ case Atom::RawString: {
+ m_writer->device()->write(atom->string().toUtf8());
+ }
break;
case Atom::SectionLeft:
+ m_hasSection = true;
+
currentSectionLevel = atom->string().toInt() + hOffset(relative);
// Level 1 is dealt with at the header level (info tag).
if (currentSectionLevel > 1) {
@@ -719,11 +899,32 @@ qsizetype DocBookGenerator::generateAtom(const Atom *atom, const Node *relative)
sectionLevels.push(currentSectionLevel);
m_writer->writeStartElement(dbNamespace, "section");
- m_writer->writeAttribute("xml:id",
- Doc::canonicalTitle(Text::sectionHeading(atom).toString()));
+ writeXmlId(Tree::refForAtom(atom));
newLine();
// Unlike startSectionBegin, don't start a title here.
}
+
+ if (matchAhead(atom, Atom::SectionHeadingLeft) &&
+ matchAhead(atom->next(), Atom::String) &&
+ matchAhead(atom->next()->next(), Atom::SectionHeadingRight) &&
+ matchAhead(atom->next()->next()->next(), Atom::SectionRight) &&
+ !atom->next()->next()->next()->next()->next()) {
+ // A lonely section at the end of the document indicates that a
+ // generated list of some sort should be within this section.
+ // Close this section later on, in generateFooter().
+ generateAtom(atom->next(), relative, nullptr);
+ generateAtom(atom->next()->next(), relative, nullptr);
+ generateAtom(atom->next()->next()->next(), relative, nullptr);
+
+ m_closeSectionAfterGeneratedList = true;
+ skipAhead += 4;
+ sectionLevels.pop();
+ }
+
+ if (!matchAhead(atom, Atom::SectionHeadingLeft)) {
+ // No section title afterwards, make one up. This likely indicates a problem in the original documentation.
+ m_writer->writeTextElement(dbNamespace, "title", "");
+ }
break;
case Atom::SectionRight:
// All the logic about closing sections is done in the SectionLeft case
@@ -758,35 +959,82 @@ qsizetype DocBookGenerator::generateAtom(const Atom *atom, const Node *relative)
m_writer->writeCharacters(atom->string());
break;
case Atom::TableLeft: {
- QPair<QString, QString> pair = getTableWidthAttr(atom);
+ std::pair<QString, QString> pair = getTableWidthAttr(atom);
QString attr = pair.second;
QString width = pair.first;
- if (inPara) {
+ if (m_inPara) {
m_writer->writeEndElement(); // para or blockquote
newLine();
- inPara = false;
+ m_inPara = false;
}
+ m_tableHeaderAlreadyOutput = false;
+
m_writer->writeStartElement(dbNamespace, "informaltable");
m_writer->writeAttribute("style", attr);
if (!width.isEmpty())
m_writer->writeAttribute("width", width);
newLine();
- m_numTableRows = 0;
} break;
case Atom::TableRight:
+ m_tableWidthAttr = {"", ""};
m_writer->writeEndElement(); // table
newLine();
break;
- case Atom::TableHeaderLeft:
+ case Atom::TableHeaderLeft: {
+ if (matchAhead(atom, Atom::TableHeaderRight)) {
+ ++skipAhead;
+ break;
+ }
+
+ if (m_tableHeaderAlreadyOutput) {
+ // Headers are only allowed at the beginning of the table: close
+ // the table and reopen one.
+ m_writer->writeEndElement(); // table
+ newLine();
+
+ const QString &attr = m_tableWidthAttr.second;
+ const QString &width = m_tableWidthAttr.first;
+
+ m_writer->writeStartElement(dbNamespace, "informaltable");
+ m_writer->writeAttribute("style", attr);
+ if (!width.isEmpty())
+ m_writer->writeAttribute("width", width);
+ newLine();
+ } else {
+ m_tableHeaderAlreadyOutput = true;
+ }
+
+ const Atom *next = atom->next();
+ QString id{""};
+ if (matchAhead(atom, Atom::Target)) {
+ id = Utilities::asAsciiPrintable(next->string());
+ next = next->next();
+ ++skipAhead;
+ }
+
m_writer->writeStartElement(dbNamespace, "thead");
newLine();
m_writer->writeStartElement(dbNamespace, "tr");
+ writeXmlId(id);
newLine();
m_inTableHeader = true;
+
+ if (!matchAhead(atom, Atom::TableItemLeft)) {
+ m_closeTableCell = true;
+ m_writer->writeStartElement(dbNamespace, "td");
+ newLine();
+ }
+ }
break;
case Atom::TableHeaderRight:
+ if (m_closeTableCell) {
+ m_closeTableCell = false;
+ m_writer->writeEndElement(); // td
+ newLine();
+ }
+
m_writer->writeEndElement(); // tr
newLine();
if (matchAhead(atom, Atom::TableHeaderLeft)) {
@@ -799,8 +1047,23 @@ qsizetype DocBookGenerator::generateAtom(const Atom *atom, const Node *relative)
m_inTableHeader = false;
}
break;
- case Atom::TableRowLeft:
+ case Atom::TableRowLeft: {
+ if (matchAhead(atom, Atom::TableRowRight)) {
+ skipAhead = 1;
+ break;
+ }
+
+ QString id{""};
+ bool hasTarget {false};
+ if (matchAhead(atom, Atom::Target)) {
+ id = Utilities::asAsciiPrintable(atom->next()->string());
+ ++skipAhead;
+ hasTarget = true;
+ }
+
m_writer->writeStartElement(dbNamespace, "tr");
+ writeXmlId(id);
+
if (atom->string().isEmpty()) {
m_writer->writeAttribute("valign", "top");
} else {
@@ -810,18 +1073,51 @@ qsizetype DocBookGenerator::generateAtom(const Atom *atom, const Node *relative)
QStringList args = atom->string().split("\"", Qt::SkipEmptyParts);
// arg1=, val1, arg2=, val2,
// \-- 1st --/ \-- 2nd --/ \-- remainder
- if (args.size() % 2) {
+ const int nArgs = args.size();
+
+ if (nArgs % 2) {
// Problem...
relative->doc().location().warning(
QStringLiteral("Error when parsing attributes for the table: got \"%1\"")
.arg(atom->string()));
}
- for (int i = 0; i + 1 < args.size(); i += 2)
- m_writer->writeAttribute(args.at(i).chopped(1), args.at(i + 1));
+ for (int i = 0; i + 1 < nArgs; i += 2) {
+ // args.at(i): name of the attribute being set.
+ // args.at(i + 1): value of the said attribute.
+ const QString &attr = args.at(i).chopped(1);
+ if (attr == "id") { // Too bad if there is an anchor later on
+ // (currently never happens).
+ writeXmlId(args.at(i + 1));
+ } else {
+ m_writer->writeAttribute(attr, args.at(i + 1));
+ }
+ }
}
newLine();
+
+ // If there is nothing in this row, close it right now. There might be keywords before the row contents.
+ bool isRowEmpty = hasTarget ? !matchAhead(atom->next(), Atom::TableItemLeft) : !matchAhead(atom, Atom::TableItemLeft);
+ if (isRowEmpty && matchAhead(atom, Atom::Keyword)) {
+ const Atom* next = atom->next();
+ while (matchAhead(next, Atom::Keyword))
+ next = next->next();
+ isRowEmpty = !matchAhead(next, Atom::TableItemLeft);
+ }
+
+ if (isRowEmpty) {
+ m_closeTableRow = true;
+ m_writer->writeEndElement(); // td
+ newLine();
+ }
+ }
break;
case Atom::TableRowRight:
+ if (m_closeTableRow) {
+ m_closeTableRow = false;
+ m_writer->writeEndElement(); // td
+ newLine();
+ }
+
m_writer->writeEndElement(); // tr
newLine();
break;
@@ -837,9 +1133,9 @@ qsizetype DocBookGenerator::generateAtom(const Atom *atom, const Node *relative)
QStringList spans = p.split(QLatin1Char(','));
if (spans.size() == 2) {
if (spans.at(0) != "1")
- m_writer->writeAttribute("colspan", spans.at(0));
+ m_writer->writeAttribute("colspan", spans.at(0).trimmed());
if (spans.at(1) != "1")
- m_writer->writeAttribute("rowspan", spans.at(1));
+ m_writer->writeAttribute("rowspan", spans.at(1).trimmed());
}
}
}
@@ -851,37 +1147,46 @@ qsizetype DocBookGenerator::generateAtom(const Atom *atom, const Node *relative)
newLine();
break;
case Atom::TableOfContents:
- break;
+ Q_FALLTHROUGH();
case Atom::Keyword:
break;
case Atom::Target:
- writeAnchor(Doc::canonicalTitle(atom->string()));
+ // Sometimes, there is a \target just before a section title with the same ID. Only output one xml:id.
+ if (matchAhead(atom, Atom::SectionRight) && matchAhead(atom->next(), Atom::SectionLeft)) {
+ QString nextId = Utilities::asAsciiPrintable(
+ Text::sectionHeading(atom->next()->next()).toString());
+ QString ownId = Utilities::asAsciiPrintable(atom->string());
+ if (nextId == ownId)
+ break;
+ }
+
+ writeAnchor(Utilities::asAsciiPrintable(atom->string()));
break;
case Atom::UnhandledFormat:
m_writer->writeStartElement(dbNamespace, "emphasis");
m_writer->writeAttribute("role", "bold");
- m_writer->writeCharacters("&lt;Missing DocBook&gt;");
+ m_writer->writeCharacters("<Missing DocBook>");
m_writer->writeEndElement(); // emphasis
break;
case Atom::UnknownCommand:
m_writer->writeStartElement(dbNamespace, "emphasis");
m_writer->writeAttribute("role", "bold");
- m_writer->writeCharacters("&lt;Unknown command&gt;");
+ if (m_useITS)
+ m_writer->writeAttribute(itsNamespace, "translate", "no");
+ m_writer->writeCharacters("<Unknown command>");
m_writer->writeStartElement(dbNamespace, "code");
m_writer->writeCharacters(atom->string());
m_writer->writeEndElement(); // code
m_writer->writeEndElement(); // emphasis
break;
- case Atom::QmlText:
- case Atom::EndQmlText:
- // don't do anything with these. They are just tags.
- break;
case Atom::CodeQuoteArgument:
case Atom::CodeQuoteCommand:
+ case Atom::ComparesLeft:
+ case Atom::ComparesRight:
case Atom::SnippetCommand:
case Atom::SnippetIdentifier:
case Atom::SnippetLocation:
- // no output (ignore)
+ // No output (ignore).
break;
default:
unknownAtom(atom);
@@ -895,65 +1200,75 @@ void DocBookGenerator::generateClassHierarchy(const Node *relative, NodeMultiMap
if (classMap.isEmpty())
return;
- NodeMap topLevel;
- for (Node *c : classMap) {
- auto *classNode = static_cast<ClassNode *>(c);
- if (classNode->baseClasses().isEmpty())
- topLevel.insert(classNode->name(), classNode);
- }
-
- QStack<NodeMap> stack;
- stack.push(topLevel);
-
- m_writer->writeStartElement(dbNamespace, "itemizedlist");
- newLine();
- while (!stack.isEmpty()) {
- if (stack.top().isEmpty()) {
- stack.pop();
- m_writer->writeEndElement(); // listitem
- newLine();
- m_writer->writeEndElement(); // itemizedlist
- newLine();
- } else {
- auto *child = static_cast<ClassNode *>(*stack.top().begin());
+ std::function<void(ClassNode *)> generateClassAndChildren
+ = [this, &relative, &generateClassAndChildren](ClassNode * classe) {
m_writer->writeStartElement(dbNamespace, "listitem");
newLine();
+
+ // This class.
m_writer->writeStartElement(dbNamespace, "para");
- generateFullName(child, relative);
+ generateFullName(classe, relative);
m_writer->writeEndElement(); // para
newLine();
- // Don't close the listitem now, as DocBook requires sublists to reside in items.
- stack.top().erase(stack.top().begin());
- NodeMap newTop;
- for (const RelatedClass &d : child->derivedClasses()) {
- if (d.m_node && !d.isPrivate() && !d.m_node->isInternal() && d.m_node->hasDoc())
- newTop.insert(d.m_node->name(), d.m_node);
+ // Children, if any.
+ bool hasChild = false;
+ for (const RelatedClass &relatedClass : classe->derivedClasses()) {
+ if (relatedClass.m_node && relatedClass.m_node->isInAPI()) {
+ hasChild = true;
+ break;
+ }
}
- if (!newTop.isEmpty()) {
- stack.push(newTop);
+
+ if (hasChild) {
m_writer->writeStartElement(dbNamespace, "itemizedlist");
newLine();
+
+ for (const RelatedClass &relatedClass: classe->derivedClasses()) {
+ if (relatedClass.m_node && relatedClass.m_node->isInAPI()) {
+ generateClassAndChildren(relatedClass.m_node);
+ }
+ }
+
+ m_writer->writeEndElement(); // itemizedlist
+ newLine();
}
- }
+
+ // End this class.
+ m_writer->writeEndElement(); // listitem
+ newLine();
+ };
+
+ m_writer->writeStartElement(dbNamespace, "itemizedlist");
+ newLine();
+
+ for (const auto &it : classMap) {
+ auto *classe = static_cast<ClassNode *>(it);
+ if (classe->baseClasses().isEmpty())
+ generateClassAndChildren(classe);
}
+
+ m_writer->writeEndElement(); // itemizedlist
+ newLine();
}
void DocBookGenerator::generateLink(const Atom *atom)
{
+ Q_ASSERT(m_inLink);
+
// From HtmlGenerator::generateLink.
- QRegularExpression funcLeftParen("\\S(\\()");
- auto match = funcLeftParen.match(atom->string());
- if (match.hasMatch()) {
- // hack for C++: move () outside of link
- qsizetype k = match.capturedStart(1);
- m_writer->writeCharacters(atom->string().left(k));
- m_writer->writeEndElement(); // link
- m_inLink = false;
- m_writer->writeCharacters(atom->string().mid(k));
- } else {
- m_writer->writeCharacters(atom->string());
+ if (m_linkNode && m_linkNode->isFunction()) {
+ auto match = XmlGenerator::m_funcLeftParen.match(atom->string());
+ if (match.hasMatch()) {
+ // C++: move () outside of link
+ qsizetype leftParenLoc = match.capturedStart(1);
+ m_writer->writeCharacters(atom->string().left(leftParenLoc));
+ endLink();
+ m_writer->writeCharacters(atom->string().mid(leftParenLoc));
+ return;
+ }
}
+ m_writer->writeCharacters(atom->string());
}
/*!
@@ -969,6 +1284,7 @@ void DocBookGenerator::beginLink(const QString &link, const Node *node, const No
&& node->isDeprecated())
m_writer->writeAttribute("role", "deprecated");
m_inLink = true;
+ m_linkNode = node;
}
void DocBookGenerator::endLink()
@@ -977,6 +1293,7 @@ void DocBookGenerator::endLink()
if (m_inLink)
m_writer->writeEndElement(); // link
m_inLink = false;
+ m_linkNode = nullptr;
}
void DocBookGenerator::generateList(const Node *relative, const QString &selector)
@@ -990,8 +1307,6 @@ void DocBookGenerator::generateList(const Node *relative, const QString &selecto
type = Node::Module;
else if (selector == QLatin1String("qml-modules"))
type = Node::QmlModule;
- else if (selector == QLatin1String("js-modules"))
- type = Node::JsModule;
if (type != Node::NoType) {
NodeList nodeList;
@@ -1003,9 +1318,8 @@ void DocBookGenerator::generateList(const Node *relative, const QString &selecto
generateAnnotatedList(relative, nodeList, selector);
} else {
/*
- \generatelist {selector} is only allowed in a
- comment where the topic is \group, \module,
- \qmlmodule, or \jsmodule
+ \generatelist {selector} is only allowed in a comment where
+ the topic is \group, \module, or \qmlmodule.
*/
Node *n = const_cast<Node *>(relative);
auto *cn = static_cast<CollectionNode *>(n);
@@ -1019,45 +1333,77 @@ void DocBookGenerator::generateList(const Node *relative, const QString &selecto
A two-column table is output.
*/
void DocBookGenerator::generateAnnotatedList(const Node *relative, const NodeList &nodeList,
- const QString &selector)
+ const QString &selector, GeneratedListType type)
{
if (nodeList.isEmpty())
return;
- // Do nothing if all items are internal or obsolete
+ // Do nothing if all items are internal or obsolete.
if (std::all_of(nodeList.cbegin(), nodeList.cend(), [](const Node *n) {
return n->isInternal() || n->isDeprecated(); })) {
return;
}
- // From WebXMLGenerator::generateAnnotatedList.
- m_writer->writeStartElement(dbNamespace, "variablelist");
- m_writer->writeAttribute("role", selector);
- newLine();
+ // Detect if there is a need for a variablelist (i.e. titles mapped to
+ // descriptions) or a regular itemizedlist (only titles).
+ bool noItemsHaveTitle =
+ type == ItemizedList || std::all_of(nodeList.begin(), nodeList.end(),
+ [](const Node* node) {
+ return node->doc().briefText().toString().isEmpty();
+ });
- for (const auto node : nodeList) {
- if (node->isInternal() || node->isDeprecated())
- continue;
- m_writer->writeStartElement(dbNamespace, "varlistentry");
- newLine();
- m_writer->writeStartElement(dbNamespace, "term");
- generateFullName(node, relative);
- m_writer->writeEndElement(); // term
- newLine();
+ // Wrap the list in a section if needed.
+ if (type == AutoSection && m_hasSection)
+ startSection("", "Contents");
- m_writer->writeStartElement(dbNamespace, "listitem");
- newLine();
- m_writer->writeStartElement(dbNamespace, "para");
- m_writer->writeCharacters(node->doc().briefText().toString());
- m_writer->writeEndElement(); // para
- newLine();
- m_writer->writeEndElement(); // listitem
+ // From WebXMLGenerator::generateAnnotatedList.
+ if (!nodeList.isEmpty()) {
+ m_writer->writeStartElement(dbNamespace, noItemsHaveTitle ? "itemizedlist" : "variablelist");
+ m_writer->writeAttribute("role", selector);
newLine();
- m_writer->writeEndElement(); // varlistentry
+
+ NodeList members{nodeList};
+ std::sort(members.begin(), members.end(), Node::nodeNameLessThan);
+ for (const auto &node : std::as_const(members)) {
+ if (node->isInternal() || node->isDeprecated())
+ continue;
+
+ if (noItemsHaveTitle) {
+ m_writer->writeStartElement(dbNamespace, "listitem");
+ newLine();
+ m_writer->writeStartElement(dbNamespace, "para");
+ } else {
+ m_writer->writeStartElement(dbNamespace, "varlistentry");
+ newLine();
+ m_writer->writeStartElement(dbNamespace, "term");
+ }
+ generateFullName(node, relative);
+ if (noItemsHaveTitle) {
+ m_writer->writeEndElement(); // para
+ newLine();
+ m_writer->writeEndElement(); // listitem
+ } else {
+ m_writer->writeEndElement(); // term
+ newLine();
+ m_writer->writeStartElement(dbNamespace, "listitem");
+ newLine();
+ m_writer->writeStartElement(dbNamespace, "para");
+ m_writer->writeCharacters(node->doc().briefText().toString());
+ m_writer->writeEndElement(); // para
+ newLine();
+ m_writer->writeEndElement(); // listitem
+ newLine();
+ m_writer->writeEndElement(); // varlistentry
+ }
+ newLine();
+ }
+
+ m_writer->writeEndElement(); // itemizedlist or variablelist
newLine();
}
- m_writer->writeEndElement(); // variablelist
- newLine();
+
+ if (type == AutoSection && m_hasSection)
+ endSection();
}
/*!
@@ -1070,7 +1416,7 @@ void DocBookGenerator::generateAnnotatedLists(const Node *relative, const NodeMu
// From HtmlGenerator::generateAnnotatedLists.
for (const QString &name : nmm.uniqueKeys()) {
if (!name.isEmpty())
- startSection(registerRef(name.toLower()), name);
+ startSection(name.toLower(), name);
generateAnnotatedList(relative, nmm.values(name), selector);
if (!name.isEmpty())
endSection();
@@ -1087,19 +1433,20 @@ void DocBookGenerator::generateAnnotatedLists(const Node *relative, const NodeMu
the name of the first and last classes in the class map
\a nmm.
*/
-void DocBookGenerator::generateCompactList(ListType listType, const Node *relative,
- const NodeMultiMap &nmm, const QString &commonPrefix,
+void DocBookGenerator::generateCompactList(const Node *relative, const NodeMultiMap &nmm,
+ bool includeAlphabet, const QString &commonPrefix,
const QString &selector)
{
// From HtmlGenerator::generateCompactList. No more "includeAlphabet", this should be handled by
// the DocBook toolchain afterwards.
// TODO: In DocBook, probably no need for this method: this is purely presentational, i.e. to be
// fully handled by the DocBook toolchain.
+
if (nmm.isEmpty())
return;
const int NumParagraphs = 37; // '0' to '9', 'A' to 'Z', '_'
- qsizetype commonPrefixLen = commonPrefix.length();
+ qsizetype commonPrefixLen = commonPrefix.size();
/*
Divide the data into 37 paragraphs: 0, ..., 9, A, ..., Z,
@@ -1111,26 +1458,25 @@ void DocBookGenerator::generateCompactList(ListType listType, const Node *relati
QString paragraphName[NumParagraphs + 1];
QSet<char> usedParagraphNames;
- NodeMultiMap::ConstIterator c = nmm.constBegin();
- while (c != nmm.constEnd()) {
+ for (auto c = nmm.constBegin(); c != nmm.constEnd(); ++c) {
QStringList pieces = c.key().split("::");
- QString key;
int idx = commonPrefixLen;
if (idx > 0 && !pieces.last().startsWith(commonPrefix, Qt::CaseInsensitive))
idx = 0;
- key = pieces.last().mid(idx).toLower();
+ QString last = pieces.last().toLower();
+ QString key = last.mid(idx);
int paragraphNr = NumParagraphs - 1;
- if (key[0].digitValue() != -1)
+ if (key[0].digitValue() != -1) {
paragraphNr = key[0].digitValue();
- else if (key[0] >= QLatin1Char('a') && key[0] <= QLatin1Char('z'))
+ } else if (key[0] >= QLatin1Char('a') && key[0] <= QLatin1Char('z')) {
paragraphNr = 10 + key[0].unicode() - 'a';
+ }
paragraphName[paragraphNr] = key[0].toUpper();
usedParagraphNames.insert(key[0].toLower().cell());
- paragraph[paragraphNr].insert(c.key(), c.value());
- ++c;
+ paragraph[paragraphNr].insert(last, c.value());
}
/*
@@ -1144,37 +1490,58 @@ void DocBookGenerator::generateCompactList(ListType listType, const Node *relati
int paragraphOffset[NumParagraphs + 1]; // 37 + 1
paragraphOffset[0] = 0;
for (int i = 0; i < NumParagraphs; i++) // i = 0..36
- paragraphOffset[i + 1] = paragraphOffset[i] + paragraph[i].count();
+ paragraphOffset[i + 1] = paragraphOffset[i] + paragraph[i].size();
+
+ // Output the alphabet as a row of links.
+ if (includeAlphabet && !usedParagraphNames.isEmpty()) {
+ m_writer->writeStartElement(dbNamespace, "simplelist");
+ newLine();
- // No table of contents in DocBook.
+ for (int i = 0; i < 26; i++) {
+ QChar ch('a' + i);
+ if (usedParagraphNames.contains(char('a' + i))) {
+ m_writer->writeStartElement(dbNamespace, "member");
+ generateSimpleLink(ch, ch.toUpper());
+ m_writer->writeEndElement(); // member
+ newLine();
+ }
+ }
- // Actual output.
- m_numTableRows = 0;
+ m_writer->writeEndElement(); // simplelist
+ newLine();
+ }
+ // Actual output.
int curParNr = 0;
int curParOffset = 0;
QString previousName;
bool multipleOccurrences = false;
- for (int i = 0; i < nmm.count(); i++) {
- while ((curParNr < NumParagraphs) && (curParOffset == paragraph[curParNr].count())) {
+ m_writer->writeStartElement(dbNamespace, "variablelist");
+ m_writer->writeAttribute("role", selector);
+ newLine();
+
+ for (int i = 0; i < nmm.size(); i++) {
+ while ((curParNr < NumParagraphs) && (curParOffset == paragraph[curParNr].size())) {
+
++curParNr;
curParOffset = 0;
}
- /*
- Starting a new paragraph means starting a new variablelist.
- */
+ // Starting a new paragraph means starting a new varlistentry.
if (curParOffset == 0) {
if (i > 0) {
- m_writer->writeEndElement(); // variablelist
+ m_writer->writeEndElement(); // itemizedlist
+ newLine();
+ m_writer->writeEndElement(); // listitem
+ newLine();
+ m_writer->writeEndElement(); // varlistentry
newLine();
}
- m_writer->writeStartElement(dbNamespace, "variablelist");
- m_writer->writeAttribute("role", selector);
- newLine();
m_writer->writeStartElement(dbNamespace, "varlistentry");
+ if (includeAlphabet)
+ writeXmlId(paragraphName[curParNr][0].toLower());
newLine();
m_writer->writeStartElement(dbNamespace, "term");
@@ -1184,14 +1551,18 @@ void DocBookGenerator::generateCompactList(ListType listType, const Node *relati
m_writer->writeEndElement(); // emphasis
m_writer->writeEndElement(); // term
newLine();
+
+ m_writer->writeStartElement(dbNamespace, "listitem");
+ newLine();
+ m_writer->writeStartElement(dbNamespace, "itemizedlist");
+ newLine();
}
- /*
- Output a listitem for the current offset in the current paragraph.
- */
+ // Output a listitem for the current offset in the current paragraph.
m_writer->writeStartElement(dbNamespace, "listitem");
newLine();
m_writer->writeStartElement(dbNamespace, "para");
+
if ((curParNr < NumParagraphs) && !paragraphName[curParNr].isEmpty()) {
NodeMultiMap::Iterator it;
NodeMultiMap::Iterator next;
@@ -1199,65 +1570,60 @@ void DocBookGenerator::generateCompactList(ListType listType, const Node *relati
for (int j = 0; j < curParOffset; j++)
++it;
- if (listType == Generic) {
- generateFullName(it.value(), relative);
- m_writer->writeStartElement(dbNamespace, "link");
- m_writer->writeAttribute(xlinkNamespace, "href", fullDocumentLocation(*it));
- m_writer->writeAttribute("type", targetType(it.value()));
- } else if (listType == Obsolete) {
- QString fn = fileName(it.value(), fileExtension());
- QString link;
- if (useOutputSubdirs())
- link = QString("../" + it.value()->outputSubdirectory() + QLatin1Char('/'));
- link += fn;
-
- m_writer->writeStartElement(dbNamespace, "link");
- m_writer->writeAttribute(xlinkNamespace, "href", link);
- m_writer->writeAttribute("type", targetType(it.value()));
+ // Cut the name into pieces to determine whether it is simple (one piece) or complex
+ // (more than one piece).
+ QStringList pieces{it.value()->fullName(relative).split("::"_L1)};
+ const auto &name{pieces.last()};
+ next = it;
+ ++next;
+ if (name != previousName)
+ multipleOccurrences = false;
+ if ((next != paragraph[curParNr].end()) && (name == next.value()->name())) {
+ multipleOccurrences = true;
+ previousName = name;
}
-
- QStringList pieces;
- if (it.value()->isQmlType() || it.value()->isJsType()) {
- QString name = it.value()->name();
- next = it;
- ++next;
- if (name != previousName)
- multipleOccurrences = false;
- if ((next != paragraph[curParNr].end()) && (name == next.value()->name())) {
- multipleOccurrences = true;
- previousName = name;
- }
- if (multipleOccurrences)
- name += ": " + it.value()->tree()->camelCaseModuleName();
- pieces << name;
- } else
- pieces = it.value()->fullName(relative).split("::");
-
+ if (multipleOccurrences && pieces.size() == 1)
+ pieces.last().append(": "_L1.arg(it.value()->tree()->camelCaseModuleName()));
+
+ // Write the link to the element, which is identical if the element is obsolete or not.
+ m_writer->writeStartElement(dbNamespace, "link");
+ m_writer->writeAttribute(xlinkNamespace, "href", linkForNode(*it, relative));
+ if (const QString type = targetType(it.value()); !type.isEmpty())
+ m_writer->writeAttribute("role", type);
m_writer->writeCharacters(pieces.last());
m_writer->writeEndElement(); // link
+ // Outside the link, give the full name of the node if it is complex.
if (pieces.size() > 1) {
m_writer->writeCharacters(" (");
generateFullName(it.value()->parent(), relative);
m_writer->writeCharacters(")");
}
}
+
m_writer->writeEndElement(); // para
newLine();
m_writer->writeEndElement(); // listitem
newLine();
- m_writer->writeEndElement(); // varlistentry
- newLine();
+
curParOffset++;
}
- if (nmm.count() > 0) {
- m_writer->writeEndElement(); // variablelist
- }
+ m_writer->writeEndElement(); // itemizedlist
+ newLine();
+ m_writer->writeEndElement(); // listitem
+ newLine();
+ m_writer->writeEndElement(); // varlistentry
+ newLine();
+
+ m_writer->writeEndElement(); // variablelist
+ newLine();
}
void DocBookGenerator::generateFunctionIndex(const Node *relative)
{
// From HtmlGenerator::generateFunctionIndex.
+
+ // First list: links to parts of the second list, one item per letter.
m_writer->writeStartElement(dbNamespace, "simplelist");
m_writer->writeAttribute("role", "functionIndex");
newLine();
@@ -1272,6 +1638,10 @@ void DocBookGenerator::generateFunctionIndex(const Node *relative)
m_writer->writeEndElement(); // simplelist
newLine();
+ // Second list: the actual list of functions, sorted by alphabetical
+ // order. One entry of the list per letter.
+ if (m_qdb->getFunctionIndex().isEmpty())
+ return;
char nextLetter = 'a';
char currentLetter;
@@ -1356,10 +1726,8 @@ bool DocBookGenerator::generateSince(const Node *node)
// From Generator::generateSince.
if (!node->since().isEmpty()) {
m_writer->writeStartElement(dbNamespace, "para");
- m_writer->writeCharacters("This " + typeString(node) + " was introduced");
- if (node->nodeType() == Node::Enum)
- m_writer->writeCharacters(" or modified");
- m_writer->writeCharacters(" in " + formatSince(node) + ".");
+ m_writer->writeCharacters("This " + typeString(node) + " was introduced in ");
+ m_writer->writeCharacters(formatSince(node) + ".");
m_writer->writeEndElement(); // para
newLine();
@@ -1369,20 +1737,31 @@ bool DocBookGenerator::generateSince(const Node *node)
return false;
}
+/*!
+ Generate the DocBook header for the file, including the abstract.
+ Equivalent to calling generateTitle and generateBrief in HTML.
+*/
void DocBookGenerator::generateHeader(const QString &title, const QString &subTitle,
const Node *node)
{
- // From HtmlGenerator::generateHeader.
refMap.clear();
// Output the DocBook header.
m_writer->writeStartElement(dbNamespace, "info");
newLine();
- m_writer->writeTextElement(dbNamespace, "title", title);
+ m_writer->writeStartElement(dbNamespace, "title");
+ if (node->genus() & Node::API && m_useITS)
+ m_writer->writeAttribute(itsNamespace, "translate", "no");
+ m_writer->writeCharacters(title);
+ m_writer->writeEndElement(); // title
newLine();
if (!subTitle.isEmpty()) {
- m_writer->writeTextElement(dbNamespace, "subtitle", subTitle);
+ m_writer->writeStartElement(dbNamespace, "subtitle");
+ if (node->genus() & Node::API && m_useITS)
+ m_writer->writeAttribute(itsNamespace, "translate", "no");
+ m_writer->writeCharacters(subTitle);
+ m_writer->writeEndElement(); // subtitle
newLine();
}
@@ -1406,8 +1785,8 @@ void DocBookGenerator::generateHeader(const QString &title, const QString &subTi
// or useSeparator field, as this content is only output in the info tag, not in the main
// content).
if (node && !node->links().empty()) {
- QPair<QString, QString> linkPair;
- QPair<QString, QString> anchorPair;
+ std::pair<QString, QString> linkPair;
+ std::pair<QString, QString> anchorPair;
const Node *linkNode;
if (node->links().contains(Node::PreviousLink)) {
@@ -1419,14 +1798,17 @@ void DocBookGenerator::generateHeader(const QString &title, const QString &subTi
anchorPair = anchorForNode(linkNode);
m_writer->writeStartElement(dbNamespace, "extendedlink");
+ m_writer->writeAttribute(xlinkNamespace, "type", "extended");
m_writer->writeEmptyElement(dbNamespace, "link");
m_writer->writeAttribute(xlinkNamespace, "to", anchorPair.first);
- m_writer->writeAttribute(xlinkNamespace, "title", "prev");
+ m_writer->writeAttribute(xlinkNamespace, "type", "arc");
+ m_writer->writeAttribute(xlinkNamespace, "arcrole", "prev");
if (linkPair.first == linkPair.second && !anchorPair.second.isEmpty())
- m_writer->writeAttribute(xlinkNamespace, "label", anchorPair.second);
+ m_writer->writeAttribute(xlinkNamespace, "title", anchorPair.second);
else
- m_writer->writeAttribute(xlinkNamespace, "label", linkPair.second);
+ m_writer->writeAttribute(xlinkNamespace, "title", linkPair.second);
m_writer->writeEndElement(); // extendedlink
+ newLine();
}
if (node->links().contains(Node::NextLink)) {
linkPair = node->links()[Node::NextLink];
@@ -1437,14 +1819,17 @@ void DocBookGenerator::generateHeader(const QString &title, const QString &subTi
anchorPair = anchorForNode(linkNode);
m_writer->writeStartElement(dbNamespace, "extendedlink");
+ m_writer->writeAttribute(xlinkNamespace, "type", "extended");
m_writer->writeEmptyElement(dbNamespace, "link");
m_writer->writeAttribute(xlinkNamespace, "to", anchorPair.first);
- m_writer->writeAttribute(xlinkNamespace, "title", "prev");
+ m_writer->writeAttribute(xlinkNamespace, "type", "arc");
+ m_writer->writeAttribute(xlinkNamespace, "arcrole", "next");
if (linkPair.first == linkPair.second && !anchorPair.second.isEmpty())
- m_writer->writeAttribute(xlinkNamespace, "label", anchorPair.second);
+ m_writer->writeAttribute(xlinkNamespace, "title", anchorPair.second);
else
- m_writer->writeAttribute(xlinkNamespace, "label", linkPair.second);
+ m_writer->writeAttribute(xlinkNamespace, "title", linkPair.second);
m_writer->writeEndElement(); // extendedlink
+ newLine();
}
if (node->links().contains(Node::StartLink)) {
linkPair = node->links()[Node::StartLink];
@@ -1455,14 +1840,17 @@ void DocBookGenerator::generateHeader(const QString &title, const QString &subTi
anchorPair = anchorForNode(linkNode);
m_writer->writeStartElement(dbNamespace, "extendedlink");
+ m_writer->writeAttribute(xlinkNamespace, "type", "extended");
m_writer->writeEmptyElement(dbNamespace, "link");
m_writer->writeAttribute(xlinkNamespace, "to", anchorPair.first);
- m_writer->writeAttribute(xlinkNamespace, "title", "start");
+ m_writer->writeAttribute(xlinkNamespace, "type", "arc");
+ m_writer->writeAttribute(xlinkNamespace, "arcrole", "start");
if (linkPair.first == linkPair.second && !anchorPair.second.isEmpty())
- m_writer->writeAttribute(xlinkNamespace, "label", anchorPair.second);
+ m_writer->writeAttribute(xlinkNamespace, "title", anchorPair.second);
else
- m_writer->writeAttribute(xlinkNamespace, "label", linkPair.second);
+ m_writer->writeAttribute(xlinkNamespace, "title", linkPair.second);
m_writer->writeEndElement(); // extendedlink
+ newLine();
}
}
@@ -1479,10 +1867,9 @@ void DocBookGenerator::generateHeader(const QString &title, const QString &subTi
bool generatedSomething = false;
Text brief;
- const NamespaceNode *ns = node->isAggregate()
- ? static_cast<const NamespaceNode *>(static_cast<const Aggregate *>(node))
- : nullptr;
- if (node->isAggregate() && ns && !ns->hasDoc() && ns->docNode()) {
+ const NamespaceNode *ns =
+ node->isNamespace() ? static_cast<const NamespaceNode *>(node) : nullptr;
+ if (ns && !ns->hasDoc() && ns->docNode()) {
NamespaceNode *NS = ns->docNode();
brief << "The " << ns->name()
<< " namespace includes the following elements from module "
@@ -1512,6 +1899,8 @@ void DocBookGenerator::generateHeader(const QString &title, const QString &subTi
generatedSomething |= generateStatus(node);
generatedSomething |= generateSince(node);
generatedSomething |= generateThreadSafeness(node);
+ generatedSomething |= generateComparisonCategory(node);
+ generatedSomething |= generateComparisonList(node);
// An abstract cannot be empty, hence use the project description.
if (!generatedSomething)
@@ -1536,6 +1925,15 @@ void DocBookGenerator::closeTextSections()
void DocBookGenerator::generateFooter()
{
+ if (m_closeSectionAfterGeneratedList) {
+ m_closeSectionAfterGeneratedList = false;
+ endSection();
+ }
+ if (m_closeSectionAfterRawTitle) {
+ m_closeSectionAfterRawTitle = false;
+ endSection();
+ }
+
closeTextSections();
m_writer->writeEndElement(); // article
}
@@ -1557,12 +1955,6 @@ void DocBookGenerator::generateObsoleteMembers(const Sections &sections)
return;
Aggregate *aggregate = sections.aggregate();
- QString link;
- if (useOutputSubdirs() && !Generator::outputSubdir().isEmpty())
- link = QString("../" + Generator::outputSubdir() + QLatin1Char('/'));
- link += fileName(aggregate, fileExtension());
- aggregate->setObsoleteLink(link);
-
startSection("obsolete", "Obsolete Members for " + aggregate->name());
m_writer->writeStartElement(dbNamespace, "para");
@@ -1577,9 +1969,8 @@ void DocBookGenerator::generateObsoleteMembers(const Sections &sections)
newLine();
for (const Section *section : details_spv) {
- const QString &title = section->title();
- QString ref = registerRef(title.toLower());
- startSection(ref, title);
+ const QString &title = "Obsolete " + section->title();
+ startSection(title.toLower(), title);
const NodeVector &members = section->obsoleteMembers();
NodeVector::ConstIterator m = members.constBegin();
@@ -1596,7 +1987,7 @@ void DocBookGenerator::generateObsoleteMembers(const Sections &sections)
}
/*!
- Generates a separate file where obsolete members of the QML
+ Generates a separate section where obsolete members of the QML
type \a qcn are listed. The \a marker is used to generate
the section lists, which are then traversed and output here.
@@ -1612,13 +2003,6 @@ void DocBookGenerator::generateObsoleteQmlMembers(const Sections &sections)
return;
Aggregate *aggregate = sections.aggregate();
- QString fn = fileName(aggregate, fileExtension());
- QString link;
- if (useOutputSubdirs() && !Generator::outputSubdir().isEmpty())
- link = QString("../" + Generator::outputSubdir() + QLatin1Char('/'));
- link += fn;
- aggregate->setObsoleteLink(link);
-
startSection("obsolete", "Obsolete Members for " + aggregate->name());
m_writer->writeStartElement(dbNamespace, "para");
@@ -1633,9 +2017,8 @@ void DocBookGenerator::generateObsoleteQmlMembers(const Sections &sections)
newLine();
for (const auto *section : details_spv) {
- const QString &title = section->title();
- QString ref = registerRef(title.toLower());
- startSection(ref, title);
+ const QString &title = "Obsolete " + section->title();
+ startSection(title.toLower(), title);
const NodeVector &members = section->obsoleteMembers();
NodeVector::ConstIterator m = members.constBegin();
@@ -1654,10 +2037,10 @@ void DocBookGenerator::generateObsoleteQmlMembers(const Sections &sections)
static QString nodeToSynopsisTag(const Node *node)
{
// Order from Node::nodeTypeString.
- if (node->isClass() || node->isQmlType() || node->isQmlBasicType())
+ if (node->isClass() || node->isQmlType())
return QStringLiteral("classsynopsis");
if (node->isNamespace())
- return QStringLiteral("namespacesynopsis");
+ return QStringLiteral("packagesynopsis");
if (node->isPageNode()) {
node->doc().location().warning("Unexpected document node in nodeToSynopsisTag");
return QString();
@@ -1667,7 +2050,7 @@ static QString nodeToSynopsisTag(const Node *node)
if (node->isTypedef())
return QStringLiteral("typedefsynopsis");
if (node->isFunction()) {
- // Signals are also encoded as functions (including QML/JS ones).
+ // Signals are also encoded as functions (including QML ones).
const auto fn = static_cast<const FunctionNode *>(node);
if (fn->isCtor() || fn->isCCtor() || fn->isMCtor())
return QStringLiteral("constructorsynopsis");
@@ -1691,11 +2074,13 @@ void DocBookGenerator::generateStartRequisite(const QString &description)
m_writer->writeStartElement(dbNamespace, "listitem");
newLine();
m_writer->writeStartElement(dbNamespace, "para");
+ m_inPara = true;
}
void DocBookGenerator::generateEndRequisite()
{
m_writer->writeEndElement(); // para
+ m_inPara = false;
newLine();
m_writer->writeEndElement(); // listitem
newLine();
@@ -1721,6 +2106,7 @@ void DocBookGenerator::generateCMakeRequisite(const QStringList &values)
m_writer->writeCharacters(values.first());
m_writer->writeEndElement(); // para
newLine();
+
m_writer->writeStartElement(dbNamespace, "para");
m_writer->writeCharacters(values.last());
generateEndRequisite();
@@ -1746,7 +2132,7 @@ void DocBookGenerator::generateSortedNames(const ClassNode *cn, const QList<Rela
int index = 0;
for (const QString &className : classNames) {
generateFullName(classMap.value(className), cn);
- m_writer->writeCharacters(Utilities::comma(index++, classNames.count()));
+ m_writer->writeCharacters(Utilities::comma(index++, classNames.size()));
}
}
@@ -1754,19 +2140,17 @@ void DocBookGenerator::generateSortedQmlNames(const Node *base, const NodeList &
{
// From Generator::appendSortedQmlNames.
QMap<QString, Node *> classMap;
- int index = 0;
for (auto sub : subs)
- if (!base->isQtQuickNode() || !sub->isQtQuickNode()
- || (base->logicalModuleName() == sub->logicalModuleName()))
- classMap[sub->plainFullName(base).toLower()] = sub;
+ classMap[sub->plainFullName(base).toLower()] = sub;
QStringList names = classMap.keys();
names.sort();
+ int index = 0;
for (const QString &name : names) {
generateFullName(classMap.value(name), base);
- m_writer->writeCharacters(Utilities::comma(index++, names.count()));
+ m_writer->writeCharacters(Utilities::comma(index++, names.size()));
}
}
@@ -1777,14 +2161,16 @@ void DocBookGenerator::generateRequisites(const Aggregate *aggregate)
{
// Adapted from HtmlGenerator::generateRequisites, but simplified: no need to store all the
// elements, they can be produced one by one.
- m_writer->writeStartElement(dbNamespace, "variablelist");
- newLine();
+
+ // Generate the requisites first separately: if some of them are generated, output them in a wrapper.
+ // This complexity is required to ensure the DocBook file is valid: an empty list is not valid. It is not easy
+ // to write a truly comprehensive condition.
+ QXmlStreamWriter* oldWriter = m_writer;
+ QString output;
+ m_writer = new QXmlStreamWriter(&output);
// Includes.
- if (!aggregate->includeFiles().isEmpty()) {
- for (const QString &include : aggregate->includeFiles())
- generateRequisite("Header", include);
- }
+ if (aggregate->includeFile()) generateRequisite("Header", *aggregate->includeFile());
// Since and project.
if (!aggregate->since().isEmpty())
@@ -1792,31 +2178,39 @@ void DocBookGenerator::generateRequisites(const Aggregate *aggregate)
if (aggregate->isClassNode() || aggregate->isNamespace()) {
// CMake and QT variable.
- if (!aggregate->physicalModuleName().isEmpty()) {
- const CollectionNode *cn =
- m_qdb->getCollectionNode(aggregate->physicalModuleName(), Node::Module);
- if (cn && !cn->qtCMakeComponent().isEmpty()) {
- const QString qtComponent = "Qt" + QString::number(QT_VERSION_MAJOR);
- const QString findpackageText = "find_package(" + qtComponent + " COMPONENTS "
- + cn->qtCMakeComponent() + " REQUIRED)";
- const QString targetLinkLibrariesText = "target_link_libraries(mytarget PRIVATE "
- + qtComponent + "::" + cn->qtCMakeComponent() + ")";
- const QStringList cmakeInfo { findpackageText, targetLinkLibrariesText };
- generateCMakeRequisite(cmakeInfo);
- }
- if (cn && !cn->qtVariable().isEmpty())
- generateRequisite("qmake", "QT += " + cn->qtVariable());
+ const CollectionNode *cn =
+ m_qdb->getCollectionNode(aggregate->physicalModuleName(), Node::Module);
+ if (cn && !cn->qtCMakeComponent().isEmpty()) {
+ const QString qtComponent = "Qt" + QString::number(QT_VERSION_MAJOR);
+ const QString findpackageText = "find_package(" + qtComponent
+ + " REQUIRED COMPONENTS " + cn->qtCMakeComponent() + ")";
+ const QString targetItem =
+ cn->qtCMakeTargetItem().isEmpty() ? cn->qtCMakeComponent() : cn->qtCMakeTargetItem();
+ const QString targetLinkLibrariesText = "target_link_libraries(mytarget PRIVATE "
+ + qtComponent + "::" + targetItem + ")";
+ const QStringList cmakeInfo { findpackageText, targetLinkLibrariesText };
+ generateCMakeRequisite(cmakeInfo);
}
+ if (cn && !cn->qtVariable().isEmpty())
+ generateRequisite("qmake", "QT += " + cn->qtVariable());
}
if (aggregate->nodeType() == Node::Class) {
- // Instantiated by.
+ // Native type information.
auto *classe = const_cast<ClassNode *>(static_cast<const ClassNode *>(aggregate));
- if (classe->qmlElement() != nullptr && classe->status() != Node::Internal) {
- generateStartRequisite("Inherited By");
- generateSortedNames(classe, classe->derivedClasses());
+ if (classe && classe->isQmlNativeType() && classe->status() != Node::Internal) {
+ generateStartRequisite("In QML");
+
+ qsizetype idx{0};
+ QList<QmlTypeNode *> nativeTypes { classe->qmlNativeTypes().cbegin(), classe->qmlNativeTypes().cend()};
+ std::sort(nativeTypes.begin(), nativeTypes.end(), Node::nodeNameLessThan);
+
+ for (const auto &item : std::as_const(nativeTypes)) {
+ generateFullName(item, classe);
+ m_writer->writeCharacters(
+ Utilities::comma(idx++, nativeTypes.size()));
+ }
generateEndRequisite();
- generateRequisite("Instantiated By", fullDocumentLocation(classe->qmlElement()));
}
// Inherits.
@@ -1835,7 +2229,7 @@ void DocBookGenerator::generateRequisites(const Aggregate *aggregate)
else if ((*r).m_access == Access::Private)
m_writer->writeCharacters(" (private)");
m_writer->writeCharacters(
- Utilities::comma(index++, classe->baseClasses().count()));
+ Utilities::comma(index++, classe->baseClasses().size()));
}
++r;
}
@@ -1851,8 +2245,44 @@ void DocBookGenerator::generateRequisites(const Aggregate *aggregate)
}
}
- m_writer->writeEndElement(); // variablelist
- newLine();
+ // Group.
+ if (!aggregate->groupNames().empty()) {
+ generateStartRequisite("Group");
+ generateGroupReferenceText(aggregate);
+ generateEndRequisite();
+ }
+
+ // Status.
+ if (auto status = formatStatus(aggregate, m_qdb); status)
+ generateRequisite("Status", status.value());
+
+ // Write the elements as a list if not empty.
+ delete m_writer;
+ m_writer = oldWriter;
+
+ if (!output.isEmpty()) {
+ // Namespaces are mangled in this output, because QXmlStreamWriter doesn't know about them. (Letting it know
+ // would imply generating the xmlns declaration one more time.)
+ static const QRegularExpression xmlTag(R"(<(/?)n\d+:)"); // Only for DocBook tags.
+ static const QRegularExpression xmlnsDocBookDefinition(R"( xmlns:n\d+=")" + QString{dbNamespace} + "\"");
+ static const QRegularExpression xmlnsXLinkDefinition(R"( xmlns:n\d+=")" + QString{xlinkNamespace} + "\"");
+ static const QRegularExpression xmlAttr(R"( n\d+:)"); // Only for XLink attributes.
+ // Space at the beginning!
+ const QString cleanOutput = output.replace(xmlTag, R"(<\1db:)")
+ .replace(xmlnsDocBookDefinition, "")
+ .replace(xmlnsXLinkDefinition, "")
+ .replace(xmlAttr, " xlink:");
+
+ m_writer->writeStartElement(dbNamespace, "variablelist");
+ if (m_useITS)
+ m_writer->writeAttribute(itsNamespace, "translate", "no");
+ newLine();
+
+ m_writer->device()->write(cleanOutput.toUtf8());
+
+ m_writer->writeEndElement(); // variablelist
+ newLine();
+ }
}
/*!
@@ -1865,23 +2295,33 @@ void DocBookGenerator::generateQmlRequisites(const QmlTypeNode *qcn)
if (!qcn)
return;
- m_writer->writeStartElement(dbNamespace, "variablelist");
- newLine();
-
- // Module name and version (i.e. import).
- QString logicalModuleVersion;
const CollectionNode *collection = qcn->logicalModule();
- // skip import statement for \internal collections
- if (!collection || !collection->isInternal() || m_showInternal) {
- logicalModuleVersion =
- collection ? collection->logicalModuleVersion() : qcn->logicalModuleVersion();
+ NodeList subs;
+ QmlTypeNode::subclasses(qcn, subs);
- QStringList importText;
- importText << "import " + qcn->logicalModuleName();
- if (!logicalModuleVersion.isEmpty())
- importText << logicalModuleVersion;
- generateRequisite("Import Statement", importText.join(' '));
+ QmlTypeNode *base = qcn->qmlBaseNode();
+ while (base && base->isInternal()) {
+ base = base->qmlBaseNode();
+ }
+
+ // Skip import statement for \internal collections
+ const bool generate_import_statement = !qcn->logicalModuleName().isEmpty() && (!collection || !collection->isInternal() || m_showInternal);
+ // Detect if anything is generated in this method. If not, exit early to avoid having an empty list.
+ const bool generates_something = generate_import_statement || !qcn->since().isEmpty() || !subs.isEmpty() || base;
+
+ if (!generates_something)
+ return;
+
+ // Start writing the elements as a list.
+ m_writer->writeStartElement(dbNamespace, "variablelist");
+ if (m_useITS)
+ m_writer->writeAttribute(itsNamespace, "translate", "no");
+ newLine();
+
+ if (generate_import_statement) {
+ QStringList parts = QStringList() << "import" << qcn->logicalModuleName() << qcn->logicalModuleVersion();
+ generateRequisite("Import Statement", parts.join(' ').trimmed());
}
// Since and project.
@@ -1889,8 +2329,6 @@ void DocBookGenerator::generateQmlRequisites(const QmlTypeNode *qcn)
generateRequisite("Since:", formatSince(qcn));
// Inherited by.
- NodeList subs;
- QmlTypeNode::subclasses(qcn, subs);
if (!subs.isEmpty()) {
generateStartRequisite("Inherited By:");
generateSortedQmlNames(qcn, subs);
@@ -1898,10 +2336,6 @@ void DocBookGenerator::generateQmlRequisites(const QmlTypeNode *qcn)
}
// Inherits.
- QmlTypeNode *base = qcn->qmlBaseNode();
- while (base && base->isInternal()) {
- base = base->qmlBaseNode();
- }
if (base) {
const Node *otherNode = nullptr;
Atom a = Atom(Atom::LinkNode, CodeMarker::stringForNode(base));
@@ -1912,16 +2346,25 @@ void DocBookGenerator::generateQmlRequisites(const QmlTypeNode *qcn)
generateEndRequisite();
}
- // Instantiates.
+ // Native type information.
ClassNode *cn = (const_cast<QmlTypeNode *>(qcn))->classNode();
- if (cn && (cn->status() != Node::Internal)) {
- Atom a = Atom(Atom::LinkNode, CodeMarker::stringForNode(qcn));
-
- generateStartRequisite("Instantiates:");
+ if (cn && cn->isQmlNativeType() && cn->status() != Node::Internal) {
+ generateStartRequisite("In C++:");
generateSimpleLink(fullDocumentLocation(cn), cn->name());
generateEndRequisite();
}
+ // Group.
+ if (!qcn->groupNames().empty()) {
+ generateStartRequisite("Group");
+ generateGroupReferenceText(qcn);
+ generateEndRequisite();
+ }
+
+ // Status.
+ if (auto status = formatStatus(qcn, m_qdb); status)
+ generateRequisite("Status:", status.value());
+
m_writer->writeEndElement(); // variablelist
newLine();
}
@@ -1931,7 +2374,30 @@ bool DocBookGenerator::generateStatus(const Node *node)
// From Generator::generateStatus.
switch (node->status()) {
case Node::Active:
- // Do nothing.
+ // Output the module 'state' description if set.
+ if (node->isModule() || node->isQmlModule()) {
+ const QString &state = static_cast<const CollectionNode*>(node)->state();
+ if (!state.isEmpty()) {
+ m_writer->writeStartElement(dbNamespace, "para");
+ m_writer->writeCharacters("This " + typeString(node) + " is in ");
+ m_writer->writeStartElement(dbNamespace, "emphasis");
+ m_writer->writeCharacters(state);
+ m_writer->writeEndElement(); // emphasis
+ m_writer->writeCharacters(" state.");
+ m_writer->writeEndElement(); // para
+ newLine();
+ return true;
+ }
+ }
+ if (const auto version = node->deprecatedSince(); !version.isEmpty()) {
+ m_writer->writeStartElement(dbNamespace, "para");
+ m_writer->writeCharacters("This " + typeString(node)
+ + " is scheduled for deprecation in version "
+ + version + ".");
+ m_writer->writeEndElement(); // para
+ newLine();
+ return true;
+ }
return false;
case Node::Preliminary:
m_writer->writeStartElement(dbNamespace, "para");
@@ -1950,8 +2416,12 @@ bool DocBookGenerator::generateStatus(const Node *node)
m_writer->writeAttribute("role", "bold");
}
m_writer->writeCharacters("This " + typeString(node) + " is deprecated");
- if (const QString &version = node->deprecatedSince(); !version.isEmpty())
- m_writer->writeCharacters(" since " + version);
+ if (const QString &version = node->deprecatedSince(); !version.isEmpty()) {
+ m_writer->writeCharacters(" since ");
+ if (node->isQmlNode() && !node->logicalModuleName().isEmpty())
+ m_writer->writeCharacters(node->logicalModuleName() + " ");
+ m_writer->writeCharacters(version);
+ }
m_writer->writeCharacters(". We strongly advise against using it in new code.");
if (node->isAggregate())
m_writer->writeEndElement(); // emphasis
@@ -1981,7 +2451,7 @@ void DocBookGenerator::generateSignatureList(const NodeList &nodes)
m_writer->writeStartElement(dbNamespace, "para");
generateSimpleLink(currentGenerator()->fullDocumentLocation(*n),
- (*n)->signature(false, true));
+ (*n)->signature(Node::SignaturePlain));
m_writer->writeEndElement(); // para
newLine();
@@ -1995,6 +2465,40 @@ void DocBookGenerator::generateSignatureList(const NodeList &nodes)
}
/*!
+ * Return a string representing a text that exposes information about
+ * the groups that the \a node is part of.
+ */
+void DocBookGenerator::generateGroupReferenceText(const Node* node)
+{
+ // From HtmlGenerator::groupReferenceText
+
+ if (!node->isAggregate())
+ return;
+ const auto aggregate = static_cast<const Aggregate *>(node);
+
+ const QStringList &groups_names{aggregate->groupNames()};
+ if (!groups_names.empty()) {
+ m_writer->writeCharacters(aggregate->name() + " is part of ");
+ m_writer->writeStartElement(dbNamespace, "simplelist");
+
+ for (qsizetype index{0}; index < groups_names.size(); ++index) {
+ CollectionNode* group{m_qdb->groups()[groups_names[index]]};
+ m_qdb->mergeCollections(group);
+
+ m_writer->writeStartElement(dbNamespace, "member");
+ if (QString target{linkForNode(group, nullptr)}; !target.isEmpty())
+ generateSimpleLink(target, group->fullTitle());
+ else
+ m_writer->writeCharacters(group->name());
+ m_writer->writeEndElement(); // member
+ }
+
+ m_writer->writeEndElement(); // simplelist
+ newLine();
+ }
+}
+
+/*!
Generates text that explains how threadsafe and/or reentrant
\a node is.
*/
@@ -2022,8 +2526,7 @@ bool DocBookGenerator::generateThreadSafeness(const Node *node)
m_writer->writeEndElement(); // warning
return true;
- }
- if (ts == Node::Reentrant || ts == Node::ThreadSafe) {
+ } else if (ts == Node::Reentrant || ts == Node::ThreadSafe) {
m_writer->writeStartElement(dbNamespace, "note");
newLine();
m_writer->writeStartElement(dbNamespace, "para");
@@ -2096,6 +2599,7 @@ bool DocBookGenerator::generateThreadSafeness(const Node *node)
newLine();
}
m_writer->writeEndElement(); // note
+ newLine();
return true;
}
@@ -2112,7 +2616,7 @@ void DocBookGenerator::generateBody(const Node *node)
// From Generator::generateBody, without warnings.
const FunctionNode *fn = node->isFunction() ? static_cast<const FunctionNode *>(node) : nullptr;
- if (!node->hasDoc() && !node->hasSharedDoc()) {
+ if (!node->hasDoc()) {
/*
Test for special function, like a destructor or copy constructor,
that has no documentation.
@@ -2143,14 +2647,17 @@ void DocBookGenerator::generateBody(const Node *node)
if (fn && !fn->overridesThis().isEmpty())
generateReimplementsClause(fn);
else if (node->isProperty()) {
- if (static_cast<const PropertyNode *>(node)->propertyType() != PropertyNode::Standard)
+ if (static_cast<const PropertyNode *>(node)->propertyType() != PropertyNode::PropertyType::StandardProperty)
generateAddendum(node, BindableProperty, nullptr, false);
}
+
+ // Generate the body.
if (!generateText(node->doc().body(), node)) {
if (node->isMarkedReimp())
return;
}
+ // Output what is after the main body.
if (fn) {
if (fn->isQmlSignal())
generateAddendum(node, QmlSignalHandler, nullptr, true);
@@ -2165,6 +2672,7 @@ void DocBookGenerator::generateBody(const Node *node)
// Warning generation skipped with respect to Generator::generateBody.
}
+ generateEnumValuesForQmlProperty(node, nullptr);
generateRequiredLinks(node);
}
@@ -2181,7 +2689,7 @@ void DocBookGenerator::generateRequiredLinks(const Node *node)
return;
const auto en = static_cast<const ExampleNode *>(node);
- QString exampleUrl = Config::instance().getString(CONFIG_URL + Config::dot + CONFIG_EXAMPLES);
+ QString exampleUrl{Config::instance().get(CONFIG_URL + Config::dot + CONFIG_EXAMPLES).asString()};
if (exampleUrl.isEmpty()) {
if (!en->noAutoList()) {
@@ -2221,19 +2729,23 @@ void DocBookGenerator::generateLinkToExample(const ExampleNode *en, const QStrin
// Construct a path to the example; <install path>/<example name>
QStringList path = QStringList()
- << Config::instance().getString(CONFIG_EXAMPLESINSTALLPATH) << en->name();
+ << Config::instance().get(CONFIG_EXAMPLESINSTALLPATH).asString() << en->name();
path.removeAll(QString());
+ // Write the link to the example. Typically, this link comes after sections, hence
+ // wrap it in a section too.
+ startSection("Example project");
+
m_writer->writeStartElement(dbNamespace, "para");
- m_writer->writeStartElement(dbNamespace, "link");
- m_writer->writeAttribute(xlinkNamespace, "href",
- exampleUrl.replace(placeholder, path.join(separator)));
- m_writer->writeCharacters(link);
- m_writer->writeEndElement(); // link
+ generateSimpleLink(exampleUrl.replace(placeholder, path.join(separator)), link);
m_writer->writeEndElement(); // para
newLine();
+
+ endSection();
}
+// TODO: [multi-purpose-function-with-flag][generate-file-list]
+
/*!
This function is called when the documentation for an example is
being formatted. It outputs a list of files for the example, which
@@ -2243,6 +2755,15 @@ void DocBookGenerator::generateLinkToExample(const ExampleNode *en, const QStrin
*/
void DocBookGenerator::generateFileList(const ExampleNode *en, bool images)
{
+ // TODO: [possibly-stale-duplicate-code][generator-insufficient-structural-abstraction]
+ // Review and compare this code with
+ // Generator::generateFileList.
+ // Some subtle changes that might be semantically equivalent are
+ // present between the two.
+ // Supposedly, this version is to be considered stale compared to
+ // Generator's one and it might be possible to remove it in favor
+ // of that as long as the difference in output are taken into consideration.
+
// From Generator::generateFileList
QString tag;
QStringList paths;
@@ -2258,25 +2779,43 @@ void DocBookGenerator::generateFileList(const ExampleNode *en, bool images)
if (paths.isEmpty())
return;
+ startSection("", "List of Files");
+
m_writer->writeStartElement(dbNamespace, "para");
m_writer->writeCharacters(tag);
m_writer->writeEndElement(); // para
newLine();
+ startSection("List of Files");
+
m_writer->writeStartElement(dbNamespace, "itemizedlist");
+ newLine();
- for (const auto &file : qAsConst(paths)) {
- if (images) {
- if (!file.isEmpty())
- addImageToCopy(en, file);
- } else {
- generateExampleFilePage(en, file);
+ for (const auto &path : std::as_const(paths)) {
+ auto maybe_resolved_file{file_resolver.resolve(path)};
+ if (!maybe_resolved_file) {
+ // TODO: [uncentralized-admonition][failed-resolve-file]
+ QString details = std::transform_reduce(
+ file_resolver.get_search_directories().cbegin(),
+ file_resolver.get_search_directories().cend(),
+ u"Searched directories:"_s,
+ std::plus(),
+ [](const DirectoryPath &directory_path) -> QString { return u' ' + directory_path.value(); }
+ );
+
+ en->location().warning(u"Cannot find file to quote from: %1"_s.arg(path), details);
+
+ continue;
}
+ auto file{*maybe_resolved_file};
+ if (images) addImageToCopy(en, file);
+ else generateExampleFilePage(en, file);
+
m_writer->writeStartElement(dbNamespace, "listitem");
newLine();
m_writer->writeStartElement(dbNamespace, "para");
- generateSimpleLink(file, file);
+ generateSimpleLink(file.get_query(), file.get_query());
m_writer->writeEndElement(); // para
m_writer->writeEndElement(); // listitem
newLine();
@@ -2284,67 +2823,75 @@ void DocBookGenerator::generateFileList(const ExampleNode *en, bool images)
m_writer->writeEndElement(); // itemizedlist
newLine();
+
+ endSection();
}
/*!
Generate a file with the contents of a C++ or QML source file.
*/
-void DocBookGenerator::generateExampleFilePage(const Node *node, const QString &file)
+void DocBookGenerator::generateExampleFilePage(const Node *node, ResolvedFile resolved_file, CodeMarker*)
{
+ // TODO: [generator-insufficient-structural-abstraction]
+
// From HtmlGenerator::generateExampleFilePage.
if (!node->isExample())
return;
+ // TODO: Understand if this is safe.
const auto en = static_cast<const ExampleNode *>(node);
// Store current (active) writer
QXmlStreamWriter *currentWriter = m_writer;
- m_writer = startDocument(en, file);
+ m_writer = startDocument(en, resolved_file.get_query());
generateHeader(en->fullTitle(), en->subtitle(), en);
Text text;
Quoter quoter;
- Doc::quoteFromFile(en->doc().location(), quoter, file);
+ Doc::quoteFromFile(en->doc().location(), quoter, resolved_file);
QString code = quoter.quoteTo(en->location(), QString(), QString());
- CodeMarker *codeMarker = CodeMarker::markerForFileName(file);
+ CodeMarker *codeMarker = CodeMarker::markerForFileName(resolved_file.get_path());
text << Atom(codeMarker->atomType(), code);
Atom a(codeMarker->atomType(), code);
generateText(text, en);
- endDocument();
- // Restore writer
- m_writer = currentWriter;
+ endDocument(); // Delete m_writer.
+ m_writer = currentWriter; // Restore writer.
}
void DocBookGenerator::generateReimplementsClause(const FunctionNode *fn)
{
// From Generator::generateReimplementsClause, without warning generation.
- if (!fn->overridesThis().isEmpty()) {
- if (fn->parent()->isClassNode()) {
- auto cn = static_cast<ClassNode *>(fn->parent());
- const FunctionNode *overrides = cn->findOverriddenFunction(fn);
- if (overrides && !overrides->isPrivate() && !overrides->parent()->isPrivate()) {
- if (overrides->hasDoc()) {
- m_writer->writeStartElement(dbNamespace, "para");
- m_writer->writeCharacters("Reimplements: ");
- QString fullName =
- overrides->parent()->name() + "::" + overrides->signature(false, true);
- generateFullName(overrides->parent(), fullName, overrides);
- m_writer->writeCharacters(".");
- return;
- }
- }
- const PropertyNode *sameName = cn->findOverriddenProperty(fn);
- if (sameName && sameName->hasDoc()) {
- m_writer->writeStartElement(dbNamespace, "para");
- m_writer->writeCharacters("Reimplements an access function for property: ");
- QString fullName = sameName->parent()->name() + "::" + sameName->name();
- generateFullName(sameName->parent(), fullName, overrides);
- m_writer->writeCharacters(".");
- return;
- }
+ if (fn->overridesThis().isEmpty() || !fn->parent()->isClassNode())
+ return;
+
+ auto cn = static_cast<ClassNode *>(fn->parent());
+
+ if (const FunctionNode *overrides = cn->findOverriddenFunction(fn);
+ overrides && !overrides->isPrivate() && !overrides->parent()->isPrivate()) {
+ if (overrides->hasDoc()) {
+ m_writer->writeStartElement(dbNamespace, "para");
+ m_writer->writeCharacters("Reimplements: ");
+ QString fullName =
+ overrides->parent()->name() + "::" + overrides->signature(Node::SignaturePlain);
+ generateFullName(overrides->parent(), fullName, overrides);
+ m_writer->writeCharacters(".");
+ m_writer->writeEndElement(); // para
+ newLine();
+ return;
}
}
+
+ if (const PropertyNode *sameName = cn->findOverriddenProperty(fn); sameName && sameName->hasDoc()) {
+ m_writer->writeStartElement(dbNamespace, "para");
+ m_writer->writeCharacters("Reimplements an access function for property: ");
+ QString fullName = sameName->parent()->name() + "::" + sameName->name();
+ generateFullName(sameName->parent(), fullName, sameName);
+ m_writer->writeCharacters(".");
+ m_writer->writeEndElement(); // para
+ newLine();
+ return;
+ }
}
void DocBookGenerator::generateAlsoList(const Node *node)
@@ -2354,6 +2901,8 @@ void DocBookGenerator::generateAlsoList(const Node *node)
supplementAlsoList(node, alsoList);
if (!alsoList.isEmpty()) {
+ startSection("See Also");
+
m_writer->writeStartElement(dbNamespace, "para");
m_writer->writeStartElement(dbNamespace, "emphasis");
m_writer->writeCharacters("See also ");
@@ -2363,47 +2912,22 @@ void DocBookGenerator::generateAlsoList(const Node *node)
m_writer->writeStartElement(dbNamespace, "simplelist");
m_writer->writeAttribute("type", "vert");
m_writer->writeAttribute("role", "see-also");
+ newLine();
+
for (const Text &text : alsoList) {
m_writer->writeStartElement(dbNamespace, "member");
generateText(text, node);
m_writer->writeEndElement(); // member
newLine();
}
+
m_writer->writeEndElement(); // simplelist
newLine();
m_writer->writeEndElement(); // para
- }
-}
-
-/*!
- Generate a list of maintainers in the output
- */
-void DocBookGenerator::generateMaintainerList(const Aggregate *node)
-{
- // From Generator::generateMaintainerList.
- const QStringList sl = getMetadataElements(node, "maintainer");
-
- if (!sl.isEmpty()) {
- m_writer->writeStartElement(dbNamespace, "para");
- m_writer->writeStartElement(dbNamespace, "emphasis");
- m_writer->writeCharacters("Maintained by: ");
- m_writer->writeEndElement(); // emphasis
- newLine();
-
- m_writer->writeStartElement(dbNamespace, "simplelist");
- m_writer->writeAttribute("type", "vert");
- m_writer->writeAttribute("role", "maintainer");
- for (const QString &maintainer : sl) {
- m_writer->writeStartElement(dbNamespace, "member");
- m_writer->writeCharacters(maintainer);
- m_writer->writeEndElement(); // member
- newLine();
- }
- m_writer->writeEndElement(); // simplelist
newLine();
- m_writer->writeEndElement(); // para
+ endSection();
}
}
@@ -2421,26 +2945,35 @@ QXmlStreamWriter *DocBookGenerator::startGenericDocument(const Node *node, const
newLine();
m_writer->writeNamespace(dbNamespace, "db");
m_writer->writeNamespace(xlinkNamespace, "xlink");
+ if (m_useITS)
+ m_writer->writeNamespace(itsNamespace, "its");
m_writer->writeStartElement(dbNamespace, "article");
m_writer->writeAttribute("version", "5.2");
if (!m_naturalLanguage.isEmpty())
m_writer->writeAttribute("xml:lang", m_naturalLanguage);
newLine();
- // Empty the section stack for the new document.
+ // Reset the state for the new document.
sectionLevels.resize(0);
+ m_inPara = false;
+ m_inList = 0;
return m_writer;
}
QXmlStreamWriter *DocBookGenerator::startDocument(const Node *node)
{
+ m_hasSection = false;
+ refMap.clear();
+
QString fileName = Generator::fileName(node, fileExtension());
return startGenericDocument(node, fileName);
}
QXmlStreamWriter *DocBookGenerator::startDocument(const ExampleNode *en, const QString &file)
{
+ m_hasSection = false;
+
QString fileName = linkForExampleFile(file);
return startGenericDocument(en, fileName);
}
@@ -2449,7 +2982,9 @@ void DocBookGenerator::endDocument()
{
m_writer->writeEndElement(); // article
m_writer->writeEndDocument();
+
m_writer->device()->close();
+ delete m_writer->device();
delete m_writer;
m_writer = nullptr;
}
@@ -2473,9 +3008,11 @@ void DocBookGenerator::generateCppReferencePage(Node *node)
title = rawTitle + " Namespace";
} else if (aggregate->isClass()) {
rawTitle = aggregate->plainName();
- QString templateDecl = node->templateDecl();
- if (!templateDecl.isEmpty())
- fullTitle = QString("%1 %2 ").arg(templateDecl, aggregate->typeWord(false));
+
+ auto templateDecl = node->templateDecl();
+ if (templateDecl)
+ fullTitle = QString("%1 %2 ").arg((*templateDecl).to_qstring(), aggregate->typeWord(false));
+
fullTitle += aggregate->plainFullName();
title = rawTitle + QLatin1Char(' ') + aggregate->typeWord(true);
} else if (aggregate->isHeader()) {
@@ -2500,54 +3037,45 @@ void DocBookGenerator::generateCppReferencePage(Node *node)
// Actual content.
if (!aggregate->doc().isEmpty()) {
- startSection(registerRef("details"), "Detailed Description");
+ startSection("details", "Detailed Description");
generateBody(aggregate);
generateAlsoList(aggregate);
- generateMaintainerList(aggregate);
endSection();
}
Sections sections(const_cast<Aggregate *>(aggregate));
- auto *sectionVector =
+ SectionVector sectionVector =
(aggregate->isNamespace() || aggregate->isHeader()) ?
- &sections.stdDetailsSections() :
- &sections.stdCppClassDetailsSections();
- SectionVector::ConstIterator section = sectionVector->constBegin();
- while (section != sectionVector->constEnd()) {
- bool headerGenerated = false;
- NodeVector::ConstIterator member = section->members().constBegin();
- while (member != section->members().constEnd()) {
- if ((*member)->access() == Access::Private) { // ### check necessary?
- ++member;
- continue;
- }
+ sections.stdDetailsSections() :
+ sections.stdCppClassDetailsSections();
+ for (const Section &section : sectionVector) {
+ if (section.members().isEmpty())
+ continue;
- if (!headerGenerated) {
- // Equivalent to h2
- startSection(registerRef(section->title().toLower()), section->title());
- headerGenerated = true;
- }
+ startSection(section.title().toLower(), section.title());
+
+ for (const Node *member : section.members()) {
+ if (member->access() == Access::Private) // ### check necessary?
+ continue;
- if ((*member)->nodeType() != Node::Class) {
+ if (member->nodeType() != Node::Class) {
// This function starts its own section.
- generateDetailedMember(*member, aggregate);
+ generateDetailedMember(member, aggregate);
} else {
startSectionBegin();
m_writer->writeCharacters("class ");
- generateFullName(*member, aggregate);
+ generateFullName(member, aggregate);
startSectionEnd();
- generateBrief(*member);
+
+ generateBrief(member);
+
endSection();
}
-
- ++member;
}
- if (headerGenerated)
- endSection();
- ++section;
+ endSection();
}
generateObsoleteMembers(sections);
@@ -2558,7 +3086,7 @@ void DocBookGenerator::generateCppReferencePage(Node *node)
void DocBookGenerator::generateSynopsisInfo(const QString &key, const QString &value)
{
m_writer->writeStartElement(dbNamespace, "synopsisinfo");
- m_writer->writeAttribute(dbNamespace, "role", key);
+ m_writer->writeAttribute("role", key);
m_writer->writeCharacters(value);
m_writer->writeEndElement(); // synopsisinfo
newLine();
@@ -2581,14 +3109,13 @@ void DocBookGenerator::generateDocBookSynopsis(const Node *node)
// From Generator::generateStatus, HtmlGenerator::generateRequisites,
// Generator::generateThreadSafeness, QDocIndexFiles::generateIndexSection.
- // This function is the only place where DocBook extensions are used.
- if (m_config->getBool(CONFIG_DOCBOOKEXTENSIONS))
+ // This function is the major place where DocBook extensions are used.
+ if (!m_useDocBook52)
return;
// Nothing to export in some cases. Note that isSharedCommentNode() returns
// true also for QML property groups.
- if (node->isGroup() || node->isSharedCommentNode() || node->isModule() || node->isJsModule()
- || node->isQmlModule() || node->isPageNode())
+ if (node->isGroup() || node->isSharedCommentNode() || node->isModule() || node->isQmlModule() || node->isPageNode())
return;
// Cast the node to several subtypes (null pointer if the node is not of the required type).
@@ -2663,7 +3190,7 @@ void DocBookGenerator::generateDocBookSynopsis(const Node *node)
m_writer->writeTextElement(dbNamespace, "modifier", "attached");
newLine();
}
- if ((const_cast<QmlPropertyNode *>(qpn))->isWritable()) {
+ if (!(const_cast<QmlPropertyNode *>(qpn))->isReadOnly()) {
m_writer->writeTextElement(dbNamespace, "modifier", "writable");
newLine();
}
@@ -2687,7 +3214,9 @@ void DocBookGenerator::generateDocBookSynopsis(const Node *node)
if (functionNode->isStatic())
generateModifier("static");
- if (!functionNode->isMacro()) {
+ if (!functionNode->isMacro() && !functionNode->isCtor() &&
+ !functionNode->isCCtor() && !functionNode->isMCtor()
+ && !functionNode->isDtor()) {
if (functionNode->returnType() == "void")
m_writer->writeEmptyElement(dbNamespace, "void");
else
@@ -2695,20 +3224,14 @@ void DocBookGenerator::generateDocBookSynopsis(const Node *node)
newLine();
}
// Remove two characters from the plain name to only get the name
- // of the method without parentheses.
- m_writer->writeTextElement(dbNamespace, "methodname", node->plainName().chopped(2));
+ // of the method without parentheses (only for functions, not macros).
+ QString name = node->plainName();
+ if (name.endsWith("()"))
+ name.chop(2);
+ m_writer->writeTextElement(dbNamespace, "methodname", name);
newLine();
- if (functionNode->isOverload())
- generateModifier("overload");
- if (functionNode->isDefault())
- generateModifier("default");
- if (functionNode->isFinal())
- generateModifier("final");
- if (functionNode->isOverride())
- generateModifier("override");
-
- if (!functionNode->isMacro() && functionNode->parameters().isEmpty()) {
+ if (functionNode->parameters().isEmpty()) {
m_writer->writeEmptyElement(dbNamespace, "void");
newLine();
}
@@ -2730,11 +3253,60 @@ void DocBookGenerator::generateDocBookSynopsis(const Node *node)
newLine();
}
+ if (functionNode->isDefault())
+ generateModifier("default");
+ if (functionNode->isFinal())
+ generateModifier("final");
+ if (functionNode->isOverride())
+ generateModifier("override");
+ } else if (node->isTypedef()) {
+ m_writer->writeTextElement(dbNamespace, "typedefname", node->plainName());
+ newLine();
+ } else {
+ node->doc().location().warning(
+ QStringLiteral("Unexpected node type in generateDocBookSynopsis: %1")
+ .arg(node->nodeTypeString()));
+ newLine();
+ }
+
+ // Enums and typedefs.
+ if (enumNode) {
+ for (const EnumItem &item : enumNode->items()) {
+ m_writer->writeStartElement(dbNamespace, "enumitem");
+ newLine();
+ m_writer->writeTextElement(dbNamespace, "enumidentifier", item.name());
+ newLine();
+ m_writer->writeTextElement(dbNamespace, "enumvalue", item.value());
+ newLine();
+ m_writer->writeEndElement(); // enumitem
+ newLine();
+ }
+
+ if (enumNode->items().isEmpty()) {
+ // If the enumeration is empty (really rare case), still produce
+ // something for the DocBook document to be valid.
+ m_writer->writeStartElement(dbNamespace, "enumitem");
+ newLine();
+ m_writer->writeEmptyElement(dbNamespace, "enumidentifier");
+ newLine();
+ m_writer->writeEndElement(); // enumitem
+ newLine();
+ }
+ }
+
+ // Below: only synopsisinfo within synopsisTag. These elements must be at
+ // the end of the tag, as per DocBook grammar.
+
+ // Information for functions that could not be output previously
+ // (synopsisinfo).
+ if (node->isFunction()) {
generateSynopsisInfo("meta", functionNode->metanessString());
- if (functionNode->isOverload())
+ if (functionNode->isOverload()) {
+ generateSynopsisInfo("overload", "overload");
generateSynopsisInfo("overload-number",
QString::number(functionNode->overloadNumber()));
+ }
if (functionNode->isRef())
generateSynopsisInfo("refness", QString::number(1));
@@ -2743,7 +3315,7 @@ void DocBookGenerator::generateDocBookSynopsis(const Node *node)
if (functionNode->hasAssociatedProperties()) {
QStringList associatedProperties;
- const NodeList &nodes = functionNode->associatedProperties();
+ const auto &nodes = functionNode->associatedProperties();
for (const Node *n : nodes) {
const auto pn = static_cast<const PropertyNode *>(n);
associatedProperties << pn->name();
@@ -2753,7 +3325,7 @@ void DocBookGenerator::generateDocBookSynopsis(const Node *node)
associatedProperties.join(QLatin1Char(',')));
}
- QString signature = functionNode->signature(false, false);
+ QString signature = functionNode->signature(Node::SignatureReturnType);
// 'const' is already part of FunctionNode::signature()
if (functionNode->isFinal())
signature += " final";
@@ -2764,13 +3336,6 @@ void DocBookGenerator::generateDocBookSynopsis(const Node *node)
else if (functionNode->isDefault())
signature += " = default";
generateSynopsisInfo("signature", signature);
- } else if (node->isTypedef()) {
- m_writer->writeTextElement(dbNamespace, "type", node->plainName());
- } else {
- node->doc().location().warning(
- QStringLiteral("Unexpected node type in generateDocBookSynopsis: %1")
- .arg(node->nodeTypeString()));
- newLine();
}
// Accessibility status.
@@ -2814,10 +3379,7 @@ void DocBookGenerator::generateDocBookSynopsis(const Node *node)
// C++ classes and name spaces.
if (aggregate) {
// Includes.
- if (!aggregate->includeFiles().isEmpty()) {
- for (const QString &include : aggregate->includeFiles())
- generateSynopsisInfo("headers", include);
- }
+ if (aggregate->includeFile()) generateSynopsisInfo("headers", *aggregate->includeFile());
// Since and project.
if (!aggregate->since().isEmpty())
@@ -2831,7 +3393,7 @@ void DocBookGenerator::generateDocBookSynopsis(const Node *node)
if (cn && !cn->qtCMakeComponent().isEmpty()) {
const QString qtComponent = "Qt" + QString::number(QT_VERSION_MAJOR);
const QString findpackageText = "find_package(" + qtComponent
- + " COMPONENTS " + cn->qtCMakeComponent() + " REQUIRED)";
+ + " REQUIRED COMPONENTS " + cn->qtCMakeComponent() + ")";
const QString targetLinkLibrariesText =
"target_link_libraries(mytarget PRIVATE " + qtComponent + "::" + cn->qtCMakeComponent()
+ ")";
@@ -2844,25 +3406,30 @@ void DocBookGenerator::generateDocBookSynopsis(const Node *node)
}
if (aggregate->nodeType() == Node::Class) {
- // Instantiated by.
+ // Native type
auto *classe = const_cast<ClassNode *>(static_cast<const ClassNode *>(aggregate));
- if (classe->qmlElement() != nullptr && classe->status() != Node::Internal) {
- const Node *otherNode = nullptr;
- Atom a = Atom(Atom::LinkNode, CodeMarker::stringForNode(classe->qmlElement()));
- QString link = getAutoLink(&a, aggregate, &otherNode);
-
+ if (classe && classe->isQmlNativeType() && classe->status() != Node::Internal) {
m_writer->writeStartElement(dbNamespace, "synopsisinfo");
- m_writer->writeAttribute(dbNamespace, "role", "instantiatedBy");
- generateSimpleLink(link, classe->qmlElement()->name());
+ m_writer->writeAttribute("role", "nativeTypeFor");
+
+ QList<QmlTypeNode *> nativeTypes { classe->qmlNativeTypes().cbegin(), classe->qmlNativeTypes().cend()};
+ std::sort(nativeTypes.begin(), nativeTypes.end(), Node::nodeNameLessThan);
+
+ for (auto item : std::as_const(nativeTypes)) {
+ const Node *otherNode{nullptr};
+ Atom a = Atom(Atom::LinkNode, CodeMarker::stringForNode(item));
+ const QString &link = getAutoLink(&a, aggregate, &otherNode);
+ generateSimpleLink(link, item->name());
+ }
+
m_writer->writeEndElement(); // synopsisinfo
- newLine();
}
// Inherits.
QList<RelatedClass>::ConstIterator r;
if (!classe->baseClasses().isEmpty()) {
m_writer->writeStartElement(dbNamespace, "synopsisinfo");
- m_writer->writeAttribute(dbNamespace, "role", "inherits");
+ m_writer->writeAttribute("role", "inherits");
r = classe->baseClasses().constBegin();
int index = 0;
@@ -2876,7 +3443,7 @@ void DocBookGenerator::generateDocBookSynopsis(const Node *node)
m_writer->writeCharacters(" (private)");
}
m_writer->writeCharacters(
- Utilities::comma(index++, classe->baseClasses().count()));
+ Utilities::comma(index++, classe->baseClasses().size()));
}
++r;
}
@@ -2888,7 +3455,7 @@ void DocBookGenerator::generateDocBookSynopsis(const Node *node)
// Inherited by.
if (!classe->derivedClasses().isEmpty()) {
m_writer->writeStartElement(dbNamespace, "synopsisinfo");
- m_writer->writeAttribute(dbNamespace, "role", "inheritedBy");
+ m_writer->writeAttribute("role", "inheritedBy");
generateSortedNames(classe, classe->derivedClasses());
m_writer->writeEndElement(); // synopsisinfo
newLine();
@@ -2922,7 +3489,7 @@ void DocBookGenerator::generateDocBookSynopsis(const Node *node)
QmlTypeNode::subclasses(qcn, subs);
if (!subs.isEmpty()) {
m_writer->writeTextElement(dbNamespace, "synopsisinfo");
- m_writer->writeAttribute(dbNamespace, "role", "inheritedBy");
+ m_writer->writeAttribute("role", "inheritedBy");
generateSortedQmlNames(qcn, subs);
m_writer->writeEndElement(); // synopsisinfo
newLine();
@@ -2938,21 +3505,22 @@ void DocBookGenerator::generateDocBookSynopsis(const Node *node)
QString link = getAutoLink(&a, base, &otherNode);
m_writer->writeTextElement(dbNamespace, "synopsisinfo");
- m_writer->writeAttribute(dbNamespace, "role", "inherits");
+ m_writer->writeAttribute("role", "inherits");
generateSimpleLink(link, base->name());
m_writer->writeEndElement(); // synopsisinfo
newLine();
}
- // Instantiates.
+ // Native type
ClassNode *cn = (const_cast<QmlTypeNode *>(qcn))->classNode();
- if (cn && (cn->status() != Node::Internal)) {
+
+ if (cn && cn->isQmlNativeType() && (cn->status() != Node::Internal)) {
const Node *otherNode = nullptr;
Atom a = Atom(Atom::LinkNode, CodeMarker::stringForNode(qcn));
QString link = getAutoLink(&a, cn, &otherNode);
m_writer->writeTextElement(dbNamespace, "synopsisinfo");
- m_writer->writeAttribute(dbNamespace, "role", "instantiates");
+ m_writer->writeAttribute("role", "nativeType");
generateSimpleLink(link, cn->name());
m_writer->writeEndElement(); // synopsisinfo
newLine();
@@ -3017,27 +3585,18 @@ void DocBookGenerator::generateDocBookSynopsis(const Node *node)
}
}
- // Enums and typedefs.
- if (enumNode) {
- for (const EnumItem &item : enumNode->items()) {
- m_writer->writeStartElement(dbNamespace, "enumitem");
- m_writer->writeAttribute(dbNamespace, "enumidentifier", item.name());
- m_writer->writeAttribute(dbNamespace, "enumvalue", item.value());
- m_writer->writeEndElement(); // enumitem
- newLine();
- }
- }
-
m_writer->writeEndElement(); // nodeToSynopsisTag (like classsynopsis)
newLine();
- // The typedef associated to this enum.
+ // The typedef associated to this enum. It is output *after* the main tag,
+ // i.e. it must be after the synopsisinfo.
if (enumNode && enumNode->flagsType()) {
m_writer->writeStartElement(dbNamespace, "typedefsynopsis");
newLine();
m_writer->writeTextElement(dbNamespace, "typedefname",
enumNode->flagsType()->fullDocumentName());
+ newLine();
m_writer->writeEndElement(); // typedefsynopsis
newLine();
@@ -3088,7 +3647,7 @@ void DocBookGenerator::typified(const QString &string, const Node *relative, boo
// Add the link, logic from HtmlGenerator::highlightedCode.
const Node *n = m_qdb->findTypeNode(pendingWord, relative, Node::DontCare);
QString href;
- if (!(n && (n->isQmlBasicType() || n->isJsBasicType()))
+ if (!(n && n->isQmlBasicType())
|| (relative
&& (relative->genus() == n->genus() || Node::DontCare == n->genus()))) {
href = linkForNode(n, relative);
@@ -3149,21 +3708,10 @@ void DocBookGenerator::generateParameter(const Parameter &parameter, const Node
} else {
paramName = ptype;
}
- if (generateExtra || pname.isEmpty()) {
- // Look for the _ character in the member name followed by a number (or n):
- // this is intended to be rendered as a subscript.
- QRegularExpression sub("([a-z]+)_([0-9]+|n)");
+ if (generateExtra || pname.isEmpty()) {
m_writer->writeStartElement(dbNamespace, "emphasis");
- auto match = sub.match(paramName);
- if (match.hasMatch()) {
- m_writer->writeCharacters(match.captured(0));
- m_writer->writeStartElement(dbNamespace, "sub");
- m_writer->writeCharacters(match.captured(1));
- m_writer->writeEndElement(); // sub
- } else {
- m_writer->writeCharacters(paramName);
- }
+ m_writer->writeCharacters(paramName);
m_writer->writeEndElement(); // emphasis
}
@@ -3183,25 +3731,29 @@ void DocBookGenerator::generateSynopsis(const Node *node, const Node *relative,
// From CppCodeMarker::markedUpSynopsis, reversed the generation of "extra" and "synopsis".
const int MaxEnumValues = 6;
- if (generateExtra)
- m_writer->writeCharacters(CodeMarker::extraSynopsis(node, style));
+ if (generateExtra) {
+ if (auto extra = CodeMarker::extraSynopsis(node, style); !extra.isEmpty())
+ m_writer->writeCharacters(extra + " ");
+ }
// Then generate the synopsis.
+ QString namePrefix {};
if (style == Section::Details) {
if (!node->isRelatedNonmember() && !node->isProxyNode() && !node->parent()->name().isEmpty()
- && !node->parent()->isHeader() && !node->isProperty() && !node->isQmlNode()
- && !node->isJsNode()) {
- m_writer->writeCharacters(taggedNode(node->parent()) + "::");
+ && !node->parent()->isHeader() && !node->isProperty() && !node->isQmlNode()) {
+ namePrefix = taggedNode(node->parent()) + "::";
}
}
switch (node->nodeType()) {
case Node::Namespace:
m_writer->writeCharacters("namespace ");
+ m_writer->writeCharacters(namePrefix);
generateSynopsisName(node, relative, generateNameLink);
break;
case Node::Class:
m_writer->writeCharacters("class ");
+ m_writer->writeCharacters(namePrefix);
generateSynopsisName(node, relative, generateNameLink);
break;
case Node::Function: {
@@ -3216,6 +3768,7 @@ void DocBookGenerator::generateSynopsis(const Node *node, const Node *relative,
// Name and parameters.
if (style != Section::AllMembers && !func->returnType().isEmpty())
typified(func->returnType(), relative, true, generateType);
+ m_writer->writeCharacters(namePrefix);
generateSynopsisName(node, relative, generateNameLink);
if (!func->isMacroWithoutParams()) {
@@ -3230,6 +3783,7 @@ void DocBookGenerator::generateSynopsis(const Node *node, const Node *relative,
}
m_writer->writeCharacters(QStringLiteral(")"));
}
+
if (func->isConst())
m_writer->writeCharacters(QStringLiteral(" const"));
@@ -3264,6 +3818,7 @@ void DocBookGenerator::generateSynopsis(const Node *node, const Node *relative,
case Node::Enum: {
const auto enume = static_cast<const EnumNode *>(node);
m_writer->writeCharacters(QStringLiteral("enum "));
+ m_writer->writeCharacters(namePrefix);
generateSynopsisName(node, relative, generateNameLink);
QString synopsis;
@@ -3297,19 +3852,22 @@ void DocBookGenerator::generateSynopsis(const Node *node, const Node *relative,
} break;
case Node::TypeAlias: {
if (style == Section::Details) {
- QString templateDecl = node->templateDecl();
- if (!templateDecl.isEmpty())
- m_writer->writeCharacters(templateDecl + QLatin1Char(' '));
+ auto templateDecl = node->templateDecl();
+ if (templateDecl)
+ m_writer->writeCharacters((*templateDecl).to_qstring() + QLatin1Char(' '));
}
+ m_writer->writeCharacters(namePrefix);
generateSynopsisName(node, relative, generateNameLink);
} break;
case Node::Typedef: {
if (static_cast<const TypedefNode *>(node)->associatedEnum())
m_writer->writeCharacters("flags ");
+ m_writer->writeCharacters(namePrefix);
generateSynopsisName(node, relative, generateNameLink);
} break;
case Node::Property: {
const auto property = static_cast<const PropertyNode *>(node);
+ m_writer->writeCharacters(namePrefix);
generateSynopsisName(node, relative, generateNameLink);
m_writer->writeCharacters(" : ");
typified(property->qualifiedDataType(), relative, false, generateType);
@@ -3323,11 +3881,13 @@ void DocBookGenerator::generateSynopsis(const Node *node, const Node *relative,
} else {
typified(variable->leftType(), relative, false, generateType);
m_writer->writeCharacters(" ");
+ m_writer->writeCharacters(namePrefix);
generateSynopsisName(node, relative, generateNameLink);
m_writer->writeCharacters(variable->rightType());
}
} break;
default:
+ m_writer->writeCharacters(namePrefix);
generateSynopsisName(node, relative, generateNameLink);
}
}
@@ -3337,13 +3897,22 @@ void DocBookGenerator::generateEnumValue(const QString &enumValue, const Node *r
// From CppCodeMarker::markedUpEnumValue, simplifications from Generator::plainCode (removing
// <@op>). With respect to CppCodeMarker::markedUpEnumValue, the order of generation of parents
// must be reversed so that they are processed in the order
+ const auto *node = relative->parent();
+
+ if (relative->isQmlProperty()) {
+ const auto *qpn = static_cast<const QmlPropertyNode*>(relative);
+ if (qpn->enumNode() && !enumValue.startsWith("%1."_L1.arg(qpn->enumPrefix()))) {
+ m_writer->writeCharacters("%1.%2"_L1.arg(qpn->enumPrefix(), enumValue));
+ return;
+ }
+ }
+
if (!relative->isEnumType()) {
m_writer->writeCharacters(enumValue);
return;
}
QList<const Node *> parents;
- const Node *node = relative->parent();
while (!node->isHeader() && node->parent()) {
parents.prepend(node);
if (node->parent() == relative || node->parent()->name().isEmpty())
@@ -3401,7 +3970,7 @@ void DocBookGenerator::generateOverloadedSignal(const Node *node)
void DocBookGenerator::generateAddendum(const Node *node, Addendum type, CodeMarker *marker,
bool generateNote)
{
- Q_UNUSED(marker);
+ Q_UNUSED(marker)
Q_ASSERT(node && !node->name().isEmpty());
if (generateNote) {
m_writer->writeStartElement(dbNamespace, "note");
@@ -3442,32 +4011,35 @@ void DocBookGenerator::generateAddendum(const Node *node, Addendum type, CodeMar
if (!node->isFunction())
return;
const auto *fn = static_cast<const FunctionNode *>(node);
- NodeList nodes = fn->associatedProperties();
- if (nodes.isEmpty())
+ auto propertyNodes = fn->associatedProperties();
+ if (propertyNodes.isEmpty())
return;
- std::sort(nodes.begin(), nodes.end(), Node::nodeNameLessThan);
- for (const auto node : qAsConst(nodes)) {
+ std::sort(propertyNodes.begin(), propertyNodes.end(), Node::nodeNameLessThan);
+ for (const auto propertyNode : std::as_const(propertyNodes)) {
QString msg;
- const auto pn = static_cast<const PropertyNode *>(node);
+ const auto pn = static_cast<const PropertyNode *>(propertyNode);
switch (pn->role(fn)) {
- case PropertyNode::Getter:
+ case PropertyNode::FunctionRole::Getter:
msg = QStringLiteral("Getter function");
break;
- case PropertyNode::Setter:
+ case PropertyNode::FunctionRole::Setter:
msg = QStringLiteral("Setter function");
break;
- case PropertyNode::Resetter:
+ case PropertyNode::FunctionRole::Resetter:
msg = QStringLiteral("Resetter function");
break;
- case PropertyNode::Notifier:
+ case PropertyNode::FunctionRole::Notifier:
msg = QStringLiteral("Notifier signal");
break;
default:
continue;
}
+ m_writer->writeStartElement(dbNamespace, "para");
m_writer->writeCharacters(msg + " for property ");
generateSimpleLink(linkForNode(pn, nullptr), pn->name());
m_writer->writeCharacters(". ");
+ m_writer->writeEndElement(); // para
+ newLine();
}
break;
}
@@ -3497,71 +4069,78 @@ void DocBookGenerator::generateAddendum(const Node *node, Addendum type, CodeMar
void DocBookGenerator::generateDetailedMember(const Node *node, const PageNode *relative)
{
// From HtmlGenerator::generateDetailedMember.
- m_writer->writeStartElement(dbNamespace, "section");
+ bool closeSupplementarySection = false;
+
if (node->isSharedCommentNode()) {
- const auto scn = reinterpret_cast<const SharedCommentNode *>(node);
+ const auto *scn = reinterpret_cast<const SharedCommentNode *>(node);
const QList<Node *> &collective = scn->collective();
bool firstFunction = true;
- for (const Node *n : collective) {
- if (n->isFunction()) {
- QString nodeRef = refForNode(n);
+ for (const auto *sharedNode : collective) {
+ if (firstFunction) {
+ startSectionBegin(sharedNode);
+ } else {
+ m_writer->writeStartElement(dbNamespace, "bridgehead");
+ m_writer->writeAttribute("renderas", "sect2");
+ writeXmlId(sharedNode);
+ }
+ if (m_useITS)
+ m_writer->writeAttribute(itsNamespace, "translate", "no");
- if (firstFunction) {
- m_writer->writeAttribute("xml:id", refForNode(collective.at(0)));
- newLine();
- m_writer->writeStartElement(dbNamespace, "title");
- generateSynopsis(n, relative, Section::Details);
- m_writer->writeEndElement(); // title
- newLine();
+ generateSynopsis(sharedNode, relative, Section::Details);
- firstFunction = false;
- } else {
- m_writer->writeStartElement(dbNamespace, "bridgehead");
- m_writer->writeAttribute("renderas", "sect2");
- m_writer->writeAttribute("xml:id", nodeRef);
- generateSynopsis(n, relative, Section::Details);
- m_writer->writeEndElement(); // bridgehead
- newLine();
- }
+ if (firstFunction) {
+ startSectionEnd();
+ firstFunction = false;
+ } else {
+ m_writer->writeEndElement(); // bridgehead
+ newLine();
}
}
} else {
const EnumNode *etn;
- QString nodeRef = refForNode(node);
if (node->isEnumType() && (etn = static_cast<const EnumNode *>(node))->flagsType()) {
- m_writer->writeAttribute("xml:id", nodeRef);
- newLine();
- m_writer->writeStartElement(dbNamespace, "title");
+ startSectionBegin(node);
+ if (m_useITS)
+ m_writer->writeAttribute(itsNamespace, "translate", "no");
generateSynopsis(etn, relative, Section::Details);
- m_writer->writeEndElement(); // title
- newLine();
+ startSectionEnd();
+
m_writer->writeStartElement(dbNamespace, "bridgehead");
+ m_writer->writeAttribute("renderas", "sect2");
generateSynopsis(etn->flagsType(), relative, Section::Details);
m_writer->writeEndElement(); // bridgehead
newLine();
} else {
- m_writer->writeAttribute("xml:id", nodeRef);
- newLine();
- m_writer->writeStartElement(dbNamespace, "title");
+ startSectionBegin(node);
+ if (m_useITS)
+ m_writer->writeAttribute(itsNamespace, "translate", "no");
generateSynopsis(node, relative, Section::Details);
- m_writer->writeEndElement(); // title
- newLine();
+ startSectionEnd();
}
}
+ Q_ASSERT(m_hasSection);
generateDocBookSynopsis(node);
generateStatus(node);
generateBody(node);
+
+ // If the body ends with a section, the rest of the description must be wrapped in a section too.
+ if (node->hasDoc() && node->doc().body().firstAtom() && node->doc().body().lastAtom()->type() == Atom::SectionRight) {
+ closeSupplementarySection = true;
+ startSection("", "Notes");
+ }
+
generateOverloadedSignal(node);
+ generateComparisonCategory(node);
generateThreadSafeness(node);
generateSince(node);
if (node->isProperty()) {
const auto property = static_cast<const PropertyNode *>(node);
- if (property->propertyType() == PropertyNode::Standard) {
- Section section(Section::Accessors, Section::Active);
+ if (property->propertyType() == PropertyNode::PropertyType::StandardProperty) {
+ Section section("", "", "", "", Section::Accessors);
section.appendMembers(property->getters().toVector());
section.appendMembers(property->setters().toVector());
@@ -3581,7 +4160,7 @@ void DocBookGenerator::generateDetailedMember(const Node *node, const PageNode *
generateSectionList(section, node);
}
- Section notifiers(Section::Accessors, Section::Active);
+ Section notifiers("", "", "", "", Section::Accessors);
notifiers.appendMembers(property->notifiers().toVector());
if (!notifiers.members().isEmpty()) {
@@ -3609,32 +4188,50 @@ void DocBookGenerator::generateDetailedMember(const Node *node, const PageNode *
if (en->flagsType()) {
m_writer->writeStartElement(dbNamespace, "para");
- m_writer->writeCharacters("The " + en->flagsType()->name() + " type is a typedef for ");
+ m_writer->writeCharacters("The ");
+ m_writer->writeStartElement(dbNamespace, "code");
+ m_writer->writeCharacters(en->flagsType()->name());
+ m_writer->writeEndElement(); // code
+ m_writer->writeCharacters(" type is a typedef for ");
+ m_writer->writeStartElement(dbNamespace, "code");
generateSimpleLink(m_qflagsHref, "QFlags");
- m_writer->writeCharacters("&lt;" + en->name() + "&gt;. ");
- m_writer->writeCharacters("It stores an OR combination of " + en->name() + "values.");
+ m_writer->writeCharacters("<" + en->name() + ">. ");
+ m_writer->writeEndElement(); // code
+ m_writer->writeCharacters("It stores an OR combination of ");
+ m_writer->writeStartElement(dbNamespace, "code");
+ m_writer->writeCharacters(en->name());
+ m_writer->writeEndElement(); // code
+ m_writer->writeCharacters(" values.");
m_writer->writeEndElement(); // para
newLine();
}
}
+
+ if (closeSupplementarySection)
+ endSection();
+
+ // The list of linked pages is always in its own section.
generateAlsoList(node);
+
+ // Close the section for this member.
endSection(); // section
}
void DocBookGenerator::generateSectionList(const Section &section, const Node *relative,
- Section::Status status)
+ bool useObsoleteMembers)
{
// From HtmlGenerator::generateSectionList, just generating a list (not tables).
const NodeVector &members =
- (status == Section::Obsolete ? section.obsoleteMembers() : section.members());
+ (useObsoleteMembers ? section.obsoleteMembers() : section.members());
if (!members.isEmpty()) {
bool hasPrivateSignals = false;
bool isInvokable = false;
m_writer->writeStartElement(dbNamespace, "itemizedlist");
+ if (m_useITS)
+ m_writer->writeAttribute(itsNamespace, "translate", "no");
newLine();
- int i = 0;
NodeVector::ConstIterator m = members.constBegin();
while (m != members.constEnd()) {
if ((*m)->access() == Access::Private) {
@@ -3661,7 +4258,6 @@ void DocBookGenerator::generateSectionList(const Section &section, const Node *r
m_writer->writeEndElement(); // listitem
newLine();
- i++;
++m;
}
@@ -3674,9 +4270,11 @@ void DocBookGenerator::generateSectionList(const Section &section, const Node *r
generateAddendum(relative, Generator::Invokable, nullptr, true);
}
- if (status != Section::Obsolete && section.style() == Section::Summary
+ if (!useObsoleteMembers && section.style() == Section::Summary
&& !section.inheritedMembers().isEmpty()) {
m_writer->writeStartElement(dbNamespace, "itemizedlist");
+ if (m_useITS)
+ m_writer->writeAttribute(itsNamespace, "translate", "no");
newLine();
generateSectionInheritedList(section, relative);
@@ -3689,7 +4287,7 @@ void DocBookGenerator::generateSectionList(const Section &section, const Node *r
void DocBookGenerator::generateSectionInheritedList(const Section &section, const Node *relative)
{
// From HtmlGenerator::generateSectionInheritedList.
- QList<QPair<Aggregate *, int>>::ConstIterator p = section.inheritedMembers().constBegin();
+ QList<std::pair<Aggregate *, int>>::ConstIterator p = section.inheritedMembers().constBegin();
while (p != section.inheritedMembers().constEnd()) {
m_writer->writeStartElement(dbNamespace, "listitem");
m_writer->writeCharacters(QString::number((*p).second) + u' ');
@@ -3707,12 +4305,12 @@ void DocBookGenerator::generateSectionInheritedList(const Section &section, cons
/*!
Generate the DocBook page for an entity that doesn't map
- to any underlying parsable C++, QML, or Javascript element.
+ to any underlying parsable C++ or QML element.
*/
void DocBookGenerator::generatePageNode(PageNode *pn)
{
- Q_ASSERT(m_writer == nullptr);
// From HtmlGenerator::generatePageNode, remove anything related to TOCs.
+ Q_ASSERT(m_writer == nullptr);
m_writer = startDocument(pn);
generateHeader(pn->fullTitle(), pn->subtitle(), pn);
@@ -3724,34 +4322,6 @@ void DocBookGenerator::generatePageNode(PageNode *pn)
}
/*!
- Extract sections of markup text and output them.
- */
-bool DocBookGenerator::generateQmlText(const Text &text, const Node *relative)
-{
- // From Generator::generateQmlText.
- const Atom *atom = text.firstAtom();
- bool result = false;
-
- if (atom != nullptr) {
- initializeTextOutput();
- while (atom) {
- if (atom->type() != Atom::QmlText)
- atom = atom->next();
- else {
- atom = atom->next();
- while (atom && (atom->type() != Atom::EndQmlText)) {
- int n = 1 + generateAtom(atom, relative);
- while (n-- > 0)
- atom = atom->next();
- }
- }
- }
- result = true;
- }
- return result;
-}
-
-/*!
Generate the DocBook page for a QML type. \qcn is the QML type.
*/
void DocBookGenerator::generateQmlTypePage(QmlTypeNode *qcn)
@@ -3763,20 +4333,22 @@ void DocBookGenerator::generateQmlTypePage(QmlTypeNode *qcn)
Generator::setQmlTypeContext(qcn);
QString title = qcn->fullTitle();
- if (qcn->isJsType())
- title += " JavaScript Type";
+ if (qcn->isQmlBasicType())
+ title.append(" QML Value Type");
else
- title += " QML Type";
+ title.append(" QML Type");
+ // TODO: for ITS attribute, only apply translate="no" on qcn->fullTitle(),
+ // not its suffix (which should be translated). generateHeader doesn't
+ // allow this kind of input, the title isn't supposed to be structured.
+ // Ideally, do the same in HTML.
generateHeader(title, qcn->subtitle(), qcn);
generateQmlRequisites(qcn);
+ generateStatus(qcn);
- startSection(registerRef("details"), "Detailed Description");
+ startSection("details", "Detailed Description");
generateBody(qcn);
- ClassNode *cn = qcn->classNode();
- if (cn)
- generateQmlText(cn->doc().body(), cn);
generateAlsoList(qcn);
endSection();
@@ -3784,7 +4356,7 @@ void DocBookGenerator::generateQmlTypePage(QmlTypeNode *qcn)
Sections sections(qcn);
for (const auto &section : sections.stdQmlTypeDetailsSections()) {
if (!section.isEmpty()) {
- startSection(registerRef(section.title().toLower()), section.title());
+ startSection(section.title().toLower(), section.title());
for (const auto &member : section.members())
generateDetailedQmlMember(member, qcn);
@@ -3802,53 +4374,6 @@ void DocBookGenerator::generateQmlTypePage(QmlTypeNode *qcn)
}
/*!
- Generate the DocBook page for the QML basic type represented
- by the QML basic type node \a qbtn.
- */
-void DocBookGenerator::generateQmlBasicTypePage(QmlBasicTypeNode *qbtn)
-{
- // From HtmlGenerator::generateQmlBasicTypePage.
- // Start producing the DocBook file.
- Q_ASSERT(m_writer == nullptr);
- m_writer = startDocument(qbtn);
-
- QString htmlTitle = qbtn->fullTitle();
- if (qbtn->isJsType())
- htmlTitle += " JavaScript Basic Type";
- else
- htmlTitle += " QML Basic Type";
-
- Sections sections(qbtn);
- generateHeader(htmlTitle, qbtn->subtitle(), qbtn);
-
- startSection(registerRef("details"), "Detailed Description");
-
- generateBody(qbtn);
- generateAlsoList(qbtn);
-
- endSection();
-
- SectionVector::ConstIterator s = sections.stdQmlTypeDetailsSections().constBegin();
- while (s != sections.stdQmlTypeDetailsSections().constEnd()) {
- if (!s->isEmpty()) {
- startSection(registerRef(s->title().toLower()), s->title());
-
- NodeVector::ConstIterator m = s->members().constBegin();
- while (m != s->members().constEnd()) {
- generateDetailedQmlMember(*m, qbtn);
- ++m;
- }
-
- endSection();
- }
- ++s;
- }
- generateFooter();
-
- endDocument();
-}
-
-/*!
Outputs the DocBook detailed documentation for a section
on a QML element reference page.
*/
@@ -3856,29 +4381,10 @@ void DocBookGenerator::generateDetailedQmlMember(Node *node, const Aggregate *re
{
// From HtmlGenerator::generateDetailedQmlMember, with elements from
// CppCodeMarker::markedUpQmlItem and HtmlGenerator::generateQmlItem.
- std::function<QString(QmlPropertyNode *)> getQmlPropertyTitle = [&](QmlPropertyNode *n) {
- if (!n->isReadOnlySet() && n->declarativeCppNode())
- n->markReadOnly(!n->isWritable());
-
- QString title;
- QStringList extra;
- if (n->isDefault())
- extra << "default";
- else if (n->isReadOnly())
- extra << "read-only";
- else if (n->isRequired())
- extra << "required";
- else if (!n->defaultValue().isEmpty())
- extra << "default: " + n->defaultValue();
-
- if (!n->since().isEmpty()) {
- if (!extra.isEmpty())
- extra.last().append(',');
- extra << "since " + n->since();
- }
- if (!extra.isEmpty())
- title = QString("[%1] ").arg(extra.join(QLatin1Char(' ')));
-
+ auto getQmlPropertyTitle = [&](QmlPropertyNode *n) {
+ QString title{CodeMarker::extraSynopsis(n, Section::Details)};
+ if (!title.isEmpty())
+ title += ' '_L1;
// Finalise generation of name, as per CppCodeMarker::markedUpQmlItem.
if (n->isAttached())
title += n->element() + QLatin1Char('.');
@@ -3887,32 +4393,31 @@ void DocBookGenerator::generateDetailedQmlMember(Node *node, const Aggregate *re
return title;
};
- std::function<void(Node *)> generateQmlMethodTitle = [&](Node *node) {
+ auto generateQmlMethodTitle = [&](Node *node) {
generateSynopsis(node, relative, Section::Details);
};
- bool generateEndSection = true;
-
if (node->isPropertyGroup()) {
- const auto scn = static_cast<const SharedCommentNode *>(node);
+ const auto *scn = static_cast<const SharedCommentNode *>(node);
QString heading;
if (!scn->name().isEmpty())
heading = scn->name() + " group";
else
heading = node->name();
- startSection(refForNode(scn), heading);
+ startSection(scn, heading);
// This last call creates a title for this section. In other words,
- // titles are forbidden for the rest of the section.
+ // titles are forbidden for the rest of the section, hence the use of
+ // bridgehead.
const QList<Node *> sharedNodes = scn->collective();
- for (const auto &node : sharedNodes) {
- if (node->isQmlProperty() || node->isJsProperty()) {
- auto *qpn = static_cast<QmlPropertyNode *>(node);
+ for (const auto &sharedNode : sharedNodes) {
+ if (sharedNode->isQmlProperty()) {
+ auto *qpn = static_cast<QmlPropertyNode *>(sharedNode);
m_writer->writeStartElement(dbNamespace, "bridgehead");
m_writer->writeAttribute("renderas", "sect2");
- m_writer->writeAttribute("xml:id", refForNode(qpn));
+ writeXmlId(qpn);
m_writer->writeCharacters(getQmlPropertyTitle(qpn));
m_writer->writeEndElement(); // bridgehead
newLine();
@@ -3920,9 +4425,9 @@ void DocBookGenerator::generateDetailedQmlMember(Node *node, const Aggregate *re
generateDocBookSynopsis(qpn);
}
}
- } else if (node->isQmlProperty() || node->isJsProperty()) {
+ } else if (node->isQmlProperty()) {
auto qpn = static_cast<QmlPropertyNode *>(node);
- startSection(refForNode(qpn), getQmlPropertyTitle(qpn));
+ startSection(qpn, getQmlPropertyTitle(qpn));
generateDocBookSynopsis(qpn);
} else if (node->isSharedCommentNode()) {
const auto scn = reinterpret_cast<const SharedCommentNode *>(node);
@@ -3931,41 +4436,50 @@ void DocBookGenerator::generateDetailedQmlMember(Node *node, const Aggregate *re
// In the section, generate a title for the first node, then bridgeheads for
// the next ones.
int i = 0;
- for (const auto m : sharedNodes) {
+ for (const auto &sharedNode : sharedNodes) {
// Ignore this element if there is nothing to generate.
- if (!node->isFunction(Node::QML) && !node->isFunction(Node::JS)
- && !node->isQmlProperty() && !node->isJsProperty()) {
+ if (!sharedNode->isFunction(Node::QML) && !sharedNode->isQmlProperty()) {
continue;
}
- // Complete the section tag.
- if (i == 0) {
- m_writer->writeStartElement(dbNamespace, "section");
- m_writer->writeAttribute("xml:id", refForNode(m));
- newLine();
- }
-
// Write the tag containing the title.
- m_writer->writeStartElement(dbNamespace, (i == 0) ? "title" : "bridgehead");
- if (i > 0)
+ if (i == 0) {
+ startSectionBegin(sharedNode);
+ } else {
+ m_writer->writeStartElement(dbNamespace, "bridgehead");
m_writer->writeAttribute("renderas", "sect2");
+ }
// Write the title.
- if (node->isFunction(Node::QML) || node->isFunction(Node::JS))
- generateQmlMethodTitle(node);
- else if (node->isQmlProperty() || node->isJsProperty())
+ if (sharedNode->isFunction(Node::QML))
+ generateQmlMethodTitle(sharedNode);
+ else if (sharedNode->isQmlProperty())
m_writer->writeCharacters(
- getQmlPropertyTitle(static_cast<QmlPropertyNode *>(node)));
+ getQmlPropertyTitle(static_cast<QmlPropertyNode *>(sharedNode)));
// Complete the title and the synopsis.
- generateDocBookSynopsis(m);
+ if (i == 0)
+ startSectionEnd();
+ else
+ m_writer->writeEndElement(); // bridgehead
+ generateDocBookSynopsis(sharedNode);
++i;
}
- if (i == 0)
- generateEndSection = false;
+ // If the list is empty, still generate a section.
+ if (i == 0) {
+ startSectionBegin(refForNode(node));
+
+ if (node->isFunction(Node::QML))
+ generateQmlMethodTitle(node);
+ else if (node->isQmlProperty())
+ m_writer->writeCharacters(
+ getQmlPropertyTitle(static_cast<QmlPropertyNode *>(node)));
+
+ startSectionEnd();
+ }
} else { // assume the node is a method/signal handler
- startSectionBegin(refForNode(node));
+ startSectionBegin(node);
generateQmlMethodTitle(node);
startSectionEnd();
}
@@ -3976,8 +4490,7 @@ void DocBookGenerator::generateDetailedQmlMember(Node *node, const Aggregate *re
generateSince(node);
generateAlsoList(node);
- if (generateEndSection)
- endSection();
+ endSection();
}
/*!
@@ -4001,26 +4514,23 @@ void DocBookGenerator::generateDocumentation(Node *node)
if (node->parent()) {
if (node->isCollectionNode()) {
/*
- A collection node collects: groups, C++ modules,
- QML modules or JavaScript modules. Testing for a
- CollectionNode must be done before testing for a
- TextPageNode because a CollectionNode is a PageNode
- at this point.
+ A collection node collects: groups, C++ modules, or QML
+ modules. Testing for a CollectionNode must be done
+ before testing for a TextPageNode because a
+ CollectionNode is a PageNode at this point.
- Don't output an HTML page for the collection
- node unless the \group, \module, \qmlmodule or
- \jsmodule command was actually seen by qdoc in
- the qdoc comment for the node.
+ Don't output an HTML page for the collection node unless
+ the \group, \module, or \qmlmodule command was actually
+ seen by qdoc in the qdoc comment for the node.
A key prerequisite in this case is the call to
- mergeCollections(cn). We must determine whether
- this group, module, QML module, or JavaScript
- module has members in other modules. We know at
- this point that cn's members list contains only
- members in the current module. Therefore, before
- outputting the page for cn, we must search for
- members of cn in the other modules and add them
- to the members list.
+ mergeCollections(cn). We must determine whether this
+ group, module, or QML module has members in other
+ modules. We know at this point that cn's members list
+ contains only members in the current module. Therefore,
+ before outputting the page for cn, we must search for
+ members of cn in the other modules and add them to the
+ members list.
*/
auto cn = static_cast<CollectionNode *>(node);
if (cn->wasSeen()) {
@@ -4038,10 +4548,8 @@ void DocBookGenerator::generateDocumentation(Node *node)
if ((node->isClassNode() || node->isHeader() || node->isNamespace())
&& node->docMustBeGenerated()) {
generateCppReferencePage(static_cast<Aggregate *>(node));
- } else if (node->isQmlType() || node->isJsType()) {
+ } else if (node->isQmlType()) { // Includes QML value types
generateQmlTypePage(static_cast<QmlTypeNode *>(node));
- } else if (node->isQmlBasicType() || node->isJsBasicType()) {
- generateQmlBasicTypePage(static_cast<QmlBasicTypeNode *>(node));
} else if (node->isProxyNode()) {
generateProxyPage(static_cast<Aggregate *>(node));
}
@@ -4073,11 +4581,10 @@ void DocBookGenerator::generateProxyPage(Aggregate *aggregate)
// Actual content.
if (!aggregate->doc().isEmpty()) {
- startSection(registerRef("details"), "Detailed Description");
+ startSection("details", "Detailed Description");
generateBody(aggregate);
generateAlsoList(aggregate);
- generateMaintainerList(aggregate);
endSection();
}
@@ -4085,7 +4592,7 @@ void DocBookGenerator::generateProxyPage(Aggregate *aggregate)
Sections sections(aggregate);
SectionVector *detailsSections = &sections.stdDetailsSections();
- for (const auto &section : qAsConst(*detailsSections)) {
+ for (const auto &section : std::as_const(*detailsSections)) {
if (section.isEmpty())
continue;
@@ -4100,6 +4607,7 @@ void DocBookGenerator::generateProxyPage(Aggregate *aggregate)
startSectionBegin();
generateFullName(member, aggregate);
startSectionEnd();
+
generateBrief(member);
endSection();
}
@@ -4141,17 +4649,15 @@ void DocBookGenerator::generateCollectionNode(CollectionNode *cn)
// Actual content.
if (cn->isModule()) {
if (!cn->noAutoList()) {
- NodeMap nmm;
- cn->getMemberNamespaces(nmm);
+ NodeMap nmm{cn->getMembers(Node::Namespace)};
if (!nmm.isEmpty()) {
- startSection(registerRef("namespaces"), "Namespaces");
+ startSection("namespaces", "Namespaces");
generateAnnotatedList(cn, nmm.values(), "namespaces");
endSection();
}
- nmm.clear();
- cn->getMemberClasses(nmm);
+ nmm = cn->getMembers([](const Node *n){ return n->isClassNode(); });
if (!nmm.isEmpty()) {
- startSection(registerRef("classes"), "Classes");
+ startSection("classes", "Classes");
generateAnnotatedList(cn, nmm.values(), "classes");
endSection();
}
@@ -4160,17 +4666,25 @@ void DocBookGenerator::generateCollectionNode(CollectionNode *cn)
bool generatedTitle = false;
if (cn->isModule() && !cn->doc().briefText().isEmpty()) {
- startSection(registerRef("details"), "Detailed Description");
+ startSection("details", "Detailed Description");
generatedTitle = true;
- } else {
- writeAnchor(registerRef("details"));
+ }
+ // The anchor is only needed if the node has a body.
+ else if (
+ // generateBody generates something.
+ !cn->doc().body().isEmpty() ||
+ // generateAlsoList generates something.
+ !cn->doc().alsoList().empty() ||
+ // generateAnnotatedList generates something.
+ (!cn->noAutoList() && (cn->isGroup() || cn->isQmlModule()))) {
+ writeAnchor("details");
}
generateBody(cn);
generateAlsoList(cn);
- if (!cn->noAutoList() && (cn->isGroup() || cn->isQmlModule() || cn->isJsModule()))
- generateAnnotatedList(cn, cn->members(), "members");
+ if (!cn->noAutoList() && (cn->isGroup() || cn->isQmlModule()))
+ generateAnnotatedList(cn, cn->members(), "members", AutoSection);
if (generatedTitle)
endSection();
@@ -4241,11 +4755,9 @@ void DocBookGenerator::generateFullName(const Node *apparentNode, const QString
Q_ASSERT(actualNode);
// From Generator::appendFullName.
- if (actualNode == nullptr)
- actualNode = apparentNode;
m_writer->writeStartElement(dbNamespace, "link");
m_writer->writeAttribute(xlinkNamespace, "href", fullDocumentLocation(actualNode));
- m_writer->writeAttribute("type", targetType(actualNode));
+ m_writer->writeAttribute("role", targetType(actualNode));
m_writer->writeCharacters(fullName);
m_writer->writeEndElement(); // link
}
diff --git a/src/qdoc/docbookgenerator.h b/src/qdoc/qdoc/src/qdoc/docbookgenerator.h
index 6f1dc0389..5defbeb82 100644
--- a/src/qdoc/docbookgenerator.h
+++ b/src/qdoc/qdoc/src/qdoc/docbookgenerator.h
@@ -1,31 +1,6 @@
-/****************************************************************************
-**
-** Copyright (C) 2019 Thibaut Cuvelier
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the tools applications of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2019 Thibaut Cuvelier
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#ifndef DOCBOOKGENERATOR_H
#define DOCBOOKGENERATOR_H
@@ -33,6 +8,7 @@
#include "codemarker.h"
#include "config.h"
#include "xmlgenerator.h"
+#include "filesystem/fileresolver.h"
#include <QtCore/qhash.h>
#include <QtCore/qxmlstream.h>
@@ -46,7 +22,7 @@ class FunctionNode;
class DocBookGenerator : public XmlGenerator
{
public:
- explicit DocBookGenerator() = default;
+ explicit DocBookGenerator(FileResolver& file_resolver);
void initializeGenerator() override;
QString format() override;
@@ -60,8 +36,6 @@ protected:
void generatePageNode(PageNode *pn);
using Generator::generateQmlTypePage;
void generateQmlTypePage(QmlTypeNode *qcn);
- using Generator::generateQmlBasicTypePage;
- void generateQmlBasicTypePage(QmlBasicTypeNode *qbtn);
using Generator::generateCollectionNode;
void generateCollectionNode(CollectionNode *cn);
using Generator::generateGenericCollectionPage;
@@ -79,6 +53,7 @@ protected:
void generateSortedNames(const ClassNode *cn, const QList<RelatedClass> &rc);
void generateSortedQmlNames(const Node *base, const NodeList &subs);
bool generateStatus(const Node *node);
+ void generateGroupReferenceText(const Node *node);
bool generateThreadSafeness(const Node *node);
bool generateSince(const Node *node);
void generateAddendum(const Node *node, Generator::Addendum type, CodeMarker *marker,
@@ -87,28 +62,29 @@ protected:
void generateBody(const Node *node);
bool generateText(const Text &text, const Node *relative) override;
- const Atom *generateAtomList(const Atom *atom, const Node *relative, bool generate,
- int &numAtoms);
- qsizetype generateAtom(const Atom *atom, const Node *relative) override;
+ qsizetype generateAtom(const Atom *atom, const Node *relative, CodeMarker*) override;
private:
+
+ enum GeneratedListType { Auto, AutoSection, ItemizedList };
+
QXmlStreamWriter *startDocument(const Node *node);
QXmlStreamWriter *startDocument(const ExampleNode *en, const QString &file);
QXmlStreamWriter *startGenericDocument(const Node *node, const QString &fileName);
void endDocument();
void generateAnnotatedList(const Node *relative, const NodeList &nodeList,
- const QString &selector);
+ const QString &selector, GeneratedListType type = Auto);
void generateAnnotatedLists(const Node *relative, const NodeMultiMap &nmm,
const QString &selector);
- void generateCompactList(ListType listType, const Node *relative, const NodeMultiMap &nmm,
+ void generateCompactList(const Node *relative, const NodeMultiMap &nmm, bool includeAlphabet,
const QString &commonPrefix, const QString &selector);
using Generator::generateFileList;
void generateFileList(const ExampleNode *en, bool images);
void generateObsoleteMembers(const Sections &sections);
void generateObsoleteQmlMembers(const Sections &sections);
void generateSectionList(const Section &section, const Node *relative,
- Section::Status status = Section::Active);
+ bool useObsoleteMembers = false);
void generateSectionInheritedList(const Section &section, const Node *relative);
void generateSynopsisName(const Node *node, const Node *relative, bool generateNameLink);
void generateParameter(const Parameter &parameter, const Node *relative, bool generateExtra,
@@ -124,14 +100,12 @@ private:
void generateBrief(const Node *node);
void generateAlsoList(const Node *node) override;
void generateSignatureList(const NodeList &nodes);
- void generateMaintainerList(const Aggregate *node) override;
void generateReimplementsClause(const FunctionNode *fn);
void generateClassHierarchy(const Node *relative, NodeMultiMap &classMap);
void generateFunctionIndex(const Node *relative);
void generateLegaleseList(const Node *relative);
- void generateExampleFilePage(const Node *en, const QString &file) override;
+ void generateExampleFilePage(const Node *en, ResolvedFile resolved_file, CodeMarker* = nullptr) override;
void generateOverloadedSignal(const Node *node);
- bool generateQmlText(const Text &text, const Node *relative) override;
void generateRequiredLinks(const Node *node);
void generateLinkToExample(const ExampleNode *en, const QString &baseUrl);
@@ -140,11 +114,15 @@ private:
void generateLink(const Atom *atom);
void beginLink(const QString &link, const Node *node, const Node *relative);
void endLink();
+ void writeXmlId(const QString &id);
+ void writeXmlId(const Node *node);
inline void newLine();
- void startSectionBegin();
- void startSectionBegin(const QString &id);
+ void startSectionBegin(const QString &id = "");
+ void startSectionBegin(const Node *node);
void startSectionEnd();
void startSection(const QString &id, const QString &title);
+ void startSection(const Node *node, const QString &title);
+ void startSection(const QString &title);
void endSection();
void writeAnchor(const QString &id);
void generateSimpleLink(const QString &href, const QString &text);
@@ -155,16 +133,32 @@ private:
void generateSynopsisInfo(const QString &key, const QString &value);
void generateModifier(const QString &value);
- bool m_inListItemLineOpen {};
- int currentSectionLevel {};
+ // Generator state when outputting the documentation.
+ bool m_inListItemLineOpen { false };
+ int currentSectionLevel { 0 };
QStack<int> sectionLevels {};
QString m_qflagsHref {};
-
+ bool m_inTeletype { false };
+ bool m_hasSection { false };
+ bool m_closeSectionAfterGeneratedList { false };
+ bool m_closeSectionAfterRawTitle { false };
+ bool m_closeFigureWrapper { false };
+ bool m_tableHeaderAlreadyOutput { false };
+ bool m_closeTableRow { false };
+ bool m_closeTableCell { false };
+ std::pair<QString, QString> m_tableWidthAttr {};
+ bool m_inPara { false }; // Ignores nesting of paragraphs (like list items).
+ bool m_inBlockquote { false };
+ unsigned m_inList { 0 }; // Depth in number of nested lists.
+
+ // Generator configuration, set before starting the generation.
QString m_project {};
QString m_projectDescription {};
QString m_naturalLanguage {};
QString m_buildVersion {};
QXmlStreamWriter *m_writer { nullptr };
+ bool m_useDocBook52 { false }; // Enable tags from DocBook 5.2. Also called "extensions".
+ bool m_useITS { false }; // Enable ITS attributes for parts that should not be translated.
Config *m_config { nullptr };
};
diff --git a/src/qdoc/docparser.cpp b/src/qdoc/qdoc/src/qdoc/docparser.cpp
index 1294f5d7a..ee9a187b7 100644
--- a/src/qdoc/docparser.cpp
+++ b/src/qdoc/qdoc/src/qdoc/docparser.cpp
@@ -1,30 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the tools applications of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "docparser.h"
#include "codemarker.h"
@@ -41,9 +16,12 @@
#include <cctype>
#include <climits>
+#include <functional>
QT_BEGIN_NAMESPACE
+using namespace Qt::StringLiterals;
+
DocUtilities &DocParser::s_utilities = DocUtilities::instance();
enum {
@@ -58,11 +36,15 @@ enum {
CMD_CAPTION,
CMD_CODE,
CMD_CODELINE,
+ CMD_COMPARESWITH,
+ CMD_DETAILS,
CMD_DIV,
CMD_DOTS,
CMD_E,
CMD_ELSE,
CMD_ENDCODE,
+ CMD_ENDCOMPARESWITH,
+ CMD_ENDDETAILS,
CMD_ENDDIV,
CMD_ENDFOOTNOTE,
CMD_ENDIF,
@@ -98,10 +80,8 @@ enum {
CMD_LINK,
CMD_LIST,
CMD_META,
- CMD_NEWCODE,
CMD_NOTE,
CMD_O,
- CMD_OLDCODE,
CMD_OMIT,
CMD_OMITVALUE,
CMD_OVERLOAD,
@@ -111,7 +91,6 @@ enum {
CMD_QUOTATION,
CMD_QUOTEFILE,
CMD_QUOTEFROMFILE,
- CMD_QUOTEFUNCTION,
CMD_RAW,
CMD_ROW,
CMD_SA,
@@ -131,6 +110,7 @@ enum {
CMD_TABLE,
CMD_TABLEOFCONTENTS,
CMD_TARGET,
+ CMD_TM,
CMD_TT,
CMD_UICONTROL,
CMD_UNDERLINE,
@@ -141,129 +121,121 @@ enum {
CMD_ENDQML,
CMD_CPP,
CMD_ENDCPP,
- CMD_QMLTEXT,
- CMD_ENDQMLTEXT,
CMD_CPPTEXT,
CMD_ENDCPPTEXT,
- CMD_JS,
- CMD_ENDJS,
NOT_A_CMD
};
static struct
{
- const char *english;
+ const char *name;
int no;
- QString *alias;
-} cmds[] = { { "a", CMD_A, nullptr },
- { "annotatedlist", CMD_ANNOTATEDLIST, nullptr },
- { "b", CMD_B, nullptr },
- { "badcode", CMD_BADCODE, nullptr },
- { "bold", CMD_BOLD, nullptr },
- { "br", CMD_BR, nullptr },
- { "brief", CMD_BRIEF, nullptr },
- { "c", CMD_C, nullptr },
- { "caption", CMD_CAPTION, nullptr },
- { "code", CMD_CODE, nullptr },
- { "codeline", CMD_CODELINE, nullptr },
- { "div", CMD_DIV, nullptr },
- { "dots", CMD_DOTS, nullptr },
- { "e", CMD_E, nullptr },
- { "else", CMD_ELSE, nullptr },
- { "endcode", CMD_ENDCODE, nullptr },
- { "enddiv", CMD_ENDDIV, nullptr },
- { "endfootnote", CMD_ENDFOOTNOTE, nullptr },
- { "endif", CMD_ENDIF, nullptr },
- { "endlegalese", CMD_ENDLEGALESE, nullptr },
- { "endlink", CMD_ENDLINK, nullptr },
- { "endlist", CMD_ENDLIST, nullptr },
- { "endmapref", CMD_ENDMAPREF, nullptr },
- { "endomit", CMD_ENDOMIT, nullptr },
- { "endquotation", CMD_ENDQUOTATION, nullptr },
- { "endraw", CMD_ENDRAW, nullptr },
- { "endsection1", CMD_ENDSECTION1, nullptr }, // ### don't document for now
- { "endsection2", CMD_ENDSECTION2, nullptr }, // ### don't document for now
- { "endsection3", CMD_ENDSECTION3, nullptr }, // ### don't document for now
- { "endsection4", CMD_ENDSECTION4, nullptr }, // ### don't document for now
- { "endsidebar", CMD_ENDSIDEBAR, nullptr },
- { "endtable", CMD_ENDTABLE, nullptr },
- { "footnote", CMD_FOOTNOTE, nullptr },
- { "generatelist", CMD_GENERATELIST, nullptr },
- { "header", CMD_HEADER, nullptr },
- { "hr", CMD_HR, nullptr },
- { "i", CMD_I, nullptr },
- { "if", CMD_IF, nullptr },
- { "image", CMD_IMAGE, nullptr },
- { "important", CMD_IMPORTANT, nullptr },
- { "include", CMD_INCLUDE, nullptr },
- { "inlineimage", CMD_INLINEIMAGE, nullptr },
- { "index", CMD_INDEX, nullptr }, // ### don't document for now
- { "input", CMD_INPUT, nullptr },
- { "keyword", CMD_KEYWORD, nullptr },
- { "l", CMD_L, nullptr },
- { "legalese", CMD_LEGALESE, nullptr },
- { "li", CMD_LI, nullptr },
- { "link", CMD_LINK, nullptr },
- { "list", CMD_LIST, nullptr },
- { "meta", CMD_META, nullptr },
- { "newcode", CMD_NEWCODE, nullptr },
- { "note", CMD_NOTE, nullptr },
- { "o", CMD_O, nullptr },
- { "oldcode", CMD_OLDCODE, nullptr },
- { "omit", CMD_OMIT, nullptr },
- { "omitvalue", CMD_OMITVALUE, nullptr },
- { "overload", CMD_OVERLOAD, nullptr },
- { "printline", CMD_PRINTLINE, nullptr },
- { "printto", CMD_PRINTTO, nullptr },
- { "printuntil", CMD_PRINTUNTIL, nullptr },
- { "quotation", CMD_QUOTATION, nullptr },
- { "quotefile", CMD_QUOTEFILE, nullptr },
- { "quotefromfile", CMD_QUOTEFROMFILE, nullptr },
- { "quotefunction", CMD_QUOTEFUNCTION, nullptr },
- { "raw", CMD_RAW, nullptr },
- { "row", CMD_ROW, nullptr },
- { "sa", CMD_SA, nullptr },
- { "section1", CMD_SECTION1, nullptr },
- { "section2", CMD_SECTION2, nullptr },
- { "section3", CMD_SECTION3, nullptr },
- { "section4", CMD_SECTION4, nullptr },
- { "sidebar", CMD_SIDEBAR, nullptr },
- { "sincelist", CMD_SINCELIST, nullptr },
- { "skipline", CMD_SKIPLINE, nullptr },
- { "skipto", CMD_SKIPTO, nullptr },
- { "skipuntil", CMD_SKIPUNTIL, nullptr },
- { "snippet", CMD_SNIPPET, nullptr },
- { "span", CMD_SPAN, nullptr },
- { "sub", CMD_SUB, nullptr },
- { "sup", CMD_SUP, nullptr },
- { "table", CMD_TABLE, nullptr },
- { "tableofcontents", CMD_TABLEOFCONTENTS, nullptr },
- { "target", CMD_TARGET, nullptr },
- { "tt", CMD_TT, nullptr },
- { "uicontrol", CMD_UICONTROL, nullptr },
- { "underline", CMD_UNDERLINE, nullptr },
- { "unicode", CMD_UNICODE, nullptr },
- { "value", CMD_VALUE, nullptr },
- { "warning", CMD_WARNING, nullptr },
- { "qml", CMD_QML, nullptr },
- { "endqml", CMD_ENDQML, nullptr },
- { "cpp", CMD_CPP, nullptr },
- { "endcpp", CMD_ENDCPP, nullptr },
- { "qmltext", CMD_QMLTEXT, nullptr },
- { "endqmltext", CMD_ENDQMLTEXT, nullptr },
- { "cpptext", CMD_CPPTEXT, nullptr },
- { "endcpptext", CMD_ENDCPPTEXT, nullptr },
- { "js", CMD_JS, nullptr },
- { "endjs", CMD_ENDJS, nullptr },
- { nullptr, 0, nullptr } };
+ bool is_formatting_command { false };
+} cmds[] = { { "a", CMD_A, true },
+ { "annotatedlist", CMD_ANNOTATEDLIST },
+ { "b", CMD_B, true },
+ { "badcode", CMD_BADCODE },
+ { "bold", CMD_BOLD, true },
+ { "br", CMD_BR },
+ { "brief", CMD_BRIEF },
+ { "c", CMD_C, true },
+ { "caption", CMD_CAPTION },
+ { "code", CMD_CODE },
+ { "codeline", CMD_CODELINE },
+ { "compareswith", CMD_COMPARESWITH },
+ { "details", CMD_DETAILS },
+ { "div", CMD_DIV },
+ { "dots", CMD_DOTS },
+ { "e", CMD_E, true },
+ { "else", CMD_ELSE },
+ { "endcode", CMD_ENDCODE },
+ { "endcompareswith", CMD_ENDCOMPARESWITH },
+ { "enddetails", CMD_ENDDETAILS },
+ { "enddiv", CMD_ENDDIV },
+ { "endfootnote", CMD_ENDFOOTNOTE },
+ { "endif", CMD_ENDIF },
+ { "endlegalese", CMD_ENDLEGALESE },
+ { "endlink", CMD_ENDLINK },
+ { "endlist", CMD_ENDLIST },
+ { "endmapref", CMD_ENDMAPREF },
+ { "endomit", CMD_ENDOMIT },
+ { "endquotation", CMD_ENDQUOTATION },
+ { "endraw", CMD_ENDRAW },
+ { "endsection1", CMD_ENDSECTION1 }, // ### don't document for now
+ { "endsection2", CMD_ENDSECTION2 }, // ### don't document for now
+ { "endsection3", CMD_ENDSECTION3 }, // ### don't document for now
+ { "endsection4", CMD_ENDSECTION4 }, // ### don't document for now
+ { "endsidebar", CMD_ENDSIDEBAR },
+ { "endtable", CMD_ENDTABLE },
+ { "footnote", CMD_FOOTNOTE },
+ { "generatelist", CMD_GENERATELIST },
+ { "header", CMD_HEADER },
+ { "hr", CMD_HR },
+ { "i", CMD_I, true },
+ { "if", CMD_IF },
+ { "image", CMD_IMAGE },
+ { "important", CMD_IMPORTANT },
+ { "include", CMD_INCLUDE },
+ { "inlineimage", CMD_INLINEIMAGE },
+ { "index", CMD_INDEX }, // ### don't document for now
+ { "input", CMD_INPUT },
+ { "keyword", CMD_KEYWORD },
+ { "l", CMD_L },
+ { "legalese", CMD_LEGALESE },
+ { "li", CMD_LI },
+ { "link", CMD_LINK },
+ { "list", CMD_LIST },
+ { "meta", CMD_META },
+ { "note", CMD_NOTE },
+ { "o", CMD_O },
+ { "omit", CMD_OMIT },
+ { "omitvalue", CMD_OMITVALUE },
+ { "overload", CMD_OVERLOAD },
+ { "printline", CMD_PRINTLINE },
+ { "printto", CMD_PRINTTO },
+ { "printuntil", CMD_PRINTUNTIL },
+ { "quotation", CMD_QUOTATION },
+ { "quotefile", CMD_QUOTEFILE },
+ { "quotefromfile", CMD_QUOTEFROMFILE },
+ { "raw", CMD_RAW },
+ { "row", CMD_ROW },
+ { "sa", CMD_SA },
+ { "section1", CMD_SECTION1 },
+ { "section2", CMD_SECTION2 },
+ { "section3", CMD_SECTION3 },
+ { "section4", CMD_SECTION4 },
+ { "sidebar", CMD_SIDEBAR },
+ { "sincelist", CMD_SINCELIST },
+ { "skipline", CMD_SKIPLINE },
+ { "skipto", CMD_SKIPTO },
+ { "skipuntil", CMD_SKIPUNTIL },
+ { "snippet", CMD_SNIPPET },
+ { "span", CMD_SPAN },
+ { "sub", CMD_SUB, true },
+ { "sup", CMD_SUP, true },
+ { "table", CMD_TABLE },
+ { "tableofcontents", CMD_TABLEOFCONTENTS },
+ { "target", CMD_TARGET },
+ { "tm", CMD_TM, true },
+ { "tt", CMD_TT, true },
+ { "uicontrol", CMD_UICONTROL, true },
+ { "underline", CMD_UNDERLINE, true },
+ { "unicode", CMD_UNICODE },
+ { "value", CMD_VALUE },
+ { "warning", CMD_WARNING },
+ { "qml", CMD_QML },
+ { "endqml", CMD_ENDQML },
+ { "cpp", CMD_CPP },
+ { "endcpp", CMD_ENDCPP },
+ { "cpptext", CMD_CPPTEXT },
+ { "endcpptext", CMD_ENDCPPTEXT },
+ { nullptr, 0 } };
int DocParser::s_tabSize;
-QStringList DocParser::s_exampleFiles;
-QStringList DocParser::s_exampleDirs;
-QStringList DocParser::s_sourceFiles;
-QStringList DocParser::s_sourceDirs;
QStringList DocParser::s_ignoreWords;
bool DocParser::s_quoting = false;
+FileResolver *DocParser::file_resolver{ nullptr };
+static void processComparesWithCommand(DocPrivate *priv, const Location &location);
static QString cleanLink(const QString &link)
{
@@ -273,19 +245,14 @@ static QString cleanLink(const QString &link)
return link.mid(colonPos + 1).simplified();
}
-void DocParser::initialize(const Config &config)
+void DocParser::initialize(const Config &config, FileResolver &file_resolver)
{
- s_tabSize = config.getInt(CONFIG_TABSIZE);
- s_exampleFiles = config.getCanonicalPathList(CONFIG_EXAMPLES);
- s_exampleDirs = config.getCanonicalPathList(CONFIG_EXAMPLEDIRS);
- s_sourceFiles = config.getCanonicalPathList(CONFIG_SOURCES);
- s_sourceDirs = config.getCanonicalPathList(CONFIG_SOURCEDIRS);
- s_ignoreWords = config.getStringList(CONFIG_IGNOREWORDS);
+ s_tabSize = config.get(CONFIG_TABSIZE).asInt();
+ s_ignoreWords = config.get(CONFIG_IGNOREWORDS).asStringList();
int i = 0;
- while (cmds[i].english) {
- cmds[i].alias = new QString(Doc::alias(cmds[i].english));
- s_utilities.cmdHash.insert(*cmds[i].alias, cmds[i].no);
+ while (cmds[i].name) {
+ s_utilities.cmdHash.insert(cmds[i].name, cmds[i].no);
if (cmds[i].no != i)
Location::internalError(QStringLiteral("command %1 missing").arg(i));
@@ -293,25 +260,15 @@ void DocParser::initialize(const Config &config)
}
// If any of the formats define quotinginformation, activate quoting
- DocParser::s_quoting = config.getBool(CONFIG_QUOTINGINFORMATION);
- for (const auto &format : config.getOutputFormats())
+ DocParser::s_quoting = config.get(CONFIG_QUOTINGINFORMATION).asBool();
+ const auto &outputFormats = config.getOutputFormats();
+ for (const auto &format : outputFormats)
DocParser::s_quoting = DocParser::s_quoting
- || config.getBool(format + Config::dot + CONFIG_QUOTINGINFORMATION);
-}
+ || config.get(format + Config::dot + CONFIG_QUOTINGINFORMATION).asBool();
-void DocParser::terminate()
-{
- s_exampleFiles.clear();
- s_exampleDirs.clear();
- s_sourceFiles.clear();
- s_sourceDirs.clear();
-
- int i = 0;
- while (cmds[i].english) {
- delete cmds[i].alias;
- cmds[i].alias = nullptr;
- ++i;
- }
+ // KLUDGE: file_resolver is temporarily a pointer. See the
+ // comment for file_resolver in the header file for more context.
+ DocParser::file_resolver = &file_resolver;
}
/*!
@@ -328,7 +285,7 @@ void DocParser::parse(const QString &source, DocPrivate *docPrivate,
{
m_input = source;
m_position = 0;
- m_inputLength = m_input.length();
+ m_inputLength = m_input.size();
m_cachedLocation = docPrivate->m_start_loc;
m_cachedPosition = 0;
m_private = docPrivate;
@@ -393,19 +350,19 @@ void DocParser::parse(const QString &source, DocPrivate *docPrivate,
case CMD_A:
enterPara();
p1 = getArgument();
- append(Atom::FormattingLeft, ATOM_FORMATTING_PARAMETER);
- append(Atom::String, p1);
- append(Atom::FormattingRight, ATOM_FORMATTING_PARAMETER);
+ appendAtom(Atom(Atom::FormattingLeft, ATOM_FORMATTING_PARAMETER));
+ appendAtom(Atom(Atom::String, p1));
+ appendAtom(Atom(Atom::FormattingRight, ATOM_FORMATTING_PARAMETER));
m_private->m_params.insert(p1);
break;
case CMD_BADCODE:
leavePara();
- append(Atom::CodeBad,
- getCode(CMD_BADCODE, marker, getMetaCommandArgument(cmdStr)));
+ appendAtom(Atom(Atom::CodeBad,
+ getCode(CMD_BADCODE, marker, getMetaCommandArgument(cmdStr))));
break;
case CMD_BR:
enterPara();
- append(Atom::BR);
+ appendAtom(Atom(Atom::BR));
break;
case CMD_BOLD:
location().warning(QStringLiteral("'\\bold' is deprecated. Use '\\b'"));
@@ -419,9 +376,9 @@ void DocParser::parse(const QString &source, DocPrivate *docPrivate,
break;
case CMD_C:
enterPara();
- p1 = untabifyEtc(getArgument(true));
+ p1 = untabifyEtc(getArgument(ArgumentParsingOptions::Verbatim));
marker = CodeMarker::markerForCode(p1);
- append(Atom::C, marker->markedUpCode(p1, nullptr, location()));
+ appendAtom(Atom(Atom::C, marker->markedUpCode(p1, nullptr, location())));
break;
case CMD_CAPTION:
leavePara();
@@ -429,39 +386,39 @@ void DocParser::parse(const QString &source, DocPrivate *docPrivate,
break;
case CMD_CODE:
leavePara();
- append(Atom::Code, getCode(CMD_CODE, nullptr, getMetaCommandArgument(cmdStr)));
+ appendAtom(Atom(Atom::Code, getCode(CMD_CODE, nullptr, getMetaCommandArgument(cmdStr))));
break;
case CMD_QML:
leavePara();
- append(Atom::Qml,
+ appendAtom(Atom(Atom::Qml,
getCode(CMD_QML, CodeMarker::markerForLanguage(QLatin1String("QML")),
- getMetaCommandArgument(cmdStr)));
+ getMetaCommandArgument(cmdStr))));
break;
- case CMD_QMLTEXT:
- append(Atom::QmlText);
+ case CMD_DETAILS:
+ leavePara();
+ appendAtom(Atom(Atom::DetailsLeft, getArgument()));
+ m_openedCommands.push(cmd);
break;
- case CMD_JS:
+ case CMD_ENDDETAILS:
leavePara();
- append(Atom::JavaScript,
- getCode(CMD_JS,
- CodeMarker::markerForLanguage(QLatin1String("JavaScript")),
- getMetaCommandArgument(cmdStr)));
+ appendAtom(Atom(Atom::DetailsRight));
+ closeCommand(cmd);
break;
case CMD_DIV:
leavePara();
- p1 = getArgument(true);
- append(Atom::DivLeft, p1);
+ p1 = getArgument(ArgumentParsingOptions::Verbatim);
+ appendAtom(Atom(Atom::DivLeft, p1));
m_openedCommands.push(cmd);
break;
case CMD_ENDDIV:
leavePara();
- append(Atom::DivRight);
+ appendAtom(Atom(Atom::DivRight));
closeCommand(cmd);
break;
case CMD_CODELINE:
if (s_quoting) {
- append(Atom::CodeQuoteCommand, cmdStr);
- append(Atom::CodeQuoteArgument, " ");
+ appendAtom(Atom(Atom::CodeQuoteCommand, cmdStr));
+ appendAtom(Atom(Atom::CodeQuoteArgument, " "));
}
if (isCode(m_lastAtom) && m_lastAtom->string().endsWith("\n\n"))
m_lastAtom->chopString();
@@ -472,8 +429,8 @@ void DocParser::parse(const QString &source, DocPrivate *docPrivate,
if (arg.isEmpty())
arg = "4";
if (s_quoting) {
- append(Atom::CodeQuoteCommand, cmdStr);
- append(Atom::CodeQuoteArgument, arg);
+ appendAtom(Atom(Atom::CodeQuoteCommand, cmdStr));
+ appendAtom(Atom(Atom::CodeQuoteArgument, arg));
}
if (isCode(m_lastAtom) && m_lastAtom->string().endsWith("\n\n"))
m_lastAtom->chopString();
@@ -506,20 +463,14 @@ void DocParser::parse(const QString &source, DocPrivate *docPrivate,
case CMD_ENDQML:
closeCommand(cmd);
break;
- case CMD_ENDQMLTEXT:
- append(Atom::EndQmlText);
- break;
- case CMD_ENDJS:
- closeCommand(cmd);
- break;
case CMD_ENDFOOTNOTE:
if (closeCommand(cmd)) {
leavePara();
- append(Atom::FootnoteRight);
+ appendAtom(Atom(Atom::FootnoteRight));
}
break;
case CMD_ENDIF:
- if (preprocessorSkipping.count() > 0) {
+ if (preprocessorSkipping.size() > 0) {
if (preprocessorSkipping.pop())
--numPreprocessorSkipping;
(void)getRestOfLine(); // ### should ensure that it's empty
@@ -533,7 +484,7 @@ void DocParser::parse(const QString &source, DocPrivate *docPrivate,
case CMD_ENDLEGALESE:
if (closeCommand(cmd)) {
leavePara();
- append(Atom::LegaleseRight);
+ appendAtom(Atom(Atom::LegaleseRight));
}
break;
case CMD_ENDLINK:
@@ -541,15 +492,15 @@ void DocParser::parse(const QString &source, DocPrivate *docPrivate,
if (m_private->m_text.lastAtom()->type() == Atom::String
&& m_private->m_text.lastAtom()->string().endsWith(QLatin1Char(' ')))
m_private->m_text.lastAtom()->chopString();
- append(Atom::FormattingRight, ATOM_FORMATTING_LINK);
+ appendAtom(Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK));
}
break;
case CMD_ENDLIST:
if (closeCommand(cmd)) {
leavePara();
if (m_openedLists.top().isStarted()) {
- append(Atom::ListItemRight, m_openedLists.top().styleString());
- append(Atom::ListRight, m_openedLists.top().styleString());
+ appendAtom(Atom(Atom::ListItemRight, m_openedLists.top().styleString()));
+ appendAtom(Atom(Atom::ListRight, m_openedLists.top().styleString()));
}
m_openedLists.pop();
}
@@ -560,7 +511,7 @@ void DocParser::parse(const QString &source, DocPrivate *docPrivate,
case CMD_ENDQUOTATION:
if (closeCommand(cmd)) {
leavePara();
- append(Atom::QuotationRight);
+ appendAtom(Atom(Atom::QuotationRight));
}
break;
case CMD_ENDRAW:
@@ -582,38 +533,39 @@ void DocParser::parse(const QString &source, DocPrivate *docPrivate,
case CMD_ENDSIDEBAR:
if (closeCommand(cmd)) {
leavePara();
- append(Atom::SidebarRight);
+ appendAtom(Atom(Atom::SidebarRight));
}
break;
case CMD_ENDTABLE:
if (closeCommand(cmd)) {
leaveTableRow();
- append(Atom::TableRight);
+ appendAtom(Atom(Atom::TableRight));
}
break;
case CMD_FOOTNOTE:
if (openCommand(cmd)) {
enterPara();
- append(Atom::FootnoteLeft);
+ appendAtom(Atom(Atom::FootnoteLeft));
}
break;
case CMD_ANNOTATEDLIST:
- append(Atom::AnnotatedList, getArgument());
+ appendAtom(Atom(Atom::AnnotatedList, getArgument()));
break;
case CMD_SINCELIST:
- append(Atom::SinceList, getRestOfLine().simplified());
+ leavePara();
+ appendAtom(Atom(Atom::SinceList, getRestOfLine().simplified()));
break;
case CMD_GENERATELIST: {
QString arg1 = getArgument();
QString arg2 = getOptionalArgument();
if (!arg2.isEmpty())
arg1 += " " + arg2;
- append(Atom::GeneratedList, arg1);
+ appendAtom(Atom(Atom::GeneratedList, arg1));
} break;
case CMD_HEADER:
if (m_openedCommands.top() == CMD_TABLE) {
leaveTableRow();
- append(Atom::TableHeaderLeft);
+ appendAtom(Atom(Atom::TableHeaderLeft));
m_inTableHeader = true;
} else {
if (m_openedCommands.contains(CMD_TABLE))
@@ -635,7 +587,7 @@ void DocParser::parse(const QString &source, DocPrivate *docPrivate,
break;
case CMD_HR:
leavePara();
- append(Atom::HR);
+ appendAtom(Atom(Atom::HR));
break;
case CMD_IF:
preprocessorSkipping.push(!Tokenizer::isTrue(getRestOfLine()));
@@ -646,8 +598,8 @@ void DocParser::parse(const QString &source, DocPrivate *docPrivate,
break;
case CMD_IMAGE:
leaveValueList();
- append(Atom::Image, getArgument());
- append(Atom::ImageText, getRestOfLine());
+ appendAtom(Atom(Atom::Image, getArgument()));
+ appendAtom(Atom(Atom::ImageText, getRestOfLine()));
break;
case CMD_IMPORTANT:
leavePara();
@@ -656,15 +608,27 @@ void DocParser::parse(const QString &source, DocPrivate *docPrivate,
case CMD_INCLUDE:
case CMD_INPUT: {
QString fileName = getArgument();
- QString identifier = getRestOfLine();
- include(fileName, identifier);
+ QStringList parameters;
+ QString identifier;
+ if (isLeftBraceAhead()) {
+ identifier = getArgument();
+ while (isLeftBraceAhead() && parameters.size() < 9)
+ parameters << getArgument();
+ } else {
+ identifier = getRestOfLine();
+ }
+ include(fileName, identifier, parameters);
break;
}
case CMD_INLINEIMAGE:
enterPara();
- append(Atom::InlineImage, getArgument());
- append(Atom::ImageText, getRestOfLine());
- append(Atom::String, " ");
+ appendAtom(Atom(Atom::InlineImage, getArgument()));
+ //Append ImageText only if the following
+ //argument is enclosed in braces.
+ if (isLeftBraceAhead()) {
+ appendAtom(Atom(Atom::ImageText, getArgument()));
+ appendAtom(Atom(Atom::String, " "));
+ }
break;
case CMD_INDEX:
if (m_paragraphState == OutsideParagraph) {
@@ -680,56 +644,42 @@ void DocParser::parse(const QString &source, DocPrivate *docPrivate,
startFormat(ATOM_FORMATTING_INDEX, cmd);
break;
case CMD_KEYWORD:
- insertTarget(getRestOfLine(), true);
+ leavePara();
+ insertKeyword(getRestOfLine());
break;
case CMD_L:
enterPara();
if (isLeftBracketAhead())
p2 = getBracketedArgument();
+
+ p1 = getArgument();
+
+ appendAtom(LinkAtom(p1, p2, location()));
+
if (isLeftBraceAhead()) {
- p1 = getArgument();
- append(p1, p2);
- if (!p2.isEmpty() && !(m_private->m_text.lastAtom()->error().isEmpty()))
- location().warning(
- QStringLiteral(
- "Check parameter in '[ ]' of '\\l' command: '%1', "
- "possible misspelling, or unrecognized module name")
- .arg(m_private->m_text.lastAtom()->error()));
- if (isLeftBraceAhead()) {
- currentLinkAtom = m_private->m_text.lastAtom();
- startFormat(ATOM_FORMATTING_LINK, cmd);
- } else {
- append(Atom::FormattingLeft, ATOM_FORMATTING_LINK);
- append(Atom::String, cleanLink(p1));
- append(Atom::FormattingRight, ATOM_FORMATTING_LINK);
- }
+ currentLinkAtom = m_private->m_text.lastAtom();
+ startFormat(ATOM_FORMATTING_LINK, cmd);
} else {
- p1 = getArgument();
- append(p1, p2);
- if (!p2.isEmpty() && !(m_private->m_text.lastAtom()->error().isEmpty()))
- location().warning(
- QStringLiteral(
- "Check parameter in '[ ]' of '\\l' command: '%1', "
- "possible misspelling, or unrecognized module name")
- .arg(m_private->m_text.lastAtom()->error()));
- append(Atom::FormattingLeft, ATOM_FORMATTING_LINK);
- append(Atom::String, cleanLink(p1));
- append(Atom::FormattingRight, ATOM_FORMATTING_LINK);
+ appendAtom(Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK));
+ appendAtom(Atom(Atom::String, cleanLink(p1)));
+ appendAtom(Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK));
}
+
p2.clear();
+
break;
case CMD_LEGALESE:
leavePara();
if (openCommand(cmd))
- append(Atom::LegaleseLeft);
+ appendAtom(Atom(Atom::LegaleseLeft));
docPrivate->m_hasLegalese = true;
break;
case CMD_LINK:
if (openCommand(cmd)) {
enterPara();
p1 = getArgument();
- append(p1);
- append(Atom::FormattingLeft, ATOM_FORMATTING_LINK);
+ appendAtom(Atom(Atom::Link, p1));
+ appendAtom(Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK));
skipSpacesOrOneEndl();
}
break;
@@ -744,10 +694,6 @@ void DocParser::parse(const QString &source, DocPrivate *docPrivate,
p1 = getArgument();
m_private->extra->m_metaMap.insert(p1, getArgument());
break;
- case CMD_NEWCODE:
- location().warning(
- QStringLiteral("Unexpected '\\%1'").arg(cmdName(CMD_NEWCODE)));
- break;
case CMD_NOTE:
leavePara();
enterPara(Atom::NoteLeft, Atom::NoteRight);
@@ -759,12 +705,12 @@ void DocParser::parse(const QString &source, DocPrivate *docPrivate,
leavePara();
if (m_openedCommands.top() == CMD_LIST) {
if (m_openedLists.top().isStarted())
- append(Atom::ListItemRight, m_openedLists.top().styleString());
+ appendAtom(Atom(Atom::ListItemRight, m_openedLists.top().styleString()));
else
- append(Atom::ListLeft, m_openedLists.top().styleString());
+ appendAtom(Atom(Atom::ListLeft, m_openedLists.top().styleString()));
m_openedLists.top().next();
- append(Atom::ListItemNumber, m_openedLists.top().numberString());
- append(Atom::ListItemLeft, m_openedLists.top().styleString());
+ appendAtom(Atom(Atom::ListItemNumber, m_openedLists.top().numberString()));
+ appendAtom(Atom(Atom::ListItemLeft, m_openedLists.top().styleString()));
enterPara();
} else if (m_openedCommands.top() == CMD_TABLE) {
p1 = "1,1";
@@ -780,29 +726,25 @@ void DocParser::parse(const QString &source, DocPrivate *docPrivate,
QStringLiteral("Missing '\\%1' or '\\%2' before '\\%3'")
.arg(cmdName(CMD_HEADER), cmdName(CMD_ROW),
cmdName(CMD_LI)));
- append(Atom::TableRowLeft);
+ appendAtom(Atom(Atom::TableRowLeft));
m_inTableRow = true;
} else if (m_inTableItem) {
- append(Atom::TableItemRight);
+ appendAtom(Atom(Atom::TableItemRight));
m_inTableItem = false;
}
- append(Atom::TableItemLeft, p1, p2);
+ appendAtom(Atom(Atom::TableItemLeft, p1, p2));
m_inTableItem = true;
} else
location().warning(
QStringLiteral("Command '\\%1' outside of '\\%2' and '\\%3'")
.arg(cmdName(cmd), cmdName(CMD_LIST), cmdName(CMD_TABLE)));
break;
- case CMD_OLDCODE:
- leavePara();
- append(Atom::CodeOld, getCode(CMD_OLDCODE, marker));
- append(Atom::CodeNew, getCode(CMD_NEWCODE, marker));
- break;
case CMD_OMIT:
getUntilEnd(cmd);
break;
case CMD_OMITVALUE: {
+ leavePara();
p1 = getArgument();
if (!m_private->m_enumItemList.contains(p1))
m_private->m_enumItemList.append(p1);
@@ -825,12 +767,25 @@ void DocParser::parse(const QString &source, DocPrivate *docPrivate,
}
break;
}
+ case CMD_COMPARESWITH:
+ leavePara();
+ p1 = getRestOfLine();
+ if (openCommand(cmd))
+ appendAtom(Atom(Atom::ComparesLeft, p1));
+ break;
+ case CMD_ENDCOMPARESWITH:
+ if (closeCommand(cmd)) {
+ leavePara();
+ appendAtom(Atom(Atom::ComparesRight));
+ processComparesWithCommand(m_private, location());
+ }
+ break;
case CMD_PRINTLINE: {
leavePara();
QString rest = getRestOfLine();
if (s_quoting) {
- append(Atom::CodeQuoteCommand, cmdStr);
- append(Atom::CodeQuoteArgument, rest);
+ appendAtom(Atom(Atom::CodeQuoteCommand, cmdStr));
+ appendAtom(Atom(Atom::CodeQuoteArgument, rest));
}
appendToCode(m_quoter.quoteLine(location(), cmdStr, rest));
break;
@@ -839,8 +794,8 @@ void DocParser::parse(const QString &source, DocPrivate *docPrivate,
leavePara();
QString rest = getRestOfLine();
if (s_quoting) {
- append(Atom::CodeQuoteCommand, cmdStr);
- append(Atom::CodeQuoteArgument, rest);
+ appendAtom(Atom(Atom::CodeQuoteCommand, cmdStr));
+ appendAtom(Atom(Atom::CodeQuoteArgument, rest));
}
appendToCode(m_quoter.quoteTo(location(), cmdStr, rest));
break;
@@ -849,8 +804,8 @@ void DocParser::parse(const QString &source, DocPrivate *docPrivate,
leavePara();
QString rest = getRestOfLine();
if (s_quoting) {
- append(Atom::CodeQuoteCommand, cmdStr);
- append(Atom::CodeQuoteArgument, rest);
+ appendAtom(Atom(Atom::CodeQuoteCommand, cmdStr));
+ appendAtom(Atom(Atom::CodeQuoteArgument, rest));
}
appendToCode(m_quoter.quoteUntil(location(), cmdStr, rest));
break;
@@ -858,18 +813,19 @@ void DocParser::parse(const QString &source, DocPrivate *docPrivate,
case CMD_QUOTATION:
if (openCommand(cmd)) {
leavePara();
- append(Atom::QuotationLeft);
+ appendAtom(Atom(Atom::QuotationLeft));
}
break;
case CMD_QUOTEFILE: {
leavePara();
+
QString fileName = getArgument();
- Doc::quoteFromFile(location(), m_quoter, fileName);
+ quoteFromFile(fileName);
if (s_quoting) {
- append(Atom::CodeQuoteCommand, cmdStr);
- append(Atom::CodeQuoteArgument, fileName);
+ appendAtom(Atom(Atom::CodeQuoteCommand, cmdStr));
+ appendAtom(Atom(Atom::CodeQuoteArgument, fileName));
}
- append(Atom::Code, m_quoter.quoteTo(location(), cmdStr, QString()));
+ appendAtom(Atom(Atom::Code, m_quoter.quoteTo(location(), cmdStr, QString())));
m_quoter.reset();
break;
}
@@ -877,25 +833,10 @@ void DocParser::parse(const QString &source, DocPrivate *docPrivate,
leavePara();
QString arg = getArgument();
if (s_quoting) {
- append(Atom::CodeQuoteCommand, cmdStr);
- append(Atom::CodeQuoteArgument, arg);
+ appendAtom(Atom(Atom::CodeQuoteCommand, cmdStr));
+ appendAtom(Atom(Atom::CodeQuoteArgument, arg));
}
- Doc::quoteFromFile(location(), m_quoter, arg);
- break;
- }
- case CMD_QUOTEFUNCTION: {
- leavePara();
- marker = quoteFromFile();
- p1 = getRestOfLine();
- if (s_quoting) {
- append(Atom::CodeQuoteCommand, cmdStr);
- append(Atom::CodeQuoteArgument, slashed(marker->functionEndRegExp(p1)));
- }
- m_quoter.quoteTo(location(), cmdStr, slashed(marker->functionBeginRegExp(p1)));
- append(Atom::Code,
- m_quoter.quoteUntil(location(), cmdStr,
- slashed(marker->functionEndRegExp(p1))));
- m_quoter.reset();
+ quoteFromFile(arg);
break;
}
case CMD_RAW:
@@ -904,18 +845,18 @@ void DocParser::parse(const QString &source, DocPrivate *docPrivate,
if (p1.isEmpty())
location().warning(QStringLiteral("Missing format name after '\\%1'")
.arg(cmdName(CMD_RAW)));
- append(Atom::FormatIf, p1);
- append(Atom::RawString, untabifyEtc(getUntilEnd(cmd)));
- append(Atom::FormatElse);
- append(Atom::FormatEndif);
+ appendAtom(Atom(Atom::FormatIf, p1));
+ appendAtom(Atom(Atom::RawString, untabifyEtc(getUntilEnd(cmd))));
+ appendAtom(Atom(Atom::FormatElse));
+ appendAtom(Atom(Atom::FormatEndif));
break;
case CMD_ROW:
if (m_openedCommands.top() == CMD_TABLE) {
p1.clear();
if (isLeftBraceAhead())
- p1 = getArgument(true);
+ p1 = getArgument(ArgumentParsingOptions::Verbatim);
leaveTableRow();
- append(Atom::TableRowLeft, p1);
+ appendAtom(Atom(Atom::TableRowLeft, p1));
m_inTableRow = true;
} else {
if (m_openedCommands.contains(CMD_TABLE))
@@ -945,15 +886,15 @@ void DocParser::parse(const QString &source, DocPrivate *docPrivate,
case CMD_SIDEBAR:
if (openCommand(cmd)) {
leavePara();
- append(Atom::SidebarLeft);
+ appendAtom(Atom(Atom::SidebarLeft));
}
break;
case CMD_SKIPLINE: {
leavePara();
QString rest = getRestOfLine();
if (s_quoting) {
- append(Atom::CodeQuoteCommand, cmdStr);
- append(Atom::CodeQuoteArgument, rest);
+ appendAtom(Atom(Atom::CodeQuoteCommand, cmdStr));
+ appendAtom(Atom(Atom::CodeQuoteArgument, rest));
}
m_quoter.quoteLine(location(), cmdStr, rest);
break;
@@ -962,8 +903,8 @@ void DocParser::parse(const QString &source, DocPrivate *docPrivate,
leavePara();
QString rest = getRestOfLine();
if (s_quoting) {
- append(Atom::CodeQuoteCommand, cmdStr);
- append(Atom::CodeQuoteArgument, rest);
+ appendAtom(Atom(Atom::CodeQuoteCommand, cmdStr));
+ appendAtom(Atom(Atom::CodeQuoteArgument, rest));
}
m_quoter.quoteTo(location(), cmdStr, rest);
break;
@@ -972,14 +913,14 @@ void DocParser::parse(const QString &source, DocPrivate *docPrivate,
leavePara();
QString rest = getRestOfLine();
if (s_quoting) {
- append(Atom::CodeQuoteCommand, cmdStr);
- append(Atom::CodeQuoteArgument, rest);
+ appendAtom(Atom(Atom::CodeQuoteCommand, cmdStr));
+ appendAtom(Atom(Atom::CodeQuoteArgument, rest));
}
m_quoter.quoteUntil(location(), cmdStr, rest);
break;
}
case CMD_SPAN:
- p1 = ATOM_FORMATTING_SPAN + getArgument(true);
+ p1 = ATOM_FORMATTING_SPAN + getArgument(ArgumentParsingOptions::Verbatim);
startFormat(p1, cmd);
break;
case CMD_SNIPPET: {
@@ -987,11 +928,12 @@ void DocParser::parse(const QString &source, DocPrivate *docPrivate,
QString snippet = getArgument();
QString identifier = getRestOfLine();
if (s_quoting) {
- append(Atom::SnippetCommand, cmdStr);
- append(Atom::SnippetLocation, snippet);
- append(Atom::SnippetIdentifier, identifier);
+ appendAtom(Atom(Atom::SnippetCommand, cmdStr));
+ appendAtom(Atom(Atom::SnippetLocation, snippet));
+ appendAtom(Atom(Atom::SnippetIdentifier, identifier));
}
- marker = Doc::quoteFromFile(location(), m_quoter, snippet);
+ marker = CodeMarker::markerForFileName(snippet);
+ quoteFromFile(snippet);
appendToCode(m_quoter.quoteSnippet(location(), identifier), marker->atomType());
break;
}
@@ -1002,11 +944,12 @@ void DocParser::parse(const QString &source, DocPrivate *docPrivate,
startFormat(ATOM_FORMATTING_SUPERSCRIPT, cmd);
break;
case CMD_TABLE:
+ leaveValueList();
p1 = getOptionalArgument();
p2 = getOptionalArgument();
if (openCommand(cmd)) {
leavePara();
- append(Atom::TableLeft, p1, p2);
+ appendAtom(Atom(Atom::TableLeft, p1, p2));
m_inTableHeader = false;
m_inTableRow = false;
m_inTableItem = false;
@@ -1018,10 +961,19 @@ void DocParser::parse(const QString &source, DocPrivate *docPrivate,
p1 = getArgument();
p1 += QLatin1Char(',');
p1 += QString::number((int)getSectioningUnit());
- append(Atom::TableOfContents, p1);
+ appendAtom(Atom(Atom::TableOfContents, p1));
break;
case CMD_TARGET:
- insertTarget(getRestOfLine(), false);
+ if (m_openedCommands.top() == CMD_TABLE && !m_inTableItem) {
+ location().warning("Found a \\target command outside table item in a table.\n"
+ "Move the \\target inside the \\li to resolve this warning.");
+ }
+ insertTarget(getRestOfLine());
+ break;
+ case CMD_TM:
+ // Ignore command while parsing \section<N> argument
+ if (m_paragraphState != InSingleLineParagraph)
+ startFormat(ATOM_FORMATTING_TRADEMARK, cmd);
break;
case CMD_TT:
startFormat(ATOM_FORMATTING_TELETYPE, cmd);
@@ -1042,7 +994,7 @@ void DocParser::parse(const QString &source, DocPrivate *docPrivate,
QStringLiteral("Invalid Unicode character '%1' specified with '%2'")
.arg(p1, cmdName(CMD_UNICODE)));
else
- append(Atom::String, QChar(unicodeChar));
+ appendAtom(Atom(Atom::String, QChar(unicodeChar)));
break;
}
case CMD_VALUE:
@@ -1052,26 +1004,26 @@ void DocParser::parse(const QString &source, DocPrivate *docPrivate,
p1 = getArgument();
if (p1.startsWith(QLatin1String("[since "))
&& p1.endsWith(QLatin1String("]"))) {
- p2 = p1.mid(7, p1.length() - 8);
+ p2 = p1.mid(7, p1.size() - 8);
p1 = getArgument();
}
if (!m_private->m_enumItemList.contains(p1))
m_private->m_enumItemList.append(p1);
m_openedLists.top().next();
- append(Atom::ListTagLeft, ATOM_LIST_VALUE);
- append(Atom::String, p1);
- append(Atom::ListTagRight, ATOM_LIST_VALUE);
+ appendAtom(Atom(Atom::ListTagLeft, ATOM_LIST_VALUE));
+ appendAtom(Atom(Atom::String, p1));
+ appendAtom(Atom(Atom::ListTagRight, ATOM_LIST_VALUE));
if (!p2.isEmpty()) {
- append(Atom::SinceTagLeft, ATOM_LIST_VALUE);
- append(Atom::String, p2);
- append(Atom::SinceTagRight, ATOM_LIST_VALUE);
+ appendAtom(Atom(Atom::SinceTagLeft, ATOM_LIST_VALUE));
+ appendAtom(Atom(Atom::String, p2));
+ appendAtom(Atom(Atom::SinceTagRight, ATOM_LIST_VALUE));
}
- append(Atom::ListItemLeft, ATOM_LIST_VALUE);
+ appendAtom(Atom(Atom::ListItemLeft, ATOM_LIST_VALUE));
skipSpacesOrOneEndl();
if (isBlankLine())
- append(Atom::Nop);
+ appendAtom(Atom(Atom::Nop));
} else {
// ### unknown problems
}
@@ -1081,20 +1033,21 @@ void DocParser::parse(const QString &source, DocPrivate *docPrivate,
enterPara(Atom::WarningLeft, Atom::WarningRight);
break;
case CMD_OVERLOAD:
+ leavePara();
m_private->m_metacommandsUsed.insert(cmdStr);
p1.clear();
if (!isBlankLine())
p1 = getRestOfLine();
if (!p1.isEmpty()) {
- append(Atom::ParaLeft);
- append(Atom::String, "This function overloads ");
- append(Atom::AutoLink, p1);
- append(Atom::String, ".");
- append(Atom::ParaRight);
+ appendAtom(Atom(Atom::ParaLeft));
+ appendAtom(Atom(Atom::String, "This function overloads "));
+ appendAtom(Atom(Atom::AutoLink, p1));
+ appendAtom(Atom(Atom::String, "."));
+ appendAtom(Atom(Atom::ParaRight));
} else {
- append(Atom::ParaLeft);
- append(Atom::String, "This is an overloaded function.");
- append(Atom::ParaRight);
+ appendAtom(Atom(Atom::ParaLeft));
+ appendAtom(Atom(Atom::String, "This is an overloaded function."));
+ appendAtom(Atom(Atom::ParaRight));
p1 = getMetaCommandArgument(cmdStr);
}
m_private->m_metaCommandMap[cmdStr].append(ArgPair(p1, QString()));
@@ -1121,27 +1074,27 @@ void DocParser::parse(const QString &source, DocPrivate *docPrivate,
}
} else if (s_utilities.macroHash.contains(cmdStr)) {
const Macro &macro = s_utilities.macroHash.value(cmdStr);
+ QStringList macroArgs;
int numPendingFi = 0;
int numFormatDefs = 0;
- QString matchExpr;
for (auto it = macro.m_otherDefs.constBegin();
it != macro.m_otherDefs.constEnd(); ++it) {
- if (it.key() == "match") {
- matchExpr = it.value();
- } else {
- append(Atom::FormatIf, it.key());
- expandMacro(cmdStr, *it, macro.numParams);
+ if (it.key() != "match") {
+ if (numFormatDefs == 0)
+ macroArgs = getMacroArguments(cmdStr, macro);
+ appendAtom(Atom(Atom::FormatIf, it.key()));
+ expandMacro(*it, macroArgs);
++numFormatDefs;
if (it == macro.m_otherDefs.constEnd()) {
- append(Atom::FormatEndif);
+ appendAtom(Atom(Atom::FormatEndif));
} else {
- append(Atom::FormatElse);
+ appendAtom(Atom(Atom::FormatElse));
++numPendingFi;
}
}
}
while (numPendingFi-- > 0)
- append(Atom::FormatEndif);
+ appendAtom(Atom(Atom::FormatEndif));
if (!macro.m_defaultDef.isEmpty()) {
if (numFormatDefs > 0) {
@@ -1150,11 +1103,10 @@ void DocParser::parse(const QString &source, DocPrivate *docPrivate,
"format-specific and qdoc-"
"syntax definitions"));
} else {
- QString expanded = expandMacroToString(cmdStr, macro.m_defaultDef,
- macro.numParams, matchExpr);
+ QString expanded = expandMacroToString(cmdStr, macro);
m_input.replace(m_backslashPosition,
m_endPosition - m_backslashPosition, expanded);
- m_inputLength = m_input.length();
+ m_inputLength = m_input.size();
m_position = m_backslashPosition;
}
}
@@ -1162,18 +1114,46 @@ void DocParser::parse(const QString &source, DocPrivate *docPrivate,
appendWord(cmdStr);
} else {
if (!cmdStr.endsWith("propertygroup")) {
- // The QML and JS property group commands are no longer required
- // for grouping QML and JS properties. They are allowed but ignored.
+ // The QML property group commands are no longer required
+ // for grouping QML properties. They are allowed but ignored.
location().warning(QStringLiteral("Unknown command '\\%1'").arg(cmdStr),
detailsUnknownCommand(metaCommandSet, cmdStr));
}
enterPara();
- append(Atom::UnknownCommand, cmdStr);
+ appendAtom(Atom(Atom::UnknownCommand, cmdStr));
}
}
} // case '\\' (qdoc markup command)
break;
}
+ case '-': { // Catch en-dash (--) and em-dash (---) markup here.
+ enterPara();
+ qsizetype dashCount = 1;
+ ++m_position;
+
+ // Figure out how many hyphens in a row.
+ while ((m_position < m_inputLength) && (m_input.at(m_position) == '-')) {
+ ++dashCount;
+ ++m_position;
+ }
+
+ if (dashCount == 3) {
+ // 3 hyphens, append an em-dash character.
+ const QChar emDash(8212);
+ appendChar(emDash);
+ } else if (dashCount == 2) {
+ // 2 hyphens; append an en-dash character.
+ const QChar enDash(8211);
+ appendChar(enDash);
+ } else {
+ // dashCount is either one or more than three. Append a hyphen
+ // the appropriate number of times. This ensures '----' doesn't
+ // end up as an em-dash followed by a hyphen in the output.
+ for (qsizetype i = 0; i < dashCount; ++i)
+ appendChar('-');
+ }
+ break;
+ }
case '{':
enterPara();
appendChar('{');
@@ -1189,7 +1169,8 @@ void DocParser::parse(const QString &source, DocPrivate *docPrivate,
enterPara();
appendChar('}');
} else {
- append(Atom::FormattingRight, *format);
+ const auto &last{m_private->m_text.lastAtom()->string()};
+ appendAtom(Atom(Atom::FormattingRight, *format));
if (*format == ATOM_FORMATTING_INDEX) {
if (m_indexStartedParagraph)
skipAllSpaces();
@@ -1200,9 +1181,11 @@ void DocParser::parse(const QString &source, DocPrivate *docPrivate,
QString suffix =
Text::subText(currentLinkAtom, m_private->m_text.lastAtom())
.toString();
- currentLinkAtom->appendString(suffix);
+ currentLinkAtom->concatenateString(suffix);
}
currentLinkAtom = nullptr;
+ } else if (*format == ATOM_FORMATTING_TRADEMARK) {
+ m_private->m_text.lastAtom()->append(last);
}
m_pendingFormats.erase(format);
}
@@ -1215,6 +1198,8 @@ void DocParser::parse(const QString &source, DocPrivate *docPrivate,
if (m_input.at(m_position + 2) == '!') {
m_position += 2;
getRestOfLine();
+ if (m_input.at(m_position - 1) == '\n')
+ --m_position;
break;
}
Q_FALLTHROUGH(); // fall through
@@ -1255,7 +1240,10 @@ void DocParser::parse(const QString &source, DocPrivate *docPrivate,
if (newWord) {
qsizetype startPos = m_position;
- bool autolink = isAutoLinkString(m_input, m_position);
+ // No auto-linking inside links
+ bool autolink = (!m_pendingFormats.isEmpty() &&
+ m_pendingFormats.last() == ATOM_FORMATTING_LINK) ?
+ false : isAutoLinkString(m_input, m_position);
if (m_position == startPos) {
if (!ch.isSpace()) {
appendChar(ch);
@@ -1267,7 +1255,7 @@ void DocParser::parse(const QString &source, DocPrivate *docPrivate,
if (s_ignoreWords.contains(word) || word.startsWith(QString("__")))
appendWord(word);
else
- append(Atom::AutoLink, word);
+ appendAtom(Atom(Atom::AutoLink, word));
} else {
appendWord(word);
}
@@ -1280,19 +1268,19 @@ void DocParser::parse(const QString &source, DocPrivate *docPrivate,
// for compatibility
if (m_openedCommands.top() == CMD_LEGALESE) {
- append(Atom::LegaleseRight);
+ appendAtom(Atom(Atom::LegaleseRight));
m_openedCommands.pop();
}
if (m_openedCommands.top() != CMD_OMIT) {
location().warning(
QStringLiteral("Missing '\\%1'").arg(endCmdName(m_openedCommands.top())));
- } else if (preprocessorSkipping.count() > 0) {
+ } else if (preprocessorSkipping.size() > 0) {
location().warning(QStringLiteral("Missing '\\%1'").arg(cmdName(CMD_ENDIF)));
}
if (m_currentSection > Doc::NoSection) {
- append(Atom::SectionRight, QString::number(m_currentSection));
+ appendAtom(Atom(Atom::SectionRight, QString::number(m_currentSection)));
m_currentSection = Doc::NoSection;
}
@@ -1317,41 +1305,84 @@ QString DocParser::detailsUnknownCommand(const QSet<QString> &metaCommandSet, co
{
QSet<QString> commandSet = metaCommandSet;
int i = 0;
- while (cmds[i].english != nullptr) {
- commandSet.insert(*cmds[i].alias);
+ while (cmds[i].name != nullptr) {
+ commandSet.insert(cmds[i].name);
++i;
}
- if (s_utilities.aliasMap.contains(str))
- return QStringLiteral("The command '\\%1' was renamed '\\%2' by the configuration"
- " file. Use the new name.")
- .arg(str, s_utilities.aliasMap[str]);
-
QString best = nearestName(str, commandSet);
if (best.isEmpty())
return QString();
return QStringLiteral("Maybe you meant '\\%1'?").arg(best);
}
-void DocParser::insertTarget(const QString &target, bool keyword)
+/*!
+ \internal
+
+ Issues a warning about the duplicate definition of a target or keyword in
+ at \a location. \a duplicateDefinition is the target being processed; the
+ already registered definition is \a previousDefinition.
+ */
+static void warnAboutPreexistingTarget(const Location &location, const QString &duplicateDefinition, const QString &previousDefinition)
{
- if (m_targetMap.contains(target)) {
- location().warning(QStringLiteral("Duplicate target name '%1'").arg(target));
- m_targetMap[target].warning(QStringLiteral("(The previous occurrence is here)"));
- } else {
- m_targetMap.insert(target, location());
- m_private->constructExtra();
- if (keyword) {
- append(Atom::Keyword, target);
- m_private->extra->m_keywords.append(m_private->m_text.lastAtom());
- } else {
- append(Atom::Target, target);
- m_private->extra->m_targets.append(m_private->m_text.lastAtom());
- }
- }
+ location.warning(
+ QStringLiteral("Duplicate target name '%1'. The previous occurrence is here: %2")
+ .arg(duplicateDefinition, previousDefinition));
+}
+
+/*!
+ \internal
+
+ \brief Registers \a target as a linkable entity.
+
+ The main purpose of this method is to register a target as defined
+ along with its location, so that becomes a valid link target from other
+ parts of the documentation.
+
+ If the \a target name is already registered, a warning is issued,
+ as multiple definitions are problematic.
+
+ \sa insertKeyword, target-command
+ */
+void DocParser::insertTarget(const QString &target)
+{
+ if (m_targetMap.contains(target))
+ return warnAboutPreexistingTarget(location(), target, m_targetMap[target].toString());
+
+ m_targetMap.insert(target, location());
+ m_private->constructExtra();
+
+ appendAtom(Atom(Atom::Target, target));
+ m_private->extra->m_targets.append(m_private->m_text.lastAtom());
+}
+
+/*!
+ \internal
+
+ \brief Registers \a keyword as a linkable entity.
+
+ The main purpose of this method is to register a keyword as defined
+ along with its location, so that becomes a valid link target from other
+ parts of the documentation.
+
+ If the \a keyword name is already registered, a warning is issued,
+ as multiple definitions are problematic.
+
+ \sa insertTarget, keyword-command
+ */
+void DocParser::insertKeyword(const QString &keyword)
+{
+ if (m_targetMap.contains(keyword))
+ return warnAboutPreexistingTarget(location(), keyword, m_targetMap[keyword].toString());
+
+ m_targetMap.insert(keyword, location());
+ m_private->constructExtra();
+
+ appendAtom(Atom(Atom::Keyword, keyword));
+ m_private->extra->m_keywords.append(m_private->m_text.lastAtom());
}
-void DocParser::include(const QString &fileName, const QString &identifier)
+void DocParser::include(const QString &fileName, const QString &identifier, const QStringList &parameters)
{
if (location().depth() > 16)
location().fatal(QStringLiteral("Too many nested '\\%1's").arg(cmdName(CMD_INCLUDE)));
@@ -1366,51 +1397,51 @@ void DocParser::include(const QString &fileName, const QString &identifier)
} else {
location().push(fileName);
QTextStream inStream(&inFile);
- QString includedStuff = inStream.readAll();
+ QString includedContent = inStream.readAll();
inFile.close();
if (identifier.isEmpty()) {
- m_input.insert(m_position, includedStuff);
- m_inputLength = m_input.length();
- m_openedInputs.push(m_position + includedStuff.length());
+ expandArgumentsInString(includedContent, parameters);
+ m_input.insert(m_position, includedContent);
+ m_inputLength = m_input.size();
+ m_openedInputs.push(m_position + includedContent.size());
} else {
- QStringList lineBuffer = includedStuff.split(QLatin1Char('\n'));
- int i = 0;
- int startLine = -1;
- while (i < lineBuffer.size()) {
- if (lineBuffer[i].startsWith("//!")) {
- if (lineBuffer[i].contains(identifier)) {
- startLine = i + 1;
- break;
- }
- }
- ++i;
+ QStringList lineBuffer = includedContent.split(QLatin1Char('\n'));
+ qsizetype bufLen{lineBuffer.size()};
+ qsizetype i;
+ QStringView trimmedLine;
+ for (i = 0; i < bufLen; ++i) {
+ trimmedLine = QStringView{lineBuffer[i]}.trimmed();
+ if (trimmedLine.startsWith(QLatin1String("//!")) &&
+ trimmedLine.contains(identifier))
+ break;
}
- if (startLine < 0) {
+ if (i < bufLen - 1) {
+ ++i;
+ } else {
location().warning(
QStringLiteral("Cannot find '%1' in '%2'").arg(identifier, filePath));
return;
}
QString result;
- i = startLine;
do {
- if (lineBuffer[i].startsWith("//!")) {
- if (i < lineBuffer.size()) {
- if (lineBuffer[i].contains(identifier)) {
- break;
- }
- }
- } else
+ trimmedLine = QStringView{lineBuffer[i]}.trimmed();
+ if (trimmedLine.startsWith(QLatin1String("//!")) &&
+ trimmedLine.contains(identifier))
+ break;
+ else
result += lineBuffer[i] + QLatin1Char('\n');
++i;
- } while (i < lineBuffer.size());
+ } while (i < bufLen);
+
+ expandArgumentsInString(result, parameters);
if (result.isEmpty()) {
location().warning(QStringLiteral("Empty qdoc snippet '%1' in '%2'")
.arg(identifier, filePath));
} else {
m_input.insert(m_position, result);
- m_inputLength = m_input.length();
- m_openedInputs.push(m_position + result.length());
+ m_inputLength = m_input.size();
+ m_openedInputs.push(m_position + result.size());
}
}
}
@@ -1421,14 +1452,14 @@ void DocParser::startFormat(const QString &format, int cmd)
{
enterPara();
- for (const auto &item : qAsConst(m_pendingFormats)) {
+ for (const auto &item : std::as_const(m_pendingFormats)) {
if (item == format) {
location().warning(QStringLiteral("Cannot nest '\\%1' commands").arg(cmdName(cmd)));
return;
}
}
- append(Atom::FormattingLeft, format);
+ appendAtom(Atom(Atom::FormattingLeft, format));
if (isLeftBraceAhead()) {
skipSpacesOrOneEndl();
@@ -1436,11 +1467,14 @@ void DocParser::startFormat(const QString &format, int cmd)
++m_braceDepth;
++m_position;
} else {
- append(Atom::String, getArgument());
- append(Atom::FormattingRight, format);
+ const auto &arg{getArgument()};
+ appendAtom(Atom(Atom::String, arg));
+ appendAtom(Atom(Atom::FormattingRight, format));
if (format == ATOM_FORMATTING_INDEX && m_indexStartedParagraph) {
skipAllSpaces();
m_indexStartedParagraph = false;
+ } else if (format == ATOM_FORMATTING_TRADEMARK) {
+ m_private->m_text.lastAtom()->append(arg);
}
}
}
@@ -1450,7 +1484,10 @@ bool DocParser::openCommand(int cmd)
int outer = m_openedCommands.top();
bool ok = true;
- if (cmd != CMD_LINK) {
+ if (cmd == CMD_COMPARESWITH && m_openedCommands.contains(cmd)) {
+ location().warning(u"Cannot nest '\\%1' commands"_s.arg(cmdName(cmd)));
+ return false;
+ } else if (cmd != CMD_LINK) {
if (outer == CMD_LIST) {
ok = (cmd == CMD_FOOTNOTE || cmd == CMD_LIST);
} else if (outer == CMD_SIDEBAR) {
@@ -1475,13 +1512,108 @@ bool DocParser::openCommand(int cmd)
/*!
Returns \c true if \a word qualifies for auto-linking.
+
+ A word qualifies for auto-linking if either:
+
+ \list
+ \li It is composed of only upper and lowercase characters
+ \li AND It contains at least one uppercase character that is not
+ the first character of word
+ \li AND it contains at least two lowercase characters
+ \endlist
+
+ Or
+
+ \list
+ \li It is composed only of uppercase characters, lowercase
+ characters, characters in [_@] and the \c {"::"} sequence.
+ \li It contains at least one uppercase character that is not
+ the first character of word or it contains at least one
+ lowercase character
+ \li AND it contains at least one character in [_@] or it
+ contains at least one \c {"::"} sequence.
+ \endlist
+
+ Inserting or suffixing, but not prefixing, any sequence in [0-9]+
+ in a word that qualifies for auto-linking by the above rules
+ preserves the auto-linkability of the word.
+
+ Suffixing the sequence \c {"()"} to a word that qualifies for
+ auto-linking by the above rules preserves the auto-linkability of
+ a word.
+
+ FInally, a word qualifies for auto-linking if:
+
+ \list
+ \li It is composed of only uppercase characters, lowercase
+ characters and the sequence \c {"()"}
+ \li AND it contains one lowercase character and a sequence of zero, one
+ or two upper or lowercase characters
+ \li AND it contains exactly one sequence \c {"()"}
+ \li AND it contains one sequence \c {"()"} as the last two
+ characters of word
+ \endlist
+
+ For example, \c {"fOo"}, \c {"FooBar"} and \c {"foobaR"} qualify
+ for auto-linking by the first rule.
+
+ \c {"QT_DEBUG"}, \c {"::Qt"} and \c {"std::move"} qualifies for
+ auto-linking by the second rule.
+
+ \c {"SIMDVector256"} qualifies by suffixing \c {"SIMDVector"},
+ which qualifies by the first rule, with the sequence \c {"256"}
+
+ \c {"FooBar::Bar()"} qualifies by suffixing \c {"FooBar::Bar"},
+ which qualifies by the first and second rule, with the sequence \c
+ {"()"}.
+
+ \c {"Foo()"} and \c {"a()"} qualifies by the last rule.
+
+ Instead, \c {"Q"}, \c {"flower"}, \c {"_"} and \c {"()"} do not
+ qualify for auto-linking.
+
+ The rules are intended as a heuristic to catch common cases in the
+ Qt documentation where a word might represent an important
+ documented element such as a class or a method that could be
+ linked to while at the same time avoiding catching common words
+ such as \c {"A"} or \c {"Nonetheless"}.
+
+ The heuristic assumes that Qt's codebase respects a style where
+ camelCasing is the standard for most of the elements, a function
+ call is identified by the use of parenthesis and certain elements,
+ such as macros, might be fully uppercase.
+
+ Furthemore, it assumes that the Qt codebase is written in a
+ language that has an identifier grammar similar to the one for
+ C++.
*/
inline bool DocParser::isAutoLinkString(const QString &word)
{
qsizetype start = 0;
- return isAutoLinkString(word, start);
+ return isAutoLinkString(word, start) && (start == word.size());
}
+/*!
+ Returns \c true if a prefix of a substring of \a word qualifies
+ for auto-linking.
+
+ Respects the same parsing rules as the unary overload.
+
+ \a curPos defines the offset, from the first character of \ word,
+ at which the parsed substring starts.
+
+ When the call completes, \a curPos represents the offset, from the
+ first character of word, that is the successor of the offset of
+ the last parsed character.
+
+ If the return value of the call is \c true, it is guaranteed that
+ the prefix of the substring of \word that contains the characters
+ from the initial value of \a curPos and up to but not including \a
+ curPos qualifies for auto-linking.
+
+ If \a curPos is initially zero, the considered substring is the
+ entirety of \a word.
+*/
bool DocParser::isAutoLinkString(const QString &word, qsizetype &curPos)
{
qsizetype len = word.size();
@@ -1512,22 +1644,18 @@ bool DocParser::isAutoLinkString(const QString &word, qsizetype &curPos)
++numStrangeSymbols;
curPos += 2;
} else if (latin1Ch == '(') {
- if (curPos > startPos) {
- if ((curPos < len - 1) && (word.at(curPos + 1) == QLatin1Char(')'))) {
- ++numStrangeSymbols;
- m_position += 2;
- break;
- } else {
- break;
- }
- } else {
- break;
+ if ((curPos < len - 1) && (word.at(curPos + 1) == QLatin1Char(')'))) {
+ ++numStrangeSymbols;
+ m_position += 2;
}
- } else {
+
+ break;
+ } else {
break;
}
}
- return ((numUppercase >= 1 && numLowercase >= 2) || numStrangeSymbols > 0);
+
+ return ((numUppercase >= 1 && numLowercase >= 2) || (numStrangeSymbols > 0 && (numUppercase + numLowercase >= 1)));
}
bool DocParser::closeCommand(int endCmd)
@@ -1565,12 +1693,13 @@ void DocParser::startSection(Doc::Sections unit, int cmd)
leaveValueList();
if (m_currentSection == Doc::NoSection) {
- m_currentSection = (Doc::Sections)(unit);
+ m_currentSection = static_cast<Doc::Sections>(unit);
m_private->constructExtra();
- } else
+ } else {
endSection(unit, cmd);
+ }
- append(Atom::SectionLeft, QString::number(unit));
+ appendAtom(Atom(Atom::SectionLeft, QString::number(unit)));
m_private->constructExtra();
m_private->extra->m_tableOfContents.append(m_private->m_text.lastAtom());
m_private->extra->m_tableOfContentsLevels.append(unit);
@@ -1581,12 +1710,44 @@ void DocParser::startSection(Doc::Sections unit, int cmd)
void DocParser::endSection(int, int) // (int unit, int endCmd)
{
leavePara();
- append(Atom::SectionRight, QString::number(m_currentSection));
+ appendAtom(Atom(Atom::SectionRight, QString::number(m_currentSection)));
m_currentSection = (Doc::NoSection);
}
+/*!
+ \internal
+ \brief Parses arguments to QDoc's see also command.
+
+ Parses space or comma separated arguments passed to the \\sa command.
+ Multi-line input requires that the arguments are comma separated. Wrap
+ arguments in curly braces for multi-word targets, and for scope resolution
+ (for example, {QString::}{count()}).
+
+ This method updates the list of links for the See also section.
+
+ \sa {DocPrivate::}{addAlso()}, getArgument()
+ */
void DocParser::parseAlso()
{
+ auto line_comment = [this]() -> bool {
+ skipSpacesOnLine();
+ if (m_position + 2 > m_inputLength)
+ return false;
+ if (m_input[m_position].unicode() == '/') {
+ if (m_input[m_position + 1].unicode() == '/') {
+ if (m_input[m_position + 2].unicode() == '!') {
+ return true;
+ }
+ }
+ }
+ return false;
+ };
+
+ auto skip_everything_until_newline = [this]() -> void {
+ while (m_position < m_inputLength && m_input[m_position] != '\n')
+ ++m_position;
+ };
+
leavePara();
skipSpacesOnLine();
while (m_position < m_inputLength && m_input[m_position] != '\n') {
@@ -1621,8 +1782,14 @@ void DocParser::parseAlso()
}
skipSpacesOnLine();
+
+ if (line_comment())
+ skip_everything_until_newline();
+
if (m_position < m_inputLength && m_input[m_position] == ',') {
m_position++;
+ if (line_comment())
+ skip_everything_until_newline();
skipSpacesOrOneEndl();
} else if (m_position >= m_inputLength || m_input[m_position] != '\n') {
location().warning(QStringLiteral("Missing comma in '\\%1'").arg(cmdName(CMD_SA)));
@@ -1630,49 +1797,18 @@ void DocParser::parseAlso()
}
}
-void DocParser::append(Atom::AtomType type, const QString &string)
-{
- Atom::AtomType lastType = m_private->m_text.lastAtom()->type();
- if ((lastType == Atom::Code)
- && m_private->m_text.lastAtom()->string().endsWith(QLatin1String("\n\n")))
- m_private->m_text.lastAtom()->chopString();
- m_private->m_text << Atom(type, string);
-}
-
-void DocParser::append(const QString &string)
-{
- Atom::AtomType lastType = m_private->m_text.lastAtom()->type();
- if ((lastType == Atom::Code)
- && m_private->m_text.lastAtom()->string().endsWith(QLatin1String("\n\n")))
- m_private->m_text.lastAtom()->chopString();
- m_private->m_text << Atom(Atom::Link, string);
-}
-
-void DocParser::append(Atom::AtomType type, const QString &p1, const QString &p2)
-{
- Atom::AtomType lastType = m_private->m_text.lastAtom()->type();
- if ((lastType == Atom::Code)
- && m_private->m_text.lastAtom()->string().endsWith(QLatin1String("\n\n")))
- m_private->m_text.lastAtom()->chopString();
- m_private->m_text << Atom(type, p1, p2);
+void DocParser::appendAtom(const Atom& atom) {
+ m_private->m_text << atom;
}
-void DocParser::append(const QString &p1, const QString &p2)
-{
- Atom::AtomType lastType = m_private->m_text.lastAtom()->type();
- if ((lastType == Atom::Code)
- && m_private->m_text.lastAtom()->string().endsWith(QLatin1String("\n\n")))
- m_private->m_text.lastAtom()->chopString();
- if (p2.isEmpty())
- m_private->m_text << Atom(Atom::Link, p1);
- else
- m_private->m_text << LinkAtom(p1, p2);
+void DocParser::appendAtom(const LinkAtom& atom) {
+ m_private->m_text << atom;
}
void DocParser::appendChar(QChar ch)
{
if (m_private->m_text.lastAtom()->type() != Atom::String)
- append(Atom::String);
+ appendAtom(Atom(Atom::String));
Atom *atom = m_private->m_text.lastAtom();
if (ch == QLatin1Char(' ')) {
if (!atom->string().endsWith(QLatin1Char(' ')))
@@ -1684,75 +1820,77 @@ void DocParser::appendChar(QChar ch)
void DocParser::appendWord(const QString &word)
{
if (m_private->m_text.lastAtom()->type() != Atom::String) {
- append(Atom::String, word);
+ appendAtom(Atom(Atom::String, word));
} else
- m_private->m_text.lastAtom()->appendString(word);
+ m_private->m_text.lastAtom()->concatenateString(word);
}
void DocParser::appendToCode(const QString &markedCode)
{
if (!isCode(m_lastAtom)) {
- append(Atom::Code);
+ appendAtom(Atom(Atom::Code));
m_lastAtom = m_private->m_text.lastAtom();
}
- m_lastAtom->appendString(markedCode);
+ m_lastAtom->concatenateString(markedCode);
}
void DocParser::appendToCode(const QString &markedCode, Atom::AtomType defaultType)
{
if (!isCode(m_lastAtom)) {
- append(defaultType, markedCode);
+ appendAtom(Atom(defaultType, markedCode));
m_lastAtom = m_private->m_text.lastAtom();
} else {
- m_lastAtom->appendString(markedCode);
+ m_lastAtom->concatenateString(markedCode);
}
}
void DocParser::enterPara(Atom::AtomType leftType, Atom::AtomType rightType, const QString &string)
{
- if (m_paragraphState == OutsideParagraph) {
+ if (m_paragraphState != OutsideParagraph)
+ return;
- if ((m_private->m_text.lastAtom()->type() != Atom::ListItemLeft)
- && (m_private->m_text.lastAtom()->type() != Atom::DivLeft)) {
- leaveValueList();
- }
+ if ((m_private->m_text.lastAtom()->type() != Atom::ListItemLeft)
+ && (m_private->m_text.lastAtom()->type() != Atom::DivLeft)
+ && (m_private->m_text.lastAtom()->type() != Atom::DetailsLeft)) {
+ leaveValueList();
+ }
- append(leftType, string);
- m_indexStartedParagraph = false;
- m_pendingParagraphLeftType = leftType;
- m_pendingParagraphRightType = rightType;
- m_pendingParagraphString = string;
- if (leftType == Atom::SectionHeadingLeft) {
- m_paragraphState = InSingleLineParagraph;
- } else {
- m_paragraphState = InMultiLineParagraph;
- }
- skipSpacesOrOneEndl();
+ appendAtom(Atom(leftType, string));
+ m_indexStartedParagraph = false;
+ m_pendingParagraphLeftType = leftType;
+ m_pendingParagraphRightType = rightType;
+ m_pendingParagraphString = string;
+ if (leftType == Atom::SectionHeadingLeft) {
+ m_paragraphState = InSingleLineParagraph;
+ } else {
+ m_paragraphState = InMultiLineParagraph;
}
+ skipSpacesOrOneEndl();
}
void DocParser::leavePara()
{
- if (m_paragraphState != OutsideParagraph) {
- if (!m_pendingFormats.isEmpty()) {
- location().warning(QStringLiteral("Missing '}'"));
- m_pendingFormats.clear();
- }
+ if (m_paragraphState == OutsideParagraph)
+ return;
- if (m_private->m_text.lastAtom()->type() == m_pendingParagraphLeftType) {
- m_private->m_text.stripLastAtom();
- } else {
- if (m_private->m_text.lastAtom()->type() == Atom::String
- && m_private->m_text.lastAtom()->string().endsWith(QLatin1Char(' '))) {
- m_private->m_text.lastAtom()->chopString();
- }
- append(m_pendingParagraphRightType, m_pendingParagraphString);
+ if (!m_pendingFormats.isEmpty()) {
+ location().warning(QStringLiteral("Missing '}'"));
+ m_pendingFormats.clear();
+ }
+
+ if (m_private->m_text.lastAtom()->type() == m_pendingParagraphLeftType) {
+ m_private->m_text.stripLastAtom();
+ } else {
+ if (m_private->m_text.lastAtom()->type() == Atom::String
+ && m_private->m_text.lastAtom()->string().endsWith(QLatin1Char(' '))) {
+ m_private->m_text.lastAtom()->chopString();
}
- m_paragraphState = OutsideParagraph;
- m_indexStartedParagraph = false;
- m_pendingParagraphRightType = Atom::Nop;
- m_pendingParagraphString.clear();
+ appendAtom(Atom(m_pendingParagraphRightType, m_pendingParagraphString));
}
+ m_paragraphState = OutsideParagraph;
+ m_indexStartedParagraph = false;
+ m_pendingParagraphRightType = Atom::Nop;
+ m_pendingParagraphString.clear();
}
void DocParser::leaveValue()
@@ -1760,11 +1898,11 @@ void DocParser::leaveValue()
leavePara();
if (m_openedLists.isEmpty()) {
m_openedLists.push(OpenedList(OpenedList::Value));
- append(Atom::ListLeft, ATOM_LIST_VALUE);
+ appendAtom(Atom(Atom::ListLeft, ATOM_LIST_VALUE));
} else {
if (m_private->m_text.lastAtom()->type() == Atom::Nop)
m_private->m_text.stripLastAtom();
- append(Atom::ListItemRight, ATOM_LIST_VALUE);
+ appendAtom(Atom(Atom::ListItemRight, ATOM_LIST_VALUE));
}
}
@@ -1774,8 +1912,8 @@ void DocParser::leaveValueList()
if (!m_openedLists.isEmpty() && (m_openedLists.top().style() == OpenedList::Value)) {
if (m_private->m_text.lastAtom()->type() == Atom::Nop)
m_private->m_text.stripLastAtom();
- append(Atom::ListItemRight, ATOM_LIST_VALUE);
- append(Atom::ListRight, ATOM_LIST_VALUE);
+ appendAtom(Atom(Atom::ListItemRight, ATOM_LIST_VALUE));
+ appendAtom(Atom(Atom::ListRight, ATOM_LIST_VALUE));
m_openedLists.pop();
}
}
@@ -1784,29 +1922,89 @@ void DocParser::leaveTableRow()
{
if (m_inTableItem) {
leavePara();
- append(Atom::TableItemRight);
+ appendAtom(Atom(Atom::TableItemRight));
m_inTableItem = false;
}
if (m_inTableHeader) {
- append(Atom::TableHeaderRight);
+ appendAtom(Atom(Atom::TableHeaderRight));
m_inTableHeader = false;
}
if (m_inTableRow) {
- append(Atom::TableRowRight);
+ appendAtom(Atom(Atom::TableRowRight));
m_inTableRow = false;
}
}
-CodeMarker *DocParser::quoteFromFile()
+void DocParser::quoteFromFile(const QString &filename)
{
- return Doc::quoteFromFile(location(), m_quoter, getArgument());
+ // KLUDGE: We dereference file_resolver as it is temporarily a pointer.
+ // See the comment for file_resolver in the header files for more context.
+ //
+ // We spefically dereference it, instead of using the arrow
+ // operator, to better represent that we do not consider this as
+ // an actual pointer, as it should not be.
+ //
+ // Do note that we are considering it informally safe to
+ // dereference the pointer, as we expect it to always hold a value
+ // at this point, but actual enforcement of this appears nowhere
+ // in the codebase.
+ auto maybe_resolved_file{(*file_resolver).resolve(filename)};
+ if (!maybe_resolved_file) {
+ // TODO: [uncentralized-admonition][failed-resolve-file]
+ // This warning is required in multiple places.
+ // To ensure the consistency of the warning and avoid
+ // duplicating code everywhere, provide a centralized effort
+ // where the warning message can be generated (but not
+ // issued).
+ // The current format is based on what was used before, review
+ // it when it is moved out.
+ QString details = std::transform_reduce(
+ (*file_resolver).get_search_directories().cbegin(),
+ (*file_resolver).get_search_directories().cend(),
+ u"Searched directories:"_s,
+ std::plus(),
+ [](const DirectoryPath &directory_path) -> QString { return u' ' + directory_path.value(); }
+ );
+
+ location().warning(u"Cannot find file to quote from: %1"_s.arg(filename), details);
+
+ // REMARK: The following is duplicated from
+ // Doc::quoteFromFile. If, for some reason (such as a file
+ // that is inaccessible), the quoting fails but, previously,
+ // the logic duplicated here was still run.
+ // This is not true anymore as quoteFromFile does require a
+ // resolved file to be run now.
+ // It is not entirely clear if this is required for the
+ // semantics of DocParser to be preserved, but for the sake of
+ // avoiding premature breakages this was retained.
+ // Do note that this should be considered temporary as the
+ // quoter state, if any will be preserved, should not be
+ // managed in such a spread and unlocal way.
+ m_quoter.reset();
+
+ CodeMarker *marker = CodeMarker::markerForFileName(QString{});
+ m_quoter.quoteFromFile(filename, QString{}, marker->markedUpCode(QString{}, nullptr, location()));
+ } else Doc::quoteFromFile(location(), m_quoter, *maybe_resolved_file);
}
/*!
Expands a macro in-place in input.
Expects the current \e pos in the input to point to a backslash, and the macro to have a
- default definition. Format-specific macros are currently not expanded.
+ default definition. Format-specific macros are not expanded.
+
+ Behavior depends on \a options:
+
+ \value ArgumentParsingOptions::Default
+ Default macro expansion; the string following the backslash
+ must be a macro with a default definition.
+ \value ArgumentParsingOptions::Verbatim
+ The string following the backslash is rendered verbatim;
+ No macro expansion is performed.
+ \value ArgumentParsingOptions::MacroArguments
+ Used for parsing argument(s) for a macro. Allows expanding
+ macros, and also preserves a subset of commands (formatting
+ commands) within the macro argument.
\note In addition to macros, a valid use for a backslash in an argument include
escaping non-alnum characters, and splitting a single argument across multiple
@@ -1814,13 +2012,16 @@ CodeMarker *DocParser::quoteFromFile()
Returns \c true on successful macro expansion.
*/
-bool DocParser::expandMacro()
+bool DocParser::expandMacro(ArgumentParsingOptions options)
{
Q_ASSERT(m_input[m_position].unicode() == '\\');
+ if (options == ArgumentParsingOptions::Verbatim)
+ return false;
+
QString cmdStr;
qsizetype backslashPos = m_position++;
- while (m_position < m_input.length() && m_input[m_position].isLetterOrNumber())
+ while (m_position < m_input.size() && m_input[m_position].isLetterOrNumber())
cmdStr += m_input[m_position++];
m_endPosition = m_position;
@@ -1828,19 +2029,22 @@ bool DocParser::expandMacro()
if (s_utilities.macroHash.contains(cmdStr)) {
const Macro &macro = s_utilities.macroHash.value(cmdStr);
if (!macro.m_defaultDef.isEmpty()) {
- QString expanded = expandMacroToString(cmdStr, macro.m_defaultDef, macro.numParams,
- macro.m_otherDefs.value("match"));
+ QString expanded = expandMacroToString(cmdStr, macro);
m_input.replace(backslashPos, m_position - backslashPos, expanded);
- m_inputLength = m_input.length();
+ m_inputLength = m_input.size();
m_position = backslashPos;
return true;
} else {
- location().warning(QStringLiteral("Macro '%1' does not have a default definition")
- .arg(cmdStr));
+ location().warning("Macro '%1' does not have a default definition"_L1.arg(cmdStr));
}
} else {
- location().warning(QStringLiteral("Unknown macro '%1'").arg(cmdStr));
- m_position = ++backslashPos;
+ int cmd = s_utilities.cmdHash.value(cmdStr, NOT_A_CMD);
+ m_position = backslashPos;
+ if (options != ArgumentParsingOptions::MacroArguments
+ || cmd == NOT_A_CMD || !cmds[cmd].is_formatting_command) {
+ location().warning("Unknown macro '%1'"_L1.arg(cmdStr));
+ ++m_position;
+ }
}
} else if (m_input[m_position].isSpace()) {
skipAllSpaces();
@@ -1852,81 +2056,45 @@ bool DocParser::expandMacro()
return false;
}
-void DocParser::expandMacro(const QString &name, const QString &def, int numParams)
+void DocParser::expandMacro(const QString &def, const QStringList &args)
{
- if (numParams == 0) {
- append(Atom::RawString, def);
+ if (args.isEmpty()) {
+ appendAtom(Atom(Atom::RawString, def));
} else {
- QStringList args;
QString rawString;
- for (int i = 0; i < numParams; ++i) {
- if (numParams == 1 || isLeftBraceAhead()) {
- args << getArgument();
- } else {
- location().warning(QStringLiteral("Macro '\\%1' invoked with too few"
- " arguments (expected %2, got %3)")
- .arg(name)
- .arg(numParams)
- .arg(i));
- numParams = i;
- break;
- }
- }
-
- int j = 0;
- while (j < def.size()) {
- int paramNo;
- if (((paramNo = def[j].unicode()) >= 1) && (paramNo <= numParams)) {
+ for (int j = 0; j < def.size(); ++j) {
+ if (int paramNo = def[j].unicode(); paramNo >= 1 && paramNo <= args.length()) {
if (!rawString.isEmpty()) {
- append(Atom::RawString, rawString);
+ appendAtom(Atom(Atom::RawString, rawString));
rawString.clear();
}
- append(Atom::String, args[paramNo - 1]);
- j += 1;
+ appendAtom(Atom(Atom::String, args[paramNo - 1]));
} else {
- rawString += def[j++];
+ rawString += def[j];
}
}
if (!rawString.isEmpty())
- append(Atom::RawString, rawString);
+ appendAtom(Atom(Atom::RawString, rawString));
}
}
-QString DocParser::expandMacroToString(const QString &name, const QString &def, int numParams,
- const QString &matchExpr)
+QString DocParser::expandMacroToString(const QString &name, const Macro &macro)
{
+ const QString &def{macro.m_defaultDef};
QString rawString;
- if (numParams == 0) {
- rawString = def;
+ if (macro.numParams == 0) {
+ rawString = macro.m_defaultDef;
} else {
- QStringList args;
- for (int i = 0; i < numParams; ++i) {
- if (numParams == 1 || isLeftBraceAhead()) {
- args << getArgument(true);
- } else {
- location().warning(QStringLiteral("Macro '\\%1' invoked with too few"
- " arguments (expected %2, got %3)")
- .arg(name)
- .arg(numParams)
- .arg(i));
- numParams = i;
- break;
- }
- }
+ QStringList args{getMacroArguments(name, macro)};
- int j = 0;
- while (j < def.size()) {
- int paramNo;
- if (((paramNo = def[j].unicode()) >= 1) && (paramNo <= numParams)) {
- rawString += args[paramNo - 1];
- j += 1;
- } else {
- rawString += def[j++];
- }
+ for (int j = 0; j < def.size(); ++j) {
+ int paramNo = def[j].unicode();
+ rawString += (paramNo >= 1 && paramNo <= args.length()) ? args[paramNo - 1] : def[j];
}
}
+ QString matchExpr{macro.m_otherDefs.value("match")};
if (matchExpr.isEmpty())
return rawString;
@@ -1970,16 +2138,17 @@ Doc::Sections DocParser::getSectioningUnit()
is the left brace. On exit, the current character is the one
that comes after the right brace.
- If \a verbatim is true, extra whitespace is retained in the
- returned string. Otherwise, extra whitespace is removed.
+ If \a options is ArgumentParsingOptions::Verbatim, no macro
+ expansion is performed, nor is the returned string stripped
+ of extra whitespace.
*/
-QString DocParser::getBracedArgument(bool verbatim)
+QString DocParser::getBracedArgument(ArgumentParsingOptions options)
{
QString arg;
int delimDepth = 0;
- if (m_position < m_input.length() && m_input[m_position] == '{') {
+ if (m_position < m_input.size() && m_input[m_position] == '{') {
++m_position;
- while (m_position < m_input.length() && delimDepth >= 0) {
+ while (m_position < m_input.size() && delimDepth >= 0) {
switch (m_input[m_position].unicode()) {
case '{':
++delimDepth;
@@ -1993,11 +2162,11 @@ QString DocParser::getBracedArgument(bool verbatim)
++m_position;
break;
case '\\':
- if (verbatim || !expandMacro())
+ if (!expandMacro(options))
arg += m_input[m_position++];
break;
default:
- if (m_input[m_position].isSpace() && !verbatim)
+ if (m_input[m_position].isSpace() && options != ArgumentParsingOptions::Verbatim)
arg += QChar(' ');
else
arg += m_input[m_position];
@@ -2012,6 +2181,9 @@ QString DocParser::getBracedArgument(bool verbatim)
}
/*!
+ Parses and returns an argument for a command, using
+ specific parsing \a options.
+
Typically, an argument ends at the next white-space. However,
braces can be used to group words:
@@ -2024,15 +2196,15 @@ QString DocParser::getBracedArgument(bool verbatim)
is an argument too, although it contains spaces. Finally,
trailing punctuation is not included in an argument, nor is 's.
*/
-QString DocParser::getArgument(bool verbatim)
+QString DocParser::getArgument(ArgumentParsingOptions options)
{
skipSpacesOrOneEndl();
int delimDepth = 0;
qsizetype startPos = m_position;
- QString arg = getBracedArgument(verbatim);
+ QString arg = getBracedArgument(options);
if (arg.isEmpty()) {
- while ((m_position < m_input.length())
+ while ((m_position < m_input.size())
&& ((delimDepth > 0) || ((delimDepth == 0) && !m_input[m_position].isSpace()))) {
switch (m_input[m_position].unicode()) {
case '(':
@@ -2052,7 +2224,7 @@ QString DocParser::getArgument(bool verbatim)
}
break;
case '\\':
- if (verbatim || !expandMacro())
+ if (!expandMacro(options))
arg += m_input[m_position++];
break;
default:
@@ -2061,13 +2233,13 @@ QString DocParser::getArgument(bool verbatim)
}
}
m_endPosition = m_position;
- if ((arg.length() > 1) && (QString(".,:;!?").indexOf(m_input[m_position - 1]) != -1)
+ if ((arg.size() > 1) && (QString(".,:;!?").indexOf(m_input[m_position - 1]) != -1)
&& !arg.endsWith("...")) {
- arg.truncate(arg.length() - 1);
+ arg.truncate(arg.size() - 1);
--m_position;
}
- if (arg.length() > 2 && m_input.mid(m_position - 2, 2) == "'s") {
- arg.truncate(arg.length() - 2);
+ if (arg.size() > 2 && m_input.mid(m_position - 2, 2) == "'s") {
+ arg.truncate(arg.size() - 2);
m_position -= 2;
}
}
@@ -2085,9 +2257,9 @@ QString DocParser::getBracketedArgument()
QString arg;
int delimDepth = 0;
skipSpacesOrOneEndl();
- if (m_position < m_input.length() && m_input[m_position] == '[') {
+ if (m_position < m_input.size() && m_input[m_position] == '[') {
++m_position;
- while (m_position < m_input.length() && delimDepth >= 0) {
+ while (m_position < m_input.size() && delimDepth >= 0) {
switch (m_input[m_position].unicode()) {
case '[':
++delimDepth;
@@ -2115,10 +2287,35 @@ QString DocParser::getBracketedArgument()
return arg;
}
+
+/*!
+ Returns the list of arguments passed to a \a macro with name \a name.
+
+ If a macro takes more than a single argument, they are expected to be
+ wrapped in braces.
+*/
+QStringList DocParser::getMacroArguments(const QString &name, const Macro &macro)
+{
+ QStringList args;
+ for (int i = 0; i < macro.numParams; ++i) {
+ if (macro.numParams == 1 || isLeftBraceAhead()) {
+ args << getArgument(ArgumentParsingOptions::MacroArguments);
+ } else {
+ location().warning(QStringLiteral("Macro '\\%1' invoked with too few"
+ " arguments (expected %2, got %3)")
+ .arg(name)
+ .arg(macro.numParams)
+ .arg(i));
+ break;
+ }
+ }
+ return args;
+}
+
QString DocParser::getOptionalArgument()
{
skipSpacesOrOneEndl();
- if (m_position + 1 < m_input.length() && m_input[m_position] == '\\'
+ if (m_position + 1 < m_input.size() && m_input[m_position] == '\\'
&& m_input[m_position + 1].isLetterOrNumber()) {
return QString();
} else {
@@ -2126,43 +2323,66 @@ QString DocParser::getOptionalArgument()
}
}
-QString DocParser::getRestOfLine()
-{
- QString t;
+/*!
+ \brief Create a string that may optionally span multiple lines as one line.
- skipSpacesOnLine();
+ Process a block of text that may span multiple lines using trailing
+ backslashes (`\`) as line continuation character. Trailing backslashes and
+ any newline character that follow them are removed.
- bool trailingSlash = false;
+ Returns a string as if it was one continuous line of text. If trailing
+ backslashes are removed, the method returns a "simplified" QString, which
+ means any sequence of internal whitespace is replaced with a single space.
- do {
- qsizetype begin = m_position;
+ Whitespace at the start and end is always removed from the returned string.
- while (m_position < m_input.size() && m_input[m_position] != '\n') {
- if (m_input[m_position] == '\\' && !trailingSlash) {
- trailingSlash = true;
+ \sa QString::simplified(), QString::trimmed().
+ */
+QString DocParser::getRestOfLine()
+{
+ auto lineHasTrailingBackslash = [this](bool trailingBackslash) -> bool {
+ while (m_position < m_inputLength && m_input[m_position] != '\n') {
+ if (m_input[m_position] == '\\' && !trailingBackslash) {
+ trailingBackslash = true;
++m_position;
- while ((m_position < m_input.size()) && m_input[m_position].isSpace()
- && (m_input[m_position] != '\n'))
- ++m_position;
+ skipSpacesOnLine();
} else {
- trailingSlash = false;
+ trailingBackslash = false;
++m_position;
}
}
+ return trailingBackslash;
+ };
+
+ QString rest_of_line;
+ skipSpacesOnLine();
+ bool trailing_backslash{ false };
+ bool return_simplified_string{ false };
+
+ for (qsizetype start_position = m_position; m_position < m_inputLength; ++m_position) {
+ trailing_backslash = lineHasTrailingBackslash(trailing_backslash);
- if (!t.isEmpty())
- t += QLatin1Char(' ');
- t += m_input.mid(begin, m_position - begin).simplified();
+ if (!rest_of_line.isEmpty())
+ rest_of_line += QLatin1Char(' ');
+ rest_of_line += m_input.sliced(start_position, m_position - start_position);
- if (trailingSlash) {
- t.chop(1);
- t = t.simplified();
+ if (trailing_backslash) {
+ rest_of_line.truncate(rest_of_line.lastIndexOf('\\'));
+ return_simplified_string = true;
}
- if (m_position < m_input.size())
+
+ if (m_position < m_inputLength)
++m_position;
- } while (m_position < m_input.size() && trailingSlash);
- return t;
+ if (!trailing_backslash)
+ break;
+ start_position = m_position;
+ }
+
+ if (return_simplified_string)
+ return rest_of_line.simplified();
+
+ return rest_of_line.trimmed();
}
/*!
@@ -2182,7 +2402,7 @@ QString DocParser::getMetaCommandArgument(const QString &cmdStr)
++parenDepth;
else if (m_input.at(m_position) == ')')
--parenDepth;
- else if (m_input.at(m_position) == '\\' && expandMacro())
+ else if (m_input.at(m_position) == '\\' && expandMacro(ArgumentParsingOptions::Default))
continue;
++m_position;
}
@@ -2205,7 +2425,7 @@ QString DocParser::getUntilEnd(int cmd)
if (!match.hasMatch()) {
location().warning(QStringLiteral("Missing '\\%1'").arg(cmdName(endCmd)));
- m_position = m_input.length();
+ m_position = m_input.size();
} else {
qsizetype end = match.capturedStart();
t = m_input.mid(m_position, end - m_position);
@@ -2214,27 +2434,46 @@ QString DocParser::getUntilEnd(int cmd)
return t;
}
-QString DocParser::getCode(int cmd, CodeMarker *marker, const QString &argStr)
+void DocParser::expandArgumentsInString(QString &str, const QStringList &args)
{
- QString code = untabifyEtc(getUntilEnd(cmd));
-
- if (!argStr.isEmpty()) {
- QStringList args = argStr.split(" ", Qt::SkipEmptyParts);
- qsizetype paramNo, j = 0;
- while (j < code.size()) {
- if (code[j] == '\\' && j < code.size() - 1 && (paramNo = code[j + 1].digitValue()) >= 1
- && paramNo <= args.size()) {
- QString p = args[paramNo - 1];
- code.replace(j, 2, p);
- j += qMin(1, p.size());
- } else {
- ++j;
- }
+ if (args.isEmpty())
+ return;
+
+ qsizetype paramNo;
+ qsizetype j = 0;
+ while (j < str.size()) {
+ if (str[j] == '\\' && j < str.size() - 1 && (paramNo = str[j + 1].digitValue()) >= 1
+ && paramNo <= args.size()) {
+ const QString &r = args[paramNo - 1];
+ str.replace(j, 2, r);
+ j += qMin(1, r.size());
+ } else {
+ ++j;
}
}
+}
+
+/*!
+ Returns the marked-up code following the code-quoting command \a cmd, expanding
+ any arguments passed in \a argStr.
+
+ Uses the \a marker to mark up the code. If it's \c nullptr, resolve the marker
+ based on the topic and the quoted code itself.
+*/
+QString DocParser::getCode(int cmd, CodeMarker *marker, const QString &argStr)
+{
+ QString code = untabifyEtc(getUntilEnd(cmd));
+ expandArgumentsInString(code, argStr.split(" ", Qt::SkipEmptyParts));
int indent = indentLevel(code);
code = dedent(indent, code);
+
+ // If we're in a QML topic, check if the QML marker recognizes the code
+ if (!marker && !m_private->m_topics.isEmpty()
+ && m_private->m_topics[0].m_topic.startsWith("qml")) {
+ auto qmlMarker = CodeMarker::markerForLanguage("QML");
+ marker = (qmlMarker && qmlMarker->recognizeCode(code)) ? qmlMarker : nullptr;
+ }
if (marker == nullptr)
marker = CodeMarker::markerForCode(code);
return marker->markedUpCode(code, nullptr, location());
@@ -2285,7 +2524,7 @@ bool DocParser::isLeftBracketAhead()
*/
void DocParser::skipSpacesOnLine()
{
- while ((m_position < m_input.length()) && m_input[m_position].isSpace()
+ while ((m_position < m_input.size()) && m_input[m_position].isSpace()
&& (m_input[m_position].unicode() != '\n'))
++m_position;
}
@@ -2296,7 +2535,7 @@ void DocParser::skipSpacesOnLine()
void DocParser::skipSpacesOrOneEndl()
{
qsizetype firstEndl = -1;
- while (m_position < m_input.length() && m_input[m_position].isSpace()) {
+ while (m_position < m_input.size() && m_input[m_position].isSpace()) {
QChar ch = m_input[m_position];
if (ch == '\n') {
if (firstEndl == -1) {
@@ -2323,7 +2562,7 @@ void DocParser::skipToNextPreprocessorCommand()
auto match = rx.match(m_input, m_position + 1); // ### + 1 necessary?
if (!match.hasMatch())
- m_position = m_input.length();
+ m_position = m_input.size();
else
m_position = match.capturedStart();
}
@@ -2335,14 +2574,14 @@ int DocParser::endCmdFor(int cmd)
return CMD_ENDCODE;
case CMD_CODE:
return CMD_ENDCODE;
+ case CMD_COMPARESWITH:
+ return CMD_ENDCOMPARESWITH;
+ case CMD_DETAILS:
+ return CMD_ENDDETAILS;
case CMD_DIV:
return CMD_ENDDIV;
case CMD_QML:
return CMD_ENDQML;
- case CMD_QMLTEXT:
- return CMD_ENDQMLTEXT;
- case CMD_JS:
- return CMD_ENDJS;
case CMD_FOOTNOTE:
return CMD_ENDFOOTNOTE;
case CMD_LEGALESE:
@@ -2351,10 +2590,6 @@ int DocParser::endCmdFor(int cmd)
return CMD_ENDLINK;
case CMD_LIST:
return CMD_ENDLIST;
- case CMD_NEWCODE:
- return CMD_ENDCODE;
- case CMD_OLDCODE:
- return CMD_NEWCODE;
case CMD_OMIT:
return CMD_ENDOMIT;
case CMD_QUOTATION:
@@ -2380,7 +2615,7 @@ int DocParser::endCmdFor(int cmd)
QString DocParser::cmdName(int cmd)
{
- return *cmds[cmd].alias;
+ return cmds[cmd].name;
}
QString DocParser::endCmdName(int cmd)
@@ -2391,7 +2626,7 @@ QString DocParser::endCmdName(int cmd)
QString DocParser::untabifyEtc(const QString &str)
{
QString result;
- result.reserve(str.length());
+ result.reserve(str.size());
int column = 0;
for (const auto &character : str) {
@@ -2414,7 +2649,7 @@ QString DocParser::untabifyEtc(const QString &str)
}
while (result.endsWith("\n\n"))
- result.truncate(result.length() - 1);
+ result.truncate(result.size() - 1);
while (result.startsWith(QLatin1Char('\n')))
result = result.mid(1);
@@ -2459,20 +2694,13 @@ QString DocParser::dedent(int level, const QString &str)
return result;
}
-QString DocParser::slashed(const QString &str)
-{
- QString result = str;
- result.replace(QLatin1Char('/'), "\\/");
- return QLatin1Char('/') + result + QLatin1Char('/');
-}
-
/*!
Returns \c true if \a atom represents a code snippet.
*/
bool DocParser::isCode(const Atom *atom)
{
Atom::AtomType type = atom->type();
- return (type == Atom::Code || type == Atom::Qml || type == Atom::JavaScript);
+ return (type == Atom::Code || type == Atom::Qml);
}
/*!
@@ -2486,4 +2714,123 @@ bool DocParser::isQuote(const Atom *atom)
|| type == Atom::SnippetLocation);
}
+/*!
+ \internal
+ Processes the arguments passed to the \\compareswith block command.
+ The arguments are stored as text within the first atom of the block
+ (Atom::ComparesLeft).
+
+ Extracts the comparison category and the list of types, and stores
+ the information into a map accessed via \a priv.
+*/
+static void processComparesWithCommand(DocPrivate *priv, const Location &location)
+{
+ static auto take_while = [](QStringView input, auto predicate) {
+ QStringView::size_type end{0};
+
+ while (end < input.size() && std::invoke(predicate, input[end]))
+ ++end;
+
+ return std::make_tuple(input.sliced(0, end), input.sliced(end));
+ };
+
+ static auto peek = [](QStringView input, QChar c) {
+ return !input.empty() && input.first() == c;
+ };
+
+ static auto skip_one = [](QStringView input) {
+ if (input.empty()) return std::make_tuple(QStringView{}, input);
+ else return std::make_tuple(input.sliced(0, 1), input.sliced(1));
+ };
+
+ static auto enclosed = [](QStringView input, QChar open, QChar close) {
+ if (!peek(input, open)) return std::make_tuple(QStringView{}, input);
+
+ auto [opened, without_open] = skip_one(input);
+ auto [parsed, remaining] = take_while(without_open, [close](QChar c){ return c != close; });
+
+ if (remaining.empty()) return std::make_tuple(QStringView{}, input);
+
+ auto [closed, without_close] = skip_one(remaining);
+
+ return std::make_tuple(parsed.trimmed(), without_close);
+ };
+
+ static auto one_of = [](auto first, auto second) {
+ return [first, second](QStringView input) {
+ auto [parsed, remaining] = std::invoke(first, input);
+
+ if (parsed.empty()) return std::invoke(second, input);
+ else return std::make_tuple(parsed, remaining);
+ };
+ };
+
+ static auto collect = [](QStringView input, auto parser) {
+ QStringList collected{};
+
+ while (true) {
+ auto [parsed, remaining] = std::invoke(parser, input);
+
+ if (parsed.empty()) break;
+ collected.append(parsed.toString());
+
+ input = remaining;
+ };
+
+ return collected;
+ };
+
+ static auto spaces = [](QStringView input) {
+ return take_while(input, [](QChar c){ return c.isSpace(); });
+ };
+
+ static auto word = [](QStringView input) {
+ return take_while(input, [](QChar c){ return !c.isSpace(); });
+ };
+
+ static auto parse_argument = [](QStringView input) {
+ auto [_, without_spaces] = spaces(input);
+
+ return one_of(
+ [](QStringView input){ return enclosed(input, '{', '}'); },
+ word
+ )(without_spaces);
+ };
+
+ const QString cmd{DocParser::cmdName(CMD_COMPARESWITH)};
+ Text text = priv->m_text.splitAtFirst(Atom::ComparesLeft);
+
+ auto *atom = text.firstAtom();
+ QStringList segments = collect(atom->string(), parse_argument);
+
+ QString categoryString;
+ if (!segments.isEmpty())
+ categoryString = segments.takeFirst();
+ auto category = comparisonCategoryFromString(categoryString.toStdString());
+
+ if (category == ComparisonCategory::None) {
+ location.warning(u"Invalid argument to \\%1 command: `%2`"_s.arg(cmd, categoryString),
+ u"Valid arguments are `strong`, `weak`, `partial`, or `equality`."_s);
+ return;
+ }
+
+ if (segments.isEmpty()) {
+ location.warning(u"Missing argument to \\%1 command."_s.arg(cmd),
+ u"Provide at least one type name, or a list of types separated by spaces."_s);
+ return;
+ }
+
+ // Store cleaned-up type names back into the atom
+ segments.removeDuplicates();
+ atom->setString(segments.join(QLatin1Char(';')));
+
+ // Add an entry to meta-command map for error handling in CppCodeParser
+ priv->m_metaCommandMap[cmd].append(ArgPair(categoryString, atom->string()));
+ priv->m_metacommandsUsed.insert(cmd);
+
+ priv->constructExtra();
+ const auto end{priv->extra->m_comparesWithMap.cend()};
+ priv->extra->m_comparesWithMap.insert(end, category, text);
+}
+
QT_END_NAMESPACE
diff --git a/src/qdoc/docparser.h b/src/qdoc/qdoc/src/qdoc/docparser.h
index d59115c16..c577e12ba 100644
--- a/src/qdoc/docparser.h
+++ b/src/qdoc/qdoc/src/qdoc/docparser.h
@@ -1,30 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the tools applications of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#ifndef DOCPARSER_H
#define DOCPARSER_H
@@ -35,6 +10,8 @@
#include "openedlist.h"
#include "quoter.h"
+#include "filesystem/fileresolver.h"
+
#include <QtCore/QCoreApplication>
#include <QtCore/qglobalstatic.h>
#include <QtCore/qhash.h>
@@ -46,6 +23,7 @@ QT_BEGIN_NAMESPACE
class Doc;
class DocPrivate;
class CodeMarker;
+struct Macro;
class DocParser
{
@@ -53,39 +31,39 @@ public:
void parse(const QString &source, DocPrivate *docPrivate, const QSet<QString> &metaCommandSet,
const QSet<QString> &possibleTopics);
- static void initialize(const Config &config);
- static void terminate();
+ static void initialize(const Config &config, FileResolver& file_resolver);
static int endCmdFor(int cmd);
static QString cmdName(int cmd);
static QString endCmdName(int cmd);
static QString untabifyEtc(const QString &str);
static int indentLevel(const QString &str);
static QString dedent(int level, const QString &str);
- static QString slashed(const QString &str);
static int s_tabSize;
- static QStringList s_exampleFiles;
- static QStringList s_exampleDirs;
- static QStringList s_sourceFiles;
- static QStringList s_sourceDirs;
static QStringList s_ignoreWords;
static bool s_quoting;
private:
+
+ enum class ArgumentParsingOptions {
+ Default,
+ Verbatim,
+ MacroArguments
+ };
+
Location &location();
QString detailsUnknownCommand(const QSet<QString> &metaCommandSet, const QString &str);
- void insertTarget(const QString &target, bool keyword);
- void include(const QString &fileName, const QString &identifier);
+ void insertTarget(const QString &target);
+ void insertKeyword(const QString &keyword);
+ void include(const QString &fileName, const QString &identifier, const QStringList &parameters);
void startFormat(const QString &format, int cmd);
bool openCommand(int cmd);
bool closeCommand(int endCmd);
void startSection(Doc::Sections unit, int cmd);
void endSection(int unit, int endCmd);
void parseAlso();
- void append(const QString &string);
- void append(Atom::AtomType type, const QString &string = QString());
- void append(Atom::AtomType type, const QString &p1, const QString &p2);
- void append(const QString &p1, const QString &p2);
+ void appendAtom(const Atom&);
+ void appendAtom(const LinkAtom&);
void appendChar(QChar ch);
void appendWord(const QString &word);
void appendToCode(const QString &code);
@@ -96,15 +74,15 @@ private:
void leaveValue();
void leaveValueList();
void leaveTableRow();
- CodeMarker *quoteFromFile();
- bool expandMacro();
- void expandMacro(const QString &name, const QString &def, int numParams);
- QString expandMacroToString(const QString &name, const QString &def, int numParams,
- const QString &matchExpr);
+ void quoteFromFile(const QString& filename);
+ bool expandMacro(ArgumentParsingOptions options);
+ void expandMacro(const QString &def, const QStringList &args);
+ QString expandMacroToString(const QString &name, const Macro &macro);
Doc::Sections getSectioningUnit();
- QString getArgument(bool verbatim = false);
- QString getBracedArgument(bool verbatim);
+ QString getArgument(ArgumentParsingOptions options = ArgumentParsingOptions::Default);
+ QString getBracedArgument(ArgumentParsingOptions options);
QString getBracketedArgument();
+ QStringList getMacroArguments(const QString &name, const Macro &macro);
QString getOptionalArgument();
QString getRestOfLine();
QString getMetaCommandArgument(const QString &cmdStr);
@@ -122,6 +100,7 @@ private:
void skipToNextPreprocessorCommand();
static bool isCode(const Atom *atom);
static bool isQuote(const Atom *atom);
+ static void expandArgumentsInString(QString &str, const QStringList &args);
QStack<qsizetype> m_openedInputs {};
@@ -154,7 +133,44 @@ private:
Atom *m_lastAtom { nullptr };
static DocUtilities &s_utilities;
+
+ // KLUDGE: When parsing documentation, there is a need to find
+ // files to resolve quoting commands. Ideally, the system that
+ // takes care of this would be a non-static member that is a
+ // reference that is passed at
+ // construction time.
+ // Nonetheless, with how the current codebase is constructed, this
+ // has proven to be extremely difficult until more changes are
+ // done. In particular, the construction of a DocParser happens in
+ // multiple places at multiple depths and, in particular, happens
+ // in one of Doc's constructor.
+ // Doc itself is built, again, in multiple places at multiple
+ // depths, making it clumsy and sometimes infeasible to pass the
+ // dependency around so that it is available at the required
+ // places. In particular, this stems from the fact that Doc is
+ // holding many responsabilities and is spread troughtout much of
+ // the codebase in different ways. DocParser mostly depends on Doc
+ // and Doc currently depends on DocParser, making the two
+ // difficult to separate.
+ //
+ // In the future, we expect Doc to mostly be removed, such as to
+ // remove this dependencies and the parsing of documentation to
+ // happen near main and atomically from other endevours, producing
+ // an intermediate representation that is consumed by later
+ // phases.
+ // At that point, it should be possible to not have this kind of
+ // indirection while, for now, the only accessible way to pass
+ // this dependency is trough the initialize method which passes
+ // for Doc::initialize.
+ //
+ // Furthemore, as we cannot late-bind a reference, and having a
+ // desire to avoid an unnecessary copy, we are thus forced to use
+ // a different storage method, in this case a pointer.
+ // This too should be removed later on, using reference or move
+ // semantic depending on the required data-flow.
+ static FileResolver* file_resolver;
};
+
QT_END_NAMESPACE
#endif // DOCPARSER_H
diff --git a/src/qdoc/qdoc/src/qdoc/docprivate.cpp b/src/qdoc/qdoc/src/qdoc/docprivate.cpp
new file mode 100644
index 000000000..a7c178d57
--- /dev/null
+++ b/src/qdoc/qdoc/src/qdoc/docprivate.cpp
@@ -0,0 +1,30 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+#include "docprivate.h"
+
+#include "text.h"
+
+#include <QtCore/qhash.h>
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ Deletes the DocPrivateExtra.
+ */
+DocPrivate::~DocPrivate()
+{
+ delete extra;
+}
+
+void DocPrivate::addAlso(const Text &also)
+{
+ m_alsoList.append(also);
+}
+
+void DocPrivate::constructExtra()
+{
+ if (extra == nullptr)
+ extra = new DocPrivateExtra;
+}
+
+QT_END_NAMESPACE
diff --git a/src/qdoc/docprivate.h b/src/qdoc/qdoc/src/qdoc/docprivate.h
index edd63a6fe..7402290c9 100644
--- a/src/qdoc/docprivate.h
+++ b/src/qdoc/qdoc/src/qdoc/docprivate.h
@@ -1,30 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the tools applications of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#ifndef DOCPRIVATE_H
#define DOCPRIVATE_H
@@ -62,6 +37,7 @@ struct DocPrivateExtra
QList<Atom *> m_keywords {};
QList<Atom *> m_targets {};
QStringMultiMap m_metaMap {};
+ QMultiMap<ComparisonCategory, Text> m_comparesWithMap {};
};
class DocPrivate
diff --git a/src/qdoc/qdoc/src/qdoc/docutilities.h b/src/qdoc/qdoc/src/qdoc/docutilities.h
new file mode 100644
index 000000000..d4483ac73
--- /dev/null
+++ b/src/qdoc/qdoc/src/qdoc/docutilities.h
@@ -0,0 +1,28 @@
+// Copyright (C) 2020 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+#ifndef DOCUTILITIES_H
+#define DOCUTILITIES_H
+
+#include "macro.h"
+#include "singleton.h"
+
+#include <QtCore/qglobal.h>
+#include <QtCore/qhash.h>
+#include <QtCore/qstring.h>
+#include <QtCore/qmap.h>
+
+QT_BEGIN_NAMESPACE
+
+typedef QHash<QString, int> QHash_QString_int;
+typedef QHash<QString, Macro> QHash_QString_Macro;
+
+struct DocUtilities : public Singleton<DocUtilities>
+{
+public:
+ QHash_QString_int cmdHash;
+ QHash_QString_Macro macroHash;
+};
+
+QT_END_NAMESPACE
+
+#endif // DOCUTILITIES_H
diff --git a/src/qdoc/qdoc/src/qdoc/editdistance.cpp b/src/qdoc/qdoc/src/qdoc/editdistance.cpp
new file mode 100644
index 000000000..303979ec3
--- /dev/null
+++ b/src/qdoc/qdoc/src/qdoc/editdistance.cpp
@@ -0,0 +1,68 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "editdistance.h"
+
+QT_BEGIN_NAMESPACE
+
+int editDistance(const QString &s, const QString &t)
+{
+#define D(i, j) d[(i)*n + (j)]
+ int i;
+ int j;
+ qsizetype m = s.size() + 1;
+ qsizetype n = t.size() + 1;
+ int *d = new int[m * n];
+ int result;
+
+ for (i = 0; i < m; ++i)
+ D(i, 0) = i;
+ for (j = 0; j < n; ++j)
+ D(0, j) = j;
+ for (i = 1; i < m; ++i) {
+ for (j = 1; j < n; ++j) {
+ if (s[i - 1] == t[j - 1]) {
+ D(i, j) = D(i - 1, j - 1);
+ } else {
+ int x = D(i - 1, j);
+ int y = D(i - 1, j - 1);
+ int z = D(i, j - 1);
+ D(i, j) = 1 + qMin(qMin(x, y), z);
+ }
+ }
+ }
+ result = D(m - 1, n - 1);
+ delete[] d;
+ return result;
+#undef D
+}
+
+QString nearestName(const QString &actual, const QSet<QString> &candidates)
+{
+ if (actual.isEmpty())
+ return QString();
+
+ int deltaBest = 10000;
+ int numBest = 0;
+ QString best;
+
+ for (const auto &candidate : candidates) {
+ if (candidate[0] == actual[0]) {
+ int delta = editDistance(actual, candidate);
+ if (delta < deltaBest) {
+ deltaBest = delta;
+ numBest = 1;
+ best = candidate;
+ } else if (delta == deltaBest) {
+ ++numBest;
+ }
+ }
+ }
+
+ if (numBest == 1 && deltaBest <= 2 && actual.size() + best.size() >= 5)
+ return best;
+
+ return QString();
+}
+
+QT_END_NAMESPACE
diff --git a/src/qdoc/qdoc/src/qdoc/editdistance.h b/src/qdoc/qdoc/src/qdoc/editdistance.h
new file mode 100644
index 000000000..dfa6a42dd
--- /dev/null
+++ b/src/qdoc/qdoc/src/qdoc/editdistance.h
@@ -0,0 +1,17 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#ifndef EDITDISTANCE_H
+#define EDITDISTANCE_H
+
+#include <QtCore/qset.h>
+#include <QtCore/qstring.h>
+
+QT_BEGIN_NAMESPACE
+
+int editDistance(const QString &s, const QString &t);
+QString nearestName(const QString &actual, const QSet<QString> &candidates);
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/qdoc/qdoc/src/qdoc/enumitem.h b/src/qdoc/qdoc/src/qdoc/enumitem.h
new file mode 100644
index 000000000..06e9d42a9
--- /dev/null
+++ b/src/qdoc/qdoc/src/qdoc/enumitem.h
@@ -0,0 +1,36 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#ifndef ENUMITEM_H
+#define ENUMITEM_H
+
+#include <QtCore/qglobal.h>
+#include <QtCore/qstring.h>
+
+#include <utility>
+
+QT_BEGIN_NAMESPACE
+
+class EnumItem
+{
+public:
+ EnumItem() = default;
+ EnumItem(QString name, QString value, QString since = QString())
+ : m_name(std::move(name)),
+ m_value(std::move(value)),
+ m_since(std::move(since)) {}
+
+ [[nodiscard]] const QString &name() const { return m_name; }
+ [[nodiscard]] const QString &value() const { return m_value; }
+ [[nodiscard]] const QString &since() const { return m_since; }
+ void setSince(const QString &since) { m_since = since; }
+
+private:
+ QString m_name {};
+ QString m_value {};
+ QString m_since {};
+};
+
+QT_END_NAMESPACE
+
+#endif // ENUMITEM_H
diff --git a/src/qdoc/qdoc/src/qdoc/enumnode.cpp b/src/qdoc/qdoc/src/qdoc/enumnode.cpp
new file mode 100644
index 000000000..48a2f81aa
--- /dev/null
+++ b/src/qdoc/qdoc/src/qdoc/enumnode.cpp
@@ -0,0 +1,82 @@
+// Copyright (C) 2020 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "enumnode.h"
+
+#include "aggregate.h"
+#include "typedefnode.h"
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class EnumNode
+ */
+
+/*!
+ Add \a item to the enum type's item list.
+ */
+void EnumNode::addItem(const EnumItem &item)
+{
+ m_items.append(item);
+ m_names.insert(item.name());
+}
+
+/*!
+ Returns the access level of the enumeration item named \a name.
+ Apparently it is private if it has been omitted by qdoc's
+ omitvalue command. Otherwise it is public.
+ */
+Access EnumNode::itemAccess(const QString &name) const
+{
+ if (doc().omitEnumItemNames().contains(name))
+ return Access::Private;
+ return Access::Public;
+}
+
+/*!
+ Returns the enum value associated with the enum \a name.
+ */
+QString EnumNode::itemValue(const QString &name) const
+{
+ for (const auto &item : std::as_const(m_items)) {
+ if (item.name() == name)
+ return item.value();
+ }
+ return QString();
+}
+
+/*!
+ Sets \a since information to a named enum \a value, if it
+ exists in this enum.
+*/
+void EnumNode::setSince(const QString &value, const QString &since)
+{
+ auto it = std::find_if(m_items.begin(), m_items.end(), [value](EnumItem ev) {
+ return ev.name() == value;
+ });
+ if (it != m_items.end())
+ it->setSince(since);
+}
+
+/*!
+ Clone this node on the heap and make the clone a child of
+ \a parent.
+
+ Returns a pointer to the clone.
+ */
+Node *EnumNode::clone(Aggregate *parent)
+{
+ auto *en = new EnumNode(*this); // shallow copy
+ en->setParent(nullptr);
+ parent->addChild(en);
+
+ return en;
+}
+
+void EnumNode::setFlagsType(TypedefNode *typedefNode)
+{
+ m_flagsType = typedefNode;
+ typedefNode->setAssociatedEnum(this);
+}
+
+QT_END_NAMESPACE
diff --git a/src/qdoc/qdoc/src/qdoc/enumnode.h b/src/qdoc/qdoc/src/qdoc/enumnode.h
new file mode 100644
index 000000000..47139ae4d
--- /dev/null
+++ b/src/qdoc/qdoc/src/qdoc/enumnode.h
@@ -0,0 +1,49 @@
+// Copyright (C) 2020 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#ifndef ENUMNODE_H
+#define ENUMNODE_H
+
+#include "access.h"
+#include "node.h"
+#include "typedefnode.h"
+
+#include <QtCore/qglobal.h>
+#include <QtCore/qlist.h>
+#include <QtCore/qset.h>
+#include <QtCore/qstring.h>
+
+QT_BEGIN_NAMESPACE
+
+class Aggregate;
+
+class EnumNode : public Node
+{
+public:
+ EnumNode(Aggregate *parent, const QString &name, bool isScoped = false)
+ : Node(Enum, parent, name), m_isScoped(isScoped)
+ {
+ }
+
+ void addItem(const EnumItem &item);
+ void setFlagsType(TypedefNode *typedefNode);
+ bool hasItem(const QString &name) const { return m_names.contains(name); }
+ bool isScoped() const { return m_isScoped; }
+
+ const QList<EnumItem> &items() const { return m_items; }
+ Access itemAccess(const QString &name) const;
+ const TypedefNode *flagsType() const { return m_flagsType; }
+ QString itemValue(const QString &name) const;
+ Node *clone(Aggregate *parent) override;
+ void setSince(const QString &value, const QString &since);
+
+private:
+ QList<EnumItem> m_items {};
+ QSet<QString> m_names {};
+ const TypedefNode *m_flagsType { nullptr };
+ bool m_isScoped { false };
+};
+
+QT_END_NAMESPACE
+
+#endif // ENUMNODE_H
diff --git a/src/qdoc/qdoc/src/qdoc/examplenode.h b/src/qdoc/qdoc/src/qdoc/examplenode.h
new file mode 100644
index 000000000..920092e4e
--- /dev/null
+++ b/src/qdoc/qdoc/src/qdoc/examplenode.h
@@ -0,0 +1,42 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#ifndef EXAMPLENODE_H
+#define EXAMPLENODE_H
+
+#include "pagenode.h"
+
+#include <QtCore/qglobal.h>
+#include <QtCore/qstring.h>
+#include <QtCore/qstringlist.h>
+
+QT_BEGIN_NAMESPACE
+
+class ExampleNode : public PageNode
+{
+public:
+ ExampleNode(Aggregate *parent, const QString &name) : PageNode(Node::Example, parent, name) {}
+ [[nodiscard]] QString imageFileName() const override { return m_imageFileName; }
+ void setImageFileName(const QString &ifn) override { m_imageFileName = ifn; }
+ [[nodiscard]] const QStringList &files() const { return m_files; }
+ [[nodiscard]] const QStringList &images() const { return m_images; }
+ [[nodiscard]] const QString &projectFile() const { return m_projectFile; }
+ void setFiles(const QStringList &files, const QString &projectFile)
+ {
+ m_files = files;
+ m_projectFile = projectFile;
+ }
+ void setImages(const QStringList &images) { m_images = images; }
+ void appendFile(QString &file) { m_files.append(file); }
+ void appendImage(QString &image) { m_images.append(image); }
+
+private:
+ QString m_imageFileName {};
+ QString m_projectFile {};
+ QStringList m_files {};
+ QStringList m_images {};
+};
+
+QT_END_NAMESPACE
+
+#endif // EXAMPLENODE_H
diff --git a/src/qdoc/qdoc/src/qdoc/externalpagenode.cpp b/src/qdoc/qdoc/src/qdoc/externalpagenode.cpp
new file mode 100644
index 000000000..30a583d00
--- /dev/null
+++ b/src/qdoc/qdoc/src/qdoc/externalpagenode.cpp
@@ -0,0 +1,28 @@
+// Copyright (C) 2020 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "externalpagenode.h"
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class ExternalPageNode
+
+ \brief The ExternalPageNode represents an external documentation page.
+
+ Qdoc can generate links to pages that are not part of the documentation
+ being generated. 3rd party software pages are often referenced by links
+ from the QT documentation. Qdoc creates an ExternalPageNode when it sees
+ an \c {\\externalpage} command. The HTML generator can then use the node
+ when it needs to create links to the external page.
+
+ ExternalPageNode inherits PageNode.
+*/
+
+/*! \fn ExternalPageNode::ExternalPageNode(Aggregate *parent, const QString &name)
+ The constructor creates an ExternalPageNode as a child node of \a parent.
+ It's \a name is the argument from the \c {\\externalpage} command. The node
+ type is Node::ExternalPage, and the page type is Node::ArticlePage.
+ */
+
+QT_END_NAMESPACE
diff --git a/src/qdoc/qdoc/src/qdoc/externalpagenode.h b/src/qdoc/qdoc/src/qdoc/externalpagenode.h
new file mode 100644
index 000000000..e67aab0e8
--- /dev/null
+++ b/src/qdoc/qdoc/src/qdoc/externalpagenode.h
@@ -0,0 +1,25 @@
+// Copyright (C) 2020 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#ifndef EXTERNALPAGENODE_H
+#define EXTERNALPAGENODE_H
+
+#include "pagenode.h"
+
+#include <QtCore/qglobal.h>
+
+QT_BEGIN_NAMESPACE
+
+class ExternalPageNode : public PageNode
+{
+public:
+ ExternalPageNode(Aggregate *parent, const QString &url)
+ : PageNode(Node::ExternalPage, parent, url)
+ {
+ setUrl(url);
+ }
+};
+
+QT_END_NAMESPACE
+
+#endif // EXTERNALPAGENODE_H
diff --git a/src/qdoc/qdoc/src/qdoc/filesystem/fileresolver.cpp b/src/qdoc/qdoc/src/qdoc/filesystem/fileresolver.cpp
new file mode 100644
index 000000000..aaa489085
--- /dev/null
+++ b/src/qdoc/qdoc/src/qdoc/filesystem/fileresolver.cpp
@@ -0,0 +1,161 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "fileresolver.h"
+
+#include "qdoc/boundaries/filesystem/filepath.h"
+
+#include <QDir>
+
+#include <iostream>
+#include <algorithm>
+
+/*!
+ * \class FileResolver
+ * \brief Encapsulate the logic that QDoc uses to find files whose
+ * path is provided by the user and that are relative to the current
+ * configuration.
+ *
+ * A FileResolver instance is configured during creation, defining the
+ * root directories that the search should be performed on.
+ *
+ * Afterwards, it can be used to resolve paths relative to those
+ * directories, by querying through the resolve() method.
+ *
+ * Queries are resolved through a linear search through root
+ * directories, finding at most one file each time.
+ * A file is considered to be resolved if, from any root directory,
+ * the query represents an existing file.
+ *
+ * For example, consider the following directory structure on some
+ * filesystem:
+ *
+ * \badcode
+ * foo/
+ * |
+ * |-bar/
+ * |-|
+ * | |-anotherfile.txt
+ * |-file.txt
+ * \endcode
+ *
+ * And consider an instance of FileResolver tha considers \e{foo/} to
+ * be a root directory for search.
+ *
+ * Then, queries such as \e {bar/anotherfile.txt} and \e {file.txt}
+ * will be resolved.
+ *
+ * Instead, queries such as \e {foobar.cpp}, \e {bar}, and \e
+ * {foo/bar/anotherfile.txt} will not be resolved, as they do not
+ * represent any file reachable from a root directory for search.
+ *
+ * It is important to note that FileResolver always searches its root
+ * directories in an order that is based on the lexicographic ordering
+ * of the path of its root directories.
+ *
+ * For example, consider the following directory structure on some
+ * filesystem:
+ *
+ * \badcode
+ * foo/
+ * |
+ * |-bar/
+ * |-|
+ * | |-file.txt
+ * |-foobar/
+ * |-|
+ * | |-file.txt
+ * \endcode
+ *
+ * And consider an instance of FileResolver that considers \e
+ * {foo/bar/} and \e {foo/foobar/} to be root directories for search.
+ *
+ * Then, when the query \e {file.txt} is resolved, it will always
+ * resolve to the file in \e {bar}, as \e {bar} will be searched
+ * before \e {foobar}.
+ *
+ * We say that \e {foobar/file.txt} is shadowed by \e {bar/file.txt}.
+ *
+ * Currently, if this is an issue, it is possible to resolve it by
+ * using a common ancestor as a root directory instead of using
+ * multiples directories.
+ *
+ * In the previous example, if \e {foo} is instead chosen as the root
+ * directory for search, then queries \e {bar/file.txt} and \e
+ * {foobar/file.txt} can be used to uniquely resolve the two files,
+ * removing the shadowing.
+ * */
+
+/*!
+ * Constructs an instance of FileResolver with the directories in \a
+ * search_directories as root directories for searching.
+ *
+ * Duplicates in \a search_directories do not affect the resolution of
+ * files for the instance.
+ *
+ * For example, if \a search_directories contains some directory D
+ * more than once, the constructed instance will resolve files
+ * equivalently to an instance constructed with a single appearance of
+ * D.
+ *
+ * The order of \a search_directories does not affect the resolution
+ * of files for an instance.
+ *
+ * For example, if \a search_directories contains a permutation of
+ * directories D1, D2, ..., Dn, then the constructed instance will
+ * resolve files equivalently to an instance constructed from a
+ * difference permutation of the same directories.
+ */
+FileResolver::FileResolver(std::vector<DirectoryPath>&& search_directories)
+ : search_directories{std::move(search_directories)}
+{
+ std::sort(this->search_directories.begin(), this->search_directories.end());
+ this->search_directories.erase (
+ std::unique(this->search_directories.begin(), this->search_directories.end()),
+ this->search_directories.end()
+ );
+}
+
+// REMARK: Note that we do not treat absolute path specially.
+// This will in general mean that they cannot get resolved (albeit
+// there is a peculiar instance in which they can considering that
+// most path formats treat multiple adjacent separators as one).
+//
+// While we need to treat them at some point with a specific
+// intention, this was avoided at the current moment as it is
+// unrequired to build the actual documentation.
+//
+// In particular, avoiding this choice now allows us to move it to a
+// later stage where we can work with the origin of the data itself.
+// User-inputted paths come into the picture during the configuration
+// process and when parsing qdoc comments, there is a good chance that
+// some amount of sophistication will be required to handle this data
+// at the code level, for example to ensure that multiplatform
+// handling of paths is performed correctly.
+//
+// This will then define how we should handle absolute paths, if we
+// can receive them at all and so on.
+
+/*!
+* Returns a ResolvedFile if \a query can be resolved or std::nullopt
+* otherwise.
+*
+* The returned ResolvedFile, if any, will contain the provided \a
+* query and the path that the \a query was resolved to.
+*/
+[[nodiscard]] std::optional<ResolvedFile> FileResolver::resolve(QString query) const {
+ for (auto& directory_path : search_directories) {
+ auto maybe_filepath = FilePath::refine(QDir(directory_path.value() + "/" + query).path());
+ if (maybe_filepath) return ResolvedFile{std::move(query), std::move(*maybe_filepath)};
+ }
+
+ return std::nullopt;
+}
+
+/*!
+ * \fn FileResolver::get_search_directories() const
+ *
+ * Returns a const-reference to a collection of root search
+ * directories that this instance will use during the resolution of
+ * files.
+ */
diff --git a/src/qdoc/qdoc/src/qdoc/filesystem/fileresolver.h b/src/qdoc/qdoc/src/qdoc/filesystem/fileresolver.h
new file mode 100644
index 000000000..be574da30
--- /dev/null
+++ b/src/qdoc/qdoc/src/qdoc/filesystem/fileresolver.h
@@ -0,0 +1,24 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#pragma once
+
+#include "qdoc/boundaries/filesystem/directorypath.h"
+#include "qdoc/boundaries/filesystem/resolvedfile.h"
+
+#include <optional>
+#include <vector>
+
+#include <QtCore/qstring.h>
+
+class FileResolver {
+public:
+ FileResolver(std::vector<DirectoryPath>&& search_directories);
+
+ [[nodiscard]] std::optional<ResolvedFile> resolve(QString filename) const;
+
+ [[nodiscard]] const std::vector<DirectoryPath>& get_search_directories() const { return search_directories; }
+
+private:
+ std::vector<DirectoryPath> search_directories;
+};
diff --git a/src/qdoc/functionnode.cpp b/src/qdoc/qdoc/src/qdoc/functionnode.cpp
index 8673d929a..82933e0ac 100644
--- a/src/qdoc/functionnode.cpp
+++ b/src/qdoc/qdoc/src/qdoc/functionnode.cpp
@@ -1,63 +1,30 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the tools applications of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "functionnode.h"
-
#include "propertynode.h"
+#include <string>
+
QT_BEGIN_NAMESPACE
/*!
\class FunctionNode
This node is used to represent any kind of function being
- documented. It can represent a C++ class member function,
- a C++ global function, a QML method, a javascript method,
- or a macro, with or without parameters.
+ documented. It can represent a C++ class member function, a C++
+ global function, a QML method, or a macro, with or without
+ parameters.
- A C++ function can be a signal a slot, a constructor of any
+ A C++ function can be a signal, a slot, a constructor of any
kind, a destructor, a copy or move assignment operator, or
- just a plain old member function or global function.
+ just a plain old member function or a global function.
- A QML or javascript method can be a plain old method, or a
+ A QML method can be a plain old method, or a
signal or signal handler.
- If the function is not an overload, its overload flag is
- false. If it is an overload, its overload flag is true.
- If it is not an overload but it has overloads, its next
- overload pointer will point to an overload function. If it
- is an overload function, its overload flag is true, and it
- may or may not have a non-null next overload pointer.
-
- So all the overloads of a function are in a linked list
- using the next overload pointer. If a function has no
- overloads, its overload flag is false and its overload
- pointer is null.
+ If the function is an overload, its overload flag is
+ true.
The function node also has an overload number. If the
node's overload flag is set, this overload number is
@@ -85,10 +52,11 @@ FunctionNode::FunctionNode(Aggregate *parent, const QString &name)
m_isRef(false),
m_isRefRef(false),
m_isInvokable(false),
+ m_explicit{false},
+ m_constexpr{false},
m_metaness(Plain),
m_virtualness(NonVirtual),
- m_overloadNumber(0),
- m_nextOverload(nullptr)
+ m_overloadNumber(0)
{
// nothing
}
@@ -116,10 +84,11 @@ FunctionNode::FunctionNode(Metaness kind, Aggregate *parent, const QString &name
m_isRef(false),
m_isRefRef(false),
m_isInvokable(false),
+ m_explicit{false},
+ m_constexpr{false},
m_metaness(kind),
m_virtualness(NonVirtual),
- m_overloadNumber(0),
- m_nextOverload(nullptr)
+ m_overloadNumber(0)
{
setGenus(getGenus(m_metaness));
if (!isCppNode() && name.startsWith("__"))
@@ -134,7 +103,6 @@ Node *FunctionNode::clone(Aggregate *parent)
{
auto *fn = new FunctionNode(*this); // shallow copy
fn->setParent(nullptr);
- fn->setNextOverload(nullptr);
parent->addChild(fn);
return fn;
}
@@ -166,15 +134,14 @@ QString FunctionNode::virtualness() const
*/
void FunctionNode::setVirtualness(const QString &value)
{
- if (value == QLatin1String("non"))
- m_virtualness = NonVirtual;
- else if (value == QLatin1String("virtual"))
- m_virtualness = NormalVirtual;
- else if (value == QLatin1String("pure")) {
+ if (value == QLatin1String("pure")) {
m_virtualness = PureVirtual;
if (parent() && parent()->isClassNode())
parent()->setAbstract(true);
+ return;
}
+
+ m_virtualness = (value == QLatin1String("virtual")) ? NormalVirtual : NonVirtual;
}
static QMap<QString, FunctionNode::Metaness> metanessMap_;
@@ -196,9 +163,6 @@ static void buildMetanessMap()
metanessMap_["qmlsignal"] = FunctionNode::QmlSignal;
metanessMap_["qmlsignalhandler"] = FunctionNode::QmlSignalHandler;
metanessMap_["qmlmethod"] = FunctionNode::QmlMethod;
- metanessMap_["jssignal"] = FunctionNode::JsSignal;
- metanessMap_["jssignalhandler"] = FunctionNode::JsSignalHandler;
- metanessMap_["jsmethos"] = FunctionNode::JsMethod;
}
static QMap<QString, FunctionNode::Metaness> topicMetanessMap_;
@@ -209,10 +173,6 @@ static void buildTopicMetanessMap()
topicMetanessMap_["qmlattachedsignal"] = FunctionNode::QmlSignal;
topicMetanessMap_["qmlmethod"] = FunctionNode::QmlMethod;
topicMetanessMap_["qmlattachedmethod"] = FunctionNode::QmlMethod;
- topicMetanessMap_["jssignal"] = FunctionNode::JsSignal;
- topicMetanessMap_["jsattachedsignal"] = FunctionNode::JsSignal;
- topicMetanessMap_["jsmethod"] = FunctionNode::JsMethod;
- topicMetanessMap_["jsattachedmethod"] = FunctionNode::JsMethod;
}
/*!
@@ -241,11 +201,8 @@ Node::Genus FunctionNode::getGenus(FunctionNode::Metaness metaness)
case FunctionNode::QmlSignalHandler:
case FunctionNode::QmlMethod:
return Node::QML;
- case FunctionNode::JsSignal:
- case FunctionNode::JsSignalHandler:
- case FunctionNode::JsMethod:
- return Node::JS;
}
+
return Node::DontCare;
}
@@ -272,46 +229,6 @@ FunctionNode::Metaness FunctionNode::getMetanessFromTopic(const QString &topic)
}
/*!
- If this function node's metaness is \a from, change the
- metaness to \a to and return \c true. Otherwise return
- false. This function is used to change Qml function node
- metaness values to Javascript function node metaness,
- values because these nodes are created as Qml function
- nodes before it is discovered that what the function node
- represents is not a Qml function but a javascript function.
-
- Note that if the function returns true, which means the node
- type was indeed changed, then the node's Genus is also changed
- from QML to JS.
-
- The function also works in the other direction, but there is
- no use case for that.
- */
-bool FunctionNode::changeMetaness(Metaness from, Metaness to)
-{
- if (m_metaness == from) {
- m_metaness = to;
- switch (to) {
- case QmlSignal:
- case QmlSignalHandler:
- case QmlMethod:
- setGenus(Node::QML);
- break;
- case JsSignal:
- case JsSignalHandler:
- case JsMethod:
- setGenus(Node::JS);
- break;
- default:
- setGenus(Node::CPP);
- break;
- }
- return true;
- }
- return false;
-}
-
-/*!
Sets the function node's overload number to \a number. If \a number
is 0, the function node's overload flag is set to false. If
\a number is greater than 0, the overload flag is set to true.
@@ -323,63 +240,6 @@ void FunctionNode::setOverloadNumber(signed short number)
}
/*!
- Appends \a functionNode to the linked list of overloads for this function.
-
- \note Although this function appends an overload function to the list of
- overloads for this function's name, it does not set the function's
- overload number or it's overload flag. If the function has the
- \c{\\overload} in its QDoc comment, that will set the overload
- flag. But qdoc treats the \c{\\overload} command as a hint that the
- function should be documented as an overload. The hint is almost
- always correct, but QDoc reserves the right to decide which function
- should be the primary function and which functions are the overloads.
- These decisions are made in Aggregate::normalizeOverloads().
- */
-void FunctionNode::appendOverload(FunctionNode *functionNode)
-{
- auto current = this;
- while (current->m_nextOverload)
- current = current->m_nextOverload;
- current->m_nextOverload = functionNode;
- functionNode->m_nextOverload = nullptr;
-}
-
-/*!
- Removes \a functionNode from the linked list of function overloads.
-*/
-void FunctionNode::removeOverload(FunctionNode *functionNode)
-{
- auto head = this;
- auto **indirect = &head;
- while ((*indirect) != functionNode) {
- if (!(*indirect)->m_nextOverload)
- return;
- indirect = &(*indirect)->m_nextOverload;
- }
- *indirect = functionNode->m_nextOverload;
-}
-
-/*!
- Returns the primary function - the first function
- from the linked list of overloads that is \e not
- marked as an overload. If found, the primary function
- is removed from the list and returned. Otherwise
- returns \c nullptr.
- */
-FunctionNode *FunctionNode::findPrimaryFunction()
-{
- auto current = this;
- while (current->m_nextOverload && current->m_nextOverload->isOverload())
- current = current->m_nextOverload;
-
- auto primary = current->m_nextOverload;
- if (primary)
- current->m_nextOverload = primary->m_nextOverload;
-
- return primary;
-}
-
-/*!
\fn void FunctionNode::setReimpFlag()
Sets the function node's reimp flag to \c true, which means
@@ -402,12 +262,6 @@ QString FunctionNode::kindString() const
return "QML signal handler";
case FunctionNode::QmlMethod:
return "QML method";
- case FunctionNode::JsSignal:
- return "JS signal";
- case FunctionNode::JsSignalHandler:
- return "JS signal handler";
- case FunctionNode::JsMethod:
- return "JS method";
default:
return "function";
}
@@ -450,12 +304,6 @@ QString FunctionNode::metanessString() const
return "qmlsignalhandler";
case FunctionNode::QmlMethod:
return "qmlmethod";
- case FunctionNode::JsSignal:
- return "jssignal";
- case FunctionNode::JsSignalHandler:
- return "jssignalhandler";
- case FunctionNode::JsMethod:
- return "jsmethod";
default:
return "plain";
}
@@ -493,24 +341,34 @@ bool FunctionNode::isDeprecated() const
*/
/*!
- Reconstructs and returns the function's signature. If \a values
- is \c true, the default values of the parameters are included.
- The return type is included unless \a noReturnType is \c true.
- Function templates are prefixed with \c {template <parameter_list>}
- if \a templateParams is \c true.
+ Reconstructs and returns the function's signature.
+
+ Specific parts of the signature are included according to
+ flags in \a options:
+
+ \value Node::SignaturePlain
+ Plain signature
+ \value Node::SignatureDefaultValues
+ Include any default argument values
+ \value Node::SignatureReturnType
+ Include return type
+ \value Node::SignatureTemplateParams
+ Include \c {template <parameter_list>} if one exists
*/
-QString FunctionNode::signature(bool values, bool noReturnType, bool templateParams) const
+QString FunctionNode::signature(Node::SignatureOptions options) const
{
QStringList elements;
- if (templateParams)
- elements << templateDecl();
- if (!noReturnType)
+ if (options & Node::SignatureTemplateParams && templateDecl())
+ elements << (*templateDecl()).to_qstring();
+ if (options & Node::SignatureReturnType)
elements << m_returnType;
elements.removeAll(QString());
if (!isMacroWithoutParams()) {
- elements << name() + QLatin1Char('(') + m_parameters.signature(values) + QLatin1Char(')');
+ elements << name() + QLatin1Char('(')
+ + m_parameters.signature(options & Node::SignatureDefaultValues)
+ + QLatin1Char(')');
if (!isMacro()) {
if (isConst())
elements << QStringLiteral("const");
@@ -526,45 +384,56 @@ QString FunctionNode::signature(bool values, bool noReturnType, bool templatePar
}
/*!
- Print some information used for debugging qdoc. Only used when debugging.
- */
-void FunctionNode::debug() const
-{
- qDebug("QML METHOD %s m_returnType %s m_parentPath %s", qPrintable(name()),
- qPrintable(m_returnType), qPrintable(m_parentPath.join(' ')));
-}
+ \fn int FunctionNode::compare(const FunctionNode *f1, const FunctionNode *f2)
-/*!
- Compares this FunctionNode to \a node. If \a sameParent is \c true, compares
- also the parent of the two nodes. Returns \c true if they describe
- the same function.
+ Compares FunctionNode \a f1 with \a f2, assumed to have identical names.
+ Returns an integer less than, equal to, or greater than zero if f1 is
+ considered less than, equal to, or greater than f2.
+
+ The main purpose is to provide stable ordering for function overloads.
*/
-bool FunctionNode::compare(const Node *node, bool sameParent) const
+[[nodiscard]] int compare(const FunctionNode *f1, const FunctionNode *f2)
{
- if (!node || !node->isFunction())
- return false;
-
- const auto *functionNode = static_cast<const FunctionNode *>(node);
- if (metaness() != functionNode->metaness())
- return false;
- if (sameParent && parent() != functionNode->parent())
- return false;
- if (m_returnType != functionNode->returnType())
- return false;
- if (isConst() != functionNode->isConst())
- return false;
- if (isAttached() != functionNode->isAttached())
- return false;
- const Parameters &p = functionNode->parameters();
- if (m_parameters.count() != p.count())
- return false;
- if (!p.isEmpty()) {
- for (int i = 0; i < p.count(); ++i) {
- if (m_parameters.at(i).type() != p.at(i).type())
- return false;
+ // Compare parameter count
+ int param_count{f1->parameters().count()};
+
+ if (int param_diff = param_count - f2->parameters().count(); param_diff != 0)
+ return param_diff;
+
+ // Constness
+ if (f1->isConst() != f2->isConst())
+ return f1->isConst() ? 1 : -1;
+
+ // Reference qualifiers
+ if (f1->isRef() != f2->isRef())
+ return f1->isRef() ? 1 : -1;
+ if (f1->isRefRef() != f2->isRefRef())
+ return f1->isRefRef() ? 1 : -1;
+
+ // Attachedness (applies to QML methods)
+ if (f1->isAttached() != f2->isAttached())
+ return f1->isAttached() ? 1 : -1;
+
+ // Parameter types
+ const Parameters &p1{f1->parameters()};
+ const Parameters &p2{f2->parameters()};
+ for (qsizetype i = 0; i < param_count; ++i) {
+ if (int type_comp = QString::compare(p1.at(i).type(), p2.at(i).type());
+ type_comp != 0) {
+ return type_comp;
}
}
- return true;
+
+ // Template declarations
+ const auto t1{f1->templateDecl()};
+ const auto t2{f2->templateDecl()};
+ if (!t1 && !t2)
+ return 0;
+
+ if (t1 && t2)
+ return (*t1).to_std_string().compare((*t2).to_std_string());
+
+ return t1 ? 1 : -1;
}
/*!
@@ -583,13 +452,13 @@ bool FunctionNode::compare(const Node *node, bool sameParent) const
*/
bool FunctionNode::isIgnored() const
{
- if (!hasDoc() && !hasSharedDoc()) {
+ if (!hasDoc()) {
if (name().startsWith(QLatin1String("qt_")) || name() == QLatin1String("metaObject")
|| name() == QLatin1String("tr") || name() == QLatin1String("trUtf8")
|| name() == QLatin1String("d_func")) {
return true;
}
- QString s = signature(false, false);
+ QString s = signature(Node::SignatureReturnType);
if (s.contains(QLatin1String("enum_type")) && s.contains(QLatin1String("operator|")))
return true;
}
@@ -597,24 +466,8 @@ bool FunctionNode::isIgnored() const
}
/*!
- Returns true if this function has overloads. Otherwise false.
- First, if this function node's overload pointer is not nullptr,
- return true. Next, if this function node's overload flag is true
- return true. Finally, if this function's parent Aggregate has a
- function by the same name as this one in its function map and
- that function has overloads, return true. Otherwise return false.
-
- There is a failsafe way to test it under any circumstances.
+ \fn bool FunctionNode::hasOverloads() const
+ Returns \c true if this function has overloads.
*/
-bool FunctionNode::hasOverloads() const
-{
- if (m_nextOverload != nullptr)
- return true;
- if (m_overloadFlag)
- return true;
- if (parent())
- return parent()->hasOverloads(this);
- return false;
-}
QT_END_NAMESPACE
diff --git a/src/qdoc/functionnode.h b/src/qdoc/qdoc/src/qdoc/functionnode.h
index b7162eb51..dca8c7e44 100644
--- a/src/qdoc/functionnode.h
+++ b/src/qdoc/qdoc/src/qdoc/functionnode.h
@@ -1,30 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the tools applications of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#ifndef FUNCTIONNODE_H
#define FUNCTIONNODE_H
@@ -36,6 +11,8 @@
#include <QtCore/qglobal.h>
#include <QtCore/qstring.h>
+#include <optional>
+
QT_BEGIN_NAMESPACE
class FunctionNode : public Node
@@ -59,9 +36,6 @@ public:
QmlSignal,
QmlSignalHandler,
QmlMethod,
- JsSignal,
- JsSignalHandler,
- JsMethod
};
FunctionNode(Aggregate *parent, const QString &name); // C++ function (Plain)
@@ -70,7 +44,6 @@ public:
Node *clone(Aggregate *parent) override;
[[nodiscard]] Metaness metaness() const { return m_metaness; }
[[nodiscard]] QString metanessString() const;
- bool changeMetaness(Metaness from, Metaness to);
void setMetaness(Metaness metaness) { m_metaness = metaness; }
[[nodiscard]] QString kindString() const;
static Metaness getMetaness(const QString &value);
@@ -78,10 +51,8 @@ public:
static Genus getGenus(Metaness metaness);
void setReturnType(const QString &type) { m_returnType = type; }
- void setParentPath(const QStringList &path) { m_parentPath = path; }
void setVirtualness(const QString &value);
void setVirtualness(Virtualness virtualness) { m_virtualness = virtualness; }
- void setVirtual() { m_virtualness = NormalVirtual; }
void setConst(bool b) { m_const = b; }
void setDefault(bool b) { m_default = b; }
void setStatic(bool b) { m_static = b; }
@@ -104,6 +75,15 @@ public:
}
[[nodiscard]] bool isDeprecated() const override;
+ void markExplicit() { m_explicit = true; }
+ bool isExplicit() const { return m_explicit; }
+
+ void markConstexpr() { m_constexpr = true; }
+ bool isConstexpr() const { return m_constexpr; }
+
+ void markNoexcept(QString expression = "") { m_noexcept = expression; }
+ const std::optional<QString>& getNoexcept() const { return m_noexcept; }
+
[[nodiscard]] bool isCppFunction() const { return m_metaness == Plain; } // Is this correct?
[[nodiscard]] bool isSignal() const { return (m_metaness == Signal); }
[[nodiscard]] bool isSlot() const { return (m_metaness == Slot); }
@@ -114,10 +94,6 @@ public:
[[nodiscard]] bool isCAssign() const { return (m_metaness == CAssign); }
[[nodiscard]] bool isMAssign() const { return (m_metaness == MAssign); }
- [[nodiscard]] bool isJsMethod() const { return (m_metaness == JsMethod); }
- [[nodiscard]] bool isJsSignal() const { return (m_metaness == JsSignal); }
- [[nodiscard]] bool isJsSignalHandler() const { return (m_metaness == JsSignalHandler); }
-
[[nodiscard]] bool isQmlMethod() const { return (m_metaness == QmlMethod); }
[[nodiscard]] bool isQmlSignal() const { return (m_metaness == QmlSignal); }
[[nodiscard]] bool isQmlSignalHandler() const { return (m_metaness == QmlSignalHandler); }
@@ -135,21 +111,17 @@ public:
[[nodiscard]] const Parameters &parameters() const { return m_parameters; }
[[nodiscard]] bool isPrivateSignal() const { return m_parameters.isPrivateSignal(); }
void setParameters(const QString &signature) { m_parameters.set(signature); }
- [[nodiscard]] QString signature(bool values, bool noReturnType,
- bool templateParams = false) const override;
+ [[nodiscard]] QString signature(Node::SignatureOptions options) const override;
[[nodiscard]] const QString &overridesThis() const { return m_overridesThis; }
- [[nodiscard]] const NodeList &associatedProperties() const { return m_associatedProperties; }
+ [[nodiscard]] const QList<PropertyNode *> &associatedProperties() const { return m_associatedProperties; }
[[nodiscard]] bool hasAssociatedProperties() const { return !m_associatedProperties.isEmpty(); }
[[nodiscard]] bool hasOneAssociatedProperty() const
{
return (m_associatedProperties.size() == 1);
}
- [[nodiscard]] Node *firstAssociatedProperty() const { return m_associatedProperties[0]; }
-
[[nodiscard]] QString element() const override { return parent()->name(); }
[[nodiscard]] bool isAttached() const override { return m_attached; }
- [[nodiscard]] bool isQtQuickNode() const override { return parent()->isQtQuickNode(); }
[[nodiscard]] QString qmlTypeName() const override { return parent()->qmlTypeName(); }
[[nodiscard]] QString logicalModuleName() const override
{
@@ -164,8 +136,6 @@ public:
return parent()->logicalModuleIdentifier();
}
- void debug() const;
-
void setFinal(bool b) { m_isFinal = b; }
[[nodiscard]] bool isFinal() const { return m_isFinal; }
@@ -184,17 +154,16 @@ public:
[[nodiscard]] bool hasTag(const QString &tag) const override { return (m_tag == tag); }
void setTag(const QString &tag) { m_tag = tag; }
[[nodiscard]] const QString &tag() const { return m_tag; }
- bool compare(const Node *node, bool sameParent = true) const;
[[nodiscard]] bool isIgnored() const;
- [[nodiscard]] bool hasOverloads() const;
+ [[nodiscard]] bool hasOverloads() const
+ {
+ return (m_overloadFlag || (parent() && parent()->hasOverloads(this)));
+ }
void setOverloadFlag() { m_overloadFlag = true; }
void setOverloadNumber(signed short number);
- void appendOverload(FunctionNode *functionNode);
- void removeOverload(FunctionNode *functionNode);
[[nodiscard]] signed short overloadNumber() const { return m_overloadNumber; }
- FunctionNode *nextOverload() { return m_nextOverload; }
- void setNextOverload(FunctionNode *functionNode) { m_nextOverload = functionNode; }
- FunctionNode *findPrimaryFunction();
+
+ friend int compare(const FunctionNode *f1, const FunctionNode *f2);
private:
void addAssociatedProperty(PropertyNode *property);
@@ -213,15 +182,18 @@ private:
bool m_isRef : 1;
bool m_isRefRef : 1;
bool m_isInvokable : 1;
+ bool m_explicit;
+ bool m_constexpr;
+
+ std::optional<QString> m_noexcept;
+
Metaness m_metaness {};
- Virtualness m_virtualness {};
+ Virtualness m_virtualness{ NonVirtual };
signed short m_overloadNumber {};
- FunctionNode *m_nextOverload { nullptr };
QString m_returnType {};
- QStringList m_parentPath {};
QString m_overridesThis {};
QString m_tag {};
- NodeList m_associatedProperties {};
+ QList<PropertyNode *> m_associatedProperties {};
Parameters m_parameters {};
};
diff --git a/src/qdoc/generator.cpp b/src/qdoc/qdoc/src/qdoc/generator.cpp
index 5f65236f2..d1b3642c3 100644
--- a/src/qdoc/generator.cpp
+++ b/src/qdoc/qdoc/src/qdoc/generator.cpp
@@ -1,30 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the tools applications of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "generator.h"
@@ -33,6 +8,7 @@
#include "classnode.h"
#include "codemarker.h"
#include "collectionnode.h"
+#include "comparisoncategory.h"
#include "config.h"
#include "doc.h"
#include "editdistance.h"
@@ -44,6 +20,7 @@
#include "propertynode.h"
#include "qdocdatabase.h"
#include "qmltypenode.h"
+#include "qmlpropertynode.h"
#include "quoter.h"
#include "sharedcommentnode.h"
#include "tokenizer.h"
@@ -58,20 +35,22 @@
# include "QtCore/qurl.h"
#endif
+#include <string>
+
+using namespace std::literals::string_literals;
+
QT_BEGIN_NAMESPACE
+using namespace Qt::StringLiterals;
+
Generator *Generator::s_currentGenerator;
-QStringList Generator::s_exampleDirs;
-QStringList Generator::s_exampleImgExts;
QMap<QString, QMap<QString, QString>> Generator::s_fmtLeftMaps;
QMap<QString, QMap<QString, QString>> Generator::s_fmtRightMaps;
QList<Generator *> Generator::s_generators;
-QStringList Generator::s_imageDirs;
-QStringList Generator::s_imageFiles;
-QMap<QString, QStringList> Generator::s_imgFileExts;
QString Generator::s_outDir;
QString Generator::s_outSubdir;
QStringList Generator::s_outFileNames;
+QSet<QString> Generator::s_trademarks;
QSet<QString> Generator::s_outputFormats;
QHash<QString, QString> Generator::s_outputPrefixes;
QHash<QString, QString> Generator::s_outputSuffixes;
@@ -94,7 +73,8 @@ static QLatin1String quot("&quot;");
Sets a pointer to the QDoc database singleton, which is
available to the generator subclasses.
*/
-Generator::Generator()
+Generator::Generator(FileResolver& file_resolver)
+ : file_resolver{file_resolver}
{
m_qdb = QDocDatabase::qdocDB();
s_generators.prepend(this);
@@ -132,14 +112,14 @@ void Generator::appendFullName(Text &text, const Node *apparentNode, const QStri
/*!
Append the signature for the function named in \a node to
- \a text, so that is is a link to the documentation for that
+ \a text, so that is a link to the documentation for that
function.
*/
void Generator::appendSignature(Text &text, const Node *node)
{
text << Atom(Atom::LinkNode, CodeMarker::stringForNode(node))
<< Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK)
- << Atom(Atom::String, node->signature(false, true))
+ << Atom(Atom::String, node->signature(Node::SignaturePlain))
<< Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK);
}
@@ -179,7 +159,7 @@ int Generator::appendSortedNames(Text &text, const ClassNode *cn, const QList<Re
const QStringList classNames = classMap.keys();
for (const auto &className : classNames) {
text << classMap[className];
- text << Utilities::comma(index++, classNames.count());
+ text << Utilities::comma(index++, classNames.size());
}
return index;
}
@@ -189,20 +169,15 @@ int Generator::appendSortedQmlNames(Text &text, const Node *base, const NodeList
QMap<QString, Text> classMap;
for (const auto sub : subs) {
- Text text;
- if (!base->isQtQuickNode() || !sub->isQtQuickNode()
- || (base->logicalModuleName() == sub->logicalModuleName())) {
- appendFullName(text, sub, base);
- classMap[text.toString().toLower()] = text;
- }
+ Text full_name;
+ appendFullName(full_name, sub, base);
+ classMap[full_name.toString().toLower()] = full_name;
}
int index = 0;
- const QStringList names = classMap.keys();
- for (const auto &name : names) {
- text << classMap[name];
- text << Utilities::comma(index++, names.count());
- }
+ const auto &names = classMap.keys();
+ for (const auto &name : names)
+ text << classMap[name] << Utilities::comma(index++, names.size());
return index;
}
@@ -212,42 +187,43 @@ int Generator::appendSortedQmlNames(Text &text, const Node *base, const NodeList
this method deals with errors when opening the file:
the returned QFile is always valid and can be written to.
- \sa beginFilePage()
+ \sa beginSubPage()
*/
QFile *Generator::openSubPageFile(const Node *node, const QString &fileName)
{
- QString path = outputDir() + QLatin1Char('/');
- if (Generator::useOutputSubdirs() && !node->outputSubdirectory().isEmpty()
- && !outputDir().endsWith(node->outputSubdirectory())) {
- path += node->outputSubdirectory() + QLatin1Char('/');
- }
- path += fileName;
+ if (s_outFileNames.contains(fileName))
+ node->location().warning("Already generated %1 for this project"_L1.arg(fileName));
+
+ QString path = outputDir() + QLatin1Char('/') + fileName;
auto outPath = s_redirectDocumentationToDevNull ? QStringLiteral("/dev/null") : path;
auto outFile = new QFile(outPath);
- if (!s_redirectDocumentationToDevNull && outFile->exists())
- qCDebug(lcQdoc) << "Output file already exists; overwriting" << qPrintable(outFile->fileName());
+ if (!s_redirectDocumentationToDevNull && outFile->exists()) {
+ const QString warningText {"Output file already exists, overwriting %1"_L1.arg(outFile->fileName())};
+ if (qEnvironmentVariableIsSet("QDOC_ALL_OVERWRITES_ARE_WARNINGS"))
+ node->location().warning(warningText);
+ else
+ qCDebug(lcQdoc) << qUtf8Printable(warningText);
+ }
- if (!outFile->open(QFile::WriteOnly)) {
+ if (!outFile->open(QFile::WriteOnly | QFile::Text)) {
node->location().fatal(
QStringLiteral("Cannot open output file '%1'").arg(outFile->fileName()));
}
qCDebug(lcQdoc, "Writing: %s", qPrintable(path));
s_outFileNames << fileName;
+ s_trademarks.clear();
return outFile;
}
/*!
Creates the file named \a fileName in the output directory.
Attaches a QTextStream to the created file, which is written
- to all over the place using out(). This function does not
- store the \a fileName in the \a node as the output file name.
-
- \sa beginSubPage()
+ to all over the place using out().
*/
-void Generator::beginFilePage(const Node *node, const QString &fileName)
+void Generator::beginSubPage(const Node *node, const QString &fileName)
{
QFile *outFile = openSubPageFile(node, fileName);
auto *out = new QTextStream(outFile);
@@ -255,23 +231,6 @@ void Generator::beginFilePage(const Node *node, const QString &fileName)
}
/*!
- Creates the file named \a fileName in the output directory.
- Attaches a QTextStream to the created file, which is written
- to all over the place using out(). This function calls another
- function, \c beginFilePage(), which is really just most of what
- this function used to contain. We needed a different version
- that doesn't store the \a fileName in the \a node as the output
- file name.
-
- \sa beginFilePage()
-*/
-void Generator::beginSubPage(const Node *node, const QString &fileName)
-{
- beginFilePage(node, fileName);
- const_cast<Node *>(node)->setOutputFileName(fileName);
-}
-
-/*!
Flush the text stream associated with the subpage, and
then pop it off the text stream stack and delete it.
This terminates output of the subpage.
@@ -283,37 +242,6 @@ void Generator::endSubPage()
delete outStreamStack.pop();
}
-/*
- the code below is effectively equivalent to:
- input.replace(QRegularExpression("[^A-Za-z0-9]+"), " ");
- input = input.trimmed();
- input.replace(QLatin1Char(' '), QLatin1Char('-'));
- input = input.toLower();
- as this function accounted for ~8% of total running time
- we optimize a bit...
-*/
-static void transmogrify(QString &input, QString &output)
-{
- // +5 prevents realloc in fileName() below
- output.reserve(input.size() + 5);
- bool begun = false;
- for (int i = 0; i != input.size(); ++i) {
- QChar c = input.at(i);
- uint u = c.unicode();
- if (u >= 'A' && u <= 'Z')
- u += 'a' - 'A';
- if ((u >= 'a' && u <= 'z') || (u >= '0' && u <= '9')) {
- output += QLatin1Char(u);
- begun = true;
- } else if (begun) {
- output += QLatin1Char('-');
- begun = false;
- }
- }
- while (output.endsWith(QLatin1Char('-')))
- output.chop(1);
-}
-
QString Generator::fileBase(const Node *node) const
{
if (!node->isPageNode() && !node->isCollectionNode())
@@ -322,53 +250,46 @@ QString Generator::fileBase(const Node *node) const
if (node->hasFileNameBase())
return node->fileNameBase();
- QString base;
- if (node->isCollectionNode()) {
- base = node->name() + outputSuffix(node);
- if (base.endsWith(".html"))
- base.truncate(base.length() - 5);
+ QString base{node->name()};
+ if (base.endsWith(".html"))
+ base.truncate(base.size() - 5);
+ if (node->isCollectionNode()) {
if (node->isQmlModule())
base.append("-qmlmodule");
- else if (node->isJsModule())
- base.append("-jsmodule");
else if (node->isModule())
base.append("-module");
- // Why not add "-group" for group pages?
+ base.append(outputSuffix(node));
} else if (node->isTextPageNode()) {
- base = node->name();
- if (base.endsWith(".html"))
- base.truncate(base.length() - 5);
-
if (node->isExample()) {
- base.prepend(s_project.toLower() + QLatin1Char('-'));
- base.append(QLatin1String("-example"));
+ base.prepend("%1-"_L1.arg(s_project.toLower()));
+ base.append("-example");
}
- } else if (node->isQmlType() || node->isQmlBasicType() || node->isJsType()
- || node->isJsBasicType()) {
- base = node->name();
+ } else if (node->isQmlType()) {
/*
To avoid file name conflicts in the html directory,
we prepend a prefix (by default, "qml-") and an optional suffix
to the file name. The suffix, if one exists, is appended to the
module name.
+
+ For historical reasons, skip the module name qualifier for QML value types
+ in order to avoid excess redirects in the online docs. TODO: re-assess
*/
- if (!node->logicalModuleName().isEmpty()
+ if (!node->logicalModuleName().isEmpty() && !node->isQmlBasicType()
&& (!node->logicalModule()->isInternal() || m_showInternal))
- base.prepend(node->logicalModuleName() + outputSuffix(node) + QLatin1Char('-'));
+ base.prepend("%1%2-"_L1.arg(node->logicalModuleName(), outputSuffix(node)));
- base.prepend(outputPrefix(node));
} else if (node->isProxyNode()) {
- base = node->name();
- base.append("-proxy");
+ base.append("-%1-proxy"_L1.arg(node->tree()->physicalModuleName()));
} else {
+ base.clear();
const Node *p = node;
forever {
const Node *pp = p->parent();
base.prepend(p->name());
if (pp == nullptr || pp->name().isEmpty() || pp->isTextPageNode())
break;
- base.prepend(QLatin1Char('-'));
+ base.prepend('-'_L1);
p = pp;
}
if (node->isNamespace() && !node->name().isEmpty()) {
@@ -378,13 +299,14 @@ QString Generator::fileBase(const Node *node) const
base.append(ns->tree()->camelCaseModuleName());
}
}
+ base.append(outputSuffix(node));
}
- QString res;
- transmogrify(base, res);
+ base.prepend(outputPrefix(node));
+ QString canonicalName{ Utilities::asAsciiPrintable(base) };
Node *n = const_cast<Node *>(node);
- n->setFileNameBase(res);
- return res;
+ n->setFileNameBase(canonicalName);
+ return canonicalName;
}
/*!
@@ -395,14 +317,13 @@ QString Generator::fileBase(const Node *node) const
*/
QString Generator::linkForExampleFile(const QString &path, const QString &fileExt)
{
- QString link = path;
+ QString link{path};
link.prepend(s_project.toLower() + QLatin1Char('-'));
- QString res;
- transmogrify(link, res);
- res.append(QLatin1Char('.'));
- res.append(fileExt.isEmpty() ? fileExtension() : fileExt);
- return res;
+ QString canonicalName{ Utilities::asAsciiPrintable(link) };
+ canonicalName.append(QLatin1Char('.'));
+ canonicalName.append(fileExt.isEmpty() ? fileExtension() : fileExt);
+ return canonicalName;
}
/*!
@@ -434,11 +355,24 @@ QString Generator::fileName(const Node *node, const QString &extension) const
return node->url();
QString name = fileBase(node) + QLatin1Char('.');
- return extension.isNull() ? name + fileExtension() : name + extension;
+ return name + (extension.isNull() ? fileExtension() : extension);
}
-QString Generator::cleanRef(const QString &ref)
+/*!
+ Clean the given \a ref to be used as an HTML anchor or an \c xml:id.
+ If \a xmlCompliant is set to \c true, a stricter process is used, as XML
+ is more rigorous in what it accepts. Otherwise, if \a xmlCompliant is set to
+ \c false, the basic HTML transformations are applied.
+
+ More specifically, only XML NCNames are allowed
+ (https://www.w3.org/TR/REC-xml-names/#NT-NCName).
+ */
+QString Generator::cleanRef(const QString &ref, bool xmlCompliant)
{
+ // XML-compliance is ensured in two ways:
+ // - no digit (0-9) at the beginning of an ID (many IDs do not respect this property)
+ // - no colon (:) anywhere in the ID (occurs very rarely)
+
QString clean;
if (ref.isEmpty())
@@ -448,8 +382,10 @@ QString Generator::cleanRef(const QString &ref)
const QChar c = ref[0];
const uint u = c.unicode();
- if ((u >= 'a' && u <= 'z') || (u >= 'A' && u <= 'Z') || (u >= '0' && u <= '9')) {
+ if ((u >= 'a' && u <= 'z') || (u >= 'A' && u <= 'Z') || (!xmlCompliant && u >= '0' && u <= '9')) {
clean += c;
+ } else if (xmlCompliant && u >= '0' && u <= '9') {
+ clean += QLatin1Char('A') + c;
} else if (u == '~') {
clean += "dtor.";
} else if (u == '_') {
@@ -458,11 +394,11 @@ QString Generator::cleanRef(const QString &ref)
clean += QLatin1Char('A');
}
- for (int i = 1; i < ref.length(); i++) {
+ for (int i = 1; i < ref.size(); i++) {
const QChar c = ref[i];
const uint u = c.unicode();
if ((u >= 'a' && u <= 'z') || (u >= 'A' && u <= 'Z') || (u >= '0' && u <= '9') || u == '-'
- || u == '_' || u == ':' || u == '.') {
+ || u == '_' || (xmlCompliant && u == ':') || u == '.') {
clean += c;
} else if (c.isSpace()) {
clean += QLatin1Char('-');
@@ -499,7 +435,7 @@ QMap<QString, QString> &Generator::formattingRightMap()
/*!
Returns the full document location.
*/
-QString Generator::fullDocumentLocation(const Node *node, bool useSubdir)
+QString Generator::fullDocumentLocation(const Node *node)
{
if (node == nullptr)
return QString();
@@ -508,18 +444,7 @@ QString Generator::fullDocumentLocation(const Node *node, bool useSubdir)
QString parentName;
QString anchorRef;
- QString fdl;
- /*
- If the useSubdir parameter is set, then the output is
- being sent to subdirectories of the output directory.
- Prepend the subdirectory name + '/' to the result.
- */
- if (useSubdir) {
- fdl = node->outputSubdirectory();
- if (!fdl.isEmpty())
- fdl.append(QLatin1Char('/'));
- }
if (node->isNamespace()) {
/*
The root namespace has no name - check for this before creating
@@ -529,20 +454,8 @@ QString Generator::fullDocumentLocation(const Node *node, bool useSubdir)
parentName = fileBase(node) + QLatin1Char('.') + currentGenerator()->fileExtension();
else
return QString();
- } else if (node->isQmlType() || node->isQmlBasicType() || node->isJsType()
- || node->isJsBasicType()) {
- QString fb = fileBase(node);
- if (fb.startsWith(outputPrefix(node)))
- return fb + QLatin1Char('.') + currentGenerator()->fileExtension();
- else {
- QString mq;
- if (!node->logicalModuleName().isEmpty()) {
- mq = node->logicalModuleName().replace(QChar('.'), QChar('-'));
- mq = mq.toLower() + QLatin1Char('-');
- }
- return fdl + outputPrefix(node) + mq + fileBase(node) + QLatin1Char('.')
- + currentGenerator()->fileExtension();
- }
+ } else if (node->isQmlType()) {
+ return fileBase(node) + QLatin1Char('.') + currentGenerator()->fileExtension();
} else if (node->isTextPageNode() || node->isCollectionNode()) {
parentName = fileBase(node) + QLatin1Char('.') + currentGenerator()->fileExtension();
} else if (fileBase(node).isEmpty())
@@ -567,15 +480,12 @@ QString Generator::fullDocumentLocation(const Node *node, bool useSubdir)
case Node::Function: {
const auto *fn = static_cast<const FunctionNode *>(node);
switch (fn->metaness()) {
- case FunctionNode::JsSignal:
case FunctionNode::QmlSignal:
anchorRef = QLatin1Char('#') + node->name() + "-signal";
break;
- case FunctionNode::JsSignalHandler:
case FunctionNode::QmlSignalHandler:
anchorRef = QLatin1Char('#') + node->name() + "-signal-handler";
break;
- case FunctionNode::JsMethod:
case FunctionNode::QmlMethod:
anchorRef = QLatin1Char('#') + node->name() + "-method";
break;
@@ -583,7 +493,7 @@ QString Generator::fullDocumentLocation(const Node *node, bool useSubdir)
if (fn->isDtor())
anchorRef = "#dtor." + fn->name().mid(1);
else if (fn->hasOneAssociatedProperty() && fn->doc().isEmpty())
- return fullDocumentLocation(fn->firstAssociatedProperty());
+ return fullDocumentLocation(fn->associatedProperties()[0]);
else if (fn->overloadNumber() > 0)
anchorRef = QLatin1Char('#') + cleanRef(fn->name()) + QLatin1Char('-')
+ QString::number(fn->overloadNumber());
@@ -616,7 +526,6 @@ QString Generator::fullDocumentLocation(const Node *node, bool useSubdir)
if (!node->isPropertyGroup())
break;
} Q_FALLTHROUGH();
- case Node::JsProperty:
case Node::QmlProperty:
if (node->isAttached())
anchorRef = QLatin1Char('#') + node->name() + "-attached-prop";
@@ -626,13 +535,11 @@ QString Generator::fullDocumentLocation(const Node *node, bool useSubdir)
case Node::Variable:
anchorRef = QLatin1Char('#') + node->name() + "-var";
break;
- case Node::JsType:
case Node::QmlType:
case Node::Page:
case Node::Group:
case Node::HeaderFile:
case Node::Module:
- case Node::JsModule:
case Node::QmlModule: {
parentName = fileBase(node);
parentName.replace(QLatin1Char('/'), QLatin1Char('-'))
@@ -649,7 +556,7 @@ QString Generator::fullDocumentLocation(const Node *node, bool useSubdir)
"-obsolete." + currentGenerator()->fileExtension());
}
- return fdl + parentName.toLower() + anchorRef;
+ return parentName.toLower() + anchorRef;
}
void Generator::generateAlsoList(const Node *node, CodeMarker *marker)
@@ -721,7 +628,7 @@ const Atom *Generator::generateAtomList(const Atom *atom, const Node *relative,
void Generator::generateBody(const Node *node, CodeMarker *marker)
{
const FunctionNode *fn = node->isFunction() ? static_cast<const FunctionNode *>(node) : nullptr;
- if (!node->hasDoc() && !node->hasSharedDoc()) {
+ if (!node->hasDoc()) {
/*
Test for special function, like a destructor or copy constructor,
that has no documentation.
@@ -783,7 +690,7 @@ void Generator::generateBody(const Node *node, CodeMarker *marker)
if (fn && !fn->overridesThis().isEmpty())
generateReimplementsClause(fn, marker);
else if (node->isProperty()) {
- if (static_cast<const PropertyNode *>(node)->propertyType() != PropertyNode::Standard)
+ if (static_cast<const PropertyNode *>(node)->propertyType() != PropertyNode::PropertyType::StandardProperty)
generateAddendum(node, BindableProperty, marker);
}
@@ -815,8 +722,8 @@ void Generator::generateBody(const Node *node, CodeMarker *marker)
const auto &documentedItemList = enume->doc().enumItemNames();
QSet<QString> documentedItems(documentedItemList.cbegin(), documentedItemList.cend());
const QSet<QString> allItems = definedItems + documentedItems;
- if (allItems.count() > definedItems.count()
- || allItems.count() > documentedItems.count()) {
+ if (allItems.size() > definedItems.size()
+ || allItems.size() > documentedItems.size()) {
for (const auto &it : allItems) {
if (!definedItems.contains(it)) {
QString details;
@@ -842,7 +749,10 @@ void Generator::generateBody(const Node *node, CodeMarker *marker)
for (const auto &name : declaredNames) {
if (!documentedNames.contains(name)) {
if (fn->isActive() || fn->isPreliminary()) {
- if (!fn->isMarkedReimp() && !fn->isOverload()) {
+ // Require no parameter documentation for overrides and overloads,
+ // and only require it for non-overloaded constructors.
+ if (!fn->isMarkedReimp() && !fn->isOverload() &&
+ !(fn->isSomeCtor() && fn->hasOverloads())) {
fn->doc().location().warning(
QStringLiteral("Undocumented parameter '%1' in %2")
.arg(name, node->plainFullName()));
@@ -876,6 +786,7 @@ void Generator::generateBody(const Node *node, CodeMarker *marker)
}
}
}
+ generateEnumValuesForQmlProperty(node, marker);
generateRequiredLinks(node, marker);
}
@@ -891,7 +802,7 @@ void Generator::generateRequiredLinks(const Node *node, CodeMarker *marker)
return;
const auto *en = static_cast<const ExampleNode *>(node);
- QString exampleUrl = Config::instance().getString(CONFIG_URL + Config::dot + CONFIG_EXAMPLES);
+ QString exampleUrl{Config::instance().get(CONFIG_URL + Config::dot + CONFIG_EXAMPLES).asString()};
if (exampleUrl.isEmpty()) {
if (!en->noAutoList()) {
@@ -936,7 +847,7 @@ void Generator::generateLinkToExample(const ExampleNode *en, CodeMarker *marker,
if (metaTagMap)
pathRoot = metaTagMap->value(QLatin1String("installpath"));
if (pathRoot.isEmpty())
- pathRoot = Config::instance().getString(CONFIG_EXAMPLESINSTALLPATH);
+ pathRoot = Config::instance().get(CONFIG_EXAMPLESINSTALLPATH).asString();
QStringList path = QStringList() << pathRoot << en->name();
path.removeAll(QString());
@@ -949,21 +860,37 @@ void Generator::generateLinkToExample(const ExampleNode *en, CodeMarker *marker,
generateText(text, nullptr, marker);
}
-void Generator::addImageToCopy(const ExampleNode *en, const QString &file)
+void Generator::addImageToCopy(const ExampleNode *en, const ResolvedFile& resolved_file)
{
QDir dirInfo;
- QString userFriendlyFilePath;
- const QString prefix("/images/used-in-examples/");
- QString srcPath = Config::findFile(en->location(), QStringList(), s_exampleDirs, file,
- s_exampleImgExts, &userFriendlyFilePath);
- s_outFileNames << prefix.mid(1) + userFriendlyFilePath;
- userFriendlyFilePath.truncate(userFriendlyFilePath.lastIndexOf('/'));
- QString imgOutDir = s_outDir + prefix + userFriendlyFilePath;
+ // TODO: [uncentralized-output-directory-structure]
+ const QString prefix("/images/used-in-examples");
+
+ // TODO: Generators probably should not need to keep track of which files were generated.
+ // Understand if we really need this information and where it should
+ // belong, considering that it should be part of whichever system
+ // would actually store the file itself.
+ s_outFileNames << prefix.mid(1) + "/" + resolved_file.get_query();
+
+
+ // TODO: [uncentralized-output-directory-structure]
+ QString imgOutDir = s_outDir + prefix + "/" + QFileInfo{resolved_file.get_query()}.path();
if (!dirInfo.mkpath(imgOutDir))
en->location().fatal(QStringLiteral("Cannot create output directory '%1'").arg(imgOutDir));
- Config::copyFile(en->location(), srcPath, file, imgOutDir);
+ Config::copyFile(en->location(), resolved_file.get_path(), QFileInfo{resolved_file.get_query()}.fileName(), imgOutDir);
}
+// TODO: [multi-purpose-function-with-flag][generate-file-list]
+// Avoid the use of a boolean flag to dispatch to the correct
+// implementation trough branching.
+// We always have to process both images and files, such that we
+// should consider to remove the branching altogheter, performing both
+// operations in a single call.
+// Otherwise, if this turns out to be infeasible, complex or
+// possibly-confusing, consider extracting the processing code outside
+// the function and provide two higer-level dispathing functions for
+// files and images.
+
/*!
This function is called when the documentation for an example is
being formatted. It outputs a list of files for the example, which
@@ -992,22 +919,33 @@ void Generator::generateFileList(const ExampleNode *en, CodeMarker *marker, bool
text << Atom::ParaLeft << tag << Atom::ParaRight;
text << Atom(Atom::ListLeft, openedList.styleString());
- QString path;
- for (const auto &file : qAsConst(paths)) {
- if (images) {
- if (!file.isEmpty())
- addImageToCopy(en, file);
- } else {
- generateExampleFilePage(en, file, marker);
+ for (const auto &path : std::as_const(paths)) {
+ auto maybe_resolved_file{file_resolver.resolve(path)};
+ if (!maybe_resolved_file) {
+ // TODO: [uncentralized-admonition][failed-resolve-file]
+ QString details = std::transform_reduce(
+ file_resolver.get_search_directories().cbegin(),
+ file_resolver.get_search_directories().cend(),
+ u"Searched directories:"_s,
+ std::plus(),
+ [](const DirectoryPath &directory_path) -> QString { return u' ' + directory_path.value(); }
+ );
+
+ en->location().warning(u"(Generator)Cannot find file to quote from: %1"_s.arg(path), details);
+
+ continue;
}
+ auto file{*maybe_resolved_file};
+ if (images) addImageToCopy(en, file);
+ else generateExampleFilePage(en, file, marker);
+
openedList.next();
text << Atom(Atom::ListItemNumber, openedList.numberString())
<< Atom(Atom::ListItemLeft, openedList.styleString()) << Atom::ParaLeft
- << Atom(atomType, file) << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK) << file
+ << Atom(atomType, file.get_query()) << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK) << file.get_query()
<< Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK) << Atom::ParaRight
<< Atom(Atom::ListItemRight, openedList.styleString());
- path = file;
}
text << Atom(Atom::ListRight, openedList.styleString());
if (!paths.isEmpty())
@@ -1036,26 +974,23 @@ void Generator::generateDocumentation(Node *node)
if (node->parent() != nullptr) {
if (node->isCollectionNode()) {
/*
- A collection node collects: groups, C++ modules,
- QML modules or JavaScript modules. Testing for a
- CollectionNode must be done before testing for a
- TextPageNode because a CollectionNode is a PageNode
- at this point.
+ A collection node collects: groups, C++ modules, or QML
+ modules. Testing for a CollectionNode must be done
+ before testing for a TextPageNode because a
+ CollectionNode is a PageNode at this point.
- Don't output an HTML page for the collection
- node unless the \group, \module, \qmlmodule or
- \jsmodule command was actually seen by qdoc in
- the qdoc comment for the node.
+ Don't output an HTML page for the collection node unless
+ the \group, \module, or \qmlmodule command was actually
+ seen by qdoc in the qdoc comment for the node.
A key prerequisite in this case is the call to
- mergeCollections(cn). We must determine whether
- this group, module, QML module, or JavaScript
- module has members in other modules. We know at
- this point that cn's members list contains only
- members in the current module. Therefore, before
- outputting the page for cn, we must search for
- members of cn in the other modules and add them
- to the members list.
+ mergeCollections(cn). We must determine whether this
+ group, module or QML module has members in other
+ modules. We know at this point that cn's members list
+ contains only members in the current module. Therefore,
+ before outputting the page for cn, we must search for
+ members of cn in the other modules and add them to the
+ members list.
*/
auto *cn = static_cast<CollectionNode *>(node);
if (cn->wasSeen()) {
@@ -1085,16 +1020,11 @@ void Generator::generateDocumentation(Node *node)
beginSubPage(node, fileName(node));
generateCppReferencePage(static_cast<Aggregate *>(node), marker);
endSubPage();
- } else if (node->isQmlType() || node->isJsType()) {
+ } else if (node->isQmlType()) {
beginSubPage(node, fileName(node));
auto *qcn = static_cast<QmlTypeNode *>(node);
generateQmlTypePage(qcn, marker);
endSubPage();
- } else if (node->isQmlBasicType() || node->isJsBasicType()) {
- beginSubPage(node, fileName(node));
- auto *qbtn = static_cast<QmlBasicTypeNode *>(node);
- generateQmlBasicTypePage(qbtn, marker);
- endSubPage();
} else if (node->isProxyNode()) {
beginSubPage(node, fileName(node));
generateProxyPage(static_cast<Aggregate *>(node), marker);
@@ -1106,94 +1036,68 @@ void Generator::generateDocumentation(Node *node)
if (node->isAggregate()) {
auto *aggregate = static_cast<Aggregate *>(node);
const NodeList &children = aggregate->childNodes();
- for (auto *node : children) {
- if (node->isPageNode() && !node->isPrivate())
- generateDocumentation(node);
- }
- }
-}
-
-/*!
- Generate a list of maintainers in the output
- */
-void Generator::generateMaintainerList(const Aggregate *node, CodeMarker *marker)
-{
- QStringList sl = getMetadataElements(node, "maintainer");
-
- if (!sl.isEmpty()) {
- Text text;
- text << Atom::ParaLeft << Atom(Atom::FormattingLeft, ATOM_FORMATTING_BOLD)
- << "Maintained by: " << Atom(Atom::FormattingRight, ATOM_FORMATTING_BOLD);
-
- for (int i = 0; i < sl.size(); ++i)
- text << sl.at(i) << Utilities::separator(i, sl.size());
-
- text << Atom::ParaRight;
- generateText(text, node, marker);
- }
-}
-
-/*!
- Extract sections of markup text surrounded by \e qmltext
- and \e endqmltext and output them.
- */
-bool Generator::generateQmlText(const Text &text, const Node *relative, CodeMarker *marker,
- const QString & /* qmlName */)
-{
- const Atom *atom = text.firstAtom();
- bool result = false;
-
- if (atom != nullptr) {
- initializeTextOutput();
- while (atom) {
- if (atom->type() != Atom::QmlText)
- atom = atom->next();
- else {
- atom = atom->next();
- while (atom && (atom->type() != Atom::EndQmlText)) {
- int n = 1 + generateAtom(atom, relative, marker);
- while (n-- > 0)
- atom = atom->next();
+ for (auto *child : children) {
+ if (child->isPageNode() && !child->isPrivate()) {
+ generateDocumentation(child);
+ } else if (!node->parent() && child->isInAPI() && !child->isRelatedNonmember()) {
+ // Warn if there are documented non-page-generating nodes in the root namespace
+ child->location().warning(u"No documentation generated for %1 '%2' in global scope."_s
+ .arg(typeString(child), child->name()),
+ u"Maybe you forgot to use the '\\relates' command?"_s);
+ child->setStatus(Node::DontDocument);
+ } else if (child->isQmlModule() && !child->wasSeen()) {
+ // An undocumented QML module that was constructed as a placeholder
+ auto *qmlModule = static_cast<CollectionNode *>(child);
+ for (const auto *member : qmlModule->members()) {
+ member->location().warning(
+ u"Undocumented QML module '%1' referred by type '%2' or its members"_s
+ .arg(qmlModule->name(), member->name()),
+ u"Maybe you forgot to document '\\qmlmodule %1'?"_s
+ .arg(qmlModule->name()));
}
+ } else if (child->isQmlType() && !child->hasDoc()) {
+ // A placeholder QML type with incorrect module identifier
+ auto *qmlType = static_cast<QmlTypeNode *>(child);
+ if (auto qmid = qmlType->logicalModuleName(); !qmid.isEmpty())
+ qmlType->location().warning(u"No such type '%1' in QML module '%2'"_s
+ .arg(qmlType->name(), qmid));
}
}
- result = true;
}
- return result;
}
void Generator::generateReimplementsClause(const FunctionNode *fn, CodeMarker *marker)
{
- if (!fn->overridesThis().isEmpty()) {
- if (fn->parent()->isClassNode()) {
- auto *cn = static_cast<ClassNode *>(fn->parent());
- const FunctionNode *overrides = cn->findOverriddenFunction(fn);
- if (overrides && !overrides->isPrivate() && !overrides->parent()->isPrivate()) {
- if (overrides->hasDoc()) {
- Text text;
- text << Atom::ParaLeft << "Reimplements: ";
- QString fullName =
- overrides->parent()->name() + "::" + overrides->signature(false, true);
- appendFullName(text, overrides->parent(), fullName, overrides);
- text << "." << Atom::ParaRight;
- generateText(text, fn, marker);
- } else {
- fn->doc().location().warning(
- QStringLiteral("Illegal \\reimp; no documented virtual function for %1")
- .arg(overrides->plainSignature()));
- }
- return;
- }
- const PropertyNode *sameName = cn->findOverriddenProperty(fn);
- if (sameName && sameName->hasDoc()) {
- Text text;
- text << Atom::ParaLeft << "Reimplements an access function for property: ";
- QString fullName = sameName->parent()->name() + "::" + sameName->name();
- appendFullName(text, sameName->parent(), fullName, sameName);
- text << "." << Atom::ParaRight;
- generateText(text, fn, marker);
- }
+ if (fn->overridesThis().isEmpty() || !fn->parent()->isClassNode())
+ return;
+
+ auto *cn = static_cast<ClassNode *>(fn->parent());
+ const FunctionNode *overrides = cn->findOverriddenFunction(fn);
+ if (overrides && !overrides->isPrivate() && !overrides->parent()->isPrivate()) {
+ if (overrides->hasDoc()) {
+ Text text;
+ text << Atom::ParaLeft << "Reimplements: ";
+ QString fullName =
+ overrides->parent()->name()
+ + "::" + overrides->signature(Node::SignaturePlain);
+ appendFullName(text, overrides->parent(), fullName, overrides);
+ text << "." << Atom::ParaRight;
+ generateText(text, fn, marker);
+ } else {
+ fn->doc().location().warning(
+ QStringLiteral("Illegal \\reimp; no documented virtual function for %1")
+ .arg(overrides->plainSignature()));
}
+ return;
+ }
+ const PropertyNode *sameName = cn->findOverriddenProperty(fn);
+ if (sameName && sameName->hasDoc()) {
+ Text text;
+ text << Atom::ParaLeft << "Reimplements an access function for property: ";
+ QString fullName = sameName->parent()->name() + "::" + sameName->name();
+ appendFullName(text, sameName->parent(), fullName, sameName);
+ text << "." << Atom::ParaRight;
+ generateText(text, fn, marker);
}
}
@@ -1202,32 +1106,112 @@ QString Generator::formatSince(const Node *node)
QStringList since = node->since().split(QLatin1Char(' '));
// If there is only one argument, assume it is the Qt version number.
- if (since.count() == 1)
+ if (since.size() == 1)
return "Qt " + since[0];
// Otherwise, use the original <project> <version> string.
return node->since();
}
+/*!
+ \internal
+ Returns a string representing status information of a \a node.
+
+ If a status description is returned, it is one of:
+ \list
+ \li Custom status set explicitly in node's documentation using
+ \c {\meta {status} {<description>}},
+ \li 'Deprecated [since <version>]' (\\deprecated [<version>]),
+ \li 'Until <version>',
+ \li 'Preliminary' (\\preliminary), or
+ \li The description adopted from associated module's state:
+ \c {\modulestate {<description>}}.
+ \endlist
+
+ Otherwise, returns \c std::nullopt.
+*/
+std::optional<QString> formatStatus(const Node *node, QDocDatabase *qdb)
+{
+ QString status;
+
+ if (const auto metaMap = node->doc().metaTagMap(); metaMap) {
+ status = metaMap->value("status");
+ if (!status.isEmpty())
+ return {status};
+ }
+ const auto since = node->deprecatedSince();
+ if (node->status() == Node::Deprecated) {
+ status = u"Deprecated"_s;
+ if (!since.isEmpty())
+ status += " since %1"_L1.arg(since);
+ } else if (!since.isEmpty()) {
+ status = "Until %1"_L1.arg(since);
+ } else if (node->status() == Node::Preliminary) {
+ status = u"Preliminary"_s;
+ } else if (const auto collection = qdb->getModuleNode(node); collection) {
+ status = collection->state();
+ }
+
+ return status.isEmpty() ? std::nullopt : std::optional(status);
+}
+
void Generator::generateSince(const Node *node, CodeMarker *marker)
{
if (!node->since().isEmpty()) {
Text text;
- text << Atom::ParaLeft << "This " << typeString(node) << " was introduced ";
- if (node->isEnumType())
- text << "or modified ";
- text << "in " << formatSince(node) << "." << Atom::ParaRight;
+ text << Atom::ParaLeft << "This " << typeString(node) << " was introduced in "
+ << formatSince(node) << "." << Atom::ParaRight;
generateText(text, node, marker);
}
}
+void Generator::generateNoexceptNote(const Node* node, CodeMarker* marker) {
+ std::vector<const Node*> nodes;
+ if (node->isSharedCommentNode()) {
+ auto shared_node = static_cast<const SharedCommentNode*>(node);
+ nodes.reserve(shared_node->collective().size());
+ nodes.insert(nodes.begin(), shared_node->collective().begin(), shared_node->collective().end());
+ } else nodes.push_back(node);
+
+ std::size_t counter{1};
+ for (const Node* node : nodes) {
+ if (node->isFunction(Node::CPP)) {
+ if (auto exception_info = static_cast<const FunctionNode*>(node)->getNoexcept(); exception_info && !(*exception_info).isEmpty()) {
+ Text text;
+ text << Atom::NoteLeft
+ << (nodes.size() > 1 ? QString::fromStdString(" ("s + std::to_string(counter) + ")"s) : QString::fromStdString("This ") + typeString(node))
+ << " does not throw any exception when " << "\"" << *exception_info << "\"" << " is true."
+ << Atom::NoteRight;
+ generateText(text, node, marker);
+ }
+ }
+
+ ++counter;
+ }
+}
+
void Generator::generateStatus(const Node *node, CodeMarker *marker)
{
Text text;
switch (node->status()) {
case Node::Active:
- // Do nothing.
+ // Output the module 'state' description if set.
+ if (node->isModule() || node->isQmlModule()) {
+ const QString &state = static_cast<const CollectionNode*>(node)->state();
+ if (!state.isEmpty()) {
+ text << Atom::ParaLeft << "This " << typeString(node) << " is in "
+ << Atom(Atom::FormattingLeft, ATOM_FORMATTING_ITALIC) << state
+ << Atom(Atom::FormattingRight, ATOM_FORMATTING_ITALIC) << " state."
+ << Atom::ParaRight;
+ break;
+ }
+ }
+ if (const auto version = node->deprecatedSince(); !version.isEmpty()) {
+ text << Atom::ParaLeft << "This " << typeString(node)
+ << " is scheduled for deprecation in version "
+ << version << "." << Atom::ParaRight;
+ }
break;
case Node::Preliminary:
text << Atom::ParaLeft << Atom(Atom::FormattingLeft, ATOM_FORMATTING_BOLD) << "This "
@@ -1239,10 +1223,14 @@ void Generator::generateStatus(const Node *node, CodeMarker *marker)
if (node->isAggregate())
text << Atom(Atom::FormattingLeft, ATOM_FORMATTING_BOLD);
text << "This " << typeString(node) << " is deprecated";
- if (const QString &version = node->deprecatedSince(); !version.isEmpty())
- text << " since " << version;
+ if (const QString &version = node->deprecatedSince(); !version.isEmpty()) {
+ text << " since ";
+ if (node->isQmlNode() && !node->logicalModuleName().isEmpty())
+ text << node->logicalModuleName() << " ";
+ text << version;
+ }
+
text << ". We strongly advise against using it in new code.";
- text << Atom::ParaRight;
if (node->isAggregate())
text << Atom(Atom::FormattingRight, ATOM_FORMATTING_BOLD);
text << Atom::ParaRight;
@@ -1263,6 +1251,8 @@ void Generator::generateAddendum(const Node *node, Addendum type, CodeMarker *ma
{
Q_ASSERT(node && !node->name().isEmpty());
Text text;
+ text << Atom(Atom::DivLeft,
+ "class=\"admonition %1\""_L1.arg(generateNote ? u"note"_s : u"auto"_s));
text << Atom::ParaLeft;
if (generateNote) {
@@ -1297,24 +1287,24 @@ void Generator::generateAddendum(const Node *node, Addendum type, CodeMarker *ma
if (!node->isFunction())
return;
const auto *fn = static_cast<const FunctionNode *>(node);
- NodeList nodes = fn->associatedProperties();
+ auto nodes = fn->associatedProperties();
if (nodes.isEmpty())
return;
std::sort(nodes.begin(), nodes.end(), Node::nodeNameLessThan);
- for (const auto *n : qAsConst(nodes)) {
+ for (const auto *n : std::as_const(nodes)) {
QString msg;
const auto *pn = static_cast<const PropertyNode *>(n);
switch (pn->role(fn)) {
- case PropertyNode::Getter:
+ case PropertyNode::FunctionRole::Getter:
msg = QStringLiteral("Getter function");
break;
- case PropertyNode::Setter:
+ case PropertyNode::FunctionRole::Setter:
msg = QStringLiteral("Setter function");
break;
- case PropertyNode::Resetter:
+ case PropertyNode::FunctionRole::Resetter:
msg = QStringLiteral("Resetter function");
break;
- case PropertyNode::Notifier:
+ case PropertyNode::FunctionRole::Notifier:
msg = QStringLiteral("Notifier signal");
break;
default:
@@ -1339,7 +1329,8 @@ void Generator::generateAddendum(const Node *node, Addendum type, CodeMarker *ma
return;
}
- text << Atom::ParaRight;
+ text << Atom::ParaRight
+ << Atom::DivRight;
generateText(text, node, marker);
}
@@ -1400,6 +1391,36 @@ bool Generator::hasExceptions(const Node *node, NodeList &reentrant, NodeList &t
return result;
}
+/*!
+ Returns \c true if a trademark symbol should be appended to the
+ output as determined by \a atom. Trademarks are tracked via the
+ use of the \\tm formatting command.
+
+ Returns true if:
+
+ \list
+ \li \a atom is of type Atom::FormattingRight containing
+ ATOM_FORMATTING_TRADEMARK, and
+ \li The trademarked string is the first appearance on the
+ current sub-page.
+ \endlist
+*/
+bool Generator::appendTrademark(const Atom *atom)
+{
+ if (atom->type() != Atom::FormattingRight)
+ return false;
+ if (atom->string() != ATOM_FORMATTING_TRADEMARK)
+ return false;
+
+ if (atom->count() > 1) {
+ if (s_trademarks.contains(atom->string(1)))
+ return false;
+ s_trademarks << atom->string(1);
+ }
+
+ return true;
+}
+
static void startNote(Text &text)
{
text << Atom::ParaLeft << Atom(Atom::FormattingLeft, ATOM_FORMATTING_BOLD)
@@ -1495,6 +1516,66 @@ void Generator::generateThreadSafeness(const Node *node, CodeMarker *marker)
}
/*!
+ \internal
+
+ Generates text that describes the comparison category of \a node.
+ The CodeMarker \a marker is passed along to generateText().
+ */
+bool Generator::generateComparisonCategory(const Node *node, CodeMarker *marker)
+{
+ auto category{node->comparisonCategory()};
+ if (category == ComparisonCategory::None)
+ return false;
+
+ Text text;
+ text << Atom::ParaLeft << "This %1 is "_L1.arg(typeString(node))
+ << Atom(Atom::FormattingLeft, ATOM_FORMATTING_ITALIC)
+ << QString::fromStdString(comparisonCategoryAsString(category))
+ << ((category == ComparisonCategory::Equality) ? "-"_L1 : "ly "_L1)
+ << Atom(Atom::String, "comparable"_L1)
+ << Atom(Atom::FormattingRight, ATOM_FORMATTING_ITALIC)
+ << "."_L1 << Atom::ParaRight;
+ generateText(text, node, marker);
+ return true;
+}
+
+/*!
+ Generates a list of types that compare to \a node with the comparison
+ category that applies for the relationship, followed by (an optional)
+ descriptive text.
+
+ Returns \c true if text was generated, \c false otherwise.
+ */
+bool Generator::generateComparisonList(const Node *node)
+{
+ Q_ASSERT(node);
+ if (!node->doc().comparesWithMap())
+ return false;
+
+ Text relationshipText;
+ for (auto [key, description] : node->doc().comparesWithMap()->asKeyValueRange()) {
+ const QString &category = QString::fromStdString(comparisonCategoryAsString(key));
+
+ relationshipText << Atom::ParaLeft << "This %1 is "_L1.arg(typeString(node))
+ << Atom(Atom::FormattingLeft, ATOM_FORMATTING_BOLD) << category
+ << ((key == ComparisonCategory::Equality) ? "-"_L1 : "ly "_L1)
+ << "comparable"_L1
+ << Atom(Atom::FormattingRight, ATOM_FORMATTING_BOLD)
+ << " with "_L1;
+
+ const QStringList types{description.firstAtom()->string().split(';'_L1)};
+ for (const auto &name : types)
+ relationshipText << Atom(Atom::AutoLink, name)
+ << Utilities::separator(types.indexOf(name), types.size());
+
+ relationshipText << Atom(Atom::ParaRight) << description;
+ }
+
+ generateText(relationshipText, node, nullptr);
+ return !relationshipText.isEmpty();
+}
+
+/*!
Returns the string containing an example code of the input node,
if it is an overloaded signal. Otherwise, returns an empty string.
*/
@@ -1562,54 +1643,13 @@ void Generator::generateDocs()
Generator *Generator::generatorForFormat(const QString &format)
{
- for (const auto &generator : qAsConst(s_generators)) {
+ for (const auto &generator : std::as_const(s_generators)) {
if (generator->format() == format)
return generator;
}
return nullptr;
}
-/*!
- Looks up the tag \a t in the map of metadata values for the
- current topic in \a inner. If values for the tag are found,
- they are returned in a string list.
-
- \note If \a t is found in the metadata map, all the pairs
- having the key \a t are erased. i.e. Once you call this
- function for a particular \a t, you consume \a t.
- */
-QStringList Generator::getMetadataElements(const Aggregate *inner, const QString &t)
-{
- QStringList result;
- QStringMultiMap *metaTagMap = inner->doc().metaTagMap();
- if (metaTagMap)
- result = metaTagMap->values(t);
- if (!result.isEmpty())
- metaTagMap->remove(t);
- return result;
-}
-
-/*!
- Returns a relative path name for an image.
- */
-QString Generator::imageFileName(const Node *relative, const QString &fileBase)
-{
- QString userFriendlyFilePath;
- QString filePath = Config::findFile(relative->doc().location(), s_imageFiles, s_imageDirs,
- fileBase, s_imgFileExts[format()], &userFriendlyFilePath);
-
- if (filePath.isEmpty())
- return QString();
-
- QString path = Config::copyFile(relative->doc().location(), filePath, userFriendlyFilePath,
- outputDir() + QLatin1String("/images"));
- qsizetype images_slash = path.lastIndexOf("images/");
- QString relImagePath;
- if (images_slash != -1)
- relImagePath = path.mid(images_slash);
- return relImagePath;
-}
-
QString Generator::indent(int level, const QString &markedCode)
{
if (level == 0)
@@ -1619,7 +1659,7 @@ QString Generator::indent(int level, const QString &markedCode)
int column = 0;
int i = 0;
- while (i < markedCode.length()) {
+ while (i < markedCode.size()) {
if (markedCode.at(i) == QLatin1Char('\n')) {
column = 0;
} else {
@@ -1638,16 +1678,7 @@ void Generator::initialize()
{
Config &config = Config::instance();
s_outputFormats = config.getOutputFormats();
- s_redirectDocumentationToDevNull = config.getBool(CONFIG_REDIRECTDOCUMENTATIONTODEVNULL);
-
- s_imageFiles = config.getCanonicalPathList(CONFIG_IMAGES);
- s_imageDirs = config.getCanonicalPathList(CONFIG_IMAGEDIRS);
- s_exampleDirs = config.getCanonicalPathList(CONFIG_EXAMPLEDIRS);
- s_exampleImgExts = config.getStringList(CONFIG_EXAMPLES + Config::dot + CONFIG_IMAGEEXTENSIONS);
-
- QString imagesDotFileExtensions = CONFIG_IMAGES + Config::dot + CONFIG_FILEEXTENSIONS;
- for (const auto &ext : config.subVars(imagesDotFileExtensions))
- s_imgFileExts[ext] = config.getStringList(imagesDotFileExtensions + Config::dot + ext);
+ s_redirectDocumentationToDevNull = config.get(CONFIG_REDIRECTDOCUMENTATIONTODEVNULL).asBool();
for (auto &g : s_generators) {
if (s_outputFormats.contains(g->format())) {
@@ -1656,26 +1687,27 @@ void Generator::initialize()
}
}
- for (const auto &n : config.subVars(CONFIG_FORMATTING)) {
+ const auto &configFormatting = config.subVars(CONFIG_FORMATTING);
+ for (const auto &n : configFormatting) {
QString formattingDotName = CONFIG_FORMATTING + Config::dot + n;
- for (const auto &f : config.subVars(formattingDotName)) {
- QString def = config.getString(formattingDotName + Config::dot + f);
+ const auto &formattingDotNames = config.subVars(formattingDotName);
+ for (const auto &f : formattingDotNames) {
+ const auto &configVar = config.get(formattingDotName + Config::dot + f);
+ QString def{configVar.asString()};
if (!def.isEmpty()) {
int numParams = Config::numParams(def);
int numOccs = def.count("\1");
if (numParams != 1) {
- config.lastLocation().warning(QStringLiteral("Formatting '%1' must "
- "have exactly one "
- "parameter (found %2)")
- .arg(n)
- .arg(numParams));
+ configVar.location().warning(QStringLiteral("Formatting '%1' must "
+ "have exactly one "
+ "parameter (found %2)")
+ .arg(n, numParams));
} else if (numOccs > 1) {
- config.lastLocation().fatal(QStringLiteral("Formatting '%1' must "
- "contain exactly one "
- "occurrence of '\\1' "
- "(found %2)")
- .arg(n)
- .arg(numOccs));
+ configVar.location().fatal(QStringLiteral("Formatting '%1' must "
+ "contain exactly one "
+ "occurrence of '\\1' "
+ "(found %2)")
+ .arg(n, numOccs));
} else {
int paramPos = def.indexOf("\1");
s_fmtLeftMaps[f].insert(n, def.left(paramPos));
@@ -1685,27 +1717,27 @@ void Generator::initialize()
}
}
- s_project = config.getString(CONFIG_PROJECT);
+ s_project = config.get(CONFIG_PROJECT).asString();
s_outDir = config.getOutputDir();
s_outSubdir = s_outDir.mid(s_outDir.lastIndexOf('/') + 1);
s_outputPrefixes.clear();
- QStringList items = config.getStringList(CONFIG_OUTPUTPREFIXES);
+ QStringList items{config.get(CONFIG_OUTPUTPREFIXES).asStringList()};
if (!items.isEmpty()) {
for (const auto &prefix : items)
s_outputPrefixes[prefix] =
- config.getString(CONFIG_OUTPUTPREFIXES + Config::dot + prefix);
- } else {
- s_outputPrefixes[QLatin1String("QML")] = QLatin1String("qml-");
- s_outputPrefixes[QLatin1String("JS")] = QLatin1String("js-");
+ config.get(CONFIG_OUTPUTPREFIXES + Config::dot + prefix).asString();
}
+ if (!items.contains(u"QML"_s))
+ s_outputPrefixes[u"QML"_s] = u"qml-"_s;
s_outputSuffixes.clear();
- for (const auto &suffix : config.getStringList(CONFIG_OUTPUTSUFFIXES))
- s_outputSuffixes[suffix] = config.getString(CONFIG_OUTPUTSUFFIXES + Config::dot + suffix);
+ for (const auto &suffix : config.get(CONFIG_OUTPUTSUFFIXES).asStringList())
+ s_outputSuffixes[suffix] = config.get(CONFIG_OUTPUTSUFFIXES
+ + Config::dot + suffix).asString();
- s_noLinkErrors = config.getBool(CONFIG_NOLINKERRORS);
- s_autolinkErrors = config.getBool(CONFIG_AUTOLINKERRORS);
+ s_noLinkErrors = config.get(CONFIG_NOLINKERRORS).asBool();
+ s_autolinkErrors = config.get(CONFIG_AUTOLINKERRORS).asBool();
}
/*!
@@ -1714,18 +1746,45 @@ void Generator::initialize()
*/
void Generator::copyTemplateFiles(const QString &configVar, const QString &subDir)
{
+ // TODO: [resolving-files-unlinked-to-doc]
+ // This is another case of resolving files, albeit it doesn't use Doc::resolveFile.
+ // While it may be left out of a first iteration of the file
+ // resolution logic, it should later be integrated into it.
+ // This should come naturally when the output directory logic is
+ // extracted and copying a file should require a validated
+ // intermediate format.
+ // Do note that what is done here is a bit different from the
+ // resolve file routine that is done for other user-given paths.
+ // Thas is, the paths will always be absolute and not relative as
+ // they are resolved from the configuration.
+ // Ideally, this could be solved in the configuration already,
+ // together with the other configuration resolution processes that
+ // do not abide by the same constraints that, for example, snippet
+ // resolution uses.
Config &config = Config::instance();
QStringList files = config.getCanonicalPathList(configVar, Config::Validate);
+ const auto &loc = config.get(configVar).location();
if (!files.isEmpty()) {
QDir dirInfo;
+ // TODO: [uncentralized-output-directory-structure]
+ // As with other places in the generation pass, the details of
+ // where something is saved in the output directory are spread
+ // to whichever part of the generation does the saving.
+ // It is hence complex to build a model of how an output
+ // directory looks like, as the knowledge has no specific
+ // entry point or chain-path that can be followed in full.
+ // Each of those operations should be centralized in a system
+ // that uniquely knows what the format of the output-directory
+ // is and how to perform operations on it.
+ // Later, move this operation to that centralized system.
QString templateDir = s_outDir + QLatin1Char('/') + subDir;
if (!dirInfo.exists(templateDir) && !dirInfo.mkdir(templateDir)) {
- config.lastLocation().fatal(
- QStringLiteral("Cannot create %1 directory '%2'").arg(subDir, templateDir));
+ // TODO: [uncentralized-admonition]
+ loc.fatal(QStringLiteral("Cannot create %1 directory '%2'").arg(subDir, templateDir));
} else {
for (const auto &file : files) {
if (!file.isEmpty())
- Config::copyFile(config.lastLocation(), file, file, templateDir);
+ Config::copyFile(loc, file, file, templateDir);
}
}
}
@@ -1741,7 +1800,7 @@ void Generator::initializeFormat()
Config &config = Config::instance();
s_outFileNames.clear();
s_useOutputSubdirs = true;
- if (config.getBool(format() + Config::dot + "nosubdirs"))
+ if (config.get(format() + Config::dot + "nosubdirs").asBool())
resetUseOutputSubdirs();
if (s_outputFormats.isEmpty())
@@ -1749,8 +1808,8 @@ void Generator::initializeFormat()
s_outDir = config.getOutputDir(format());
if (s_outDir.isEmpty()) {
- config.lastLocation().fatal(QStringLiteral("No output directory specified in "
- "configuration file or on the command line"));
+ Location().fatal(QStringLiteral("No output directory specified in "
+ "configuration file or on the command line"));
} else {
s_outSubdir = s_outDir.mid(s_outDir.lastIndexOf('/') + 1);
}
@@ -1759,13 +1818,11 @@ void Generator::initializeFormat()
if (outputDir.exists()) {
if (!config.generating() && Generator::useOutputSubdirs()) {
if (!outputDir.isEmpty())
- config.lastLocation().error(
- QStringLiteral("Output directory '%1' exists but is not empty")
+ Location().error(QStringLiteral("Output directory '%1' exists but is not empty")
.arg(s_outDir));
}
} else if (!outputDir.mkpath(QStringLiteral("."))) {
- config.lastLocation().fatal(
- QStringLiteral("Cannot create output directory '%1'").arg(s_outDir));
+ Location().fatal(QStringLiteral("Cannot create output directory '%1'").arg(s_outDir));
}
// Output directory exists, which is enough for prepare phase.
@@ -1774,8 +1831,7 @@ void Generator::initializeFormat()
const QLatin1String imagesDir("images");
if (!outputDir.exists(imagesDir) && !outputDir.mkdir(imagesDir))
- config.lastLocation().fatal(
- QStringLiteral("Cannot create images directory '%1'").arg(outputDir.filePath(imagesDir)));
+ Location().fatal(QStringLiteral("Cannot create images directory '%1'").arg(outputDir.filePath(imagesDir)));
copyTemplateFiles(format() + Config::dot + CONFIG_STYLESHEETS, "style");
copyTemplateFiles(format() + Config::dot + CONFIG_SCRIPTS, "scripts");
@@ -1783,21 +1839,9 @@ void Generator::initializeFormat()
// Use a format-specific .quotinginformation if defined, otherwise a global value
if (config.subVars(format()).contains(CONFIG_QUOTINGINFORMATION))
- m_quoting = config.getBool(format() + Config::dot + CONFIG_QUOTINGINFORMATION);
+ m_quoting = config.get(format() + Config::dot + CONFIG_QUOTINGINFORMATION).asBool();
else
- m_quoting = config.getBool(CONFIG_QUOTINGINFORMATION);
-}
-
-/*!
- Appends each directory path in \a moreImageDirs to the
- list of image directories.
- */
-void Generator::augmentImageDirs(QSet<QString> &moreImageDirs)
-{
- if (moreImageDirs.isEmpty())
- return;
- for (const auto &it : moreImageDirs)
- s_imageDirs.append(it);
+ m_quoting = config.get(CONFIG_QUOTINGINFORMATION).asBool();
}
/*!
@@ -1830,36 +1874,42 @@ QString Generator::outFileName()
QString Generator::outputPrefix(const Node *node)
{
- // Prefix is applied to QML and JS types
- if (node->isQmlType() || node->isQmlBasicType())
- return s_outputPrefixes[QLatin1String("QML")];
- if (node->isJsType() || node->isJsBasicType())
- return s_outputPrefixes[QLatin1String("JS")];
+ // Omit prefix for module pages
+ if (node->isPageNode() && !node->isCollectionNode()) {
+ switch (node->genus()) {
+ case Node::QML:
+ return s_outputPrefixes[u"QML"_s];
+ case Node::CPP:
+ return s_outputPrefixes[u"CPP"_s];
+ default:
+ break;
+ }
+ }
return QString();
}
QString Generator::outputSuffix(const Node *node)
{
- // Suffix is applied to QML and JS types, as
- // well as module pages.
- if (node->isQmlModule() || node->isQmlType() || node->isQmlBasicType())
- return s_outputSuffixes[QLatin1String("QML")];
- if (node->isJsModule() || node->isJsType() || node->isJsBasicType())
- return s_outputSuffixes[QLatin1String("JS")];
+ if (node->isPageNode()) {
+ switch (node->genus()) {
+ case Node::QML:
+ return s_outputSuffixes[u"QML"_s];
+ case Node::CPP:
+ return s_outputSuffixes[u"CPP"_s];
+ default:
+ break;
+ }
+ }
+
return QString();
}
bool Generator::parseArg(const QString &src, const QString &tag, int *pos, int n,
- QStringView *contents, QStringView *par1, bool debug)
+ QStringView *contents, QStringView *par1)
{
#define SKIP_CHAR(c) \
- if (debug) \
- qDebug() << "looking for " << c << " at " << QString(src.data() + i, n - i); \
- if (i >= n || src[i] != c) { \
- if (debug) \
- qDebug() << " char '" << c << "' not found"; \
+ if (i >= n || src[i] != c) \
return false; \
- } \
++i;
#define SKIP_SPACE \
@@ -1873,15 +1923,12 @@ bool Generator::parseArg(const QString &src, const QString &tag, int *pos, int n
// SKIP_CHAR('<');
// SKIP_CHAR('@');
- if (tag != QStringView(src).mid(i, tag.length())) {
+ if (tag != QStringView(src).mid(i, tag.size())) {
return false;
}
- if (debug)
- qDebug() << "haystack:" << src << "needle:" << tag << "i:" << i;
-
// skip tag
- i += tag.length();
+ i += tag.size();
// parse stuff like: linkTag("(<@link node=\"([^\"]+)\">).*(</@link>)");
if (par1) {
@@ -1891,8 +1938,6 @@ bool Generator::parseArg(const QString &src, const QString &tag, int *pos, int n
while (i < n && src[i].isLetter())
++i;
if (src[i] == '=') {
- if (debug)
- qDebug() << "read parameter" << QString(src.data() + j, i - j);
SKIP_CHAR('=');
SKIP_CHAR('"');
// skip parameter name
@@ -1902,9 +1947,6 @@ bool Generator::parseArg(const QString &src, const QString &tag, int *pos, int n
*par1 = QStringView(src).mid(j, i - j);
SKIP_CHAR('"');
SKIP_SPACE;
- } else {
- if (debug)
- qDebug() << "no optional parameter found";
}
}
SKIP_SPACE;
@@ -1913,7 +1955,7 @@ bool Generator::parseArg(const QString &src, const QString &tag, int *pos, int n
// find contents up to closing "</@tag>
j = i;
for (; true; ++i) {
- if (i + 4 + tag.length() > n)
+ if (i + 4 + tag.size() > n)
return false;
if (src[i] != '<')
continue;
@@ -1921,22 +1963,21 @@ bool Generator::parseArg(const QString &src, const QString &tag, int *pos, int n
continue;
if (src[i + 2] != '@')
continue;
- if (tag != QStringView(src).mid(i + 3, tag.length()))
+ if (tag != QStringView(src).mid(i + 3, tag.size()))
continue;
- if (src[i + 3 + tag.length()] != '>')
+ if (src[i + 3 + tag.size()] != '>')
continue;
break;
}
*contents = QStringView(src).mid(j, i - j);
- i += tag.length() + 4;
+ i += tag.size() + 4;
*pos = i;
- if (debug)
- qDebug() << " tag " << tag << " found: pos now: " << i;
return true;
#undef SKIP_CHAR
+#undef SKIP_SPACE
}
QString Generator::plainCode(const QString &markedCode)
@@ -1950,11 +1991,6 @@ QString Generator::plainCode(const QString &markedCode)
return t;
}
-void Generator::setImageFileExtensions(const QStringList &extensions)
-{
- s_imgFileExts[format()] = extensions;
-}
-
int Generator::skipAtoms(const Atom *atom, Atom::AtomType type) const
{
int skipAhead = 0;
@@ -2017,6 +2053,8 @@ void Generator::supplementAlsoList(const Node *node, QList<Text> &alsoList)
}
if (i == alsoList.size()) {
+ if (alternateFunc->isDeprecated() && !fn->isDeprecated())
+ return;
alternateName += "()";
Text also;
@@ -2030,18 +2068,71 @@ void Generator::supplementAlsoList(const Node *node, QList<Text> &alsoList)
}
}
+void Generator::generateEnumValuesForQmlProperty(const Node *node, CodeMarker *marker)
+{
+ if (!node->isQmlProperty())
+ return;
+
+ const auto *qpn = static_cast<const QmlPropertyNode*>(node);
+
+ if (!qpn->enumNode())
+ return;
+
+ // Retrieve atoms from C++ enum \value list
+ const auto body{qpn->enumNode()->doc().body()};
+ const auto *start{body.firstAtom()};
+ Text text;
+
+ while ((start = start->find(Atom::ListLeft, ATOM_LIST_VALUE))) {
+ const auto end = start->find(Atom::ListRight, ATOM_LIST_VALUE);
+ // Skip subsequent ListLeft atoms, collating multiple lists into one
+ text << body.subText(text.isEmpty() ? start : start->next(), end);
+ start = end;
+ }
+ if (text.isEmpty())
+ return;
+
+ text << Atom(Atom::ListRight, ATOM_LIST_VALUE);
+ if (marker)
+ generateText(text, qpn, marker);
+ else
+ generateText(text, qpn);
+}
+
void Generator::terminate()
{
- for (const auto &generator : qAsConst(s_generators)) {
+ for (const auto &generator : std::as_const(s_generators)) {
if (s_outputFormats.contains(generator->format()))
generator->terminateGenerator();
}
+ // REMARK: Generators currently, due to recent changes and the
+ // transitive nature of the current codebase, receive some of
+ // their dependencies in the constructor and some of them in their
+ // initialize-terminate lifetime.
+ // This means that generators need to be constructed and
+ // destructed between usages such that if multiple usages are
+ // required, the generators present in the list will have been
+ // destroyed by then such that accessing them would be an error.
+ // The current codebase calls initialize and the correspective
+ // terminate with the same scope as the lifetime of the
+ // generators.
+ // Then, clearing the list ensures that, if another generator
+ // execution is needed, the stale generators will not be removed
+ // as to be replaced by newly constructed ones.
+ // Do note that it is not clear that this will happen for any call
+ // in Qt's documentation and this should work only because of the
+ // form of the current codebase and the scoping of the
+ // initialize-terminate calls. As such, this should be considered
+ // a patchwork that may or may not be doing anything and that may
+ // break due to changes in other parts of the codebase.
+ //
+ // This is still to be considered temporary as the whole
+ // initialize-terminate idiom must be removed from the codebase.
+ s_generators.clear();
+
s_fmtLeftMaps.clear();
s_fmtRightMaps.clear();
- s_imgFileExts.clear();
- s_imageFiles.clear();
- s_imageDirs.clear();
s_outDir.clear();
}
@@ -2055,8 +2146,8 @@ QString Generator::trimmedTrailing(const QString &string, const QString &prefix,
const QString &suffix)
{
QString trimmed = string;
- while (trimmed.length() > 0 && trimmed[trimmed.length() - 1].isSpace())
- trimmed.truncate(trimmed.length() - 1);
+ while (trimmed.size() > 0 && trimmed[trimmed.size() - 1].isSpace())
+ trimmed.truncate(trimmed.size() - 1);
trimmed.append(suffix);
trimmed.prepend(prefix);
@@ -2075,8 +2166,7 @@ QString Generator::typeString(const Node *node)
case Node::Union:
return "union";
case Node::QmlType:
- case Node::QmlBasicType:
- case Node::JsBasicType:
+ case Node::QmlValueType:
return "type";
case Node::Page:
return "documentation";
@@ -2088,15 +2178,15 @@ QString Generator::typeString(const Node *node)
case Node::Function: {
const auto fn = static_cast<const FunctionNode *>(node);
switch (fn->metaness()) {
- case FunctionNode::JsSignal:
case FunctionNode::QmlSignal:
return "signal";
- case FunctionNode::JsSignalHandler:
case FunctionNode::QmlSignalHandler:
return "signal handler";
- case FunctionNode::JsMethod:
case FunctionNode::QmlMethod:
return "method";
+ case FunctionNode::MacroWithParams:
+ case FunctionNode::MacroWithoutParams:
+ return "macro";
default:
break;
}
@@ -2106,7 +2196,6 @@ QString Generator::typeString(const Node *node)
case Node::QmlProperty:
return "property";
case Node::Module:
- case Node::JsModule:
case Node::QmlModule:
return "module";
case Node::SharedComment: {
diff --git a/src/qdoc/generator.h b/src/qdoc/qdoc/src/qdoc/generator.h
index 77b894416..faddc8eb7 100644
--- a/src/qdoc/generator.h
+++ b/src/qdoc/qdoc/src/qdoc/generator.h
@@ -1,47 +1,23 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the tools applications of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#ifndef GENERATOR_H
#define GENERATOR_H
#include "text.h"
#include "utilities.h"
+#include "filesystem/fileresolver.h"
#include <QtCore/qlist.h>
#include <QtCore/qmap.h>
#include <QtCore/qstring.h>
#include <QtCore/qstringlist.h>
#include <QtCore/qtextstream.h>
+#include <optional>
QT_BEGIN_NAMESPACE
typedef QMultiMap<QString, Node *> NodeMultiMap;
-typedef QMap<Node *, NodeMultiMap> ParentMaps;
class Aggregate;
class CodeMarker;
@@ -50,7 +26,6 @@ class FunctionNode;
class Location;
class Node;
class QDocDatabase;
-class QmlBasicTypeNode;
class Generator
{
@@ -65,7 +40,7 @@ public:
BindableProperty
};
- Generator();
+ Generator(FileResolver& file_resolver);
virtual ~Generator();
virtual bool canHandleFormat(const QString &format) { return format == this->format(); }
@@ -76,7 +51,7 @@ public:
virtual void terminateGenerator();
virtual QString typeString(const Node *node);
- QString fullDocumentLocation(const Node *node, bool useSubdir = false);
+ QString fullDocumentLocation(const Node *node);
QString linkForExampleFile(const QString &path, const QString &fileExt = QString());
static QString exampleFileTitle(const ExampleNode *relative, const QString &fileName);
static Generator *currentGenerator() { return s_currentGenerator; }
@@ -86,7 +61,6 @@ public:
static const QString &outputSubdir() { return s_outSubdir; }
static void terminate();
static const QStringList &outputFileNames() { return s_outFileNames; }
- static void augmentImageDirs(QSet<QString> &moreImageDirs);
static bool noLinkErrors() { return s_noLinkErrors; }
static bool autolinkErrors() { return s_autolinkErrors; }
static QString defaultModuleName() { return s_project; }
@@ -94,55 +68,32 @@ public:
static bool useOutputSubdirs() { return s_useOutputSubdirs; }
static void setQmlTypeContext(QmlTypeNode *t) { s_qmlTypeContext = t; }
static QmlTypeNode *qmlTypeContext() { return s_qmlTypeContext; }
- static QString cleanRef(const QString &ref);
+ static QString cleanRef(const QString &ref, bool xmlCompliant = false);
static QString plainCode(const QString &markedCode);
virtual QString fileBase(const Node *node) const;
protected:
static QFile *openSubPageFile(const Node *node, const QString &fileName);
- void beginFilePage(const Node *node, const QString &fileName);
- void endFilePage() { endSubPage(); } // for symmetry
void beginSubPage(const Node *node, const QString &fileName);
void endSubPage();
[[nodiscard]] virtual QString fileExtension() const = 0;
- virtual void generateExampleFilePage(const Node *, const QString &, CodeMarker *) {}
- virtual void generateExampleFilePage(const Node *node, const QString &str)
- {
- generateExampleFilePage(node, str, nullptr);
- }
+ virtual void generateExampleFilePage(const Node *, ResolvedFile, CodeMarker * = nullptr) {}
virtual void generateAlsoList(const Node *node, CodeMarker *marker);
virtual void generateAlsoList(const Node *node) { generateAlsoList(node, nullptr); }
virtual qsizetype generateAtom(const Atom *, const Node *, CodeMarker *) { return 0; }
- virtual qsizetype generateAtom(const Atom *atom, const Node *node)
- {
- return generateAtom(atom, node, nullptr);
- }
virtual void generateBody(const Node *node, CodeMarker *marker);
virtual void generateCppReferencePage(Aggregate *, CodeMarker *) {}
virtual void generateProxyPage(Aggregate *, CodeMarker *) {}
virtual void generateQmlTypePage(QmlTypeNode *, CodeMarker *) {}
- virtual void generateQmlBasicTypePage(QmlBasicTypeNode *, CodeMarker *) {}
virtual void generatePageNode(PageNode *, CodeMarker *) {}
virtual void generateCollectionNode(CollectionNode *, CodeMarker *) {}
virtual void generateGenericCollectionPage(CollectionNode *, CodeMarker *) {}
virtual void generateDocumentation(Node *node);
- virtual void generateMaintainerList(const Aggregate *node, CodeMarker *marker);
- virtual void generateMaintainerList(const Aggregate *node)
- {
- generateMaintainerList(node, nullptr);
- };
- virtual bool generateQmlText(const Text &text, const Node *relative, CodeMarker *marker,
- const QString &qmlName);
- virtual bool generateQmlText(const Text &text, const Node *relative)
- {
- return generateQmlText(text, relative, nullptr, QString());
- }
virtual bool generateText(const Text &text, const Node *relative, CodeMarker *marker);
virtual bool generateText(const Text &text, const Node *relative)
{
return generateText(text, relative, nullptr);
};
- virtual QString imageFileName(const Node *relative, const QString &fileBase);
virtual int skipAtoms(const Atom *atom, Atom::AtomType type) const;
static bool matchAhead(const Atom *atom, Atom::AtomType expectedAtomType);
@@ -157,12 +108,14 @@ protected:
QMap<QString, QString> &formattingRightMap();
const Atom *generateAtomList(const Atom *atom, const Node *relative, CodeMarker *marker,
bool generate, int &numGeneratedAtoms);
+ void generateEnumValuesForQmlProperty(const Node *node, CodeMarker *marker);
void generateRequiredLinks(const Node *node, CodeMarker *marker);
void generateLinkToExample(const ExampleNode *en, CodeMarker *marker,
const QString &exampleUrl);
virtual void generateFileList(const ExampleNode *en, CodeMarker *marker, bool images);
static QString formatSince(const Node *node);
void generateSince(const Node *node, CodeMarker *marker);
+ void generateNoexceptNote(const Node *node, CodeMarker *marker);
void generateStatus(const Node *node, CodeMarker *marker);
virtual void generateAddendum(const Node *node, Addendum type, CodeMarker *marker,
bool generateNote);
@@ -171,15 +124,16 @@ protected:
generateAddendum(node, type, marker, true);
};
void generateThreadSafeness(const Node *node, CodeMarker *marker);
- QStringList getMetadataElements(const Aggregate *inner, const QString &t);
+ bool generateComparisonCategory(const Node *node, CodeMarker *marker = nullptr);
+ bool generateComparisonList(const Node *node);
+
void generateOverloadedSignal(const Node *node, CodeMarker *marker);
static QString getOverloadedSignalCode(const Node *node);
QString indent(int level, const QString &markedCode);
QTextStream &out();
QString outFileName();
bool parseArg(const QString &src, const QString &tag, int *pos, int n, QStringView *contents,
- QStringView *par1 = nullptr, bool debug = false);
- void setImageFileExtensions(const QStringList &extensions);
+ QStringView *par1 = nullptr);
void unknownAtom(const Atom *atom);
int appendSortedQmlNames(Text &text, const Node *base, const NodeList &subs);
@@ -199,24 +153,26 @@ protected:
void appendSignature(Text &text, const Node *node);
void signatureList(const NodeList &nodes, const Node *relative, CodeMarker *marker);
- void addImageToCopy(const ExampleNode *en, const QString &file);
+ void addImageToCopy(const ExampleNode *en, const ResolvedFile& resolved_file);
+ // TODO: This seems to be used as the predicate in std::sort calls.
+ // Remove it as it is unneeded.
+ // Indeed, it could be replaced by std::less and, furthermore,
+ // std::sort already defaults to operator< when no predicate is
+ // provided.
static bool comparePaths(const QString &a, const QString &b) { return (a < b); }
+ static bool appendTrademark(const Atom *atom);
private:
static Generator *s_currentGenerator;
- static QStringList s_exampleDirs;
- static QStringList s_exampleImgExts;
static QMap<QString, QMap<QString, QString>> s_fmtLeftMaps;
static QMap<QString, QMap<QString, QString>> s_fmtRightMaps;
static QList<Generator *> s_generators;
- static QStringList s_imageDirs;
- static QStringList s_imageFiles;
- static QMap<QString, QStringList> s_imgFileExts;
static QString s_project;
static QString s_outDir;
static QString s_outSubdir;
static QStringList s_outFileNames;
static QSet<QString> s_outputFormats;
+ static QSet<QString> s_trademarks;
static QHash<QString, QString> s_outputPrefixes;
static QHash<QString, QString> s_outputSuffixes;
static bool s_noLinkErrors;
@@ -229,6 +185,8 @@ private:
static void copyTemplateFiles(const QString &configVar, const QString &subDir);
protected:
+ FileResolver& file_resolver;
+
QDocDatabase *m_qdb { nullptr };
bool m_inLink { false };
bool m_inContents { false };
@@ -242,6 +200,8 @@ protected:
QString m_sectionNumber {};
};
+std::optional<QString> formatStatus(const Node *node, QDocDatabase *qdb);
+
QT_END_NAMESPACE
#endif
diff --git a/src/qdoc/qdoc/src/qdoc/headernode.cpp b/src/qdoc/qdoc/src/qdoc/headernode.cpp
new file mode 100644
index 000000000..ab576fbd6
--- /dev/null
+++ b/src/qdoc/qdoc/src/qdoc/headernode.cpp
@@ -0,0 +1,43 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "headernode.h"
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class Headernode
+ \brief This class represents a C++ header file.
+ */
+
+HeaderNode::HeaderNode(Aggregate *parent, const QString &name) : Aggregate(HeaderFile, parent, name)
+{
+ // Set the include file with enclosing angle brackets removed
+ if (name.startsWith(QChar('<')) && name.size() > 2)
+ Aggregate::setIncludeFile(name.mid(1).chopped(1));
+ else
+ Aggregate::setIncludeFile(name);
+}
+
+/*!
+ Returns true if this header file node is not private and
+ contains at least one public child node with documentation.
+ */
+bool HeaderNode::docMustBeGenerated() const
+{
+ if (isInAPI())
+ return true;
+ return hasDocumentedChildren();
+}
+
+/*!
+ Returns true if this header file node contains at least one
+ child that has documentation and is not private or internal.
+ */
+bool HeaderNode::hasDocumentedChildren() const
+{
+ return std::any_of(m_children.cbegin(), m_children.cend(),
+ [](Node *child) { return child->isInAPI(); });
+}
+
+QT_END_NAMESPACE
diff --git a/src/qdoc/qdoc/src/qdoc/headernode.h b/src/qdoc/qdoc/src/qdoc/headernode.h
new file mode 100644
index 000000000..b20ff8fdb
--- /dev/null
+++ b/src/qdoc/qdoc/src/qdoc/headernode.h
@@ -0,0 +1,46 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#ifndef HEADERNODE_H
+#define HEADERNODE_H
+
+#include "aggregate.h"
+
+#include <QtCore/qglobal.h>
+#include <QtCore/qstring.h>
+
+QT_BEGIN_NAMESPACE
+
+class HeaderNode : public Aggregate
+{
+public:
+ HeaderNode(Aggregate *parent, const QString &name);
+ [[nodiscard]] bool docMustBeGenerated() const override;
+ [[nodiscard]] bool isFirstClassAggregate() const override { return true; }
+ [[nodiscard]] bool isRelatableType() const override { return true; }
+ [[nodiscard]] QString title() const override { return (m_title.isEmpty() ? name() : m_title); }
+ [[nodiscard]] QString subtitle() const override { return m_subtitle; }
+ [[nodiscard]] QString fullTitle() const override
+ {
+ return (m_title.isEmpty() ? name() : name() + " - " + m_title);
+ }
+ bool setTitle(const QString &title) override
+ {
+ m_title = title;
+ return true;
+ }
+ bool setSubtitle(const QString &subtitle) override
+ {
+ m_subtitle = subtitle;
+ return true;
+ }
+ [[nodiscard]] bool hasDocumentedChildren() const;
+
+private:
+ QString m_title {};
+ QString m_subtitle {};
+};
+
+QT_END_NAMESPACE
+
+#endif // HEADERNODE_H
diff --git a/src/qdoc/helpprojectwriter.cpp b/src/qdoc/qdoc/src/qdoc/helpprojectwriter.cpp
index 447cb8264..968bb7b25 100644
--- a/src/qdoc/helpprojectwriter.cpp
+++ b/src/qdoc/qdoc/src/qdoc/helpprojectwriter.cpp
@@ -1,30 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the tools applications of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "helpprojectwriter.h"
@@ -41,12 +16,12 @@
#include "qdocdatabase.h"
#include "typedefnode.h"
-#include <QtCore/qcryptographichash.h>
-#include <QtCore/qdebug.h>
#include <QtCore/qhash.h>
QT_BEGIN_NAMESPACE
+using namespace Qt::StringLiterals;
+
HelpProjectWriter::HelpProjectWriter(const QString &defaultFileName, Generator *g)
{
reset(defaultFileName, g);
@@ -68,58 +43,55 @@ void HelpProjectWriter::reset(const QString &defaultFileName, Generator *g)
Config &config = Config::instance();
m_outputDir = config.getOutputDir();
- const QStringList names = config.getStringList(CONFIG_QHP + Config::dot + "projects");
+ const QStringList names{config.get(CONFIG_QHP + Config::dot + "projects").asStringList()};
for (const auto &projectName : names) {
HelpProject project;
project.m_name = projectName;
QString prefix = CONFIG_QHP + Config::dot + projectName + Config::dot;
- project.m_helpNamespace = config.getString(prefix + "namespace");
- project.m_virtualFolder = config.getString(prefix + "virtualFolder");
- project.m_version = config.getString(CONFIG_VERSION);
- project.m_fileName = config.getString(prefix + "file");
+ project.m_helpNamespace = config.get(prefix + "namespace").asString();
+ project.m_virtualFolder = config.get(prefix + "virtualFolder").asString();
+ project.m_version = config.get(CONFIG_VERSION).asString();
+ project.m_fileName = config.get(prefix + "file").asString();
if (project.m_fileName.isEmpty())
project.m_fileName = defaultFileName;
- project.m_extraFiles = config.getStringSet(prefix + "extraFiles");
- project.m_extraFiles += config.getStringSet(CONFIG_QHP + Config::dot + "extraFiles");
- project.m_indexTitle = config.getString(prefix + "indexTitle");
- project.m_indexRoot = config.getString(prefix + "indexRoot");
- const auto &filterAttributes = config.getStringList(prefix + "filterAttributes");
- project.m_filterAttributes =
- QSet<QString>(filterAttributes.cbegin(), filterAttributes.cend());
- project.m_includeIndexNodes = config.getBool(prefix + "includeIndexNodes");
+ project.m_extraFiles = config.get(prefix + "extraFiles").asStringSet();
+ project.m_extraFiles += config.get(CONFIG_QHP + Config::dot + "extraFiles").asStringSet();
+ project.m_indexTitle = config.get(prefix + "indexTitle").asString();
+ project.m_indexRoot = config.get(prefix + "indexRoot").asString();
+ project.m_filterAttributes = config.get(prefix + "filterAttributes").asStringSet();
+ project.m_includeIndexNodes = config.get(prefix + "includeIndexNodes").asBool();
const QSet<QString> customFilterNames = config.subVars(prefix + "customFilters");
for (const auto &filterName : customFilterNames) {
- QString name = config.getString(prefix + "customFilters" + Config::dot + filterName
- + Config::dot + "name");
- const auto &filters =
- config.getStringList(prefix + "customFilters" + Config::dot + filterName
- + Config::dot + "filterAttributes");
- project.m_customFilters[name] = QSet<QString>(filters.cbegin(), filters.cend());
+ QString name{config.get(prefix + "customFilters" + Config::dot + filterName
+ + Config::dot + "name").asString()};
+ project.m_customFilters[name] =
+ config.get(prefix + "customFilters" + Config::dot + filterName
+ + Config::dot + "filterAttributes").asStringSet();
}
- const auto excludedPrefixes = config.getStringSet(prefix + "excluded");
+ const auto excludedPrefixes = config.get(prefix + "excluded").asStringSet();
for (auto name : excludedPrefixes)
project.m_excluded.insert(name.replace(QLatin1Char('\\'), QLatin1Char('/')));
- const auto subprojectPrefixes = config.getStringList(prefix + "subprojects");
+ const auto subprojectPrefixes{config.get(prefix + "subprojects").asStringList()};
for (const auto &name : subprojectPrefixes) {
SubProject subproject;
QString subprefix = prefix + "subprojects" + Config::dot + name + Config::dot;
- subproject.m_title = config.getString(subprefix + "title");
+ subproject.m_title = config.get(subprefix + "title").asString();
if (subproject.m_title.isEmpty())
continue;
- subproject.m_indexTitle = config.getString(subprefix + "indexTitle");
- subproject.m_sortPages = config.getBool(subprefix + "sortPages");
- subproject.m_type = config.getString(subprefix + "type");
- readSelectors(subproject, config.getStringList(subprefix + "selectors"));
+ subproject.m_indexTitle = config.get(subprefix + "indexTitle").asString();
+ subproject.m_sortPages = config.get(subprefix + "sortPages").asBool();
+ subproject.m_type = config.get(subprefix + "type").asString();
+ readSelectors(subproject, config.get(subprefix + "selectors").asStringList());
project.m_subprojects.append(subproject);
}
if (project.m_subprojects.isEmpty()) {
SubProject subproject;
- readSelectors(subproject, config.getStringList(prefix + "selectors"));
+ readSelectors(subproject, config.get(prefix + "selectors").asStringList());
project.m_subprojects.insert(0, subproject);
}
@@ -149,13 +121,12 @@ void HelpProjectWriter::readSelectors(SubProject &subproject, const QStringList
typeHash["variable"] = Node::Variable;
typeHash["group"] = Node::Group;
typeHash["module"] = Node::Module;
- typeHash["jsmodule"] = Node::JsModule;
typeHash["qmlmodule"] = Node::QmlModule;
- typeHash["qmlproperty"] = Node::JsProperty;
- typeHash["jsproperty"] = Node::QmlProperty;
+ typeHash["qmlproperty"] = Node::QmlProperty;
typeHash["qmlclass"] = Node::QmlType; // Legacy alias for 'qmltype'
typeHash["qmltype"] = Node::QmlType;
- typeHash["qmlbasictype"] = Node::QmlBasicType;
+ typeHash["qmlbasictype"] = Node::QmlValueType; // Legacy alias for 'qmlvaluetype'
+ typeHash["qmlvaluetype"] = Node::QmlValueType;
for (const QString &selector : selectors) {
QStringList pieces = selector.split(QLatin1Char(':'));
@@ -170,11 +141,10 @@ void HelpProjectWriter::readSelectors(SubProject &subproject, const QStringList
subproject.m_selectors << typeHash.value(typeName);
if (!pieces.isEmpty()) {
pieces = pieces[0].split(QLatin1Char(','));
- for (const auto &piece : qAsConst(pieces)) {
+ for (const auto &piece : std::as_const(pieces)) {
if (typeHash[typeName] == Node::Group
|| typeHash[typeName] == Node::Module
- || typeHash[typeName] == Node::QmlModule
- || typeHash[typeName] == Node::JsModule) {
+ || typeHash[typeName] == Node::QmlModule) {
subproject.m_groups << piece.toLower();
}
}
@@ -190,7 +160,7 @@ void HelpProjectWriter::addExtraFile(const QString &file)
Keyword HelpProjectWriter::keywordDetails(const Node *node) const
{
- QString ref = m_gen->fullDocumentLocation(node, false);
+ QString ref = m_gen->fullDocumentLocation(node);
if (node->parent() && !node->parent()->name().isEmpty()) {
QString name = (node->isEnumType() || node->isTypedef())
@@ -200,7 +170,7 @@ Keyword HelpProjectWriter::keywordDetails(const Node *node) const
? node->parent()->name()+"::"+node->name()
: node->name();
return Keyword(name, id, ref);
- } else if (node->isQmlType() || node->isQmlBasicType()) {
+ } else if (node->isQmlType()) {
const QString &name = node->name();
QString moduleName = node->logicalModuleName();
QStringList ids("QML." + name);
@@ -211,8 +181,11 @@ Keyword HelpProjectWriter::keywordDetails(const Node *node) const
ids << "QML." + moduleName + majorVersion + "." + name;
}
return Keyword(name, ids, ref);
- } else if (node->isJsType() || node->isJsBasicType()) {
- return Keyword(node->name(), "JS." + node->name(), ref);
+ } else if (node->isQmlModule()) {
+ const QLatin1Char delim('.');
+ QStringList parts = node->logicalModuleName().split(delim) << "QML";
+ std::reverse(parts.begin(), parts.end());
+ return Keyword(node->logicalModuleName(), parts.join(delim), ref);
} else if (node->isTextPageNode()) {
const auto *pageNode = static_cast<const PageNode *>(node);
return Keyword(pageNode->fullTitle(), pageNode->fullTitle(), ref);
@@ -241,7 +214,7 @@ bool HelpProjectWriter::generateSection(HelpProject &project, QXmlStreamWriter &
// Only add nodes to the set for each subproject if they match a selector.
// Those that match will be listed in the table of contents.
- for (int i = 0; i < project.m_subprojects.length(); i++) {
+ for (int i = 0; i < project.m_subprojects.size(); i++) {
SubProject subproject = project.m_subprojects[i];
// No selectors: accept all nodes.
if (subproject.m_selectors.isEmpty()) {
@@ -279,20 +252,18 @@ bool HelpProjectWriter::generateSection(HelpProject &project, QXmlStreamWriter &
project.m_keywords.append(keywordDetails(node));
break;
case Node::QmlType:
- case Node::QmlBasicType:
- case Node::JsType:
- case Node::JsBasicType:
+ case Node::QmlValueType:
if (node->doc().hasKeywords()) {
const auto keywords = node->doc().keywords();
for (const Atom *keyword : keywords) {
if (!keyword->string().isEmpty()) {
project.m_keywords.append(Keyword(keyword->string(), keyword->string(),
- m_gen->fullDocumentLocation(node, false)));
+ m_gen->fullDocumentLocation(node)));
}
else
node->doc().location().warning(
QStringLiteral("Bad keyword in %1")
- .arg(m_gen->fullDocumentLocation(node, false)));
+ .arg(m_gen->fullDocumentLocation(node)));
}
}
project.m_keywords.append(keywordDetails(node));
@@ -318,7 +289,7 @@ bool HelpProjectWriter::generateSection(HelpProject &project, QXmlStreamWriter &
} else {
name = id = item.name();
}
- QString ref = m_gen->fullDocumentLocation(node, false);
+ QString ref = m_gen->fullDocumentLocation(node);
project.m_keywords.append(Keyword(name, id, ref));
}
}
@@ -326,8 +297,7 @@ bool HelpProjectWriter::generateSection(HelpProject &project, QXmlStreamWriter &
case Node::Group:
case Node::Module:
- case Node::QmlModule:
- case Node::JsModule: {
+ case Node::QmlModule: {
const auto *cn = static_cast<const CollectionNode *>(node);
if (!cn->fullTitle().isEmpty()) {
if (cn->doc().hasKeywords()) {
@@ -336,11 +306,11 @@ bool HelpProjectWriter::generateSection(HelpProject &project, QXmlStreamWriter &
if (!keyword->string().isEmpty()) {
project.m_keywords.append(
Keyword(keyword->string(), keyword->string(),
- m_gen->fullDocumentLocation(node, false)));
+ m_gen->fullDocumentLocation(node)));
} else
cn->doc().location().warning(
QStringLiteral("Bad keyword in %1")
- .arg(m_gen->fullDocumentLocation(node, false)));
+ .arg(m_gen->fullDocumentLocation(node)));
}
}
project.m_keywords.append(keywordDetails(node));
@@ -349,7 +319,6 @@ bool HelpProjectWriter::generateSection(HelpProject &project, QXmlStreamWriter &
case Node::Property:
case Node::QmlProperty:
- case Node::JsProperty:
project.m_keywords.append(keywordDetails(node));
break;
@@ -357,13 +326,11 @@ bool HelpProjectWriter::generateSection(HelpProject &project, QXmlStreamWriter &
const auto *funcNode = static_cast<const FunctionNode *>(node);
/*
- QML and JS methods, signals, and signal handlers used to be node types,
+ QML methods, signals, and signal handlers used to be node types,
but now they are Function nodes with a Metaness value that specifies
- what kind of function they are, QmlSignal, JsSignal, QmlMethod, etc. It
- suffices at this point to test whether the node is of the QML or JS Genus,
- because we already know it is NodeType::Function.
- */
- if (funcNode->isQmlNode() || funcNode->isJsNode()) {
+ what kind of function they are, QmlSignal, QmlMethod, etc.
+ */
+ if (funcNode->isQmlNode()) {
project.m_keywords.append(keywordDetails(node));
break;
}
@@ -390,7 +357,7 @@ bool HelpProjectWriter::generateSection(HelpProject &project, QXmlStreamWriter &
// Use the location of any associated enum node in preference
// to that of the typedef.
if (enumNode)
- typedefDetails.m_ref = m_gen->fullDocumentLocation(enumNode, false);
+ typedefDetails.m_ref = m_gen->fullDocumentLocation(enumNode);
project.m_keywords.append(typedefDetails);
} break;
@@ -410,9 +377,9 @@ bool HelpProjectWriter::generateSection(HelpProject &project, QXmlStreamWriter &
if (!keyword->string().isEmpty()) {
project.m_keywords.append(
Keyword(keyword->string(), keyword->string(),
- m_gen->fullDocumentLocation(node, false)));
+ m_gen->fullDocumentLocation(node)));
} else {
- QString loc = m_gen->fullDocumentLocation(node, false);
+ QString loc = m_gen->fullDocumentLocation(node);
pn->doc().location().warning(QStringLiteral("Bad keyword in %1").arg(loc));
}
}
@@ -454,48 +421,43 @@ void HelpProjectWriter::generateSections(HelpProject &project, QXmlStreamWriter
const auto *aggregate = static_cast<const Aggregate *>(node);
// Ensure that we don't visit nodes more than once.
- QSet<const Node *> childSet;
- const NodeList &children = aggregate->childNodes();
- for (const auto *child : children) {
+ NodeList childSet;
+ NodeList children = aggregate->childNodes();
+ std::sort(children.begin(), children.end(), Node::nodeNameLessThan);
+ for (auto *child : children) {
// Skip related non-members adopted by some other aggregate
if (child->parent() != aggregate)
continue;
if (child->isIndexNode() || child->isPrivate())
continue;
if (child->isTextPageNode()) {
- childSet << child;
+ if (!childSet.contains(child))
+ childSet << child;
} else {
// Store member status of children
project.m_memberStatus[node].insert(child->status());
if (child->isFunction() && static_cast<const FunctionNode *>(child)->isOverload())
continue;
- childSet << child;
+ if (!childSet.contains(child))
+ childSet << child;
}
}
- for (const auto *child : qAsConst(childSet))
+ for (const auto *child : std::as_const(childSet))
generateSections(project, writer, child);
}
}
void HelpProjectWriter::generate()
{
+ // Warn if .qhp configuration was expected but not provided
+ if (auto &config = Config::instance(); m_projects.isEmpty() && config.get(CONFIG_QHP).asBool()) {
+ config.location().warning(u"Documentation configuration for '%1' doesn't define a help project (qhp)"_s
+ .arg(config.get(CONFIG_PROJECT).asString()));
+ }
for (HelpProject &project : m_projects)
generateProject(project);
}
-void HelpProjectWriter::writeHashFile(QFile &file)
-{
- QCryptographicHash hash(QCryptographicHash::Sha1);
- hash.addData(&file);
-
- QFile hashFile(file.fileName() + ".sha1");
- if (!hashFile.open(QFile::WriteOnly | QFile::Text))
- return;
-
- hashFile.write(hash.result().toHex());
- hashFile.close();
-}
-
void HelpProjectWriter::writeSection(QXmlStreamWriter &writer, const QString &path,
const QString &value)
{
@@ -510,10 +472,7 @@ void HelpProjectWriter::writeSection(QXmlStreamWriter &writer, const QString &pa
*/
void HelpProjectWriter::addMembers(HelpProject &project, QXmlStreamWriter &writer, const Node *node)
{
- if (node->isQmlBasicType() || node->isJsBasicType())
- return;
-
- QString href = m_gen->fullDocumentLocation(node, false);
+ QString href = m_gen->fullDocumentLocation(node);
href = href.left(href.size() - 5);
if (href.isEmpty())
return;
@@ -523,10 +482,9 @@ void HelpProjectWriter::addMembers(HelpProject &project, QXmlStreamWriter &write
derivedClass = !(static_cast<const ClassNode *>(node)->baseClasses().isEmpty());
// Do not generate a 'List of all members' for namespaces or header files,
- // but always generate it for derived classes and QML classes
- if (!node->isNamespace() && !node->isHeader()
- && (derivedClass || node->isQmlType() || node->isJsType()
- || !project.m_memberStatus[node].isEmpty())) {
+ // but always generate it for derived classes and QML types (but not QML value types)
+ if (!node->isNamespace() && !node->isHeader() && !node->isQmlBasicType()
+ && (derivedClass || node->isQmlType() || !project.m_memberStatus[node].isEmpty())) {
QString membersPath = href + QStringLiteral("-members.html");
writeSection(writer, membersPath, QStringLiteral("List of all members"));
}
@@ -538,7 +496,7 @@ void HelpProjectWriter::addMembers(HelpProject &project, QXmlStreamWriter &write
void HelpProjectWriter::writeNode(HelpProject &project, QXmlStreamWriter &writer, const Node *node)
{
- QString href = m_gen->fullDocumentLocation(node, false);
+ QString href = m_gen->fullDocumentLocation(node);
QString objName = node->name();
switch (node->nodeType()) {
@@ -547,9 +505,7 @@ void HelpProjectWriter::writeNode(HelpProject &project, QXmlStreamWriter &writer
case Node::Struct:
case Node::Union:
case Node::QmlType:
- case Node::JsType:
- case Node::QmlBasicType:
- case Node::JsBasicType: {
+ case Node::QmlValueType: {
QString typeStr = m_gen->typeString(node);
if (!typeStr.isEmpty())
typeStr[0] = typeStr[0].toTitleCase();
@@ -575,7 +531,6 @@ void HelpProjectWriter::writeNode(HelpProject &project, QXmlStreamWriter &writer
case Node::Page:
case Node::Group:
case Node::Module:
- case Node::JsModule:
case Node::QmlModule: {
writer.writeStartElement("section");
writer.writeAttribute("ref", href);
@@ -608,7 +563,7 @@ void HelpProjectWriter::generateProject(HelpProject &project)
project.m_keywords.clear();
QFile file(m_outputDir + QDir::separator() + project.m_fileName);
- if (!file.open(QFile::WriteOnly | QFile::Text))
+ if (!file.open(QFile::WriteOnly))
return;
QXmlStreamWriter writer(&file);
@@ -632,7 +587,7 @@ void HelpProjectWriter::generateProject(HelpProject &project)
writer.writeAttribute("name", it.key());
QStringList sortedAttributes = it.value().values();
sortedAttributes.sort();
- for (const auto &filter : qAsConst(sortedAttributes))
+ for (const auto &filter : std::as_const(sortedAttributes))
writer.writeTextElement("filterAttribute", filter);
writer.writeEndElement(); // customFilter
}
@@ -643,7 +598,7 @@ void HelpProjectWriter::generateProject(HelpProject &project)
// Write filterAttribute elements.
QStringList sortedFilterAttributes = project.m_filterAttributes.values();
sortedFilterAttributes.sort();
- for (const auto &filterName : qAsConst(sortedFilterAttributes))
+ for (const auto &filterName : std::as_const(sortedFilterAttributes))
writer.writeTextElement("filterAttribute", filterName);
writer.writeStartElement("toc");
@@ -655,7 +610,7 @@ void HelpProjectWriter::generateProject(HelpProject &project)
node = m_qdb->findNodeByNameAndType(QStringList("index.html"), &Node::isPageNode);
QString indexPath;
if (node)
- indexPath = m_gen->fullDocumentLocation(node, false);
+ indexPath = m_gen->fullDocumentLocation(node);
else
indexPath = "index.html";
writer.writeAttribute("ref", indexPath);
@@ -663,7 +618,7 @@ void HelpProjectWriter::generateProject(HelpProject &project)
generateSections(project, writer, rootNode);
- for (int i = 0; i < project.m_subprojects.length(); i++) {
+ for (int i = 0; i < project.m_subprojects.size(); i++) {
SubProject subproject = project.m_subprojects[i];
if (subproject.m_type == QLatin1String("manual")) {
@@ -697,7 +652,7 @@ void HelpProjectWriter::generateProject(HelpProject &project)
const Node *page = m_qdb->findNodeForTarget(atom->string(), nullptr);
writer.writeStartElement("section");
- QString indexPath = m_gen->fullDocumentLocation(page, false);
+ QString indexPath = m_gen->fullDocumentLocation(page);
writer.writeAttribute("ref", indexPath);
writer.writeAttribute("title", atom->linkText());
@@ -719,21 +674,21 @@ void HelpProjectWriter::generateProject(HelpProject &project)
writer.writeStartElement("section");
QString indexPath = m_gen->fullDocumentLocation(
- m_qdb->findNodeForTarget(subproject.m_indexTitle, nullptr), false);
+ m_qdb->findNodeForTarget(subproject.m_indexTitle, nullptr));
writer.writeAttribute("ref", indexPath);
writer.writeAttribute("title", subproject.m_title);
if (subproject.m_sortPages) {
QStringList titles = subproject.m_nodes.keys();
titles.sort();
- for (const auto &title : qAsConst(titles)) {
+ for (const auto &title : std::as_const(titles)) {
writeNode(project, writer, subproject.m_nodes[title]);
}
} else {
// Find a contents node and navigate from there, using the NextLink values.
QSet<QString> visited;
bool contentsFound = false;
- for (const auto *node : qAsConst(subproject.m_nodes)) {
+ for (const auto *node : std::as_const(subproject.m_nodes)) {
QString nextTitle = node->links().value(Node::NextLink).first;
if (!nextTitle.isEmpty()
&& node->links().value(Node::ContentsLink).first.isEmpty()) {
@@ -761,7 +716,7 @@ void HelpProjectWriter::generateProject(HelpProject &project)
std::sort(subnodes.begin(), subnodes.end(), Node::nodeNameLessThan);
- for (const auto *node : qAsConst(subnodes))
+ for (const auto *node : std::as_const(subnodes))
writeNode(project, writer, node);
}
}
@@ -778,8 +733,8 @@ void HelpProjectWriter::generateProject(HelpProject &project)
writer.writeStartElement("keywords");
std::sort(project.m_keywords.begin(), project.m_keywords.end());
- for (const auto &k : qAsConst(project.m_keywords)) {
- for (const auto &id : qAsConst(k.m_ids)) {
+ for (const auto &k : std::as_const(project.m_keywords)) {
+ for (const auto &id : std::as_const(k.m_ids)) {
writer.writeStartElement("keyword");
writer.writeAttribute("name", k.m_name);
writer.writeAttribute("id", id);
@@ -799,7 +754,7 @@ void HelpProjectWriter::generateProject(HelpProject &project)
files.unite(project.m_extraFiles);
QStringList sortedFiles = files.values();
sortedFiles.sort();
- for (const auto &usedFile : qAsConst(sortedFiles)) {
+ for (const auto &usedFile : std::as_const(sortedFiles)) {
if (!usedFile.isEmpty())
writer.writeTextElement("file", usedFile);
}
@@ -808,7 +763,6 @@ void HelpProjectWriter::generateProject(HelpProject &project)
writer.writeEndElement(); // filterSection
writer.writeEndElement(); // QtHelpProject
writer.writeEndDocument();
- writeHashFile(file);
file.close();
}
diff --git a/src/qdoc/helpprojectwriter.h b/src/qdoc/qdoc/src/qdoc/helpprojectwriter.h
index caf0f8065..11dd67fb1 100644
--- a/src/qdoc/helpprojectwriter.h
+++ b/src/qdoc/qdoc/src/qdoc/helpprojectwriter.h
@@ -1,30 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the tools applications of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#ifndef HELPPROJECTWRITER_H
#define HELPPROJECTWRITER_H
@@ -114,7 +89,6 @@ private:
void generateSections(HelpProject &project, QXmlStreamWriter &writer, const Node *node);
bool generateSection(HelpProject &project, QXmlStreamWriter &writer, const Node *node);
Keyword keywordDetails(const Node *node) const;
- void writeHashFile(QFile &file);
void writeNode(HelpProject &project, QXmlStreamWriter &writer, const Node *node);
void readSelectors(SubProject &subproject, const QStringList &selectors);
void addMembers(HelpProject &project, QXmlStreamWriter &writer, const Node *node);
diff --git a/src/qdoc/htmlgenerator.cpp b/src/qdoc/qdoc/src/qdoc/htmlgenerator.cpp
index ccde100f8..d0f9fb2b4 100644
--- a/src/qdoc/htmlgenerator.cpp
+++ b/src/qdoc/qdoc/src/qdoc/htmlgenerator.cpp
@@ -1,30 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the tools applications of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "htmlgenerator.h"
@@ -47,6 +22,7 @@
#include "tagfilewriter.h"
#include "tree.h"
#include "quoter.h"
+#include "utilities.h"
#include <QtCore/qlist.h>
#include <QtCore/qmap.h>
@@ -55,18 +31,23 @@
#include <QtCore/qregularexpression.h>
#include <cctype>
+#include <deque>
+#include <string>
QT_BEGIN_NAMESPACE
-static bool showBrokenLinks = false;
+using namespace Qt::StringLiterals;
+
bool HtmlGenerator::s_inUnorderedList { false };
+HtmlGenerator::HtmlGenerator(FileResolver& file_resolver) : XmlGenerator(file_resolver) {}
+
static void addLink(const QString &linkTarget, QStringView nestedStuff, QString *res)
{
if (!linkTarget.isEmpty()) {
*res += QLatin1String("<a href=\"");
*res += linkTarget;
- *res += QLatin1String("\">");
+ *res += QLatin1String("\" translate=\"no\">");
*res += nestedStuff;
*res += QLatin1String("</a>");
} else {
@@ -129,65 +110,52 @@ void HtmlGenerator::initializeGenerator()
} defaults[] = { { ATOM_FORMATTING_BOLD, "<b>", "</b>" },
{ ATOM_FORMATTING_INDEX, "<!--", "-->" },
{ ATOM_FORMATTING_ITALIC, "<i>", "</i>" },
- { ATOM_FORMATTING_PARAMETER, "<i>", "</i>" },
+ { ATOM_FORMATTING_PARAMETER, "<i translate=\"no\">", "</i>" },
{ ATOM_FORMATTING_SUBSCRIPT, "<sub>", "</sub>" },
{ ATOM_FORMATTING_SUPERSCRIPT, "<sup>", "</sup>" },
- { ATOM_FORMATTING_TELETYPE, "<code>",
+ { ATOM_FORMATTING_TELETYPE, "<code translate=\"no\">",
"</code>" }, // <tt> tag is not supported in HTML5
- { ATOM_FORMATTING_UICONTROL, "<b>", "</b>" },
+ { ATOM_FORMATTING_TRADEMARK, "<span translate=\"no\">", "&#8482;" },
+ { ATOM_FORMATTING_UICONTROL, "<b translate=\"no\">", "</b>" },
{ ATOM_FORMATTING_UNDERLINE, "<u>", "</u>" },
{ nullptr, nullptr, nullptr } };
Generator::initializeGenerator();
config = &Config::instance();
- setImageFileExtensions(QStringList() << "png"
- << "jpg"
- << "jpeg"
- << "gif");
/*
The formatting maps are owned by Generator. They are cleared in
Generator::terminate().
*/
- int i = 0;
- while (defaults[i].key) {
+ for (int i = 0; defaults[i].key; ++i) {
formattingLeftMap().insert(QLatin1String(defaults[i].key), QLatin1String(defaults[i].left));
formattingRightMap().insert(QLatin1String(defaults[i].key),
QLatin1String(defaults[i].right));
- i++;
}
- m_endHeader = config->getString(HtmlGenerator::format() + Config::dot + CONFIG_ENDHEADER);
- m_postHeader =
- config->getString(HtmlGenerator::format() + Config::dot + HTMLGENERATOR_POSTHEADER);
- m_postPostHeader =
- config->getString(HtmlGenerator::format() + Config::dot + HTMLGENERATOR_POSTPOSTHEADER);
- m_prologue = config->getString(HtmlGenerator::format() + Config::dot + HTMLGENERATOR_PROLOGUE);
-
- m_footer = config->getString(HtmlGenerator::format() + Config::dot + HTMLGENERATOR_FOOTER);
- m_address = config->getString(HtmlGenerator::format() + Config::dot + HTMLGENERATOR_ADDRESS);
- m_noNavigationBar =
- config->getBool(HtmlGenerator::format() + Config::dot + HTMLGENERATOR_NONAVIGATIONBAR);
- m_navigationSeparator = config->getString(HtmlGenerator::format() + Config::dot
- + HTMLGENERATOR_NAVIGATIONSEPARATOR);
- tocDepth = config->getInt(HtmlGenerator::format() + Config::dot + HTMLGENERATOR_TOCDEPTH);
-
- m_project = config->getString(CONFIG_PROJECT);
+ QString formatDot{HtmlGenerator::format() + Config::dot};
+ m_endHeader = config->get(formatDot + CONFIG_ENDHEADER).asString();
+ m_postHeader = config->get(formatDot + HTMLGENERATOR_POSTHEADER).asString();
+ m_postPostHeader = config->get(formatDot + HTMLGENERATOR_POSTPOSTHEADER).asString();
+ m_prologue = config->get(formatDot + HTMLGENERATOR_PROLOGUE).asString();
- m_projectDescription = config->getString(CONFIG_DESCRIPTION);
- if (m_projectDescription.isEmpty() && !m_project.isEmpty())
- m_projectDescription = m_project + QLatin1String(" Reference Documentation");
+ m_footer = config->get(formatDot + HTMLGENERATOR_FOOTER).asString();
+ m_address = config->get(formatDot + HTMLGENERATOR_ADDRESS).asString();
+ m_noNavigationBar = config->get(formatDot + HTMLGENERATOR_NONAVIGATIONBAR).asBool();
+ m_navigationSeparator = config->get(formatDot + HTMLGENERATOR_NAVIGATIONSEPARATOR).asString();
+ tocDepth = config->get(formatDot + HTMLGENERATOR_TOCDEPTH).asInt();
- m_projectUrl = config->getString(CONFIG_URL);
- tagFile_ = config->getString(CONFIG_TAGFILE);
+ m_project = config->get(CONFIG_PROJECT).asString();
+ m_projectDescription = config->get(CONFIG_DESCRIPTION)
+ .asString(m_project + QLatin1String(" Reference Documentation"));
- naturalLanguage = config->getString(CONFIG_NATURALLANGUAGE);
- if (naturalLanguage.isEmpty())
- naturalLanguage = QLatin1String("en");
+ m_projectUrl = config->get(CONFIG_URL).asString();
+ tagFile_ = config->get(CONFIG_TAGFILE).asString();
+ naturalLanguage = config->get(CONFIG_NATURALLANGUAGE).asString(QLatin1String("en"));
- m_codeIndent = config->getInt(CONFIG_CODEINDENT); // QTBUG-27798
- m_codePrefix = config->getString(CONFIG_CODEPREFIX);
- m_codeSuffix = config->getString(CONFIG_CODESUFFIX);
+ m_codeIndent = config->get(CONFIG_CODEINDENT).asInt();
+ m_codePrefix = config->get(CONFIG_CODEPREFIX).asString();
+ m_codeSuffix = config->get(CONFIG_CODESUFFIX).asString();
/*
The help file write should be allocated once and only once
@@ -202,31 +170,42 @@ void HtmlGenerator::initializeGenerator()
m_manifestWriter = new ManifestWriter();
// Documentation template handling
- m_headerScripts =
- config->getString(HtmlGenerator::format() + Config::dot + CONFIG_HEADERSCRIPTS);
- m_headerStyles = config->getString(HtmlGenerator::format() + Config::dot + CONFIG_HEADERSTYLES);
+ m_headerScripts = config->get(formatDot + CONFIG_HEADERSCRIPTS).asString();
+ m_headerStyles = config->get(formatDot + CONFIG_HEADERSTYLES).asString();
// Retrieve the config for the navigation bar
- m_homepage = config->getString(CONFIG_NAVIGATION + Config::dot + CONFIG_HOMEPAGE);
+ m_homepage = config->get(CONFIG_NAVIGATION
+ + Config::dot + CONFIG_HOMEPAGE).asString();
- m_hometitle = config->getString(CONFIG_NAVIGATION + Config::dot + CONFIG_HOMETITLE, m_homepage);
+ m_hometitle = config->get(CONFIG_NAVIGATION
+ + Config::dot + CONFIG_HOMETITLE)
+ .asString(m_homepage);
- m_landingpage = config->getString(CONFIG_NAVIGATION + Config::dot + CONFIG_LANDINGPAGE);
+ m_landingpage = config->get(CONFIG_NAVIGATION
+ + Config::dot + CONFIG_LANDINGPAGE).asString();
- m_landingtitle =
- config->getString(CONFIG_NAVIGATION + Config::dot + CONFIG_LANDINGTITLE, m_landingpage);
+ m_landingtitle = config->get(CONFIG_NAVIGATION
+ + Config::dot + CONFIG_LANDINGTITLE)
+ .asString(m_landingpage);
- m_cppclassespage = config->getString(CONFIG_NAVIGATION + Config::dot + CONFIG_CPPCLASSESPAGE);
+ m_cppclassespage = config->get(CONFIG_NAVIGATION
+ + Config::dot + CONFIG_CPPCLASSESPAGE).asString();
- m_cppclassestitle = config->getString(CONFIG_NAVIGATION + Config::dot + CONFIG_CPPCLASSESTITLE,
- QLatin1String("C++ Classes"));
+ m_cppclassestitle = config->get(CONFIG_NAVIGATION
+ + Config::dot + CONFIG_CPPCLASSESTITLE)
+ .asString(QLatin1String("C++ Classes"));
- m_qmltypespage = config->getString(CONFIG_NAVIGATION + Config::dot + CONFIG_QMLTYPESPAGE);
+ m_qmltypespage = config->get(CONFIG_NAVIGATION
+ + Config::dot + CONFIG_QMLTYPESPAGE).asString();
- m_qmltypestitle = config->getString(CONFIG_NAVIGATION + Config::dot + CONFIG_QMLTYPESTITLE,
- QLatin1String("QML Types"));
+ m_qmltypestitle = config->get(CONFIG_NAVIGATION
+ + Config::dot + CONFIG_QMLTYPESTITLE)
+ .asString(QLatin1String("QML Types"));
- m_buildversion = config->getString(CONFIG_BUILDVERSION);
+ m_trademarkspage = config->get(CONFIG_NAVIGATION
+ + Config::dot + CONFIG_TRADEMARKSPAGE).asString();
+
+ m_buildversion = config->get(CONFIG_BUILDVERSION).asString();
}
/*!
@@ -281,25 +260,25 @@ void HtmlGenerator::generateDocs()
/*!
Generate an html file with the contents of a C++ or QML source file.
*/
-void HtmlGenerator::generateExampleFilePage(const Node *en, const QString &file, CodeMarker *marker)
+void HtmlGenerator::generateExampleFilePage(const Node *en, ResolvedFile resolved_file, CodeMarker *marker)
{
SubTitleSize subTitleSize = LargeSubTitle;
QString fullTitle = en->fullTitle();
- beginFilePage(en, linkForExampleFile(file));
+ beginSubPage(en, linkForExampleFile(resolved_file.get_query()));
generateHeader(fullTitle, en, marker);
generateTitle(fullTitle, Text() << en->subtitle(), subTitleSize, en, marker);
Text text;
Quoter quoter;
- Doc::quoteFromFile(en->doc().location(), quoter, file);
+ Doc::quoteFromFile(en->doc().location(), quoter, resolved_file);
QString code = quoter.quoteTo(en->location(), QString(), QString());
- CodeMarker *codeMarker = CodeMarker::markerForFileName(file);
+ CodeMarker *codeMarker = CodeMarker::markerForFileName(resolved_file.get_path());
text << Atom(codeMarker->atomType(), code);
Atom a(codeMarker->atomType(), code);
generateText(text, en, codeMarker);
- endFilePage();
+ endSubPage();
}
/*!
@@ -338,7 +317,7 @@ qsizetype HtmlGenerator::generateAtom(const Atom *atom, const Node *relative, Co
out() << protectEnc(atom->string());
} else {
beginLink(link, node, relative);
- generateLink(atom, marker);
+ generateLink(atom);
endLink();
}
} else {
@@ -379,37 +358,34 @@ qsizetype HtmlGenerator::generateAtom(const Atom *atom, const Node *relative, Co
}
break;
case Atom::Qml:
- out() << "<pre class=\"qml\">"
+ out() << "<pre class=\"qml\" translate=\"no\">"
<< trimmedTrailing(highlightedCode(indent(m_codeIndent, atom->string()), relative,
false, Node::QML),
m_codePrefix, m_codeSuffix)
<< "</pre>\n";
break;
- case Atom::JavaScript:
- out() << "<pre class=\"js\">"
- << trimmedTrailing(highlightedCode(indent(m_codeIndent, atom->string()), relative,
- false, Node::JS),
- m_codePrefix, m_codeSuffix)
- << "</pre>\n";
- break;
- case Atom::CodeNew:
- out() << "<p>you can rewrite it as</p>\n";
- Q_FALLTHROUGH();
case Atom::Code:
- out() << "<pre class=\"cpp\">"
+ out() << "<pre class=\"cpp\" translate=\"no\">"
<< trimmedTrailing(highlightedCode(indent(m_codeIndent, atom->string()), relative),
m_codePrefix, m_codeSuffix)
<< "</pre>\n";
break;
- case Atom::CodeOld:
- out() << "<p>For example, if you have code like</p>\n";
- Q_FALLTHROUGH();
case Atom::CodeBad:
- out() << "<pre class=\"cpp plain\">"
+ out() << "<pre class=\"cpp plain\" translate=\"no\">"
<< trimmedTrailing(protectEnc(plainCode(indent(m_codeIndent, atom->string()))),
m_codePrefix, m_codeSuffix)
<< "</pre>\n";
break;
+ case Atom::DetailsLeft:
+ out() << "<details>\n";
+ if (!atom->string().isEmpty())
+ out() << "<summary>" << protectEnc(atom->string()) << "</summary>\n";
+ else
+ out() << "<summary>...</summary>\n";
+ break;
+ case Atom::DetailsRight:
+ out() << "</details>\n";
+ break;
case Atom::DivLeft:
out() << "<div";
if (!atom->string().isEmpty())
@@ -436,25 +412,26 @@ qsizetype HtmlGenerator::generateAtom(const Atom *atom, const Node *relative, Co
case Atom::FormatIf:
break;
case Atom::FormattingLeft:
- if (atom->string().startsWith("span ")) {
+ if (atom->string().startsWith("span "))
out() << '<' + atom->string() << '>';
- } else
+ else
out() << formattingLeftMap()[atom->string()];
- if (atom->string() == ATOM_FORMATTING_PARAMETER) {
- if (atom->next() != nullptr && atom->next()->type() == Atom::String) {
- QRegularExpression subscriptRegExp("^([a-z]+)_([0-9n])$");
- auto match = subscriptRegExp.match(atom->next()->string());
- if (match.hasMatch()) {
- out() << match.captured(1) << "<sub>" << match.captured(2)
- << "</sub>";
- skipAhead = 1;
- }
- }
- }
break;
case Atom::FormattingRight:
if (atom->string() == ATOM_FORMATTING_LINK) {
endLink();
+ } else if (atom->string() == ATOM_FORMATTING_TRADEMARK) {
+ if (appendTrademark(atom)) {
+ // Make the trademark symbol a link to navigation.trademarkspage (if set)
+ const Node *node{nullptr};
+ const Atom tm_link(Atom::NavLink, m_trademarkspage);
+ if (const auto &link = getLink(&tm_link, relative, &node);
+ !link.isEmpty() && node != relative)
+ out() << "<a href=\"%1\">%2</a>"_L1.arg(link, formattingRightMap()[atom->string()]);
+ else
+ out() << formattingRightMap()[atom->string()];
+ }
+ out() << "</span>";
} else if (atom->string().startsWith("span ")) {
out() << "</span>";
} else {
@@ -479,34 +456,36 @@ qsizetype HtmlGenerator::generateAtom(const Atom *atom, const Node *relative, Co
} else if (atom->string().contains("classes ")) {
QString rootName = atom->string().mid(atom->string().indexOf("classes") + 7).trimmed();
generateCompactList(Generic, relative, m_qdb->getCppClasses(), true, rootName);
- } else if (atom->string() == QLatin1String("qmlbasictypes")) {
- generateCompactList(Generic, relative, m_qdb->getQmlBasicTypes(), true,
+ } else if (atom->string() == QLatin1String("qmlvaluetypes")
+ || atom->string() == QLatin1String("qmlbasictypes")) {
+ generateCompactList(Generic, relative, m_qdb->getQmlValueTypes(), true,
QStringLiteral(""));
} else if (atom->string() == QLatin1String("qmltypes")) {
generateCompactList(Generic, relative, m_qdb->getQmlTypes(), true, QStringLiteral(""));
} else if ((idx = atom->string().indexOf(QStringLiteral("bymodule"))) != -1) {
- QString moduleName = atom->string().mid(idx + 8).trimmed();
- Node::NodeType type = typeFromString(atom);
QDocDatabase *qdb = QDocDatabase::qdocDB();
- const CollectionNode *cn = qdb->getCollectionNode(moduleName, type);
- if (cn) {
- if (type == Node::Module) {
- NodeMap m;
- cn->getMemberClasses(m);
- if (!m.isEmpty()) {
- generateAnnotatedList(relative, marker, m.values());
- }
- } else
+ QString moduleName = atom->string().mid(idx + 8).trimmed();
+ Node::NodeType moduleType = typeFromString(atom);
+ if (const auto *cn = qdb->getCollectionNode(moduleName, moduleType)) {
+ NodeMap map;
+ switch (moduleType) {
+ case Node::Module:
+ // classesbymodule <module_name>
+ map = cn->getMembers([](const Node *n) { return n->isClassNode(); });
+ generateAnnotatedList(relative, marker, map.values());
+ break;
+ case Node::QmlModule:
+ if (atom->string().contains(QLatin1String("qmlvaluetypes")))
+ map = cn->getMembers(Node::QmlValueType); // qmlvaluetypesbymodule <module_name>
+ else
+ map = cn->getMembers(Node::QmlType); // qmltypesbymodule <module_name>
+ generateAnnotatedList(relative, marker, map.values());
+ break;
+ default: // fall back to listing all members
generateAnnotatedList(relative, marker, cn->members());
+ break;
+ }
}
- } else if (atom->string().startsWith("examplefiles")
- || atom->string().startsWith("exampleimages")) {
- if (relative->isExample()) {
- qDebug() << "GENERATE FILE LIST CALLED" << relative->name() << atom->string();
- } else
- relative->location().warning(QString("'\\generatelist %1' can only be used with "
- "'\\example' topic command")
- .arg(atom->string()));
} else if (atom->string() == QLatin1String("classhierarchy")) {
generateClassHierarchy(relative, m_qdb->getCppClasses());
} else if (atom->string() == QLatin1String("obsoleteclasses")) {
@@ -563,7 +542,7 @@ qsizetype HtmlGenerator::generateAtom(const Atom *atom, const Node *relative, Co
for (const auto &section : sinceSections) {
if (!section.members().isEmpty()) {
out() << "<li>"
- << "<a href=\"#" << Doc::canonicalTitle(section.title()) << "\">"
+ << "<a href=\"#" << Utilities::asAsciiPrintable(section.title()) << "\">"
<< section.title() << "</a></li>\n";
}
}
@@ -572,29 +551,36 @@ qsizetype HtmlGenerator::generateAtom(const Atom *atom, const Node *relative, Co
int index = 0;
for (const auto &section : sinceSections) {
if (!section.members().isEmpty()) {
- out() << "<h3 id=\"" << Doc::canonicalTitle(section.title()) << "\">"
+ out() << "<h3 id=\"" << Utilities::asAsciiPrintable(section.title()) << "\">"
<< protectEnc(section.title()) << "</h3>\n";
if (index == Sections::SinceClasses)
generateCompactList(Generic, relative, ncmap, false, QStringLiteral("Q"));
else if (index == Sections::SinceQmlTypes)
generateCompactList(Generic, relative, nqcmap, false, QStringLiteral(""));
- else if (index == Sections::SinceMemberFunctions) {
- ParentMaps parentmaps;
- ParentMaps::iterator pmap;
+ else if (index == Sections::SinceMemberFunctions
+ || index == Sections::SinceQmlMethods
+ || index == Sections::SinceQmlProperties) {
+
+ QMap<QString, NodeMultiMap> parentmaps;
+
const QList<Node *> &members = section.members();
for (const auto &member : members) {
- Node *parent = (*member).parent();
- pmap = parentmaps.find(parent);
- if (pmap == parentmaps.end())
- pmap = parentmaps.insert(parent, NodeMultiMap());
- pmap->insert(member->name(), member);
+ QString parent_full_name = (*member).parent()->fullName();
+
+ auto parent_entry = parentmaps.find(parent_full_name);
+ if (parent_entry == parentmaps.end())
+ parent_entry = parentmaps.insert(parent_full_name, NodeMultiMap());
+ parent_entry->insert(member->name(), member);
}
+
for (auto map = parentmaps.begin(); map != parentmaps.end(); ++map) {
NodeVector nv = map->values().toVector();
- out() << "<p>Class ";
+ auto parent = nv.front()->parent();
- out() << "<a href=\"" << linkForNode(map.key(), relative) << "\">";
- QStringList pieces = map.key()->fullName().split("::");
+ out() << ((index == Sections::SinceMemberFunctions) ? "<p>Class " : "<p>QML Type ");
+
+ out() << "<a href=\"" << linkForNode(parent, relative) << "\" translate=\"no\">";
+ QStringList pieces = parent->fullName().split("::");
out() << protectEnc(pieces.last());
out() << "</a>"
<< ":</p>\n";
@@ -602,6 +588,15 @@ qsizetype HtmlGenerator::generateAtom(const Atom *atom, const Node *relative, Co
generateSection(nv, relative, marker);
out() << "<br/>";
}
+ } else if (index == Sections::SinceEnumValues) {
+ out() << "<div class=\"table\"><table class=\"alignedsummary\" translate=\"no\">\n";
+ const auto map_it = m_qdb->newEnumValueMaps().constFind(atom->string());
+ for (auto it = map_it->cbegin(); it != map_it->cend(); ++it) {
+ out() << "<tr><td class=\"memItemLeft\"> enum value </td><td class=\"memItemRight\">"
+ << "<b><a href=\"" << linkForNode(it.value(), nullptr) << "\">"
+ << it.key() << "</a></b></td></tr>\n";
+ }
+ out() << "</table></div>\n";
} else {
generateSection(section.members(), relative, marker);
}
@@ -617,28 +612,62 @@ qsizetype HtmlGenerator::generateAtom(const Atom *atom, const Node *relative, Co
break;
case Atom::Image:
case Atom::InlineImage: {
- QString fileName = imageFileName(relative, atom->string());
QString text;
- if (atom->next() != nullptr)
+ if (atom->next() && atom->next()->type() == Atom::ImageText)
text = atom->next()->string();
if (atom->type() == Atom::Image)
out() << "<p class=\"centerAlign\">";
- if (fileName.isEmpty()) {
+
+ auto maybe_resolved_file{file_resolver.resolve(atom->string())};
+ if (!maybe_resolved_file) {
+ // TODO: [uncentralized-admonition]
relative->location().warning(
QStringLiteral("Missing image: %1").arg(protectEnc(atom->string())));
out() << "<font color=\"red\">[Missing image " << protectEnc(atom->string())
<< "]</font>";
} else {
- QString prefix;
- out() << "<img src=\"" << protectEnc(prefix + fileName) << '"';
+ ResolvedFile file{*maybe_resolved_file};
+ QString file_name{QFileInfo{file.get_path()}.fileName()};
+
+ // TODO: [operation-can-fail-making-the-output-incorrect]
+ // The operation of copying the file can fail, making the
+ // output refer to an image that does not exist.
+ // This should be fine as HTML will take care of managing
+ // the rendering of a missing image, but what html will
+ // render is in stark contrast with what we do when the
+ // image does not exist at all.
+ // It may be more correct to unify the behavior between
+ // the two either by considering images that cannot be
+ // copied as missing or letting the HTML renderer
+ // always taking care of the two cases.
+ // Do notice that effectively doing this might be
+ // unnecessary as extracting the output directory logic
+ // should ensure that a safe assumption for copy should be
+ // made at the API boundary.
+
+ // TODO: [uncentralized-output-directory-structure]
+ Config::copyFile(relative->doc().location(), file.get_path(), file_name, outputDir() + QLatin1String("/images"));
+
+ // TODO: [uncentralized-output-directory-structure]
+ out() << "<img src=\"" << "images/" + protectEnc(file_name) << '"';
+
+ // TODO: [same-result-branching]
+ // If text is empty protectEnc should return the empty
+ // string itself, such that the two branches would still
+ // result in the same output.
+ // Ensure that this is the case and then flatten the branch if so.
if (!text.isEmpty())
out() << " alt=\"" << protectEnc(text) << '"';
else
out() << " alt=\"\"";
+
out() << " />";
- m_helpProjectWriter->addExtraFile(fileName);
- setImageFileName(relative, fileName);
+
+ // TODO: [uncentralized-output-directory-structure]
+ m_helpProjectWriter->addExtraFile("images/" + file_name);
+ setImageFileName(relative, "images/" + file_name);
}
+
if (atom->type() == Atom::Image)
out() << "</p>";
} break;
@@ -673,31 +702,29 @@ qsizetype HtmlGenerator::generateAtom(const Atom *atom, const Node *relative, Co
out() << "<br/>";
break;
case Atom::Link:
+ // Prevent nested links in table of contents
+ if (m_inContents)
+ break;
+ Q_FALLTHROUGH();
case Atom::NavLink: {
const Node *node = nullptr;
QString link = getLink(atom, relative, &node);
if (link.isEmpty() && (node != relative) && !noLinkErrors()) {
- relative->doc().location().warning(
+ Location location = atom->isLinkAtom() ? static_cast<const LinkAtom*>(atom)->location
+ : relative->doc().location();
+ location.warning(
QStringLiteral("Can't link to '%1'").arg(atom->string()));
- } else {
- node = nullptr;
}
beginLink(link, node, relative);
skipAhead = 1;
} break;
case Atom::ExampleFileLink: {
QString link = linkForExampleFile(atom->string());
- if (link.isEmpty() && !noLinkErrors())
- relative->doc().location().warning(
- QStringLiteral("Can't link to '%1'").arg(atom->string()));
beginLink(link);
skipAhead = 1;
} break;
case Atom::ExampleImageLink: {
QString link = atom->string();
- if (link.isEmpty() && !noLinkErrors())
- relative->doc().location().warning(
- QStringLiteral("Can't link to '%1'").arg(atom->string()));
link = "images/used-in-examples/" + link;
beginLink(link);
skipAhead = 1;
@@ -763,10 +790,10 @@ qsizetype HtmlGenerator::generateAtom(const Atom *atom, const Node *relative, Co
if (atom->string() == ATOM_LIST_TAG) {
out() << "<dt>";
} else { // (atom->string() == ATOM_LIST_VALUE)
- QPair<QString, int> pair = getAtomListValue(atom);
+ std::pair<QString, int> pair = getAtomListValue(atom);
skipAhead = pair.second;
QString t = protectEnc(plainCode(marker->markedUpEnumValue(pair.first, relative)));
- out() << "<tr><td class=\"topAlign\"><code>" << t << "</code>";
+ out() << "<tr><td class=\"topAlign\"><code translate=\"no\">" << t << "</code>";
if (relative->isEnumType()) {
out() << "</td><td class=\"topAlign tblval\">";
@@ -775,7 +802,7 @@ qsizetype HtmlGenerator::generateAtom(const Atom *atom, const Node *relative, Co
if (itemValue.isEmpty())
out() << '?';
else
- out() << "<code>" << protectEnc(itemValue) << "</code>";
+ out() << "<code translate=\"no\">" << protectEnc(itemValue) << "</code>";
}
}
break;
@@ -849,7 +876,7 @@ qsizetype HtmlGenerator::generateAtom(const Atom *atom, const Node *relative, Co
case Atom::SectionHeadingLeft: {
int unit = atom->string().toInt() + hOffset(relative);
out() << "<h" + QString::number(unit) + QLatin1Char(' ') << "id=\""
- << Doc::canonicalTitle(Text::sectionHeading(atom).toString()) << "\">";
+ << Tree::refForAtom(atom) << "\">";
m_inSectionHeading = true;
break;
}
@@ -863,13 +890,13 @@ qsizetype HtmlGenerator::generateAtom(const Atom *atom, const Node *relative, Co
break;
case Atom::String:
if (m_inLink && !m_inContents && !m_inSectionHeading) {
- generateLink(atom, marker);
+ generateLink(atom);
} else {
out() << protectEnc(atom->string());
}
break;
case Atom::TableLeft: {
- QPair<QString, QString> pair = getTableWidthAttr(atom);
+ std::pair<QString, QString> pair = getTableWidthAttr(atom);
QString attr = pair.second;
QString width = pair.first;
@@ -952,20 +979,18 @@ qsizetype HtmlGenerator::generateAtom(const Atom *atom, const Node *relative, Co
case Atom::Keyword:
break;
case Atom::Target:
- out() << "<span id=\"" << Doc::canonicalTitle(atom->string()) << "\"></span>";
+ out() << "<span id=\"" << Utilities::asAsciiPrintable(atom->string()) << "\"></span>";
break;
case Atom::UnhandledFormat:
out() << "<b class=\"redFont\">&lt;Missing HTML&gt;</b>";
break;
case Atom::UnknownCommand:
- out() << R"(<b class="redFont"><code>\)" << protectEnc(atom->string()) << "</code></b>";
- break;
- case Atom::QmlText:
- case Atom::EndQmlText:
- // don't do anything with these. They are just tags.
+ out() << R"(<b class="redFont"><code translate=\"no\">\)" << protectEnc(atom->string()) << "</code></b>";
break;
case Atom::CodeQuoteArgument:
case Atom::CodeQuoteCommand:
+ case Atom::ComparesLeft:
+ case Atom::ComparesRight:
case Atom::SnippetCommand:
case Atom::SnippetIdentifier:
case Atom::SnippetLocation:
@@ -978,6 +1003,67 @@ qsizetype HtmlGenerator::generateAtom(const Atom *atom, const Node *relative, Co
}
/*!
+ * Return a string representing a text that exposes information about
+ * the user-visible groups that the \a node is part of. A user-visible
+ * group is a group that generates an output page, that is, a \\group
+ * topic exists for the group and can be linked to.
+ *
+ * The returned string is composed of comma separated links to the
+ * groups, with their title as the user-facing text, surrounded by
+ * some introductory text.
+ *
+ * For example, if a node named N is part of the groups with title A
+ * and B, the line rendered form of the line will be "N is part of the
+ * A, B groups", where A and B are clickable links that target the
+ * respective page of each group.
+ *
+ * If a node has a single group, the comma is removed for readability
+ * pusposes and "groups" is expressed as a singular noun.
+ * For example, "N is part of the A group".
+ *
+ * The returned string is empty when the node is not linked to any
+ * group that has a valid link target.
+ *
+ * This string is used in the summary of c++ classes or qml types to
+ * link them to some of the overview documentation that is generated
+ * through the "\group" command.
+ *
+ * Note that this is currently, incorrectly, a member of
+ * HtmlGenerator as it requires access to some protected/private
+ * members for escaping and linking.
+ */
+QString HtmlGenerator::groupReferenceText(PageNode* node) {
+ auto link_for_group = [this](const CollectionNode *group) -> QString {
+ QString target{linkForNode(group, nullptr)};
+ return (target.isEmpty()) ? protectEnc(group->name()) : "<a href=\"" + target + "\">" + protectEnc(group->fullTitle()) + "</a>";
+ };
+
+ QString text{};
+
+ const QStringList &groups_names{node->groupNames()};
+ if (groups_names.isEmpty())
+ return text;
+
+ std::vector<CollectionNode *> groups_nodes(groups_names.size(), nullptr);
+ std::transform(groups_names.cbegin(), groups_names.cend(), groups_nodes.begin(),
+ [this](const QString &group_name) -> CollectionNode* {
+ CollectionNode *group{m_qdb->groups()[group_name]};
+ m_qdb->mergeCollections(group);
+ return (group && group->wasSeen()) ? group : nullptr;
+ });
+ groups_nodes.erase(std::remove(groups_nodes.begin(), groups_nodes.end(), nullptr), groups_nodes.end());
+
+ if (!groups_nodes.empty()) {
+ text += node->name() + " is part of ";
+
+ for (std::vector<CollectionNode *>::size_type index{0}; index < groups_nodes.size(); ++index) {
+ text += link_for_group(groups_nodes[index]) + Utilities::separator(index, groups_nodes.size());
+ }
+ }
+ return text;
+}
+
+/*!
Generate a reference page for the C++ class, namespace, or
header file documented in \a node using the code \a marker
provided.
@@ -993,7 +1079,7 @@ void HtmlGenerator::generateCppReferencePage(Aggregate *aggregate, CodeMarker *m
Sections sections(aggregate);
QString word = aggregate->typeWord(true);
- QString templateDecl = aggregate->templateDecl();
+ auto templateDecl = aggregate->templateDecl();
if (aggregate->isNamespace()) {
rawTitle = aggregate->plainName();
fullTitle = aggregate->plainFullName();
@@ -1014,10 +1100,10 @@ void HtmlGenerator::generateCppReferencePage(Aggregate *aggregate, CodeMarker *m
}
Text subtitleText;
- if (rawTitle != fullTitle || !templateDecl.isEmpty()) {
+ if (rawTitle != fullTitle || templateDecl) {
if (aggregate->isClassNode()) {
- if (!templateDecl.isEmpty())
- subtitleText << templateDecl + QLatin1Char(' ');
+ if (templateDecl)
+ subtitleText << (*templateDecl).to_qstring() + QLatin1Char(' ');
subtitleText << aggregate->typeWord(false) + QLatin1Char(' ');
const QStringList ancestors = fullTitle.split(QLatin1String("::"));
for (const auto &a : ancestors) {
@@ -1071,12 +1157,22 @@ void HtmlGenerator::generateCppReferencePage(Aggregate *aggregate, CodeMarker *m
out() << "<li><a href=\"" << obsoleteLink << "\">"
<< "Deprecated members</a></li>\n";
}
+
+ if (QString groups_text{groupReferenceText(aggregate)}; !groups_text.isEmpty()) {
+ openUnorderedList();
+
+ out() << "<li>" << groups_text << "</li>\n";
+ }
+
closeUnorderedList();
+ generateComparisonCategory(aggregate, marker);
+ generateComparisonList(aggregate);
+
generateThreadSafeness(aggregate, marker);
bool needOtherSection = false;
- for (const auto &section : qAsConst(*summarySections)) {
+ for (const auto &section : std::as_const(*summarySections)) {
if (section.members().isEmpty() && section.reimplementedMembers().isEmpty()) {
if (!section.inheritedMembers().isEmpty())
needOtherSection = true;
@@ -1105,7 +1201,7 @@ void HtmlGenerator::generateCppReferencePage(Aggregate *aggregate, CodeMarker *m
out() << "<h3>Additional Inherited Members</h3>\n"
"<ul>\n";
- for (const auto &section : qAsConst(*summarySections)) {
+ for (const auto &section : std::as_const(*summarySections)) {
if (section.members().isEmpty() && !section.inheritedMembers().isEmpty())
generateSectionInheritedList(section, aggregate);
}
@@ -1129,11 +1225,10 @@ void HtmlGenerator::generateCppReferencePage(Aggregate *aggregate, CodeMarker *m
generateBody(aggregate, marker);
out() << "</div>\n";
generateAlsoList(aggregate, marker);
- generateMaintainerList(aggregate, marker);
generateExtractionMark(aggregate, EndMark);
}
- for (const auto &section : qAsConst(*detailsSections)) {
+ for (const auto &section : std::as_const(*detailsSections)) {
bool headerGenerated = false;
if (section.isEmpty())
continue;
@@ -1229,11 +1324,10 @@ void HtmlGenerator::generateProxyPage(Aggregate *aggregate, CodeMarker *marker)
generateBody(aggregate, marker);
out() << "</div>\n";
generateAlsoList(aggregate, marker);
- generateMaintainerList(aggregate, marker);
generateExtractionMark(aggregate, EndMark);
}
- for (const auto &section : qAsConst(*detailsSections)) {
+ for (const auto &section : std::as_const(*detailsSections)) {
if (section.isEmpty())
continue;
@@ -1289,10 +1383,11 @@ void HtmlGenerator::generateQmlTypePage(QmlTypeNode *qcn, CodeMarker *marker)
Generator::setQmlTypeContext(qcn);
SubTitleSize subTitleSize = LargeSubTitle;
QString htmlTitle = qcn->fullTitle();
- if (qcn->isJsType())
- htmlTitle += " JavaScript Type";
+ if (qcn->isQmlBasicType())
+ htmlTitle.append(" QML Value Type");
else
- htmlTitle += " QML Type";
+ htmlTitle.append(" QML Type");
+
generateHeader(htmlTitle, qcn, marker);
Sections sections(qcn);
@@ -1301,11 +1396,17 @@ void HtmlGenerator::generateQmlTypePage(QmlTypeNode *qcn, CodeMarker *marker)
generateTitle(htmlTitle, Text() << qcn->subtitle(), subTitleSize, qcn, marker);
generateBrief(qcn, marker);
generateQmlRequisites(qcn, marker);
+ generateStatus(qcn, marker);
- QString allQmlMembersLink = generateAllQmlMembersFile(sections, marker);
+ QString allQmlMembersLink;
+
+ // No 'All Members' file for QML value types
+ if (!qcn->isQmlBasicType())
+ allQmlMembersLink = generateAllQmlMembersFile(sections, marker);
QString obsoleteLink = generateObsoleteQmlMembersFile(sections, marker);
if (!allQmlMembersLink.isEmpty() || !obsoleteLink.isEmpty()) {
- out() << "<ul>\n";
+ openUnorderedList();
+
if (!allQmlMembersLink.isEmpty()) {
out() << "<li><a href=\"" << allQmlMembersLink << "\">"
<< "List of all members, including inherited members</a></li>\n";
@@ -1314,9 +1415,16 @@ void HtmlGenerator::generateQmlTypePage(QmlTypeNode *qcn, CodeMarker *marker)
out() << "<li><a href=\"" << obsoleteLink << "\">"
<< "Deprecated members</a></li>\n";
}
- out() << "</ul>\n";
}
+ if (QString groups_text{groupReferenceText(qcn)}; !groups_text.isEmpty()) {
+ openUnorderedList();
+
+ out() << "<li>" << groups_text << "</li>\n";
+ }
+
+ closeUnorderedList();
+
const QList<Section> &stdQmlTypeSummarySections = sections.stdQmlTypeSummarySections();
for (const auto &section : stdQmlTypeSummarySections) {
if (!section.isEmpty()) {
@@ -1331,9 +1439,6 @@ void HtmlGenerator::generateQmlTypePage(QmlTypeNode *qcn, CodeMarker *marker)
<< "Detailed Description"
<< "</h2>\n";
generateBody(qcn, marker);
- ClassNode *cn = qcn->classNode();
- if (cn)
- generateQmlText(cn->doc().body(), cn, marker, qcn->name());
generateAlsoList(qcn, marker);
generateExtractionMark(qcn, EndMark);
@@ -1353,60 +1458,8 @@ void HtmlGenerator::generateQmlTypePage(QmlTypeNode *qcn, CodeMarker *marker)
}
/*!
- Generate the HTML page for the QML basic type represented
- by the QML basic type node \a qbtn.
- */
-void HtmlGenerator::generateQmlBasicTypePage(QmlBasicTypeNode *qbtn, CodeMarker *marker)
-{
- SubTitleSize subTitleSize = LargeSubTitle;
- QString htmlTitle = qbtn->fullTitle();
- if (qbtn->isJsType())
- htmlTitle += " JavaScript Basic Type";
- else
- htmlTitle += " QML Basic Type";
-
- marker = CodeMarker::markerForLanguage(QLatin1String("QML"));
-
- generateHeader(htmlTitle, qbtn, marker);
- Sections sections(qbtn);
- generateTableOfContents(qbtn, marker, &sections.stdQmlTypeSummarySections());
- generateTitle(htmlTitle, Text() << qbtn->subtitle(), subTitleSize, qbtn, marker);
-
- const QList<Section> &stdQmlTypeSummarySections = sections.stdQmlTypeSummarySections();
- for (const auto &section : stdQmlTypeSummarySections) {
- if (!section.isEmpty()) {
- const QString &ref = registerRef(section.title().toLower());
- out() << "<h2 id=\"" << ref << "\">" << protectEnc(section.title()) << "</h2>\n";
- generateQmlSummary(section.members(), qbtn, marker);
- }
- }
-
- generateExtractionMark(qbtn, DetailedDescriptionMark);
- out() << R"(<div class="descr" id=")" << registerRef("details")
- << "\">\n";
-
- generateBody(qbtn, marker);
- out() << "</div>\n";
- generateAlsoList(qbtn, marker);
- generateExtractionMark(qbtn, EndMark);
-
- const QList<Section> &stdQmlTypeDetailsSections = sections.stdQmlTypeDetailsSections();
- for (const auto &section : stdQmlTypeDetailsSections) {
- if (!section.isEmpty()) {
- out() << "<h2>" << protectEnc(section.title()) << "</h2>\n";
- const QList<Node *> &members = section.members();
- for (const auto member : members) {
- generateDetailedQmlMember(member, qbtn, marker);
- out() << "<br/>\n";
- }
- }
- }
- generateFooter(qbtn);
-}
-
-/*!
Generate the HTML page for an entity that doesn't map
- to any underlying parsable C++, QML, or Javascript element.
+ to any underlying parsable C++ or QML element.
*/
void HtmlGenerator::generatePageNode(PageNode *pn, CodeMarker *marker)
{
@@ -1461,15 +1514,13 @@ void HtmlGenerator::generateCollectionNode(CollectionNode *cn, CodeMarker *marke
if (cn->isModule()) {
if (!cn->noAutoList()) {
- NodeMap nmm;
- cn->getMemberNamespaces(nmm);
+ NodeMap nmm{cn->getMembers(Node::Namespace)};
if (!nmm.isEmpty()) {
ref = registerRef("namespaces");
out() << "<h2 id=\"" << ref << "\">Namespaces</h2>\n";
generateAnnotatedList(cn, marker, nmm.values());
}
- nmm.clear();
- cn->getMemberClasses(nmm);
+ nmm = cn->getMembers([](const Node *n){ return n->isClassNode(); });
if (!nmm.isEmpty()) {
ref = registerRef("classes");
out() << "<h2 id=\"" << ref << "\">Classes</h2>\n";
@@ -1497,7 +1548,7 @@ void HtmlGenerator::generateCollectionNode(CollectionNode *cn, CodeMarker *marke
generateExtractionMark(cn, EndMark);
if (!cn->noAutoList()) {
- if (cn->isGroup() || cn->isQmlModule() || cn->isJsModule())
+ if (cn->isGroup() || cn->isQmlModule())
generateAnnotatedList(cn, marker, cn->members());
}
generateFooter(cn);
@@ -1541,7 +1592,26 @@ QString HtmlGenerator::fileExtension() const
}
/*!
- Output navigation list in the html file.
+ Output a navigation bar (breadcrumbs) for the html file.
+ For API reference pages, items for the navigation bar are (in order):
+ \table
+ \header \li Item \li Related configuration variable \li Notes
+ \row \li home \li navigation.homepage \li e.g. 'Qt 6.2'
+ \row \li landing \li navigation.landingpage \li Module landing page
+ \row \li types \li navigation.cppclassespage (C++)\br
+ navigation.qmltypespage (QML) \li Types only
+ \row \li module \li n/a (automatic) \li Module page if different
+ from previous item
+ \row \li page \li n/a \li Current page title
+ \endtable
+
+ For other page types (page nodes) the navigation bar is constructed from home
+ page, landing page, and the chain of PageNode::navigationParent() items (if one exists).
+ This chain is constructed from the \\list structure on a page or pages defined in
+ \c navigation.toctitles configuration variable.
+
+ Finally, if no other navigation data exists for a page but it is a member of a
+ single group (using \\ingroup), add that group page to the navigation bar.
*/
void HtmlGenerator::generateNavigationBar(const QString &title, const Node *node,
CodeMarker *marker, const QString &buildversion,
@@ -1553,53 +1623,89 @@ void HtmlGenerator::generateNavigationBar(const QString &title, const Node *node
Text navigationbar;
// Set list item types based on the navigation bar type
+ // TODO: Do we still need table items?
Atom::AtomType itemLeft = tableItems ? Atom::TableItemLeft : Atom::ListItemLeft;
Atom::AtomType itemRight = tableItems ? Atom::TableItemRight : Atom::ListItemRight;
- if (m_hometitle == title)
- return;
- if (!m_homepage.isEmpty())
- navigationbar << Atom(itemLeft) << Atom(Atom::NavLink, m_homepage)
+ // Helper to add an item to navigation bar based on a string link target
+ auto addNavItem = [&](const QString &link, const QString &title) {
+ navigationbar << Atom(itemLeft) << Atom(Atom::NavLink, link)
<< Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK)
- << Atom(Atom::String, m_hometitle)
+ << Atom(Atom::String, title)
<< Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK) << Atom(itemRight);
- if (!m_landingpage.isEmpty() && m_landingtitle != title)
- navigationbar << Atom(itemLeft) << Atom(Atom::NavLink, m_landingpage)
+ };
+
+ // Helper to add an item to navigation bar based on a target node
+ auto addNavItemNode = [&](const Node *node, const QString &title) {
+ navigationbar << Atom(itemLeft) << Atom(Atom::LinkNode, CodeMarker::stringForNode(node))
<< Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK)
- << Atom(Atom::String, m_landingtitle)
+ << Atom(Atom::String, title)
<< Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK) << Atom(itemRight);
+ };
+
+ // Resolve the associated module (collection) node and its 'state' description
+ const auto *moduleNode = m_qdb->getModuleNode(node);
+ QString moduleState;
+ if (moduleNode && !moduleNode->state().isEmpty())
+ moduleState = QStringLiteral(" (%1)").arg(moduleNode->state());
+
+ if (m_hometitle == title)
+ return;
+ if (!m_homepage.isEmpty())
+ addNavItem(m_homepage, m_hometitle);
+ if (!m_landingpage.isEmpty() && m_landingtitle != title)
+ addNavItem(m_landingpage, m_landingtitle);
if (node->isClassNode()) {
if (!m_cppclassespage.isEmpty() && !m_cppclassestitle.isEmpty())
- navigationbar << Atom(itemLeft) << Atom(Atom::NavLink, m_cppclassespage)
- << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK)
- << Atom(Atom::String, m_cppclassestitle)
- << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK) << Atom(itemRight);
-
- if (!node->name().isEmpty())
- navigationbar << Atom(itemLeft) << Atom(Atom::String, node->name()) << Atom(itemRight);
- } else if (node->isQmlType() || node->isQmlBasicType() || node->isJsType()
- || node->isJsBasicType()) {
+ addNavItem(m_cppclassespage, m_cppclassestitle);
+ if (!node->physicalModuleName().isEmpty()) {
+ // Add explicit link to the \module page if:
+ // - It's not the C++ classes page that's already added, OR
+ // - It has a \modulestate associated with it
+ if (moduleNode && (!moduleState.isEmpty() || moduleNode->title() != m_cppclassespage))
+ addNavItemNode(moduleNode, moduleNode->name() + moduleState);
+ }
+ navigationbar << Atom(itemLeft) << Atom(Atom::String, node->name()) << Atom(itemRight);
+ } else if (node->isQmlType()) {
if (!m_qmltypespage.isEmpty() && !m_qmltypestitle.isEmpty())
- navigationbar << Atom(itemLeft) << Atom(Atom::NavLink, m_qmltypespage)
- << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK)
- << Atom(Atom::String, m_qmltypestitle)
- << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK) << Atom(itemRight)
- << Atom(itemLeft) << Atom(Atom::String, title) << Atom(itemRight);
+ addNavItem(m_qmltypespage, m_qmltypestitle);
+ // Add explicit link to the \qmlmodule page if:
+ // - It's not the QML types page that's already added, OR
+ // - It has a \modulestate associated with it
+ if (moduleNode && (!moduleState.isEmpty() || moduleNode->title() != m_qmltypespage)) {
+ addNavItemNode(moduleNode, moduleNode->name() + moduleState);
+ }
+ navigationbar << Atom(itemLeft) << Atom(Atom::String, node->name()) << Atom(itemRight);
} else {
if (node->isPageNode()) {
- QStringList groups = static_cast<const PageNode *>(node)->groupNames();
- if (groups.length() == 1) {
- const Node *groupNode =
- m_qdb->findNodeByNameAndType(QStringList(groups[0]), &Node::isGroup);
- if (groupNode && !groupNode->title().isEmpty()) {
- navigationbar << Atom(itemLeft) << Atom(Atom::NavLink, groupNode->name())
- << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK)
- << Atom(Atom::String, groupNode->title())
- << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK)
- << Atom(itemRight);
+ auto currentNode{static_cast<const PageNode*>(node)};
+ std::deque<const Node *> navNodes;
+ // Cutoff at 16 items in case there's a circular dependency
+ qsizetype navItems = 0;
+ while (currentNode->navigationParent() && ++navItems < 16) {
+ if (std::find(navNodes.cbegin(), navNodes.cend(),
+ currentNode->navigationParent()) == navNodes.cend())
+ navNodes.push_front(currentNode->navigationParent());
+ currentNode = currentNode->navigationParent();
+ }
+ // If no nav. parent was found but the page is a \group member, add a link to the
+ // (first) group page.
+ if (navNodes.empty()) {
+ const QStringList groups = static_cast<const PageNode *>(node)->groupNames();
+ for (const auto &groupName : groups) {
+ const auto *groupNode = m_qdb->findNodeByNameAndType(QStringList{groupName}, &Node::isGroup);
+ if (groupNode && !groupNode->title().isEmpty()) {
+ navNodes.push_front(groupNode);
+ break;
+ }
}
}
+ while (!navNodes.empty()) {
+ if (navNodes.front()->isPageNode())
+ addNavItemNode(navNodes.front(), navNodes.front()->title());
+ navNodes.pop_front();
+ }
}
if (!navigationbar.isEmpty()) {
navigationbar << Atom(itemLeft) << Atom(Atom::String, title) << Atom(itemRight);
@@ -1645,6 +1751,12 @@ void HtmlGenerator::generateHeader(const QString &title, const Node *node, CodeM
if (node && !node->doc().location().isEmpty())
out() << "<!-- " << node->doc().location().fileName() << " -->\n";
+ if (node && !node->doc().briefText().isEmpty()) {
+ out() << " <meta name=\"description\" content=\""
+ << protectEnc(node->doc().briefText().toString())
+ << "\">\n";
+ }
+
// determine the rest of the <title> element content: "title | titleSuffix version"
QString titleSuffix;
if (!m_landingtitle.isEmpty()) {
@@ -1678,7 +1790,7 @@ void HtmlGenerator::generateHeader(const QString &title, const Node *node, CodeM
QVersionNumber projectVersion = QVersionNumber::fromString(m_qdb->version());
if (!projectVersion.isNull()) {
QVersionNumber titleVersion;
- QRegularExpression re(QLatin1String(R"(\d+\.\d+)"));
+ static const QRegularExpression re(QLatin1String(R"(\d+\.\d+)"));
const QString &versionedTitle = titleSuffix.isEmpty() ? title : titleSuffix;
auto match = re.match(versionedTitle);
if (match.hasMatch())
@@ -1705,8 +1817,8 @@ void HtmlGenerator::generateHeader(const QString &title, const Node *node, CodeM
refMap.clear();
if (node && !node->links().empty()) {
- QPair<QString, QString> linkPair;
- QPair<QString, QString> anchorPair;
+ std::pair<QString, QString> linkPair;
+ std::pair<QString, QString> anchorPair;
const Node *linkNode;
bool useSeparator = false;
@@ -1777,14 +1889,18 @@ void HtmlGenerator::generateTitle(const QString &title, const Text &subtitle,
CodeMarker *marker)
{
out() << QString(m_prologue).replace("\\" + COMMAND_VERSION, m_qdb->version());
+ QString attribute;
+ if (relative->genus() & Node::API)
+ attribute = R"( translate="no")";
+
if (!title.isEmpty())
- out() << "<h1 class=\"title\">" << protectEnc(title) << "</h1>\n";
+ out() << "<h1 class=\"title\"" << attribute << ">" << protectEnc(title) << "</h1>\n";
if (!subtitle.isEmpty()) {
out() << "<span";
if (subTitleSize == SmallSubTitle)
- out() << " class=\"small-subtitle\">";
+ out() << " class=\"small-subtitle\"" << attribute << ">";
else
- out() << " class=\"subtitle\">";
+ out() << " class=\"subtitle\"" << attribute << ">";
generateText(subtitle, relative, marker);
out() << "</span>\n";
}
@@ -1815,15 +1931,16 @@ void HtmlGenerator::generateRequisites(Aggregate *aggregate, CodeMarker *marker)
const QString sinceText = "Since";
const QString inheritedBytext = "Inherited By";
const QString inheritsText = "Inherits";
- const QString instantiatedByText = "Instantiated By";
+ const QString nativeTypeText = "In QML";
const QString qtVariableText = "qmake";
const QString cmakeText = "CMake";
+ const QString statusText = "Status";
// The order of the requisites matter
- const QStringList requisiteorder { headerText, cmakeText, qtVariableText, sinceText,
- instantiatedByText, inheritsText, inheritedBytext };
+ const QStringList requisiteorder { headerText, cmakeText, qtVariableText, sinceText,
+ nativeTypeText, inheritsText, inheritedBytext, statusText };
- addIncludeFilesToMap(aggregate, marker, requisites, &text, headerText);
+ addIncludeFileToMap(aggregate, marker, requisites, text, headerText);
addSinceToMap(aggregate, requisites, &text, sinceText);
if (aggregate->isClassNode() || aggregate->isNamespace()) {
@@ -1833,13 +1950,16 @@ void HtmlGenerator::generateRequisites(Aggregate *aggregate, CodeMarker *marker)
if (aggregate->isClassNode()) {
auto *classe = dynamic_cast<ClassNode *>(aggregate);
- if (classe->qmlElement() != nullptr && !classe->isInternal())
- addInstantiatedByToMap(requisites, &text, instantiatedByText, classe);
+ if (classe->isQmlNativeType() && !classe->isInternal())
+ addQmlNativeTypesToMap(requisites, &text, nativeTypeText, classe);
addInheritsToMap(requisites, &text, inheritsText, classe);
addInheritedByToMap(requisites, &text, inheritedBytext, classe);
}
+ // Add the state description (if any) to the map
+ addStatusToMap(aggregate, requisites, text, statusText);
+
if (!requisites.isEmpty()) {
// generate the table
generateTheTable(requisiteorder, requisites, headerText, aggregate, marker);
@@ -1854,7 +1974,7 @@ void HtmlGenerator::generateTheTable(const QStringList &requisiteOrder,
const QString &headerText, const Aggregate *aggregate,
CodeMarker *marker)
{
- out() << "<div class=\"table\"><table class=\"alignedsummary\">\n";
+ out() << "<div class=\"table\"><table class=\"alignedsummary\" translate=\"no\">\n";
for (auto it = requisiteOrder.constBegin(); it != requisiteOrder.constEnd(); ++it) {
@@ -1868,7 +1988,7 @@ void HtmlGenerator::generateTheTable(const QStringList &requisiteOrder,
out() << requisites.value(*it).toString();
else
generateText(requisites.value(*it), aggregate, marker);
- out() << "</td></tr>";
+ out() << "</td></tr>\n";
}
}
out() << "</table></div>\n";
@@ -1911,7 +2031,7 @@ void HtmlGenerator::addInheritsToMap(QMap<QString, Text> &requisites, Text *text
} else if (cls.m_access == Access::Private) {
*text << " (private)";
}
- *text << Utilities::comma(index++, classe->baseClasses().count());
+ *text << Utilities::comma(index++, classe->baseClasses().size());
}
}
*text << Atom::ParaRight;
@@ -1921,21 +2041,29 @@ void HtmlGenerator::addInheritsToMap(QMap<QString, Text> &requisites, Text *text
}
/*!
- * \internal
- * Add the instantiated by information to the map.
+ \internal
+ Add the QML/C++ native type information to the map.
*/
-void HtmlGenerator::addInstantiatedByToMap(QMap<QString, Text> &requisites, Text *text,
- const QString &instantiatedByText,
- ClassNode *classe) const
+ void HtmlGenerator::addQmlNativeTypesToMap(QMap<QString, Text> &requisites, Text *text,
+ const QString &nativeTypeText, ClassNode *classe) const
{
- if (text != nullptr) {
- text->clear();
- *text << Atom(Atom::LinkNode, CodeMarker::stringForNode(classe->qmlElement()))
+ if (!text)
+ return;
+
+ text->clear();
+
+ QList<QmlTypeNode *> nativeTypes { classe->qmlNativeTypes().cbegin(), classe->qmlNativeTypes().cend()};
+ std::sort(nativeTypes.begin(), nativeTypes.end(), Node::nodeNameLessThan);
+ qsizetype index { 0 };
+
+ for (const auto &item : std::as_const(nativeTypes)) {
+ *text << Atom(Atom::LinkNode, CodeMarker::stringForNode(item))
<< Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK)
- << Atom(Atom::String, classe->qmlElement()->name())
+ << Atom(Atom::String, item->name())
<< Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK);
- requisites.insert(instantiatedByText, *text);
+ *text << Utilities::comma(index++, nativeTypes.size());
}
+ requisites.insert(nativeTypeText, *text);
}
/*!
@@ -1948,17 +2076,19 @@ void HtmlGenerator::addCMakeInfoToMap(const Aggregate *aggregate, QMap<QString,
if (!aggregate->physicalModuleName().isEmpty() && text != nullptr) {
const CollectionNode *cn =
m_qdb->getCollectionNode(aggregate->physicalModuleName(), Node::Module);
- if (cn && !cn->qtCMakeComponent().isEmpty()) {
- text->clear();
- const QString qtComponent = "Qt" + QString::number(QT_VERSION_MAJOR);
- const QString findPackageText = "find_package(" + qtComponent + " COMPONENTS "
- + cn->qtCMakeComponent() + " REQUIRED)";
- const QString targetLinkLibrariesText = "target_link_libraries(mytarget PRIVATE "
- + qtComponent + "::" + cn->qtCMakeComponent() + ")";
- const Atom lineBreak = Atom(Atom::RawString, " <br/>\n");
- *text << findPackageText << lineBreak << targetLinkLibrariesText;
- requisites.insert(CMakeInfo, *text);
- }
+ if (!cn || cn->qtCMakeComponent().isEmpty())
+ return;
+
+ text->clear();
+ const QString qtComponent = "Qt" + QString::number(QT_VERSION_MAJOR);
+ const QString findPackageText = "find_package(" + qtComponent + " REQUIRED COMPONENTS "
+ + cn->qtCMakeComponent() + ")";
+ const QString targetText = cn->qtCMakeTargetItem().isEmpty() ? cn->qtCMakeComponent() : cn->qtCMakeTargetItem();
+ const QString targetLinkLibrariesText = "target_link_libraries(mytarget PRIVATE "
+ + qtComponent + "::" + targetText + ")";
+ const Atom lineBreak = Atom(Atom::RawString, " <br/>\n");
+ *text << findPackageText << lineBreak << targetLinkLibrariesText;
+ requisites.insert(CMakeInfo, *text);
}
}
@@ -1998,22 +2128,52 @@ void HtmlGenerator::addSinceToMap(const Aggregate *aggregate, QMap<QString, Text
/*!
* \internal
+ * Adds the status description for \a aggregate, together with a <span> element, to the \a
+ * requisites map.
+ *
+ * The span element can be used for adding CSS styling/icon associated with a specific status.
+ * The span class name is constructed by converting the description (sans \\deprecated
+ * version info) to lowercase and replacing all non-alphanum characters with hyphens. In
+ * addition, the span has a class \c status. For example,
+ * 'Tech Preview' -> class="status tech-preview"
+*/
+void HtmlGenerator::addStatusToMap(const Aggregate *aggregate, QMap<QString, Text> &requisites,
+ Text &text, const QString &statusText) const
+{
+ auto status{formatStatus(aggregate, m_qdb)};
+ if (!status)
+ return;
+
+ QString spanClass;
+ if (aggregate->status() == Node::Deprecated)
+ spanClass = u"deprecated"_s; // Disregard any version info
+ else
+ spanClass = Utilities::asAsciiPrintable(status.value());
+
+ text.clear();
+ text << Atom(Atom::String, status.value())
+ << Atom(Atom::FormattingLeft, ATOM_FORMATTING_SPAN +
+ "class=\"status %1\""_L1.arg(spanClass))
+ << Atom(Atom::FormattingRight, ATOM_FORMATTING_SPAN);
+ requisites.insert(statusText, text);
+}
+
+/*!
+ * \internal
* Adds the includes (from the \\includefile command) to the map.
*/
-void HtmlGenerator::addIncludeFilesToMap(const Aggregate *aggregate, CodeMarker *marker,
- QMap<QString, Text> &requisites, Text *text,
+void HtmlGenerator::addIncludeFileToMap(const Aggregate *aggregate, CodeMarker *marker,
+ QMap<QString, Text> &requisites, Text& text,
const QString &headerText)
{
- QStringList includeFiles = aggregate->includeFiles();
- includeFiles.erase(std::remove_if(includeFiles.begin(), includeFiles.end(),
- [](auto includeFile) { return includeFile.isEmpty(); }), includeFiles.end());
+ if (aggregate->includeFile()) {
+ text.clear();
+ text << highlightedCode(
+ indent(m_codeIndent, marker->markedUpInclude(*aggregate->includeFile())),
+ aggregate
+ );
- if (!includeFiles.isEmpty() && text != nullptr) {
- text->clear();
- *text << highlightedCode(
- indent(m_codeIndent, marker->markedUpIncludes(includeFiles)),
- aggregate);
- requisites.insert(headerText, *text);
+ requisites.insert(headerText, text);
}
}
@@ -2032,30 +2192,22 @@ void HtmlGenerator::generateQmlRequisites(QmlTypeNode *qcn, CodeMarker *marker)
const QString sinceText = "Since:";
const QString inheritedBytext = "Inherited By:";
const QString inheritsText = "Inherits:";
- const QString instantiatesText = "Instantiates:";
+ const QString nativeTypeText = "In C++:";
+ const QString statusText = "Status:";
// add the module name and version to the map
QString logicalModuleVersion;
const CollectionNode *collection = qcn->logicalModule();
// skip import statement of \internal collections
- if (!collection || !collection->isInternal() || m_showInternal) {
- logicalModuleVersion =
- collection ? collection->logicalModuleVersion() : qcn->logicalModuleVersion();
-
- if (qcn->logicalModuleName().isEmpty())
- qcn->doc().location().warning(QStringLiteral("Could not resolve QML import "
- "statement for type '%1'")
- .arg(qcn->name()),
- QStringLiteral("Maybe you forgot to use the "
- "'\\%1' command?")
- .arg(COMMAND_INQMLMODULE));
-
+ if (!qcn->logicalModuleName().isEmpty() && (!collection || !collection->isInternal() || m_showInternal)) {
+ QStringList parts = QStringList() << "import" << qcn->logicalModuleName() << qcn->logicalModuleVersion();
text.clear();
- text << "import " + qcn->logicalModuleName();
- if (!logicalModuleVersion.isEmpty())
- text << QLatin1Char(' ') + logicalModuleVersion;
+ text << parts.join(' ').trimmed();
requisites.insert(importText, text);
+ } else if (!qcn->isQmlBasicType() && qcn->logicalModuleName().isEmpty()) {
+ qcn->doc().location().warning(QStringLiteral("Could not resolve QML import statement for type '%1'").arg(qcn->name()),
+ QStringLiteral("Maybe you forgot to use the '\\%1' command?").arg(COMMAND_INQMLMODULE));
}
// add the since and project into the map
@@ -2065,15 +2217,15 @@ void HtmlGenerator::generateQmlRequisites(QmlTypeNode *qcn, CodeMarker *marker)
requisites.insert(sinceText, text);
}
- // add the instantiates to the map
+ // add the native type to the map
ClassNode *cn = qcn->classNode();
- if (cn && !cn->isInternal()) {
+ if (cn && cn->isQmlNativeType() && !cn->isInternal()) {
text.clear();
text << Atom(Atom::LinkNode, CodeMarker::stringForNode(cn));
text << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK);
text << Atom(Atom::String, cn->name());
text << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK);
- requisites.insert(instantiatesText, text);
+ requisites.insert(nativeTypeText, text);
}
// add the inherits to the map
@@ -2101,13 +2253,16 @@ void HtmlGenerator::generateQmlRequisites(QmlTypeNode *qcn, CodeMarker *marker)
requisites.insert(inheritedBytext, text);
}
+ // Add the state description (if any) to the map
+ addStatusToMap(qcn, requisites, text, statusText);
+
// The order of the requisites matter
- const QStringList requisiteorder { importText, sinceText, instantiatesText, inheritsText,
- inheritedBytext };
+ const QStringList requisiteorder { importText, sinceText, nativeTypeText, inheritsText,
+ inheritedBytext, statusText };
if (!requisites.isEmpty()) {
// generate the table
- out() << "<div class=\"table\"><table class=\"alignedsummary\">\n";
+ out() << "<div class=\"table\"><table class=\"alignedsummary\" translate=\"no\">\n";
for (const auto &requisite : requisiteorder) {
if (requisites.contains(requisite)) {
@@ -2174,7 +2329,6 @@ void HtmlGenerator::generateTableOfContents(const Node *node, CodeMarker *marker
// disable nested links in table of contents
m_inContents = true;
- m_inLink = true;
out() << "<div class=\"sidebar\">\n";
out() << "<div class=\"toc\">\n";
@@ -2194,16 +2348,14 @@ void HtmlGenerator::generateTableOfContents(const Node *node, CodeMarker *marker
}
out() << "<li class=\"level" << sectionNumber << "\"><a href=\"#" << registerRef("details")
<< "\">Detailed Description</a></li>\n";
- for (const auto &entry : qAsConst(toc)) {
+ for (const auto &entry : std::as_const(toc)) {
if (entry->string().toInt() == 1) {
detailsBase = 1;
break;
}
}
- } else if (sections
- && (node->isClassNode() || node->isNamespace() || node->isQmlType() ||
- node->isJsType() || node->isQmlBasicType() || node->isJsBasicType())) {
- for (const auto &section : qAsConst(*sections)) {
+ } else if (sections && (node->isClassNode() || node->isNamespace() || node->isQmlType())) {
+ for (const auto &section : std::as_const(*sections)) {
if (!section.members().isEmpty()) {
openUnorderedList();
out() << "<li class=\"level" << sectionNumber << "\"><a href=\"#"
@@ -2238,9 +2390,8 @@ void HtmlGenerator::generateTableOfContents(const Node *node, CodeMarker *marker
openUnorderedList();
int numAtoms;
Text headingText = Text::sectionHeading(atom);
- QString s = headingText.toString();
out() << "<li class=\"level" << sectionNumber << "\">";
- out() << "<a href=\"" << '#' << Doc::canonicalTitle(s) << "\">";
+ out() << "<a href=\"" << '#' << Tree::refForAtom(atom) << "\">";
generateAtomList(headingText.firstAtom(), node, marker, true, numAtoms);
out() << "</a></li>\n";
}
@@ -2309,52 +2460,49 @@ QString HtmlGenerator::generateAllQmlMembersFile(const Sections &sections, CodeM
generateFullName(aggregate, nullptr);
out() << ", including inherited members.</p>\n";
- ClassKeysNodesList &cknl = sections.allMembersSection().classKeysNodesList();
- if (!cknl.isEmpty()) {
- for (int i = 0; i < cknl.size(); i++) {
- ClassKeysNodes *ckn = cknl[i];
- const QmlTypeNode *qcn = ckn->first;
- KeysAndNodes &kn = ckn->second;
- QStringList &keys = kn.first;
- NodeVector &nodes = kn.second;
- if (nodes.isEmpty())
+ ClassNodesList &cknl = sections.allMembersSection().classNodesList();
+ for (int i = 0; i < cknl.size(); i++) {
+ ClassNodes ckn = cknl[i];
+ const QmlTypeNode *qcn = ckn.first;
+ NodeVector &nodes = ckn.second;
+ if (nodes.isEmpty())
+ continue;
+ if (i != 0) {
+ out() << "<p>The following members are inherited from ";
+ generateFullName(qcn, nullptr);
+ out() << ".</p>\n";
+ }
+ openUnorderedList();
+ for (int j = 0; j < nodes.size(); j++) {
+ Node *node = nodes[j];
+ if (node->access() == Access::Private || node->isInternal())
continue;
- if (i != 0) {
- out() << "<p>The following members are inherited from ";
- generateFullName(qcn, nullptr);
- out() << ".</p>\n";
- }
- openUnorderedList();
- for (int j = 0; j < keys.size(); j++) {
- Node *node = nodes[j];
- if (node->access() == Access::Private || node->isInternal())
- continue;
- if (node->isSharingComment() && node->sharedCommentNode()->isPropertyGroup())
- continue;
-
- std::function<void(Node *)> generate = [&](Node *n) {
- out() << "<li class=\"fn\">";
- generateQmlItem(n, aggregate, marker, true);
- if (n->isDefault())
- out() << " [default]";
- else if (n->isAttached())
- out() << " [attached]";
- // Indent property group members
- if (n->isPropertyGroup()) {
- out() << "<ul>\n";
- const QList<Node *> &collective =
- static_cast<SharedCommentNode *>(n)->collective();
- std::for_each(collective.begin(), collective.end(), generate);
- out() << "</ul>\n";
- }
- out() << "</li>\n";
- };
- generate(node);
- }
- closeUnorderedList();
+ if (node->isSharingComment() && node->sharedCommentNode()->isPropertyGroup())
+ continue;
+
+ std::function<void(Node *)> generate = [&](Node *n) {
+ out() << "<li class=\"fn\" translate=\"no\">";
+ generateQmlItem(n, aggregate, marker, true);
+ if (n->isDefault())
+ out() << " [default]";
+ else if (n->isAttached())
+ out() << " [attached]";
+ // Indent property group members
+ if (n->isPropertyGroup()) {
+ out() << "<ul>\n";
+ const QList<Node *> &collective =
+ static_cast<SharedCommentNode *>(n)->collective();
+ std::for_each(collective.begin(), collective.end(), generate);
+ out() << "</ul>\n";
+ }
+ out() << "</li>\n";
+ };
+ generate(node);
}
+ closeUnorderedList();
}
+
generateFooter();
endSubPage();
return fileName;
@@ -2370,11 +2518,6 @@ QString HtmlGenerator::generateObsoleteMembersFile(const Sections &sections, Cod
Aggregate *aggregate = sections.aggregate();
QString title = "Obsolete Members for " + aggregate->name();
QString fileName = fileBase(aggregate) + "-obsolete." + fileExtension();
- QString link;
- if (useOutputSubdirs() && !Generator::outputSubdir().isEmpty())
- link = QString("../" + Generator::outputSubdir() + QLatin1Char('/'));
- link += fileName;
- aggregate->setObsoleteLink(link);
beginSubPage(aggregate, fileName);
generateHeader(title, aggregate, marker);
@@ -2382,7 +2525,7 @@ QString HtmlGenerator::generateObsoleteMembersFile(const Sections &sections, Cod
generateTitle(title, Text(), SmallSubTitle, aggregate, marker);
out() << "<p><b>The following members of class "
- << "<a href=\"" << linkForNode(aggregate, nullptr) << "\">"
+ << "<a href=\"" << linkForNode(aggregate, nullptr) << "\" translate=\"no\">"
<< protectEnc(aggregate->name()) << "</a>"
<< " are deprecated.</b> "
<< "They are provided to keep old source code working. "
@@ -2390,7 +2533,7 @@ QString HtmlGenerator::generateObsoleteMembersFile(const Sections &sections, Cod
for (const auto &section : summary_spv) {
out() << "<h2>" << protectEnc(section->title()) << "</h2>\n";
- generateSectionList(*section, aggregate, marker, Section::Obsolete);
+ generateSectionList(*section, aggregate, marker, true);
}
for (const auto &section : details_spv) {
@@ -2412,9 +2555,6 @@ QString HtmlGenerator::generateObsoleteMembersFile(const Sections &sections, Cod
Generates a separate file where deprecated members of the QML
type \a qcn are listed. The \a marker is used to generate
the section lists, which are then traversed and output here.
-
- Note that this function currently only handles correctly the
- case where \a status is \c {Section::Obsolete}.
*/
QString HtmlGenerator::generateObsoleteQmlMembersFile(const Sections &sections, CodeMarker *marker)
{
@@ -2426,11 +2566,6 @@ QString HtmlGenerator::generateObsoleteQmlMembersFile(const Sections &sections,
Aggregate *aggregate = sections.aggregate();
QString title = "Obsolete Members for " + aggregate->name();
QString fileName = fileBase(aggregate) + "-obsolete." + fileExtension();
- QString link;
- if (useOutputSubdirs() && !Generator::outputSubdir().isEmpty())
- link = QString("../" + Generator::outputSubdir() + QLatin1Char('/'));
- link += fileName;
- aggregate->setObsoleteLink(link);
beginSubPage(aggregate, fileName);
generateHeader(title, aggregate, marker);
@@ -2530,12 +2665,12 @@ void HtmlGenerator::generateAnnotatedList(const Node *relative, CodeMarker *mark
NodeList nodes = nmm.values();
std::sort(nodes.begin(), nodes.end(), Node::nodeNameLessThan);
- for (const auto *node : qAsConst(nodes)) {
+ for (const auto *node : std::as_const(nodes)) {
if (++row % 2 == 1)
out() << "<tr class=\"odd topAlign\">";
else
out() << "<tr class=\"even topAlign\">";
- out() << "<td class=\"tblName\"><p>";
+ out() << "<td class=\"tblName\" translate=\"no\"><p>";
generateFullName(node, relative);
out() << "</p></td>";
@@ -2598,7 +2733,7 @@ void HtmlGenerator::generateCompactList(ListType listType, const Node *relative,
return;
const int NumParagraphs = 37; // '0' to '9', 'A' to 'Z', '_'
- qsizetype commonPrefixLen = commonPrefix.length();
+ qsizetype commonPrefixLen = commonPrefix.size();
/*
Divide the data into 37 paragraphs: 0, ..., 9, A, ..., Z,
@@ -2642,13 +2777,13 @@ void HtmlGenerator::generateCompactList(ListType listType, const Node *relative,
qsizetype paragraphOffset[NumParagraphs + 1]; // 37 + 1
paragraphOffset[0] = 0;
for (int i = 0; i < NumParagraphs; i++) // i = 0..36
- paragraphOffset[i + 1] = paragraphOffset[i] + paragraph[i].count();
+ paragraphOffset[i + 1] = paragraphOffset[i] + paragraph[i].size();
/*
Output the alphabet as a row of links.
*/
if (includeAlphabet) {
- out() << "<p class=\"centerAlign functionIndex\"><b>";
+ out() << "<p class=\"centerAlign functionIndex\" translate=\"no\"><b>";
for (int i = 0; i < 26; i++) {
QChar ch('a' + i);
if (usedParagraphNames.contains(char('a' + i)))
@@ -2660,7 +2795,7 @@ void HtmlGenerator::generateCompactList(ListType listType, const Node *relative,
/*
Output a <div> element to contain all the <dl> elements.
*/
- out() << "<div class=\"flowListDiv\">\n";
+ out() << "<div class=\"flowListDiv\" translate=\"no\">\n";
m_numTableRows = 0;
int curParNr = 0;
@@ -2668,8 +2803,8 @@ void HtmlGenerator::generateCompactList(ListType listType, const Node *relative,
QString previousName;
bool multipleOccurrences = false;
- for (int i = 0; i < nmm.count(); i++) {
- while ((curParNr < NumParagraphs) && (curParOffset == paragraph[curParNr].count())) {
+ for (int i = 0; i < nmm.size(); i++) {
+ while ((curParNr < NumParagraphs) && (curParOffset == paragraph[curParNr].size())) {
++curParNr;
curParOffset = 0;
}
@@ -2710,29 +2845,25 @@ void HtmlGenerator::generateCompactList(ListType listType, const Node *relative,
} else if (listType == Obsolete) {
QString fileName = fileBase(it.value()) + "-obsolete." + fileExtension();
QString link;
- if (useOutputSubdirs()) {
- link = QString("../" + it.value()->outputSubdirectory() + QLatin1Char('/'));
- }
+ if (useOutputSubdirs())
+ link = "../%1/"_L1.arg(it.value()->tree()->physicalModuleName());
link += fileName;
out() << "<a href=\"" << link << "\">";
}
- QStringList pieces;
- if (it.value()->isQmlType() || it.value()->isJsType()) {
- QString name = it.value()->name();
- next = it;
- ++next;
- if (name != previousName)
- multipleOccurrences = false;
- if ((next != paragraph[curParNr].end()) && (name == next.value()->name())) {
- multipleOccurrences = true;
- previousName = name;
- }
- if (multipleOccurrences)
- name += ": " + it.value()->tree()->camelCaseModuleName();
- pieces << name;
- } else
- pieces = it.value()->fullName(relative).split("::");
+ QStringList pieces{it.value()->fullName(relative).split("::"_L1)};
+ const auto &name{pieces.last()};
+ next = it;
+ ++next;
+ if (name != previousName)
+ multipleOccurrences = false;
+ if ((next != paragraph[curParNr].end()) && (name == next.value()->name())) {
+ multipleOccurrences = true;
+ previousName = name;
+ }
+ if (multipleOccurrences && pieces.size() == 1)
+ pieces.last().append(": %1"_L1.arg(it.value()->tree()->camelCaseModuleName()));
+
out() << protectEnc(pieces.last());
out() << "</a>";
if (pieces.size() > 1) {
@@ -2744,7 +2875,7 @@ void HtmlGenerator::generateCompactList(ListType listType, const Node *relative,
out() << "</dd>\n";
curParOffset++;
}
- if (nmm.count() > 0)
+ if (nmm.size() > 0)
out() << "</dl>\n";
out() << "</div>\n";
@@ -2752,7 +2883,7 @@ void HtmlGenerator::generateCompactList(ListType listType, const Node *relative,
void HtmlGenerator::generateFunctionIndex(const Node *relative)
{
- out() << "<p class=\"centerAlign functionIndex\"><b>";
+ out() << "<p class=\"centerAlign functionIndex\" translate=\"no\"><b>";
for (int i = 0; i < 26; i++) {
QChar ch('a' + i);
out() << QString("<a href=\"#%1\">%2</a>&nbsp;").arg(ch).arg(ch.toUpper());
@@ -2761,7 +2892,7 @@ void HtmlGenerator::generateFunctionIndex(const Node *relative)
char nextLetter = 'a';
- out() << "<ul>\n";
+ out() << "<ul translate=\"no\">\n";
NodeMapMap &funcIndex = m_qdb->getFunctionIndex();
for (auto fnMap = funcIndex.constBegin(); fnMap != funcIndex.constEnd(); ++fnMap) {
const QString &key = fnMap.key();
@@ -2813,21 +2944,12 @@ void HtmlGenerator::generateQmlItem(const Node *node, const Node *relative, Code
bool summary)
{
QString marked = marker->markedUpQmlItem(node, summary);
- QRegularExpression templateTag("(<[^@>]*>)");
- auto match = templateTag.match(marked);
- if (match.hasMatch()) {
- QString contents = protectEnc(match.captured(1));
- marked.replace(match.capturedStart(1), match.capturedLength(1), contents);
- }
-
- // Look for the _ character in the member name followed by a number (or n):
- // this is intended to be rendered as a subscript.
- marked.replace(QRegularExpression("<@param>([a-z]+)_([0-9]+|n)</@param>"), "<i>\\1<sub>\\2</sub></i>");
- // Replace some markup by HTML tags. Do both the opening and the closing tag
- // in one go (instead of <@param> and </@param> separately, for instance).
marked.replace("@param>", "i>");
- marked.replace("@extra>", "code>");
+ marked.replace("<@extra>", "<code class=\"%1 extra\" translate=\"no\">"_L1
+ .arg(summary ? "summary"_L1 : "details"_L1));
+ marked.replace("</@extra>", "</code>");
+
if (summary) {
marked.remove("<@name>");
@@ -2839,22 +2961,23 @@ void HtmlGenerator::generateQmlItem(const Node *node, const Node *relative, Code
}
/*!
- This function generates a simple bullet list for the members
- of collection node \a {cn}. The collection node must be a group
- and must not be empty. If it is empty, nothing is output, and
- false is returned. Otherewise, the list is generated and true is returned.
+ This function generates a simple unordered list for the members
+ of collection node \a {cn}. Returns \c true if the list was
+ generated (collection has members), \c false otherwise.
*/
bool HtmlGenerator::generateGroupList(CollectionNode *cn)
{
m_qdb->mergeCollections(cn);
if (cn->members().isEmpty())
return false;
+
+ NodeList members{cn->members()};
+ std::sort(members.begin(), members.end(), Node::nodeNameLessThan);
out() << "<ul>\n";
- const auto members = cn->members();
- for (const auto *node : members) {
- out() << "<li>"
- << "<a href=\"#" << Doc::canonicalTitle(node->title()) << "\">" << node->title()
- << "</a></li>\n";
+ for (const auto *node : std::as_const(members)) {
+ out() << "<li translate=\"no\">";
+ generateFullName(node, nullptr);
+ out() << "</li>\n";
}
out() << "</ul>\n";
return true;
@@ -2870,8 +2993,6 @@ void HtmlGenerator::generateList(const Node *relative, CodeMarker *marker, const
type = Node::Module;
else if (selector == QLatin1String("qml-modules"))
type = Node::QmlModule;
- else if (selector == QLatin1String("js-modules"))
- type = Node::JsModule;
if (type != Node::NoType) {
NodeList nodeList;
m_qdb->mergeCollections(type, cnm, relative);
@@ -2883,13 +3004,13 @@ void HtmlGenerator::generateList(const Node *relative, CodeMarker *marker, const
} else {
/*
\generatelist {selector} is only allowed in a
- comment where the topic is \group, \module,
- \qmlmodule, or \jsmodule
+ comment where the topic is \group, \module, or
+ \qmlmodule.
*/
if (relative && !relative->isCollectionNode()) {
relative->doc().location().warning(
QStringLiteral("\\generatelist {%1} is only allowed in \\group, "
- "\\module, \\qmlmodule, and \\jsmodule comments.")
+ "\\module and \\qmlmodule comments.")
.arg(selector));
return;
}
@@ -2906,14 +3027,14 @@ void HtmlGenerator::generateSection(const NodeVector &nv, const Node *relative,
if (!nv.isEmpty()) {
bool twoColumn = false;
if (nv.first()->isProperty()) {
- twoColumn = (nv.count() >= 5);
+ twoColumn = (nv.size() >= 5);
alignNames = false;
}
if (alignNames) {
- out() << "<div class=\"table\"><table class=\"alignedsummary\">\n";
+ out() << "<div class=\"table\"><table class=\"alignedsummary\" translate=\"no\">\n";
} else {
if (twoColumn)
- out() << "<div class=\"table\"><table class=\"propsummary\">\n"
+ out() << "<div class=\"table\"><table class=\"propsummary\" translate=\"no\">\n"
<< "<tr><td class=\"topAlign\">";
out() << "<ul>\n";
}
@@ -2926,9 +3047,9 @@ void HtmlGenerator::generateSection(const NodeVector &nv, const Node *relative,
if (alignNames) {
out() << "<tr><td class=\"memItemLeft rightAlign topAlign\"> ";
} else {
- if (twoColumn && i == (nv.count() + 1) / 2)
+ if (twoColumn && i == (nv.size() + 1) / 2)
out() << "</ul></td><td class=\"topAlign\"><ul>\n";
- out() << "<li class=\"fn\">";
+ out() << "<li class=\"fn\" translate=\"no\">";
}
generateSynopsis(member, relative, marker, Section::Summary, alignNames);
@@ -2949,27 +3070,27 @@ void HtmlGenerator::generateSection(const NodeVector &nv, const Node *relative,
}
void HtmlGenerator::generateSectionList(const Section &section, const Node *relative,
- CodeMarker *marker, Section::Status status)
+ CodeMarker *marker, bool useObsoleteMembers)
{
bool alignNames = true;
const NodeVector &members =
- (status == Section::Obsolete ? section.obsoleteMembers() : section.members());
+ (useObsoleteMembers ? section.obsoleteMembers() : section.members());
if (!members.isEmpty()) {
bool hasPrivateSignals = false;
bool isInvokable = false;
bool twoColumn = false;
if (section.style() == Section::AllMembers) {
alignNames = false;
- twoColumn = (members.count() >= 16);
+ twoColumn = (members.size() >= 16);
} else if (members.first()->isProperty()) {
- twoColumn = (members.count() >= 5);
+ twoColumn = (members.size() >= 5);
alignNames = false;
}
if (alignNames) {
- out() << "<div class=\"table\"><table class=\"alignedsummary\">\n";
+ out() << "<div class=\"table\"><table class=\"alignedsummary\" translate=\"no\">\n";
} else {
if (twoColumn)
- out() << "<div class=\"table\"><table class=\"propsummary\">\n"
+ out() << "<div class=\"table\"><table class=\"propsummary\" translate=\"no\">\n"
<< "<tr><td class=\"topAlign\">";
out() << "<ul>\n";
}
@@ -2982,18 +3103,12 @@ void HtmlGenerator::generateSectionList(const Section &section, const Node *rela
if (alignNames) {
out() << "<tr><td class=\"memItemLeft topAlign rightAlign\"> ";
} else {
- if (twoColumn && i == (members.count() + 1) / 2)
+ if (twoColumn && i == (members.size() + 1) / 2)
out() << "</ul></td><td class=\"topAlign\"><ul>\n";
- out() << "<li class=\"fn\">";
+ out() << "<li class=\"fn\" translate=\"no\">";
}
- QString prefix;
- const QStringList &keys = section.keys(status);
- if (!keys.isEmpty()) {
- prefix = keys.at(i).mid(1);
- prefix = prefix.left(keys.at(i).indexOf("::") + 1);
- }
- generateSynopsis(member, relative, marker, section.style(), alignNames, &prefix);
+ generateSynopsis(member, relative, marker, section.style(), alignNames);
if (member->isFunction()) {
const auto *fn = static_cast<const FunctionNode *>(member);
if (fn->isPrivateSignal()) {
@@ -3027,7 +3142,7 @@ void HtmlGenerator::generateSectionList(const Section &section, const Node *rela
}
}
- if (status != Section::Obsolete && section.style() == Section::Summary
+ if (!useObsoleteMembers && section.style() == Section::Summary
&& !section.inheritedMembers().isEmpty()) {
out() << "<ul>\n";
generateSectionInheritedList(section, relative);
@@ -3037,9 +3152,9 @@ void HtmlGenerator::generateSectionList(const Section &section, const Node *rela
void HtmlGenerator::generateSectionInheritedList(const Section &section, const Node *relative)
{
- const QList<QPair<Aggregate *, int>> &inheritedMembers = section.inheritedMembers();
+ const QList<std::pair<Aggregate *, int>> &inheritedMembers = section.inheritedMembers();
for (const auto &member : inheritedMembers) {
- out() << "<li class=\"fn\">";
+ out() << "<li class=\"fn\" translate=\"no\">";
out() << member.second << ' ';
if (member.second == 1) {
out() << section.singular();
@@ -3053,33 +3168,23 @@ void HtmlGenerator::generateSectionInheritedList(const Section &section, const N
}
void HtmlGenerator::generateSynopsis(const Node *node, const Node *relative, CodeMarker *marker,
- Section::Style style, bool alignNames, const QString *prefix)
+ Section::Style style, bool alignNames)
{
QString marked = marker->markedUpSynopsis(node, relative, style);
-
- if (prefix)
- marked.prepend(*prefix);
- QRegularExpression templateTag("(<[^@>]*>)");
- auto match = templateTag.match(marked);
- if (match.hasMatch()) {
- QString contents = protectEnc(match.captured(1));
- marked.replace(match.capturedStart(1), match.capturedLength(1), contents);
- }
-
- marked.replace(QRegularExpression("<@param>([a-z]+)_([1-9n])</@param>"), "<i>\\1<sub>\\2</sub></i>");
- marked.replace("<@param>", "<i>");
- marked.replace("</@param>", "</i>");
+ marked.replace("@param>", "i>");
if (style == Section::Summary) {
- marked.remove("<@name>"); // was "<b>"
- marked.remove("</@name>"); // was "</b>"
+ marked.remove("<@name>");
+ marked.remove("</@name>");
}
if (style == Section::AllMembers) {
- QRegularExpression extraRegExp("<@extra>.*</@extra>", QRegularExpression::InvertedGreedinessOption);
+ static const QRegularExpression extraRegExp("<@extra>.*</@extra>",
+ QRegularExpression::InvertedGreedinessOption);
marked.remove(extraRegExp);
} else {
- marked.replace("<@extra>", "<code>");
+ marked.replace("<@extra>", "<code class=\"%1 extra\" translate=\"no\">"_L1
+ .arg(style == Section::Summary ? "summary"_L1 : "details"_L1));
marked.replace("</@extra>", "</code>");
}
@@ -3134,7 +3239,7 @@ QString HtmlGenerator::highlightedCode(const QString &markedCode, const Node *re
par1 = QStringView();
const Node *n = m_qdb->findTypeNode(arg.toString(), relative, genus);
html += QLatin1String("<span class=\"type\">");
- if (n && (n->isQmlBasicType() || n->isJsBasicType())) {
+ if (n && (n->isQmlBasicType())) {
if (relative && (relative->genus() == n->genus() || genus == n->genus()))
addLink(linkForNode(n, relative), arg, &html);
else
@@ -3197,7 +3302,7 @@ QString HtmlGenerator::highlightedCode(const QString &markedCode, const Node *re
bool handled = false;
for (int k = 0; k != nTags; ++k) {
const QLatin1String &tag = spanTags[2 * k];
- if (i + tag.size() <= src.length() && tag == QStringView(src).mid(i, tag.size())) {
+ if (i + tag.size() <= src.size() && tag == QStringView(src).mid(i, tag.size())) {
html += spanTags[2 * k + 1];
i += tag.size();
handled = true;
@@ -3216,7 +3321,7 @@ QString HtmlGenerator::highlightedCode(const QString &markedCode, const Node *re
bool handled = false;
for (int k = 0; k != nTags; ++k) {
const QLatin1String &tag = spanTags[2 * k];
- if (i + tag.size() <= src.length() && tag == QStringView(src).mid(i, tag.size())) {
+ if (i + tag.size() <= src.size() && tag == QStringView(src).mid(i, tag.size())) {
html += QLatin1String("</span>");
i += tag.size();
handled = true;
@@ -3238,24 +3343,22 @@ QString HtmlGenerator::highlightedCode(const QString &markedCode, const Node *re
return html;
}
-void HtmlGenerator::generateLink(const Atom *atom, CodeMarker *marker)
+void HtmlGenerator::generateLink(const Atom *atom)
{
- auto match = m_funcLeftParen.match(atom->string());
- if (match.hasMatch() && marker->recognizeLanguage("Cpp")) {
- // hack for C++: move () outside of link
- qsizetype k = match.capturedStart(1);
- out() << protectEnc(atom->string().left(k));
- if (m_link.isEmpty()) {
- if (showBrokenLinks)
- out() << "</i>";
- } else {
- out() << "</a>";
+ Q_ASSERT(m_inLink);
+
+ if (m_linkNode && m_linkNode->isFunction()) {
+ auto match = XmlGenerator::m_funcLeftParen.match(atom->string());
+ if (match.hasMatch()) {
+ // C++: move () outside of link
+ qsizetype leftParenLoc = match.capturedStart(1);
+ out() << protectEnc(atom->string().left(leftParenLoc));
+ endLink();
+ out() << protectEnc(atom->string().mid(leftParenLoc));
+ return;
}
- m_inLink = false;
- out() << protectEnc(atom->string().mid(k));
- } else {
- out() << protectEnc(atom->string());
}
+ out() << protectEnc(atom->string());
}
QString HtmlGenerator::protectEnc(const QString &string)
@@ -3273,7 +3376,7 @@ QString HtmlGenerator::protect(const QString &string)
html += (x);
QString html;
- qsizetype n = string.length();
+ qsizetype n = string.size();
for (int i = 0; i < n; ++i) {
QChar ch = string.at(i);
@@ -3284,14 +3387,12 @@ QString HtmlGenerator::protect(const QString &string)
APPEND("&lt;");
} else if (ch == QLatin1Char('>')) {
APPEND("&gt;");
+ } else if (ch == QChar(8211)) {
+ APPEND("&ndash;");
+ } else if (ch == QChar(8212)) {
+ APPEND("&mdash;");
} else if (ch == QLatin1Char('"')) {
APPEND("&quot;");
- } else if ((ch == QLatin1Char('*') && i + 1 < n && string.at(i) == QLatin1Char('/'))
- || (ch == QLatin1Char('.') && i > 2 && string.at(i - 2) == QLatin1Char('.'))) {
- // we escape '*/' and the last dot in 'e.g.' and 'i.e.' for the Javadoc generator
- APPEND("&#x");
- html += QString::number(ch.unicode(), 16);
- html += QLatin1Char(';');
} else {
if (!html.isEmpty())
html += ch;
@@ -3350,7 +3451,7 @@ void HtmlGenerator::generateDetailedMember(const Node *node, const PageNode *rel
out() << "<div class=\"fngroup\">\n";
for (const auto *sharedNode : collective) {
nodeRef = refForNode(sharedNode);
- out() << R"(<h3 class="fn fngroupitem" id=")" << nodeRef << "\">";
+ out() << R"(<h3 class="fn fngroupitem" translate="no" id=")" << nodeRef << "\">";
generateSynopsis(sharedNode, relative, marker, Section::Details);
out() << "</h3>";
}
@@ -3366,7 +3467,7 @@ void HtmlGenerator::generateDetailedMember(const Node *node, const PageNode *rel
generateSynopsis(etn->flagsType(), relative, marker, Section::Details);
out() << "</h3>\n";
} else {
- out() << R"(<h3 class="fn" id=")" << nodeRef << "\">";
+ out() << R"(<h3 class="fn" translate="no" id=")" << nodeRef << "\">";
generateSynopsis(node, relative, marker, Section::Details);
out() << "</h3>" << '\n';
}
@@ -3375,13 +3476,15 @@ void HtmlGenerator::generateDetailedMember(const Node *node, const PageNode *rel
generateStatus(node, marker);
generateBody(node, marker);
generateOverloadedSignal(node, marker);
+ generateComparisonCategory(node, marker);
generateThreadSafeness(node, marker);
generateSince(node, marker);
+ generateNoexceptNote(node, marker);
if (node->isProperty()) {
const auto property = static_cast<const PropertyNode *>(node);
- if (property->propertyType() == PropertyNode::Standard) {
- Section section(Section::Accessors, Section::Active);
+ if (property->propertyType() == PropertyNode::PropertyType::StandardProperty) {
+ Section section("", "", "", "", Section::Accessors);
section.appendMembers(property->getters().toVector());
section.appendMembers(property->setters().toVector());
@@ -3392,7 +3495,7 @@ void HtmlGenerator::generateDetailedMember(const Node *node, const PageNode *rel
generateSectionList(section, node, marker);
}
- Section notifiers(Section::Accessors, Section::Active);
+ Section notifiers("", "", "", "", Section::Accessors);
notifiers.appendMembers(property->notifiers().toVector());
if (!notifiers.members().isEmpty()) {
@@ -3422,39 +3525,42 @@ void HtmlGenerator::generateDetailedMember(const Node *node, const PageNode *rel
void HtmlGenerator::beginLink(const QString &link)
{
m_link = link;
- if (m_link.isEmpty()) {
- if (showBrokenLinks)
- out() << "<i>";
- }
- out() << "<a href=\"" << m_link << "\">";
m_inLink = true;
+ m_linkNode = nullptr;
+
+ if (!m_link.isEmpty())
+ out() << "<a href=\"" << m_link << "\" translate=\"no\">";
}
void HtmlGenerator::beginLink(const QString &link, const Node *node, const Node *relative)
{
m_link = link;
- if (m_link.isEmpty()) {
- if (showBrokenLinks)
- out() << "<i>";
- } else if (node == nullptr || (relative != nullptr && node->status() == relative->status()))
- out() << "<a href=\"" << m_link << "\">";
+ m_inLink = true;
+ m_linkNode = node;
+ if (m_link.isEmpty())
+ return;
+
+ const QString &translate_attr =
+ (node && node->genus() & Node::API) ? " translate=\"no\""_L1 : ""_L1;
+
+ if (node == nullptr || (relative != nullptr && node->status() == relative->status()))
+ out() << "<a href=\"" << m_link << "\"%1>"_L1.arg(translate_attr);
else if (node->isDeprecated())
- out() << "<a href=\"" << m_link << "\" class=\"obsolete\">";
+ out() << "<a href=\"" << m_link << "\" class=\"obsolete\"%1>"_L1.arg(translate_attr);
else
- out() << "<a href=\"" << m_link << "\">";
- m_inLink = true;
+ out() << "<a href=\"" << m_link << "\"%1>"_L1.arg(translate_attr);
}
void HtmlGenerator::endLink()
{
- if (m_inLink) {
- if (m_link.isEmpty() && showBrokenLinks) {
- out() << "</i>";
- } else {
- out() << "</a>";
- }
- }
+ if (!m_inLink)
+ return;
+
m_inLink = false;
+ m_linkNode = nullptr;
+
+ if (!m_link.isEmpty())
+ out() << "</a>";
}
/*!
@@ -3467,7 +3573,7 @@ void HtmlGenerator::generateQmlSummary(const NodeVector &members, const Node *re
if (!members.isEmpty()) {
out() << "<ul>\n";
for (const auto &member : members) {
- out() << "<li class=\"fn\">";
+ out() << "<li class=\"fn\" translate=\"no\">";
generateQmlItem(member, relative, marker, true);
if (member->isPropertyGroup()) {
const auto *scn = static_cast<const SharedCommentNode *>(member);
@@ -3475,8 +3581,8 @@ void HtmlGenerator::generateQmlSummary(const NodeVector &members, const Node *re
out() << "<ul>\n";
const QList<Node *> &sharedNodes = scn->collective();
for (const auto &node : sharedNodes) {
- if (node->isQmlProperty() || node->isJsProperty()) {
- out() << "<li class=\"fn\">";
+ if (node->isQmlProperty()) {
+ out() << "<li class=\"fn\" translate=\"no\">";
generateQmlItem(node, relative, marker, true);
out() << "</li>\n";
}
@@ -3499,7 +3605,7 @@ void HtmlGenerator::generateDetailedQmlMember(Node *node, const Aggregate *relat
{
generateExtractionMark(node, MemberMark);
- QString qmlItemHeader("<div class=\"qmlproto\">\n"
+ QString qmlItemHeader("<div class=\"qmlproto\" translate=\"no\">\n"
"<div class=\"table\"><table class=\"qmlname\">\n");
QString qmlItemStart("<tr valign=\"top\" class=\"odd\" id=\"%1\">\n"
@@ -3508,38 +3614,13 @@ void HtmlGenerator::generateDetailedQmlMember(Node *node, const Aggregate *relat
QString qmlItemFooter("</table></div></div>\n");
- std::function<void(QmlPropertyNode *)> generateQmlProperty = [&](QmlPropertyNode *n) {
+ auto generateQmlProperty = [&](Node *n) {
out() << qmlItemStart.arg(refForNode(n), "tblQmlPropNode");
-
- if (!n->isReadOnlySet() && n->declarativeCppNode())
- n->markReadOnly(!n->isWritable());
-
- QStringList extra;
- if (n->isDefault())
- extra << "default";
- else if (n->isReadOnly())
- extra << "read-only";
- else if (n->isRequired())
- extra << "required";
- else if (!n->defaultValue().isEmpty()) {
- extra << "default: " + n->defaultValue();
- }
-
- if (!n->since().isEmpty()) {
- if (!extra.isEmpty())
- extra.last().append(',');
- extra << "since " + n->since();
- }
-
- if (!extra.isEmpty())
- out() << QString("<span class=\"qmlextra\">[%1] </span>")
- .arg(extra.join(QLatin1Char(' ')));
-
generateQmlItem(n, relative, marker, false);
out() << qmlItemEnd;
};
- std::function<void(Node *)> generateQmlMethod = [&](Node *n) {
+ auto generateQmlMethod = [&](Node *n) {
out() << qmlItemStart.arg(refForNode(n), "tblQmlFuncNode");
generateSynopsis(n, relative, marker, Section::Details, false);
out() << qmlItemEnd;
@@ -3558,13 +3639,13 @@ void HtmlGenerator::generateDetailedQmlMember(Node *node, const Aggregate *relat
}
const QList<Node *> sharedNodes = scn->collective();
for (const auto &sharedNode : sharedNodes) {
- if (sharedNode->isQmlProperty() || sharedNode->isJsProperty())
- generateQmlProperty(static_cast<QmlPropertyNode *>(sharedNode));
+ if (sharedNode->isQmlProperty())
+ generateQmlProperty(sharedNode);
}
out() << qmlItemFooter;
- } else if (node->isQmlProperty() || node->isJsProperty()) {
+ } else if (node->isQmlProperty()) {
out() << qmlItemHeader;
- generateQmlProperty(static_cast<QmlPropertyNode *>(node));
+ generateQmlProperty(node);
out() << qmlItemFooter;
} else if (node->isSharedCommentNode()) {
const auto *scn = reinterpret_cast<const SharedCommentNode *>(node);
@@ -3573,10 +3654,11 @@ void HtmlGenerator::generateDetailedQmlMember(Node *node, const Aggregate *relat
out() << "<div class=\"fngroup\">\n";
out() << qmlItemHeader;
for (const auto &sharedNode : sharedNodes) {
- if (sharedNode->isFunction(Node::QML) || sharedNode->isFunction(Node::JS))
+ // Generate the node only if it's a QML method
+ if (sharedNode->isFunction(Node::QML))
generateQmlMethod(sharedNode);
- else if (sharedNode->isQmlProperty() || sharedNode->isJsProperty())
- generateQmlProperty(static_cast<QmlPropertyNode *>(sharedNode));
+ else if (sharedNode->isQmlProperty())
+ generateQmlProperty(sharedNode);
}
out() << qmlItemFooter;
if (sharedNodes.size() > 1)
diff --git a/src/qdoc/htmlgenerator.h b/src/qdoc/qdoc/src/qdoc/htmlgenerator.h
index 2b86de692..62fe7df38 100644
--- a/src/qdoc/htmlgenerator.h
+++ b/src/qdoc/qdoc/src/qdoc/htmlgenerator.h
@@ -1,40 +1,15 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the tools applications of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#ifndef HTMLGENERATOR_H
#define HTMLGENERATOR_H
#include "codemarker.h"
#include "xmlgenerator.h"
+#include "filesystem/fileresolver.h"
#include <QtCore/qhash.h>
#include <QtCore/qregularexpression.h>
-#include <QtCore/qxmlstream.h>
QT_BEGIN_NAMESPACE
@@ -47,7 +22,7 @@ class ManifestWriter;
class HtmlGenerator : public XmlGenerator
{
public:
- HtmlGenerator() = default;
+ HtmlGenerator(FileResolver& file_resolver);
~HtmlGenerator() override;
void initializeGenerator() override;
@@ -59,12 +34,11 @@ public:
static QString protect(const QString &string);
protected:
- void generateExampleFilePage(const Node *en, const QString &file, CodeMarker *marker) override;
+ void generateExampleFilePage(const Node *en, ResolvedFile resolved_file, CodeMarker *marker) override;
qsizetype generateAtom(const Atom *atom, const Node *relative, CodeMarker *marker) override;
void generateCppReferencePage(Aggregate *aggregate, CodeMarker *marker) override;
void generateProxyPage(Aggregate *aggregate, CodeMarker *marker) override;
void generateQmlTypePage(QmlTypeNode *qcn, CodeMarker *marker) override;
- void generateQmlBasicTypePage(QmlBasicTypeNode *qbtn, CodeMarker *marker) override;
void generatePageNode(PageNode *pn, CodeMarker *marker) override;
void generateCollectionNode(CollectionNode *cn, CodeMarker *marker) override;
void generateGenericCollectionPage(CollectionNode *cn, CodeMarker *marker) override;
@@ -103,15 +77,14 @@ private:
bool generateGroupList(CollectionNode *cn);
void generateList(const Node *relative, CodeMarker *marker, const QString &selector);
void generateSectionList(const Section &section, const Node *relative, CodeMarker *marker,
- Section::Status = Section::Active);
+ bool useObsoloteMembers = false);
void generateQmlSummary(const NodeVector &members, const Node *relative, CodeMarker *marker);
void generateQmlItem(const Node *node, const Node *relative, CodeMarker *marker, bool summary);
void generateDetailedQmlMember(Node *node, const Aggregate *relative, CodeMarker *marker);
void generateSection(const NodeVector &nv, const Node *relative, CodeMarker *marker);
void generateSynopsis(const Node *node, const Node *relative, CodeMarker *marker,
- Section::Style style, bool alignNames = false,
- const QString *prefix = nullptr);
+ Section::Style style, bool alignNames = false);
void generateSectionInheritedList(const Section &section, const Node *relative);
QString highlightedCode(const QString &markedCode, const Node *relative,
bool alignNames = false, Node::Genus genus = Node::DontCare);
@@ -119,7 +92,7 @@ private:
void generateFullName(const Node *apparentNode, const Node *relative,
const Node *actualNode = nullptr);
void generateDetailedMember(const Node *node, const PageNode *relative, CodeMarker *marker);
- void generateLink(const Atom *atom, CodeMarker *marker);
+ void generateLink(const Atom *atom);
QString fileBase(const Node *node) const override;
QString fileName(const Node *node);
@@ -128,17 +101,20 @@ private:
void beginLink(const QString &link, const Node *node, const Node *relative);
void endLink();
void generateExtractionMark(const Node *node, ExtractionMarkType markType);
- void addIncludeFilesToMap(const Aggregate *aggregate, CodeMarker *marker,
- QMap<QString, Text> &requisites, Text *text,
+ void addIncludeFileToMap(const Aggregate *aggregate, CodeMarker *marker,
+ QMap<QString, Text> &requisites, Text& text,
const QString &headerText);
void addSinceToMap(const Aggregate *aggregate, QMap<QString, Text> &requisites, Text *text,
const QString &sinceText) const;
+ void addStatusToMap(const Aggregate *aggregate, QMap<QString, Text> &requisites, Text &text,
+ const QString &statusText) const;
void addCMakeInfoToMap(const Aggregate *aggregate, QMap<QString, Text> &requisites, Text *text,
const QString &CMakeInfo) const;
void addQtVariableToMap(const Aggregate *aggregate, QMap<QString, Text> &requisites, Text *text,
const QString &qtVariableText) const;
- void addInstantiatedByToMap(QMap<QString, Text> &requisites, Text *text,
- const QString &instantiatedByText, ClassNode *classe) const;
+ void addQmlNativeTypesToMap(QMap<QString, Text> &requisites, Text *text,
+ const QString &nativeTypeText,
+ ClassNode *classe) const;
void addInheritsToMap(QMap<QString, Text> &requisites, Text *text, const QString &inheritsText,
ClassNode *classe);
void addInheritedByToMap(QMap<QString, Text> &requisites, Text *text,
@@ -149,6 +125,8 @@ private:
inline void openUnorderedList();
inline void closeUnorderedList();
+ QString groupReferenceText(PageNode* node);
+
static bool s_inUnorderedList;
int m_codeIndent { 0 };
@@ -156,7 +134,6 @@ private:
QString m_codeSuffix {};
HelpProjectWriter *m_helpProjectWriter { nullptr };
ManifestWriter *m_manifestWriter { nullptr };
- QRegularExpression m_funcLeftParen { "\\S(\\()" };
QString m_headerScripts {};
QString m_headerStyles {};
QString m_endHeader {};
@@ -179,6 +156,7 @@ private:
QString m_cppclassestitle {};
QString m_qmltypespage {};
QString m_qmltypestitle {};
+ QString m_trademarkspage {};
QString m_buildversion {};
QString m_qflagsHref {};
int tocDepth {};
diff --git a/src/qdoc/qdoc/src/qdoc/importrec.h b/src/qdoc/qdoc/src/qdoc/importrec.h
new file mode 100644
index 000000000..84f8f35ac
--- /dev/null
+++ b/src/qdoc/qdoc/src/qdoc/importrec.h
@@ -0,0 +1,33 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#ifndef IMPORTREC_H
+#define IMPORTREC_H
+
+#include <QtCore/qglobal.h>
+#include <QtCore/qstring.h>
+
+#include <utility>
+
+QT_BEGIN_NAMESPACE
+
+struct ImportRec
+{
+ QString m_moduleName {};
+ QString m_majorMinorVersion {};
+ QString m_importUri {}; // subdirectory of module directory
+
+ ImportRec(QString name, QString version, QString importUri)
+ : m_moduleName(std::move(name)),
+ m_majorMinorVersion(std::move(version)),
+ m_importUri(std::move(importUri))
+ {
+ }
+ QString &name() { return m_moduleName; }
+ QString &version() { return m_majorMinorVersion; }
+ [[nodiscard]] bool isEmpty() const { return m_moduleName.isEmpty(); }
+};
+
+QT_END_NAMESPACE
+
+#endif // IMPORTREC_H
diff --git a/src/qdoc/location.cpp b/src/qdoc/qdoc/src/qdoc/location.cpp
index cda9535ba..714e232d7 100644
--- a/src/qdoc/location.cpp
+++ b/src/qdoc/qdoc/src/qdoc/location.cpp
@@ -1,30 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the tools applications of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "location.h"
@@ -92,6 +67,9 @@ Location::Location(const Location &other)
*/
Location &Location::operator=(const Location &other)
{
+ if (this == &other)
+ return *this;
+
QStack<StackEntry> *oldStk = m_stk;
m_stkBottom = other.m_stkBottom;
@@ -308,21 +286,22 @@ void Location::report(const QString &message, const QString &details) const
void Location::initialize()
{
Config &config = Config::instance();
- s_tabSize = config.getInt(CONFIG_TABSIZE);
+ s_tabSize = config.get(CONFIG_TABSIZE).asInt();
s_programName = config.programName();
- s_project = config.getString(CONFIG_PROJECT);
+ s_project = config.get(CONFIG_PROJECT).asString();
if (!config.singleExec())
s_warningCount = 0;
if (qEnvironmentVariableIsSet("QDOC_ENABLE_WARNINGLIMIT")
- || config.getBool(CONFIG_WARNINGLIMIT + Config::dot + "enabled"))
- s_warningLimit = config.getInt(CONFIG_WARNINGLIMIT);
+ || config.get(CONFIG_WARNINGLIMIT + Config::dot + "enabled").asBool())
+ s_warningLimit = config.get(CONFIG_WARNINGLIMIT).asInt();
QRegularExpression regExp = config.getRegExp(CONFIG_SPURIOUS);
if (regExp.isValid()) {
s_spuriousRegExp = new QRegularExpression(regExp);
} else {
- config.lastLocation().warning(
- QStringLiteral("Invalid regular expression '%1'").arg(regExp.pattern()));
+ config.get(CONFIG_SPURIOUS).location()
+ .warning(QStringLiteral("Invalid regular expression '%1'")
+ .arg(regExp.pattern()));
}
}
@@ -367,7 +346,7 @@ void Location::emitMessage(MessageType type, const QString &message, const QStri
if (type == Warning && s_spuriousRegExp != nullptr) {
auto match = s_spuriousRegExp->match(message, 0, QRegularExpression::NormalMatch,
QRegularExpression::AnchorAtOffsetMatchOption);
- if (match.hasMatch() && match.capturedLength() == message.length())
+ if (match.hasMatch() && match.capturedLength() == message.size())
return;
}
diff --git a/src/qdoc/location.h b/src/qdoc/qdoc/src/qdoc/location.h
index bb1b6f01f..8427bc917 100644
--- a/src/qdoc/location.h
+++ b/src/qdoc/qdoc/src/qdoc/location.h
@@ -1,30 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the tools applications of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#ifndef LOCATION_H
#define LOCATION_H
diff --git a/src/qdoc/qdoc/src/qdoc/macro.h b/src/qdoc/qdoc/src/qdoc/macro.h
new file mode 100644
index 000000000..11e77fa29
--- /dev/null
+++ b/src/qdoc/qdoc/src/qdoc/macro.h
@@ -0,0 +1,27 @@
+// Copyright (C) 2020 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+#ifndef MACRO_H
+#define MACRO_H
+
+#include "location.h"
+
+#include <QtCore/qmap.h>
+#include <QtCore/qstring.h>
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ * Simple structure used by the Doc and DocParser classes.
+ */
+struct Macro
+{
+public:
+ QString m_defaultDef {};
+ Location m_defaultDefLocation {};
+ QMap<QString, QString> m_otherDefs {};
+ int numParams {};
+};
+
+QT_END_NAMESPACE
+
+#endif // MACRO_H
diff --git a/src/qdoc/main.cpp b/src/qdoc/qdoc/src/qdoc/main.cpp
index 1f7a9810b..5e48a6a8a 100644
--- a/src/qdoc/main.cpp
+++ b/src/qdoc/qdoc/src/qdoc/main.cpp
@@ -1,30 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the tools applications of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "clangcodeparser.h"
#include "codemarker.h"
@@ -34,23 +9,28 @@
#include "doc.h"
#include "docbookgenerator.h"
#include "htmlgenerator.h"
-#include "jscodemarker.h"
#include "location.h"
#include "puredocparser.h"
#include "qdocdatabase.h"
#include "qmlcodemarker.h"
#include "qmlcodeparser.h"
+#include "sourcefileparser.h"
#include "utilities.h"
-#include "qtranslator.h"
#include "tokenizer.h"
#include "tree.h"
#include "webxmlgenerator.h"
+#include "filesystem/fileresolver.h"
+#include "boundaries/filesystem/directorypath.h"
+
+#include <QtCore/qcompilerdetection.h>
#include <QtCore/qdatetime.h>
#include <QtCore/qdebug.h>
#include <QtCore/qglobal.h>
#include <QtCore/qhashfunctions.h>
+#include <set>
+
#ifndef QT_BOOTSTRAPPED
# include <QtCore/qcoreapplication.h>
#endif
@@ -60,17 +40,74 @@
QT_BEGIN_NAMESPACE
+using namespace Qt::StringLiterals;
+
bool creationTimeBefore(const QFileInfo &fi1, const QFileInfo &fi2)
{
return fi1.lastModified() < fi2.lastModified();
}
-#ifndef QT_NO_TRANSLATION
-typedef QPair<QString, QTranslator *> Translator;
-static QList<Translator> translators;
-#endif
+/*!
+ \internal
+ Inspects each file path in \a sources. File paths with a known
+ source file type are parsed to extract user-provided
+ documentation and information about source code level elements.
+
+ \note Unknown source file types are silently ignored.
+
+ The validity or availability of the file paths may or may not cause QDoc
+ to generate warnings; this depends on the implementation of
+ parseSourceFile() for the relevant parser.
+
+ \sa CodeParser::parserForSourceFile, CodeParser::sourceFileNameFilter
+*/
+static void parseSourceFiles(
+ std::vector<QString>&& sources,
+ SourceFileParser& source_file_parser,
+ CppCodeParser& cpp_code_parser
+) {
+ ParserErrorHandler error_handler{};
+ std::stable_sort(sources.begin(), sources.end());
+
+ sources.erase (
+ std::unique(sources.begin(), sources.end()),
+ sources.end()
+ );
+
+ auto qml_sources =
+ std::stable_partition(sources.begin(), sources.end(), [](const QString& source){
+ return CodeParser::parserForSourceFile(source) == CodeParser::parserForLanguage("QML");
+ });
+
+
+ std::for_each(qml_sources, sources.end(),
+ [&source_file_parser, &cpp_code_parser, &error_handler](const QString& source){
+ qCDebug(lcQdoc, "Parsing %s", qPrintable(source));
+
+ auto [untied_documentation, tied_documentation] = source_file_parser(tag_source_file(source));
+ std::vector<FnMatchError> errors{};
+
+ for (auto untied : untied_documentation) {
+ auto result = cpp_code_parser.processTopicArgs(untied);
+ tied_documentation.insert(tied_documentation.end(), result.first.begin(), result.first.end());
+ };
+
+ cpp_code_parser.processMetaCommands(tied_documentation);
+
+ // Process errors that occurred during parsing
+ for (const auto &e : errors)
+ error_handler(e);
+ });
+
+ std::for_each(sources.begin(), qml_sources, [&cpp_code_parser](const QString& source){
+ auto *codeParser = CodeParser::parserForSourceFile(source);
+ if (!codeParser) return;
-static ClangCodeParser *clangParser_ = nullptr;
+ qCDebug(lcQdoc, "Parsing %s", qPrintable(source));
+ codeParser->parseSourceFile(Config::instance().location(), source, cpp_code_parser);
+ });
+
+}
/*!
Read some XML indexes containing definitions from other
@@ -86,8 +123,8 @@ static void loadIndexFiles(const QSet<QString> &formats)
Config &config = Config::instance();
QDocDatabase *qdb = QDocDatabase::qdocDB();
QStringList indexFiles;
- const QStringList configIndexes = config.getStringList(CONFIG_INDEXES);
- bool warn = !config.getBool(CONFIG_NOLINKERRORS);
+ const QStringList configIndexes{config.get(CONFIG_INDEXES).asStringList()};
+ bool warn = !config.get(CONFIG_NOLINKERRORS).asBool();
for (const auto &index : configIndexes) {
QFileInfo fi(index);
@@ -97,18 +134,19 @@ static void loadIndexFiles(const QSet<QString> &formats)
Location().warning(QString("Index file not found: %1").arg(index));
}
- config.dependModules() += config.getStringList(CONFIG_DEPENDS);
+ config.dependModules() += config.get(CONFIG_DEPENDS).asStringList();
config.dependModules().removeDuplicates();
bool useNoSubDirs = false;
QSet<QString> subDirs;
+ // Add format-specific output subdirectories to the set of
+ // subdirectories where we look for index files
for (const auto &format : formats) {
- if (config.getBool(format + Config::dot + "nosubdirs")) {
+ if (config.get(format + Config::dot + "nosubdirs").asBool()) {
useNoSubDirs = true;
- QString singleOutputSubdir = config.getString(format + Config::dot + "outputsubdir");
- if (singleOutputSubdir.isEmpty())
- singleOutputSubdir = "html";
- subDirs << singleOutputSubdir;
+ const auto singleOutputSubdir{QDir(config.getOutputDir(format)).dirName()};
+ if (!singleOutputSubdir.isEmpty())
+ subDirs << singleOutputSubdir;
}
}
@@ -153,11 +191,13 @@ static void loadIndexFiles(const QSet<QString> &formats)
}
}
// Remove self-dependencies and possible duplicates
- config.dependModules().removeAll(config.getString(CONFIG_PROJECT).toLower());
+ QString project{config.get(CONFIG_PROJECT).asString()};
+ config.dependModules().removeAll(project.toLower());
config.dependModules().removeDuplicates();
- qCCritical(lcQdoc) << "qdocconf file has depends = *; loading all "
- << config.dependModules().count()
- << " index files found";
+ qCCritical(lcQdoc) << "Configuration file for"
+ << project << "has depends = *; loading all"
+ << config.dependModules().size()
+ << "index files found";
}
for (const auto &module : config.dependModules()) {
QList<QFileInfo> foundIndices;
@@ -166,7 +206,7 @@ static void loadIndexFiles(const QSet<QString> &formats)
subDirs << module;
for (const auto &dir : config.indexDirs()) {
- for (const auto &subDir : subDirs) {
+ for (const auto &subDir : std::as_const(subDirs)) {
QString fileToLookFor = dir + QLatin1Char('/') + subDir + QLatin1Char('/')
+ module + ".index";
if (QFile::exists(fileToLookFor)) {
@@ -189,7 +229,7 @@ static void loadIndexFiles(const QSet<QString> &formats)
*/
QStringList indexPaths;
indexPaths.reserve(foundIndices.size());
- for (const auto &found : qAsConst(foundIndices))
+ for (const auto &found : std::as_const(foundIndices))
indexPaths << found.absoluteFilePath();
if (warn) {
Location().warning(
@@ -210,7 +250,7 @@ static void loadIndexFiles(const QSet<QString> &formats)
} else if (!asteriskUsed && warn) {
Location().warning(
QString(R"("%1" Cannot locate index file for dependency "%2")")
- .arg(config.getString(CONFIG_PROJECT), module));
+ .arg(config.get(CONFIG_PROJECT).asString(), module));
}
}
} else if (warn) {
@@ -227,12 +267,16 @@ static void loadIndexFiles(const QSet<QString> &formats)
Prints to stderr the name of the project that QDoc is running for,
in which mode and which phase.
- If QDoc is running in debug mode, also logs the command line arguments.
+ If QDoc is not running in debug mode or --log-progress command line
+ option is not set, do nothing.
*/
-void logStartEndMessage(const QLatin1String &startStop, const Config &config)
+void logStartEndMessage(const QLatin1String &startStop, Config &config)
{
+ if (!config.get(CONFIG_LOGPROGRESS).asBool())
+ return;
+
const QString runName = " qdoc for "
- + config.getString(CONFIG_PROJECT)
+ + config.get(CONFIG_PROJECT).asString()
+ QLatin1String(" in ")
+ QLatin1String(config.singleExec() ? "single" : "dual")
+ QLatin1String(" process mode: ")
@@ -264,7 +308,7 @@ static void processQdocconfFile(const QString &fileName)
*/
Location::initialize();
config.load(fileName);
- QString project = config.getString(CONFIG_PROJECT);
+ QString project{config.get(CONFIG_PROJECT).asString()};
if (project.isEmpty()) {
qCCritical(lcQdoc) << QLatin1String("qdoc can't run; no project set in qdocconf file");
exit(1);
@@ -282,57 +326,109 @@ static void processQdocconfFile(const QString &fileName)
qCDebug(lcQdoc).noquote() << "Arguments:" << QCoreApplication::arguments();
}
- /*
- Initialize all the classes and data structures with the
- qdoc configuration. This is safe to do for each qdocconf
- file processed, because all the data structures created
- are either cleared after they have been used, or they
- are cleared in the terminate() functions below.
- */
- Location::initialize();
- Tokenizer::initialize();
- CodeMarker::initialize();
- CodeParser::initialize();
- Generator::initialize();
- Doc::initialize();
+ // <<TODO: [cleanup-temporary-kludges]
+ // The underlying validation should be performed at the
+ // configuration level during parsing.
+ // This cannot be done straightforwardly with how the Config class
+ // is implemented.
+ // When the Config class will be deprived of logic and
+ // restructured, the compiler will notify us of this kludge, but
+ // remember to reevaluate the code itself considering the new
+ // data-flow and the possibility for optimizations as this is not
+ // done for temporary code. Indeed some of the code is visibly wasteful.
+ // Similarly, ensure that the loose definition that we use here is
+ // not preserved.
-#ifndef QT_NO_TRANSLATION
- /*
- Load the language translators, if the configuration specifies any,
- but only if they haven't already been loaded. This works in both
- -prepare/-generate mode and -singleexec mode.
- */
- const QStringList fileNames = config.getStringList(CONFIG_TRANSLATORS);
- for (const auto &file : fileNames) {
- bool found = false;
- if (!translators.isEmpty()) {
- for (const auto &translator : translators) {
- if (translator.first == file) {
- found = true;
- break;
- }
- }
- }
- if (!found) {
- auto *translator = new QTranslator(nullptr);
- if (!translator->load(file)) {
- config.lastLocation().error(
- QCoreApplication::translate("QDoc", "Cannot load translator '%1'")
- .arg(file));
- } else {
- QCoreApplication::instance()->installTranslator(translator);
- translators.append(Translator(file, translator));
- }
+ QStringList search_directories{config.getCanonicalPathList(CONFIG_EXAMPLEDIRS)};
+ QStringList image_search_directories{config.getCanonicalPathList(CONFIG_IMAGEDIRS)};
+
+ const auto& [excludedDirs, excludedFiles] = config.getExcludedPaths();
+
+ qCDebug(lcQdoc, "Adding doc/image dirs found in exampledirs to imagedirs");
+ QSet<QString> exampleImageDirs;
+ QStringList exampleImageList = config.getExampleImageFiles(excludedDirs, excludedFiles);
+ for (const auto &image : exampleImageList) {
+ if (image.contains("doc/images")) {
+ QString t = image.left(image.lastIndexOf("doc/images") + 10);
+ if (!exampleImageDirs.contains(t))
+ exampleImageDirs.insert(t);
}
}
-#endif
- /*
- Get the source language (Cpp) from the configuration
- and the location in the configuration file where the
- source language was set.
- */
- Location langLocation = config.lastLocation();
+ // REMARK: The previous system discerned between search directories based on the kind of file that was searched for.
+ // For example, an image search was bounded to some directories
+ // that may or may not be the same as the ones where examples are
+ // searched for.
+ // The current Qt documentation does not use this feature. That
+ // is, the output of QDoc when a unified search list is used is
+ // the same as the output for that of separated lists.
+ // For this reason, we currently simplify the process, albeit this
+ // may at some point change, by joining the various lists into a
+ // single search list and a unified interface.
+ // Do note that the configuration still allows for those
+ // parameters to be user defined in a split-way as this will not
+ // be able to be changed until Config itself is looked upon.
+ // Hence, we join the various directory sources into one list for the time being.
+ // Do note that this means that the amount of searched directories for a file is now increased.
+ // This shouldn't matter as the amount of directories is expected
+ // to be generally small and the search routine complexity is
+ // linear in the amount of directories.
+ // There are some complications that may arise in very specific
+ // cases by this choice (some of which where there before under
+ // possibly different circumstances), making some files
+ // unreachable.
+ // See the remarks in FileResolver for more infomration.
+ std::copy(image_search_directories.begin(), image_search_directories.end(), std::back_inserter(search_directories));
+ std::copy(exampleImageDirs.begin(), exampleImageDirs.end(), std::back_inserter(search_directories));
+
+ std::vector<DirectoryPath> validated_search_directories{};
+ for (const QString& path : search_directories) {
+ auto maybe_validated_path{DirectoryPath::refine(path)};
+ if (!maybe_validated_path)
+ // TODO: [uncentralized-admonition]
+ qCDebug(lcQdoc).noquote() << u"%1 is not a valid path, it will be ignored when resolving a file"_s.arg(path);
+ else validated_search_directories.push_back(*maybe_validated_path);
+ }
+
+ // TODO>>
+
+ FileResolver file_resolver{std::move(validated_search_directories)};
+
+ // REMARK: The constructor for generators doesn't actually perform
+ // initialization of their content.
+ // Indeed, Generators use the general antipattern of the static
+ // initialize-terminate non-scoped mutable state that we see in
+ // many parts of QDoc.
+ // In their constructor, Generators mainly register themselves into a static list.
+ // Previously, this was done at the start of main.
+ // To be able to pass a correct FileResolver or other systems, we
+ // need to construct them after the configuration has been read
+ // and has been destructured.
+ // For this reason, their construction was moved here.
+ // This function may be called more than once for some of QDoc's
+ // call, albeit this should not actually happen in Qt's
+ // documentation.
+ // Then, constructing the generators here might provide for some
+ // unexpected behavior as new generators are appended to the list
+ // and never used, considering that the list is searched in a
+ // linearly fashion and each generator of some type T, in the
+ // current codebase, will always be found if another instance of
+ // that same type would have been found.
+ // Furthermore, those instances would be destroyed by then, such
+ // that accessing them would be erroneous.
+ // To avoid this, the static list was made to be cleared in
+ // Generator::terminate, which, in theory, will be called before
+ // the generators will be constructed again.
+ // We could have used the initialize method for this, but this
+ // would force us into a limited and more complex semantic, see an
+ // example of this in DocParser, and would restrain us further to
+ // the initialize-terminate idiom which is expect to be purged in
+ // the future.
+ HtmlGenerator htmlGenerator{file_resolver};
+ WebXMLGenerator webXMLGenerator{file_resolver};
+ DocBookGenerator docBookGenerator{file_resolver};
+
+ Generator::initialize();
/*
Initialize the qdoc database, where all the parsed source files
@@ -344,12 +440,11 @@ static void processQdocconfFile(const QString &fileName)
So it is safe to call qdocDB() any time.
*/
QDocDatabase *qdb = QDocDatabase::qdocDB();
- qdb->setVersion(config.getString(CONFIG_VERSION));
+ qdb->setVersion(config.get(CONFIG_VERSION).asString());
/*
By default, the only output format is HTML.
*/
- QSet<QString> outputFormats = config.getOutputFormats();
- Location outputFormatsLocation = config.lastLocation();
+ const QSet<QString> outputFormats = config.getOutputFormats();
qdb->clearSearchOrder();
if (!config.singleExec()) {
@@ -364,15 +459,9 @@ static void processQdocconfFile(const QString &fileName)
else
qdb->setPrimaryTree(project);
- const QString moduleHeader = config.getString(CONFIG_MODULEHEADER);
- if (!moduleHeader.isNull())
- clangParser_->setModuleHeader(moduleHeader);
- else
- clangParser_->setModuleHeader(project);
-
// Retrieve the dependencies if loadIndexFiles() was not called
if (config.dependModules().isEmpty()) {
- config.dependModules() = config.getStringList(CONFIG_DEPENDS);
+ config.dependModules() = config.get(CONFIG_DEPENDS).asStringList();
config.dependModules().removeDuplicates();
}
qdb->setSearchOrder(config.dependModules());
@@ -380,61 +469,83 @@ static void processQdocconfFile(const QString &fileName)
// Store the title of the index (landing) page
NamespaceNode *root = qdb->primaryTreeRoot();
if (root) {
- QString title = config.getString(CONFIG_NAVIGATION + Config::dot + CONFIG_LANDINGPAGE);
+ QString title{config.get(CONFIG_NAVIGATION + Config::dot + CONFIG_LANDINGPAGE).asString()};
root->tree()->setIndexTitle(
- config.getString(CONFIG_NAVIGATION + Config::dot + CONFIG_LANDINGTITLE, title));
+ config.get(CONFIG_NAVIGATION + Config::dot + CONFIG_LANDINGTITLE).asString(title));
}
- const auto &excludedDirList = config.getCanonicalPathList(CONFIG_EXCLUDEDIRS);
- QSet<QString> excludedDirs = QSet<QString>(excludedDirList.cbegin(), excludedDirList.cend());
- const auto &excludedFilesList = config.getCanonicalPathList(CONFIG_EXCLUDEFILES);
- QSet<QString> excludedFiles =
- QSet<QString>(excludedFilesList.cbegin(), excludedFilesList.cend());
- qCDebug(lcQdoc, "Adding doc/image dirs found in exampledirs to imagedirs");
- QSet<QString> exampleImageDirs;
- QStringList exampleImageList = config.getExampleImageFiles(excludedDirs, excludedFiles);
- for (const auto &image : exampleImageList) {
- if (image.contains("doc/images")) {
- QString t = image.left(image.lastIndexOf("doc/images") + 10);
- if (!exampleImageDirs.contains(t))
- exampleImageDirs.insert(t);
+ std::vector<QByteArray> include_paths{};
+ {
+ auto args = config.getCanonicalPathList(CONFIG_INCLUDEPATHS,
+ Config::IncludePaths);
+#ifdef Q_OS_MACOS
+ args.append(Utilities::getInternalIncludePaths(QStringLiteral("clang++")));
+#elif defined(Q_OS_LINUX)
+ args.append(Utilities::getInternalIncludePaths(QStringLiteral("g++")));
+#endif
+
+ for (const auto &path : std::as_const(args)) {
+ if (!path.isEmpty())
+ include_paths.push_back(path.toUtf8());
+ }
+
+ include_paths.erase(std::unique(include_paths.begin(), include_paths.end()),
+ include_paths.end());
+ }
+
+ QList<QByteArray> clang_defines{};
+ {
+ const QStringList config_defines{config.get(CONFIG_DEFINES).asStringList()};
+ for (const QString &def : config_defines) {
+ if (!def.contains(QChar('*'))) {
+ QByteArray tmp("-D");
+ tmp.append(def.toUtf8());
+ clang_defines.append(tmp.constData());
+ }
}
}
- Generator::augmentImageDirs(exampleImageDirs);
+ std::optional<PCHFile> pch = std::nullopt;
if (config.dualExec() || config.preparing()) {
- QStringList headerList;
- QStringList sourceList;
+ const QString moduleHeader = config.get(CONFIG_MODULEHEADER).asString();
+ pch = buildPCH(
+ QDocDatabase::qdocDB(),
+ moduleHeader.isNull() ? project : moduleHeader,
+ Config::instance().getHeaderFiles(),
+ include_paths,
+ clang_defines
+ );
+ }
- qCDebug(lcQdoc, "Reading headerdirs");
- headerList =
- config.getAllFiles(CONFIG_HEADERS, CONFIG_HEADERDIRS, excludedDirs, excludedFiles);
- QMap<QString, QString> headers;
- QMultiMap<QString, QString> headerFileNames;
- for (const auto &header : headerList) {
- if (header.contains(QLatin1String("doc/snippets")))
- continue;
- if (headers.contains(header))
- continue;
- headers.insert(header, header);
- QString t = header.mid(header.lastIndexOf('/') + 1);
- headerFileNames.insert(t, t);
- }
+ ClangCodeParser clangParser(QDocDatabase::qdocDB(), Config::instance(), include_paths, clang_defines, pch);
+ PureDocParser docParser{config.location()};
+
+ /*
+ Initialize all the classes and data structures with the
+ qdoc configuration. This is safe to do for each qdocconf
+ file processed, because all the data structures created
+ are either cleared after they have been used, or they
+ are cleared in the terminate() functions below.
+ */
+ Location::initialize();
+ Tokenizer::initialize();
+ CodeMarker::initialize();
+ CodeParser::initialize();
+ Doc::initialize(file_resolver);
+
+ if (config.dualExec() || config.preparing()) {
+ QStringList sourceList;
qCDebug(lcQdoc, "Reading sourcedirs");
sourceList =
config.getAllFiles(CONFIG_SOURCES, CONFIG_SOURCEDIRS, excludedDirs, excludedFiles);
- QMap<QString, QString> sources;
- QMultiMap<QString, QString> sourceFileNames;
+
+ std::vector<QString> sources{};
for (const auto &source : sourceList) {
if (source.contains(QLatin1String("doc/snippets")))
continue;
- if (sources.contains(source))
- continue;
- sources.insert(source, source);
- QString t = source.mid(source.lastIndexOf('/') + 1);
- sourceFileNames.insert(t, t);
+ sources.emplace_back(source);
}
/*
Find all the qdoc files in the example dirs, and add
@@ -443,45 +554,25 @@ static void processQdocconfFile(const QString &fileName)
qCDebug(lcQdoc, "Reading exampledirs");
QStringList exampleQdocList = config.getExampleQdocFiles(excludedDirs, excludedFiles);
for (const auto &example : exampleQdocList) {
- if (!sources.contains(example)) {
- sources.insert(example, example);
- QString t = example.mid(example.lastIndexOf('/') + 1);
- sourceFileNames.insert(t, t);
- }
- }
- /*
- Parse each header file in the set using the appropriate parser and add it
- to the big tree.
- */
-
- qCDebug(lcQdoc, "Parsing header files");
- int parsed = 0;
- for (auto it = headers.constBegin(); it != headers.constEnd(); ++it) {
- CodeParser *codeParser = CodeParser::parserForHeaderFile(it.key());
- if (codeParser) {
- ++parsed;
- qCDebug(lcQdoc, "Parsing %s", qPrintable(it.key()));
- codeParser->parseHeaderFile(config.location(), it.key());
- }
+ sources.emplace_back(example);
}
- clangParser_->precompileHeaders();
-
/*
Parse each source text file in the set using the appropriate parser and
add it to the big tree.
*/
- parsed = 0;
- qCInfo(lcQdoc) << "Parse source files for" << project;
- for (const auto &key : sources.keys()) {
- auto *codeParser = CodeParser::parserForSourceFile(key);
- if (codeParser) {
- ++parsed;
- qCDebug(lcQdoc, "Parsing %s", qPrintable(key));
- codeParser->parseSourceFile(config.location(), key);
- }
- }
- qCInfo(lcQdoc) << "Source files parsed for" << project;
+ if (config.get(CONFIG_LOGPROGRESS).asBool())
+ qCInfo(lcQdoc) << "Parse source files for" << project;
+
+
+ auto headers = config.getHeaderFiles();
+ CppCodeParser cpp_code_parser(FnCommandParser(qdb, headers, clang_defines, pch));
+
+ SourceFileParser source_file_parser{clangParser, docParser};
+ parseSourceFiles(std::move(sources), source_file_parser, cpp_code_parser);
+
+ if (config.get(CONFIG_LOGPROGRESS).asBool())
+ qCInfo(lcQdoc) << "Source files parsed for" << project;
}
/*
Now the primary tree has been built from all the header and
@@ -501,11 +592,14 @@ static void processQdocconfFile(const QString &fileName)
qCDebug(lcQdoc, "Generating docs");
for (const auto &format : outputFormats) {
auto *generator = Generator::generatorForFormat(format);
- if (generator == nullptr)
- outputFormatsLocation.fatal(
- QCoreApplication::translate("QDoc", "Unknown output format '%1'").arg(format));
- generator->initializeFormat();
- generator->generateDocs();
+ if (generator) {
+ generator->initializeFormat();
+ generator->generateDocs();
+ } else {
+ config.get(CONFIG_OUTPUTFORMATS)
+ .location()
+ .fatal(QStringLiteral("QDoc: Unknown output format '%1'").arg(format));
+ }
}
qCDebug(lcQdoc, "Terminating qdoc classes");
@@ -525,6 +619,54 @@ static void processQdocconfFile(const QString &fileName)
qCDebug(lcQdoc, "qdoc classes terminated");
}
+/*!
+ \internal
+ For each file in \a qdocFiles, first clear the configured module
+ dependencies and then pass the file to processQdocconfFile().
+
+ \sa processQdocconfFile(), singleExecutionMode(), dualExecutionMode()
+*/
+static void clearModuleDependenciesAndProcessQdocconfFile(const QStringList &qdocFiles)
+{
+ for (const auto &file : std::as_const(qdocFiles)) {
+ Config::instance().dependModules().clear();
+ processQdocconfFile(file);
+ }
+}
+
+/*!
+ \internal
+
+ A single QDoc process for prepare and generate phases.
+ The purpose is to first generate all index files for all documentation
+ projects that combined make out the documentation set being generated.
+ This allows QDoc to link to all content contained in all projects, e.g.
+ user-defined types or overview documentation, regardless of the project
+ that content belongs to when generating the final output.
+*/
+static void singleExecutionMode()
+{
+ const QStringList qdocFiles = Config::loadMaster(Config::instance().qdocFiles().at(0));
+
+ Config::instance().setQDocPass(Config::Prepare);
+ clearModuleDependenciesAndProcessQdocconfFile(qdocFiles);
+
+ Config::instance().setQDocPass(Config::Generate);
+ QDocDatabase::qdocDB()->processForest();
+ clearModuleDependenciesAndProcessQdocconfFile(qdocFiles);
+}
+
+/*!
+ \internal
+
+ Process each .qdocconf-file passed as command line argument(s).
+*/
+static void dualExecutionMode()
+{
+ const QStringList qdocFiles = Config::instance().qdocFiles();
+ clearModuleDependenciesAndProcessQdocconfFile(qdocFiles);
+}
+
QT_END_NAMESPACE
int main(int argc, char **argv)
@@ -533,7 +675,8 @@ int main(int argc, char **argv)
// Initialize Qt:
#ifndef QT_BOOTSTRAPPED
- qSetGlobalQHashSeed(0); // set the hash seed to 0 if it wasn't set yet
+ // use deterministic hash seed
+ QHashSeed::setDeterministicGlobalSeed();
#endif
QCoreApplication app(argc, argv);
app.setApplicationVersion(QLatin1String(QT_VERSION_STR));
@@ -543,14 +686,11 @@ int main(int argc, char **argv)
Create code parsers for the languages to be parsed,
and create a tree for C++.
*/
- ClangCodeParser clangParser;
- clangParser_ = &clangParser;
QmlCodeParser qmlParser;
- PureDocParser docParser;
/*
Create code markers for plain text, C++,
- javascript, and QML.
+ and QML.
The plain CodeMarker must be instantiated first because it is used as
fallback when the other markers cannot be used.
@@ -560,62 +700,21 @@ int main(int argc, char **argv)
*/
CodeMarker fallbackMarker;
CppCodeMarker cppMarker;
- JsCodeMarker jsMarker;
QmlCodeMarker qmlMarker;
- HtmlGenerator htmlGenerator;
- WebXMLGenerator webXMLGenerator;
- DocBookGenerator docBookGenerator;
-
- Config::instance().init(QCoreApplication::translate("QDoc", "qdoc"), app.arguments());
- Config &config = Config::instance();
-
- // Get the list of files to act on:
- QStringList qdocFiles = config.qdocFiles();
- if (qdocFiles.isEmpty())
- config.showHelp();
+ Config::instance().init("QDoc", app.arguments());
- if (config.singleExec())
- qdocFiles = Config::loadMaster(qdocFiles.at(0));
+ if (Config::instance().qdocFiles().isEmpty())
+ Config::instance().showHelp();
- if (config.singleExec()) {
- // single qdoc process for prepare and generate phases
- config.setQDocPass(Config::Prepare);
- for (const auto &file : qAsConst(qdocFiles)) {
- config.dependModules().clear();
- processQdocconfFile(file);
- }
- config.setQDocPass(Config::Generate);
- QDocDatabase::qdocDB()->processForest();
- for (const auto &file : qAsConst(qdocFiles)) {
- config.dependModules().clear();
- processQdocconfFile(file);
- }
+ if (Config::instance().singleExec()) {
+ singleExecutionMode();
} else {
- // separate qdoc processes for prepare and generate phases
- for (const auto &file : qAsConst(qdocFiles)) {
- config.dependModules().clear();
- processQdocconfFile(file);
- }
+ dualExecutionMode();
}
// Tidy everything away:
-#ifndef QT_NO_TRANSLATION
- if (!translators.isEmpty()) {
- for (const auto &translator : translators)
- delete translator.second;
- }
- translators.clear();
-#endif
QmlTypeNode::terminate();
-
-#ifdef DEBUG_SHUTDOWN_CRASH
- qDebug() << "main(): Delete qdoc database";
-#endif
QDocDatabase::destroyQdocDB();
-#ifdef DEBUG_SHUTDOWN_CRASH
- qDebug() << "main(): qdoc database deleted";
-#endif
-
return Location::exitCode();
}
diff --git a/src/qdoc/manifestwriter.cpp b/src/qdoc/qdoc/src/qdoc/manifestwriter.cpp
index f47e64ebb..97bf7f190 100644
--- a/src/qdoc/manifestwriter.cpp
+++ b/src/qdoc/qdoc/src/qdoc/manifestwriter.cpp
@@ -1,30 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the tools applications of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "manifestwriter.h"
#include "config.h"
@@ -133,6 +108,36 @@ void writeFilesToOpen(QXmlStreamWriter &writer, const QString &installPath,
}
/*!
+ \internal
+ \brief Writes example metadata into \a writer.
+
+ For instance,
+
+
+ \ meta category {Application Example}
+
+ becomes
+
+ <meta>
+ <entry name="category">Application Example</entry>
+ <meta>
+*/
+static void writeMetaInformation(QXmlStreamWriter &writer, const QStringMultiMap &map)
+{
+ if (map.isEmpty())
+ return;
+
+ writer.writeStartElement("meta");
+ for (auto it = map.constBegin(); it != map.constEnd(); ++it) {
+ writer.writeStartElement("entry");
+ writer.writeAttribute(QStringLiteral("name"), it.key());
+ writer.writeCharacters(it.value());
+ writer.writeEndElement(); // tag
+ }
+ writer.writeEndElement(); // meta
+}
+
+/*!
\class ManifestWriter
\internal
\brief The ManifestWriter is responsible for writing manifest files.
@@ -140,17 +145,18 @@ void writeFilesToOpen(QXmlStreamWriter &writer, const QString &installPath,
ManifestWriter::ManifestWriter()
{
Config &config = Config::instance();
- m_project = config.getString(CONFIG_PROJECT);
+ m_project = config.get(CONFIG_PROJECT).asString();
m_outputDirectory = config.getOutputDir();
m_qdb = QDocDatabase::qdocDB();
const QString prefix = CONFIG_QHP + Config::dot + m_project + Config::dot;
m_manifestDir =
- QLatin1String("qthelp://") + config.getString(prefix + QLatin1String("namespace"));
- m_manifestDir += QLatin1Char('/') + config.getString(prefix + QLatin1String("virtualFolder"))
+ QLatin1String("qthelp://") + config.get(prefix + QLatin1String("namespace")).asString();
+ m_manifestDir +=
+ QLatin1Char('/') + config.get(prefix + QLatin1String("virtualFolder")).asString()
+ QLatin1Char('/');
readManifestMetaContent();
- m_examplesPath = config.getString(CONFIG_EXAMPLESINSTALLPATH);
+ m_examplesPath = config.get(CONFIG_EXAMPLESINSTALLPATH).asString();
if (!m_examplesPath.isEmpty())
m_examplesPath += QLatin1Char('/');
}
@@ -185,31 +191,80 @@ void ManifestWriter::processManifestMetaContent(const QString &fullName, F match
*/
void ManifestWriter::generateManifestFiles()
{
- generateManifestFile("examples", "example");
- generateManifestFile("demos", "demo");
+ generateExampleManifestFile();
m_qdb->exampleNodeMap().clear();
m_manifestMetaContent.clear();
}
+/*
+ Returns Qt module name as lower case tag, stripping Qt prefix:
+ QtQuickControls -> quickcontrols
+ QtOpenGL -> opengl
+ QtQuick3D -> quick3d
+ */
+static QString moduleNameAsTag(const QString &module)
+{
+ QString moduleName = module;
+ if (moduleName.startsWith("Qt"))
+ moduleName = moduleName.mid(2);
+ // Some examples are in QtDoc module, but 'doc' as tag makes little sense
+ if (moduleName == "Doc")
+ return QString();
+ return moduleName.toLower();
+}
+
+/*
+ Return tags that were added with
+ \ meta {tag} {tag1[,tag2,...]}
+ or
+ \ meta {tags} {tag1[,tag2,...]}
+ from example metadata
+ */
+static QSet<QString> tagsAddedWithMetaCommand(const ExampleNode *example)
+{
+ Q_ASSERT(example);
+
+ QSet<QString> tags;
+ const QStringMultiMap *metaTagMap = example->doc().metaTagMap();
+ if (metaTagMap) {
+ QStringList originalTags = metaTagMap->values("tag");
+ originalTags << metaTagMap->values("tags");
+ for (const auto &tag : originalTags) {
+ const auto &tagList = tag.toLower().split(QLatin1Char(','), Qt::SkipEmptyParts);
+ tags += QSet<QString>(tagList.constBegin(), tagList.constEnd());
+ }
+ }
+ return tags;
+}
+
+/*
+ Writes the contents of tags into writer, formatted as
+ <tags>tag1,tag2..</tags>
+ */
+static void writeTagsElement(QXmlStreamWriter *writer, const QSet<QString> &tags)
+{
+ Q_ASSERT(writer);
+ if (tags.isEmpty())
+ return;
+
+ writer->writeStartElement("tags");
+ QStringList sortedTags = tags.values();
+ sortedTags.sort();
+ writer->writeCharacters(sortedTags.join(","));
+ writer->writeEndElement(); // tags
+}
+
/*!
- This function is called by generateManifestFiles(), once
- for each manifest file to be generated. \a manifest is the
- type of manifest file.
+ This function is called by generateExampleManifestFiles(), once
+ for each manifest file to be generated.
*/
-void ManifestWriter::generateManifestFile(const QString &manifest, const QString &element)
+void ManifestWriter::generateExampleManifestFile()
{
const ExampleNodeMap &exampleNodeMap = m_qdb->exampleNodeMap();
if (exampleNodeMap.isEmpty())
return;
- bool demos = (manifest == QLatin1String("demos"));
- if (!std::any_of(exampleNodeMap.cbegin(), exampleNodeMap.cend(),
- [demos](const ExampleNode *en) {
- return demos == en->name().startsWith("demos");
- }))
- return;
-
- const QString outputFileName = manifest + "-manifest.xml";
+ const QString outputFileName = "examples-manifest.xml";
QFile outputFile(m_outputDirectory + QLatin1Char('/') + outputFileName);
if (!outputFile.open(QFile::WriteOnly | QFile::Text))
return;
@@ -219,21 +274,19 @@ void ManifestWriter::generateManifestFile(const QString &manifest, const QString
writer.writeStartDocument();
writer.writeStartElement("instructionals");
writer.writeAttribute("module", m_project);
- writer.writeStartElement(manifest);
+ writer.writeStartElement("examples");
for (const auto &example : exampleNodeMap.values()) {
QMap<QString, QString> usedAttributes;
- if (demos != example->name().startsWith("demos"))
- continue;
- m_tags.clear();
+ QSet<QString> tags;
const QString installPath = retrieveExampleInstallationPath(example);
const QString fullName = m_project + QLatin1Char('/') + example->title();
processManifestMetaContent(
- fullName, [&](const ManifestMetaFilter &filter) { m_tags += filter.m_tags; });
- includeTagsAddedWithMetaCommand(example);
+ fullName, [&](const ManifestMetaFilter &filter) { tags += filter.m_tags; });
+ tags += tagsAddedWithMetaCommand(example);
// omit from the manifest if explicitly marked broken
- if (m_tags.contains("broken"))
+ if (tags.contains("broken"))
continue;
// attributes that are always written for the element
@@ -250,7 +303,7 @@ void ManifestWriter::generateManifestFile(const QString &manifest, const QString
for (const auto &attribute : attributes) {
const QLatin1Char div(':');
QStringList attrList = attribute.split(div);
- if (attrList.count() == 1)
+ if (attrList.size() == 1)
attrList.append(QStringLiteral("true"));
QString attrName = attrList.takeFirst();
if (!usedAttributes.contains(attrName))
@@ -258,125 +311,51 @@ void ManifestWriter::generateManifestFile(const QString &manifest, const QString
}
});
- // write the example/demo element
- writer.writeStartElement(element);
+ writer.writeStartElement("example");
for (auto it = usedAttributes.cbegin(); it != usedAttributes.cend(); ++it)
writer.writeAttribute(it.key(), it.value());
warnAboutUnusedAttributes(usedAttributes.keys(), example);
writeDescription(&writer, example);
- addWordsFromModuleNamesAsTags();
- addTitleWordsToTags(example);
- cleanUpTags();
- writeTagsElement(&writer);
+
+ const QString moduleNameTag = moduleNameAsTag(m_project);
+ if (!moduleNameTag.isEmpty())
+ tags << moduleNameTag;
+ writeTagsElement(&writer, tags);
const QString exampleName = example->name().mid(example->name().lastIndexOf('/') + 1);
const auto files = example->files();
const QMap<int, QString> filesToOpen = getFilesToOpen(files, exampleName);
writeFilesToOpen(writer, installPath, filesToOpen);
- writer.writeEndElement(); // example/demo
- }
-
- writer.writeEndElement(); // examples
- writer.writeEndElement(); // instructionals
- writer.writeEndDocument();
- outputFile.close();
-}
-
-/*!
- \internal
-
- Populates the tags and writes the tags element, then clears the tags member.
- */
-void ManifestWriter::writeTagsElement(QXmlStreamWriter *writer)
-{
- Q_ASSERT(writer);
- if (m_tags.isEmpty())
- return;
-
- writer->writeStartElement("tags");
- QStringList sortedTags = m_tags.values();
- sortedTags.sort();
- writer->writeCharacters(sortedTags.join(","));
- writer->writeEndElement(); // tags
-}
-
-/*!
- \internal
-
- Clean up tags, exclude invalid and common words.
- */
-void ManifestWriter::cleanUpTags()
-{
- QSet<QString> cleanedTags;
-
- for (auto tag : m_tags) {
- if (tag.at(0) == '(')
- tag.remove(0, 1).chop(1);
- if (tag.endsWith(QLatin1Char(':')))
- tag.chop(1);
-
- if (tag.length() < 2 || tag.at(0).isDigit() || tag.at(0) == '-'
- || tag == QLatin1String("qt") || tag == QLatin1String("the")
- || tag == QLatin1String("and") || tag == QLatin1String("doc")
- || tag.startsWith(QLatin1String("example")) || tag.startsWith(QLatin1String("chapter")))
- continue;
- cleanedTags << tag;
- }
- m_tags = cleanedTags;
-}
-
-/*!
- \internal
-
- Add the example's title as tags.
- */
-void ManifestWriter::addTitleWordsToTags(const ExampleNode *example)
-{
- Q_ASSERT(example);
-
- const auto &titleWords = example->title().toLower().split(QLatin1Char(' '));
- m_tags += QSet<QString>(titleWords.cbegin(), titleWords.cend());
-}
-
-/*!
- \internal
+ if (const QStringMultiMap *metaTagMapP = example->doc().metaTagMap()) {
+ // Write \meta elements into the XML, except for 'tag', 'installpath',
+ // as they are handled separately
+ QStringMultiMap map = *metaTagMapP;
+ erase_if(map, [](QStringMultiMap::iterator iter) {
+ return iter.key() == "tag" || iter.key() == "tags" || iter.key() == "installpath";
+ });
+ writeMetaInformation(writer, map);
+ }
- Add words from module name as tags
- QtQuickControls -> qt,quick,controls
- QtOpenGL -> qt,opengl
- QtQuick3D -> qt,quick3d
- */
-void ManifestWriter::addWordsFromModuleNamesAsTags()
-{
- // '?<=': positive lookbehind
- QRegularExpression re("([A-Z]+[a-z0-9]*((?<=3)D|GL)?)");
- qsizetype pos = 0;
- QRegularExpressionMatch match;
- while ((match = re.match(m_project, pos)).hasMatch()) {
- m_tags << match.captured(1).toLower();
- pos = match.capturedEnd();
+ writer.writeEndElement(); // example
}
-}
-
-/*!
- \internal
- Include tags added via \meta {tag} {tag1[,tag2,...]}
- within \example topic.
- */
-void ManifestWriter::includeTagsAddedWithMetaCommand(const ExampleNode *example)
-{
- Q_ASSERT(example);
+ writer.writeEndElement(); // examples
- const QStringMultiMap *metaTagMap = example->doc().metaTagMap();
- if (metaTagMap) {
- for (const auto &tag : metaTagMap->values("tag")) {
- const auto &tagList = tag.toLower().split(QLatin1Char(','), Qt::SkipEmptyParts);
- m_tags += QSet<QString>(tagList.constBegin(), tagList.constEnd());
+ if (!m_exampleCategories.isEmpty()) {
+ writer.writeStartElement("categories");
+ for (const auto &examplecategory : m_exampleCategories) {
+ writer.writeStartElement("category");
+ writer.writeCharacters(examplecategory);
+ writer.writeEndElement();
}
+ writer.writeEndElement(); // categories
}
+
+ writer.writeEndElement(); // instructionals
+ writer.writeEndDocument();
+ outputFile.close();
}
/*!
@@ -389,17 +368,21 @@ void ManifestWriter::includeTagsAddedWithMetaCommand(const ExampleNode *example)
void ManifestWriter::readManifestMetaContent()
{
Config &config = Config::instance();
- const QStringList names =
- config.getStringList(CONFIG_MANIFESTMETA + Config::dot + QStringLiteral("filters"));
+ const QStringList names{config.get(CONFIG_MANIFESTMETA
+ + Config::dot
+ + QStringLiteral("filters")).asStringList()};
for (const auto &manifest : names) {
ManifestMetaFilter filter;
QString prefix = CONFIG_MANIFESTMETA + Config::dot + manifest + Config::dot;
- filter.m_names = config.getStringSet(prefix + QStringLiteral("names"));
- filter.m_attributes = config.getStringSet(prefix + QStringLiteral("attributes"));
- filter.m_tags = config.getStringSet(prefix + QStringLiteral("tags"));
+ filter.m_names = config.get(prefix + QStringLiteral("names")).asStringSet();
+ filter.m_attributes = config.get(prefix + QStringLiteral("attributes")).asStringSet();
+ filter.m_tags = config.get(prefix + QStringLiteral("tags")).asStringSet();
m_manifestMetaContent.append(filter);
}
+
+ m_exampleCategories = config.get(CONFIG_MANIFESTMETA
+ + QStringLiteral(".examplecategories")).asStringList();
}
/*!
diff --git a/src/qdoc/qdoc/src/qdoc/manifestwriter.h b/src/qdoc/qdoc/src/qdoc/manifestwriter.h
new file mode 100644
index 000000000..730835b9e
--- /dev/null
+++ b/src/qdoc/qdoc/src/qdoc/manifestwriter.h
@@ -0,0 +1,46 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+#ifndef MANIFESTWRITER_H
+#define MANIFESTWRITER_H
+
+#include <QtCore/qlist.h>
+#include <QtCore/qset.h>
+#include <QtCore/qstring.h>
+
+QT_BEGIN_NAMESPACE
+
+class ExampleNode;
+class QDocDatabase;
+class QXmlStreamWriter;
+class ManifestWriter
+{
+ struct ManifestMetaFilter
+ {
+ QSet<QString> m_names {};
+ QSet<QString> m_attributes {};
+ QSet<QString> m_tags {};
+ };
+
+public:
+ ManifestWriter();
+ void generateManifestFiles();
+ void generateExampleManifestFile();
+ void readManifestMetaContent();
+ QString retrieveExampleInstallationPath(const ExampleNode *example) const;
+
+private:
+ QString m_manifestDir {};
+ QString m_examplesPath {};
+ QString m_outputDirectory {};
+ QString m_project {};
+ QDocDatabase *m_qdb { nullptr };
+ QList<ManifestMetaFilter> m_manifestMetaContent {};
+ QStringList m_exampleCategories {};
+
+ template <typename F>
+ void processManifestMetaContent(const QString &fullName, F matchFunc);
+};
+
+QT_END_NAMESPACE
+
+#endif // MANIFESTWRITER_H
diff --git a/src/qdoc/namespacenode.cpp b/src/qdoc/qdoc/src/qdoc/namespacenode.cpp
index d931a543d..22686c050 100644
--- a/src/qdoc/namespacenode.cpp
+++ b/src/qdoc/qdoc/src/qdoc/namespacenode.cpp
@@ -1,30 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the tools applications of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "namespacenode.h"
@@ -48,24 +23,6 @@ QT_BEGIN_NAMESPACE
*/
/*!
- The destructor removes all children from the child list that
- have a parent() that is not this NamespaceNode. This situation
- can arise because of elements that are related to this namespace
- using the \c {\\relates} command.
-
- \note The child nodes remaining in the child list after the ones
- with a different parent() have been removed are deleted in the
- destructor of the Aggregate base class.
- */
-NamespaceNode::~NamespaceNode()
-{
- for (auto &child : m_children) {
- if (child->parent() != this)
- child = nullptr;
- }
-}
-
-/*!
Returns true if this namespace is to be documented in the
current module. There can be elements declared in this
namespace spread over multiple modules. Those elements are
@@ -95,7 +52,7 @@ bool NamespaceNode::hasDocumentedChildren() const
*/
void NamespaceNode::reportDocumentedChildrenInUndocumentedNamespace() const
{
- for (const auto *node : qAsConst(m_children)) {
+ for (const auto *node : std::as_const(m_children)) {
if (node->isInAPI()) {
QString msg1 = node->name();
if (node->isFunction())
diff --git a/src/qdoc/namespacenode.h b/src/qdoc/qdoc/src/qdoc/namespacenode.h
index 373759968..80d068838 100644
--- a/src/qdoc/namespacenode.h
+++ b/src/qdoc/qdoc/src/qdoc/namespacenode.h
@@ -1,30 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the tools applications of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#ifndef NAMESPACENODE_H
#define NAMESPACENODE_H
@@ -42,7 +17,7 @@ class NamespaceNode : public Aggregate
{
public:
NamespaceNode(Aggregate *parent, const QString &name) : Aggregate(Namespace, parent, name) {}
- ~NamespaceNode() override;
+ ~NamespaceNode() override = default;
[[nodiscard]] Tree *tree() const override { return (parent() ? parent()->tree() : m_tree); }
[[nodiscard]] bool isFirstClassAggregate() const override { return true; }
diff --git a/src/qdoc/node.cpp b/src/qdoc/qdoc/src/qdoc/node.cpp
index 839b36b5c..1aadbdeb1 100644
--- a/src/qdoc/node.cpp
+++ b/src/qdoc/qdoc/src/qdoc/node.cpp
@@ -1,30 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the tools applications of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "node.h"
@@ -49,9 +24,6 @@
QT_BEGIN_NAMESPACE
-QStringMap Node::operators;
-QMap<QString, Node::NodeType> Node::goals;
-
/*!
\class Node
\brief The Node class is the base class for all the nodes in QDoc's parse tree.
@@ -76,87 +48,6 @@ QMap<QString, Node::NodeType> Node::goals;
*/
/*!
- Initialize the map of search goals. This is called once
- by QDocDatabase::initializeDB(). The map key is a string
- representing a value in the enum Node::NodeType. The map value
- is the enum value.
-
- There should be an entry in the map for each value in the
- NodeType enum.
- */
-void Node::initialize()
-{
- goals.insert("namespace", Node::Namespace);
- goals.insert("class", Node::Class);
- goals.insert("struct", Node::Struct);
- goals.insert("union", Node::Union);
- goals.insert("header", Node::HeaderFile);
- goals.insert("headerfile", Node::HeaderFile);
- goals.insert("page", Node::Page);
- goals.insert("enum", Node::Enum);
- goals.insert("example", Node::Example);
- goals.insert("externalpage", Node::ExternalPage);
- goals.insert("typedef", Node::Typedef);
- goals.insert("typealias", Node::TypeAlias);
- goals.insert("function", Node::Function);
- goals.insert("proxy", Node::Proxy);
- goals.insert("property", Node::Property);
- goals.insert("variable", Node::Variable);
- goals.insert("group", Node::Group);
- goals.insert("module", Node::Module);
- goals.insert("qmltype", Node::QmlType);
- goals.insert("qmlmodule", Node::QmlModule);
- goals.insert("qmlproperty", Node::QmlProperty);
- goals.insert("qmlsignal", Node::Function);
- goals.insert("qmlsignalhandler", Node::Function);
- goals.insert("qmlmethod", Node::Function);
- goals.insert("qmlbasictype", Node::QmlBasicType);
- goals.insert("sharedcomment", Node::SharedComment);
- goals.insert("collection", Node::Collection);
-}
-
-/*!
- If this Node's type is \a from, change the type to \a to
- and return \c true. Otherwise return false. This function
- is used to change Qml node types to Javascript node types,
- because these nodes are created as Qml nodes before it is
- discovered that the entity represented by the node is not
- Qml but javascript.
-
- Note that if the function returns true, which means the node
- type was indeed changed, then the node's Genus is also changed
- from QML to JS.
-
- The function also works in the other direction, but there is
- no use case for that.
- */
-bool Node::changeType(NodeType from, NodeType to)
-{
- if (m_nodeType == from) {
- m_nodeType = to;
- switch (to) {
- case QmlType:
- case QmlModule:
- case QmlProperty:
- case QmlBasicType:
- setGenus(Node::QML);
- break;
- case JsType:
- case JsModule:
- case JsProperty:
- case JsBasicType:
- setGenus(Node::JS);
- break;
- default:
- setGenus(Node::CPP);
- break;
- }
- return true;
- }
- return false;
-}
-
-/*!
Returns \c true if the node \a n1 is less than node \a n2. The
comparison is performed by comparing properties of the nodes
in order of increasing complexity.
@@ -177,7 +68,8 @@ bool Node::nodeNameLessThan(const Node *n1, const Node *n2)
const auto *f2 = static_cast<const FunctionNode *>(n2);
LT_RETURN_IF_NOT_EQUAL(f1->isConst(), f2->isConst());
- LT_RETURN_IF_NOT_EQUAL(f1->signature(false, false), f2->signature(false, false));
+ LT_RETURN_IF_NOT_EQUAL(f1->signature(Node::SignatureReturnType),
+ f2->signature(Node::SignatureReturnType));
}
LT_RETURN_IF_NOT_EQUAL(n1->nodeType(), n2->nodeType());
@@ -212,7 +104,7 @@ bool Node::nodeNameLessThan(const Node *n1, const Node *n2)
\value ExternalPage The Node subclass is ExternalPageNode, which is for
linking to an external page.
\value Function The Node subclass is FunctionNode, which can represent C++,
- QML, and Javascript functions.
+ and QML functions.
\value Typedef The Node subclass is TypedefNode, which represents a C++
typedef.
\value Property The Node subclass is PropertyNode, which represents a use of
@@ -228,16 +120,8 @@ bool Node::nodeNameLessThan(const Node *n1, const Node *n2)
module.
\value QmlProperty The Node subclass is QmlPropertyNode, which represents a
property in a QML type.
- \value QmlBasicType The Node subclass is QmlBasicTypeNode, which represents a
- basic type like int, etc.
- \value JsType The Node subclass is QmlTypeNode, which represents a javascript
- type.
- \value JsModule The Node subclass is CollectionNode, which represents a
- javascript module.
- \value JsProperty The Node subclass is QmlPropertyNode, which represents a
- javascript property.
- \value JsBasicType The Node subclass is QmlBasicTypeNode, which represents a
- basic type like int, etc.
+ \value QmlBasicType The Node subclass is QmlTypeNode, which represents a
+ value type like int, etc.
\value SharedComment The Node subclass is SharedCommentNode, which represents
a collection of nodes that share the same documentation comment.
\omitvalue Collection
@@ -251,19 +135,36 @@ bool Node::nodeNameLessThan(const Node *n1, const Node *n2)
\enum Node::Genus
An unsigned char value that specifies whether the Node represents a
- C++ element, a QML element, a javascript element, or a text document.
+ C++ element, a QML element, or a text document.
The Genus values are also passed to search functions to specify the
Genus of Tree Node that can satisfy the search.
\value DontCare The Genus is not specified. Used when calling Tree search functions to indicate
the search can accept any Genus of Node.
\value CPP The Node represents a C++ element.
- \value JS The Node represents a javascript element.
\value QML The Node represents a QML element.
\value DOC The Node represents a text document.
*/
/*!
+ \internal
+ \fn setComparisonCategory(const ComparisonCategory category)
+
+ Sets the comparison category of this node to \a category.
+
+ \sa ComparisonCategory, comparisonCategory()
+ */
+
+/*!
+ \internal
+ \fn ComparisonCategory comparisonCategory()
+
+ Returns the comparison category of this node.
+
+ \sa ComparisonCategory, setComparisonCategory()
+ */
+
+/*!
\enum Access
An unsigned char value that indicates the C++ access level.
@@ -309,24 +210,6 @@ bool Node::nodeNameLessThan(const Node *n1, const Node *n2)
*/
/*!
- \enum Node::PageType
-
- An unsigned char value that indicates what kind of documentation page
- the Node represents. I think it is not very useful anymore.
-
- \value NoPageType
- \value AttributionPage
- \value ApiPage
- \value ArticlePage
- \value ExamplePage
- \value HowToPage
- \value OverviewPage
- \value TutorialPage
- \value FAQPage
- \omitvalue OnBeyondZebra
-*/
-
-/*!
\enum Node::FlagValue
A value used in PropertyNode and QmlPropertyNode that can be -1, 0, or +1.
@@ -394,26 +277,6 @@ bool Node::nodeNameLessThan(const Node *n1, const Node *n2)
Returns true if this node was created from something in an index file.
*/
-/*! \fn bool Node::isJsBasicType() const
- Returns true if the node type is \c JsBasicType.
- */
-
-/*! \fn bool Node::isJsModule() const
- Returns true if the node type is \c JsModule.
- */
-
-/*! \fn bool Node::isJsNode() const
- Returns true if this node's Genus value is \c JS.
- */
-
-/*! \fn bool Node::isJsProperty() const
- Returns true if the node type is \c JsProperty.
- */
-
-/*! \fn bool Node::isJsType() const
- Returns true if the node type is \c JsType.
- */
-
/*! \fn bool Node::isModule() const
Returns true if the node type is \c Module.
*/
@@ -467,7 +330,7 @@ bool Node::nodeNameLessThan(const Node *n1, const Node *n2)
*/
/*! \fn bool Node::isQmlType() const
- Returns true if the node type is \c QmlType.
+ Returns true if the node type is \c QmlType or \c QmlValueType.
*/
/*! \fn bool Node::isRelatedNonmember() const
@@ -546,14 +409,6 @@ bool Node::nodeNameLessThan(const Node *n1, const Node *n2)
function will return \e true.
*/
-/*! \fn bool Node::isQtQuickNode() const
- Returns true if this node represents a QML element in the QtQuick module.
-*/
-
-/*! \fn bool Node::isReadOnly() const
- Returns true if the QML property node is marked as a read-only property.
-*/
-
/*! \fn bool Node::isRelatableType() const
Returns true if this node is something you can relate things to with
the \e relates command. NamespaceNode, ClassNode, HeaderNode, and
@@ -630,7 +485,7 @@ QString Node::plainSignature() const
QString fullName;
const Node *node = this;
while (node) {
- fullName.prepend(node->signature(false, true));
+ fullName.prepend(node->signature(Node::SignaturePlain));
if (node->parent()->name().isEmpty())
break;
fullName.prepend(QLatin1String("::"));
@@ -652,17 +507,6 @@ QString Node::fullName(const Node *relative) const
}
/*!
- Try to match this node's type with one of the \a types.
- If a match is found, return true. If no match is found,
- return false.
- */
-bool Node::match(const QList<int> &types) const
-{
- return std::any_of(types.cbegin(), types.cend(),
- [this](const int type) { return nodeType() == type; });
-}
-
-/*!
Sets this Node's Doc to \a doc. If \a replace is false and
this Node already has a Doc, and if this doc is not marked
with the \\reimp command, a warning is reported that the
@@ -673,8 +517,8 @@ bool Node::match(const QList<int> &types) const
void Node::setDoc(const Doc &doc, bool replace)
{
if (!m_doc.isEmpty() && !replace && !doc.isMarkedReimp()) {
- doc.location().warning(QStringLiteral("Overrides a previous doc"));
- m_doc.location().warning(QStringLiteral("(The previous doc is here)"));
+ doc.location().warning(QStringLiteral("Overrides a previous doc"),
+ QStringLiteral("from here: %1").arg(m_doc.location().toString()));
}
m_doc = doc;
}
@@ -718,95 +562,8 @@ Node::Node(NodeType type, Aggregate *parent, QString name)
{
if (m_parent)
m_parent->addChild(this);
- m_outSubDir = Generator::outputSubdir();
- if (operators.isEmpty()) {
- operators.insert("++", "inc");
- operators.insert("--", "dec");
- operators.insert("==", "eq");
- operators.insert("!=", "ne");
- operators.insert("<<", "lt-lt");
- operators.insert(">>", "gt-gt");
- operators.insert("+=", "plus-assign");
- operators.insert("-=", "minus-assign");
- operators.insert("*=", "mult-assign");
- operators.insert("/=", "div-assign");
- operators.insert("%=", "mod-assign");
- operators.insert("&=", "bitwise-and-assign");
- operators.insert("|=", "bitwise-or-assign");
- operators.insert("^=", "bitwise-xor-assign");
- operators.insert("<<=", "bitwise-left-shift-assign");
- operators.insert(">>=", "bitwise-right-shift-assign");
- operators.insert("||", "logical-or");
- operators.insert("&&", "logical-and");
- operators.insert("()", "call");
- operators.insert("[]", "subscript");
- operators.insert("->", "pointer");
- operators.insert("->*", "pointer-star");
- operators.insert("+", "plus");
- operators.insert("-", "minus");
- operators.insert("*", "mult");
- operators.insert("/", "div");
- operators.insert("%", "mod");
- operators.insert("|", "bitwise-or");
- operators.insert("&", "bitwise-and");
- operators.insert("^", "bitwise-xor");
- operators.insert("!", "not");
- operators.insert("~", "bitwise-not");
- operators.insert("<=", "lt-eq");
- operators.insert(">=", "gt-eq");
- operators.insert("<", "lt");
- operators.insert(">", "gt");
- operators.insert("=", "assign");
- operators.insert(",", "comma");
- operators.insert("delete[]", "delete-array");
- operators.insert("delete", "delete");
- operators.insert("new[]", "new-array");
- operators.insert("new", "new");
- }
- setPageType(getPageType(type));
- setGenus(getGenus(type));
-}
-/*!
- Determines the appropriate PageType value for the NodeType
- value \a t and returns that PageType value.
- */
-Node::PageType Node::getPageType(Node::NodeType t)
-{
- switch (t) {
- case Node::Namespace:
- case Node::Class:
- case Node::Struct:
- case Node::Union:
- case Node::HeaderFile:
- case Node::Enum:
- case Node::Function:
- case Node::Typedef:
- case Node::Property:
- case Node::Variable:
- case Node::QmlType:
- case Node::QmlProperty:
- case Node::QmlBasicType:
- case Node::JsType:
- case Node::JsProperty:
- case Node::JsBasicType:
- case Node::SharedComment:
- return Node::ApiPage;
- case Node::Example:
- return Node::ExamplePage;
- case Node::Page:
- case Node::ExternalPage:
- return Node::NoPageType;
- case Node::Group:
- case Node::Module:
- case Node::QmlModule:
- case Node::JsModule:
- case Node::Collection:
- return Node::OverviewPage;
- case Node::Proxy:
- default:
- return Node::NoPageType;
- }
+ setGenus(getGenus(type));
}
/*!
@@ -826,6 +583,7 @@ Node::Genus Node::getGenus(Node::NodeType t)
case Node::Struct:
case Node::Union:
case Node::Module:
+ case Node::TypeAlias:
case Node::Typedef:
case Node::Property:
case Node::Variable:
@@ -836,13 +594,8 @@ Node::Genus Node::getGenus(Node::NodeType t)
case Node::QmlType:
case Node::QmlModule:
case Node::QmlProperty:
- case Node::QmlBasicType:
+ case Node::QmlValueType:
return Node::QML;
- case Node::JsType:
- case Node::JsModule:
- case Node::JsProperty:
- case Node::JsBasicType:
- return Node::JS;
case Node::Page:
case Node::Group:
case Node::Example:
@@ -931,22 +684,13 @@ QString Node::nodeTypeString(NodeType t)
case QmlType:
return QLatin1String("QML type");
- case QmlBasicType:
- return QLatin1String("QML basic type");
+ case QmlValueType:
+ return QLatin1String("QML value type");
case QmlModule:
return QLatin1String("QML module");
case QmlProperty:
return QLatin1String("QML property");
- case JsType:
- return QLatin1String("JS type");
- case JsBasicType:
- return QLatin1String("JS basic type");
- case JsModule:
- return QLatin1String("JS module");
- case JsProperty:
- return QLatin1String("JS property");
-
case SharedComment:
return QLatin1String("shared comment");
case Collection:
@@ -957,27 +701,6 @@ QString Node::nodeTypeString(NodeType t)
return QString();
}
-/*!
- Set the page type according to the string \a t.
- */
-void Node::setPageType(const QString &t)
-{
- if ((t == "API") || (t == "api"))
- m_pageType = ApiPage;
- else if (t == "howto")
- m_pageType = HowToPage;
- else if (t == "overview")
- m_pageType = OverviewPage;
- else if (t == "tutorial")
- m_pageType = TutorialPage;
- else if (t == "faq")
- m_pageType = FAQPage;
- else if (t == "article")
- m_pageType = ArticlePage;
- else if (t == "example")
- m_pageType = ExamplePage;
-}
-
/*! Converts the boolean value \a b to an enum representation
of the boolean type, which includes an enum value for the
\e {default value} of the item, i.e. true, false, or default.
@@ -1012,7 +735,7 @@ bool Node::fromFlagValue(FlagValue fv, bool defaultValue)
*/
void Node::setLink(LinkType linkType, const QString &link, const QString &desc)
{
- QPair<QString, QString> linkPair;
+ std::pair<QString, QString> linkPair;
linkPair.first = link;
linkPair.second = desc;
m_linkMap[linkType] = linkPair;
@@ -1031,7 +754,7 @@ void Node::setSince(const QString &since)
project = Config::dot + parts.first();
QVersionNumber cutoff =
- QVersionNumber::fromString(Config::instance().getString(CONFIG_IGNORESINCE + project))
+ QVersionNumber::fromString(Config::instance().get(CONFIG_IGNORESINCE + project).asString())
.normalized();
if (!cutoff.isNull() && QVersionNumber::fromString(parts.last()).normalized() < cutoff)
@@ -1091,39 +814,6 @@ Node::ThreadSafeness Node::inheritedThreadSafeness() const
}
/*!
- If this node is a QML or JS type node, return a pointer to
- it. If it is a child of a QML or JS type node, return the
- pointer to its parent QMLor JS type node. Otherwise return
- 0;
- */
-QmlTypeNode *Node::qmlTypeNode()
-{
- if (isQmlNode() || isJsNode()) {
- Node *n = this;
- while (n && !(n->isQmlType() || n->isJsType()))
- n = n->parent();
- if (n && (n->isQmlType() || n->isJsType()))
- return static_cast<QmlTypeNode *>(n);
- }
- return nullptr;
-}
-
-/*!
- If this node is a QML node, find its QML class node,
- and return a pointer to the C++ class node from the
- QML class node. That pointer will be null if the QML
- class node is a component. It will be non-null if
- the QML class node is a QML element.
- */
-ClassNode *Node::declarativeCppNode()
-{
- QmlTypeNode *qcn = qmlTypeNode();
- if (qcn)
- return qcn->classNode();
- return nullptr;
-}
-
-/*!
Returns \c true if the node's status is \c Internal, or if
its parent is a class with \c Internal status.
*/
@@ -1178,11 +868,20 @@ void Node::setLocation(const Location &t)
}
/*!
- Returns true if this node is sharing a comment and the
- shared comment is not empty.
+ Returns \c true if this node is documented, or it represents
+ a documented node read from the index ('had doc'), or this
+ node is sharing a non-empty doc with other nodes.
+
+ \sa Doc
*/
-bool Node::hasSharedDoc() const
+bool Node::hasDoc() const
{
+ if (m_hadDoc)
+ return true;
+
+ if (!m_doc.isEmpty())
+ return true;
+
return (m_sharedCommentNode && m_sharedCommentNode->hasDoc());
}
@@ -1239,7 +938,7 @@ QString Node::fullDocumentName() const
if (!n->name().isEmpty())
pieces.insert(0, n->name());
- if ((n->isQmlType() || n->isJsType()) && !n->logicalModuleName().isEmpty()) {
+ if (n->isQmlType() && !n->logicalModuleName().isEmpty()) {
pieces.insert(0, n->logicalModuleName());
break;
}
@@ -1256,7 +955,7 @@ QString Node::fullDocumentName() const
// Create a name based on the type of the ancestor node.
QString concatenator = "::";
- if (n->isQmlType() || n->isJsType())
+ if (n->isQmlType())
concatenator = QLatin1Char('.');
if (n->isTextPageNode())
@@ -1266,66 +965,38 @@ QString Node::fullDocumentName() const
}
/*!
- Find the module (Qt Core, Qt GUI, etc.) to which the class belongs.
- We do this by obtaining the full path to the header file's location
- and examine everything between "src/" and the filename. This is
- semi-dirty because we are assuming a particular directory structure.
+ Sets the Node status to Node::Deprecated, unless \a sinceVersion represents
+ a future version.
- This function is only really useful if the class's module has not
- been defined in the header file with a QT_MODULE macro or with an
- \inmodule command in the documentation.
- */
-QString Node::physicalModuleName() const
-{
- if (!m_physicalModuleName.isEmpty())
- return m_physicalModuleName;
-
- QString path = location().filePath();
- QString pattern = QString("src") + QDir::separator();
- qsizetype start = path.lastIndexOf(pattern);
-
- if (start == -1)
- return QString();
-
- QString moduleDir = path.mid(start + pattern.size());
- qsizetype finish = moduleDir.indexOf(QDir::separator());
-
- if (finish == -1)
- return QString();
-
- QString physicalModuleName = moduleDir.left(finish);
-
- if (physicalModuleName == QLatin1String("corelib"))
- return QLatin1String("QtCore");
- else if (physicalModuleName == QLatin1String("uitools"))
- return QLatin1String("QtUiTools");
- else if (physicalModuleName == QLatin1String("gui"))
- return QLatin1String("QtGui");
- else if (physicalModuleName == QLatin1String("network"))
- return QLatin1String("QtNetwork");
- else if (physicalModuleName == QLatin1String("opengl"))
- return QLatin1String("QtOpenGL");
- else if (physicalModuleName == QLatin1String("svg"))
- return QLatin1String("QtSvg");
- else if (physicalModuleName == QLatin1String("sql"))
- return QLatin1String("QtSql");
- else if (physicalModuleName == QLatin1String("qtestlib"))
- return QLatin1String("QtTest");
- else if (moduleDir.contains("webkit"))
- return QLatin1String("QtWebKit");
- else if (physicalModuleName == QLatin1String("xml"))
- return QLatin1String("QtXml");
- else
- return QString();
-}
-void Node::setDeprecatedSince(const QString &sinceVersion)
+ Stores \a sinceVersion representing the version in which the deprecation
+ took (or will take) place.
+
+ Fetches the current version from the config ('version' variable) as a
+ string, and compared to \a sinceVersion. If both string represent a valid
+ version and \a sinceVersion is greater than the currect version, do not
+ mark the node as deprecated; leave it active.
+*/
+void Node::setDeprecated(const QString &sinceVersion)
{
+
if (!m_deprecatedSince.isEmpty())
qCWarning(lcQdoc) << QStringLiteral(
"Setting deprecated since version for %1 to %2 even though it "
"was already set to %3. This is very unexpected.")
.arg(this->m_name, sinceVersion, this->m_deprecatedSince);
m_deprecatedSince = sinceVersion;
+
+ if (!sinceVersion.isEmpty()) {
+ QVersionNumber since = QVersionNumber::fromString(sinceVersion).normalized();
+ QVersionNumber current = QVersionNumber::fromString(
+ Config::instance().get(CONFIG_VERSION).asString())
+ .normalized();
+ if (!current.isNull() && !since.isNull()) {
+ if (current < since)
+ return;
+ }
+ }
+ setStatus(Deprecated);
}
/*! \fn Node *Node::clone(Aggregate *parent)
@@ -1354,27 +1025,14 @@ void Node::setDeprecatedSince(const QString &sinceVersion)
Sets this node's Genus to \a t.
*/
-/*! \fn PageType Node::pageType() const
- Returns this node's page type.
-
- \sa PageType
-*/
-
-/*! \fn void Node::setPageType(PageType t)
- Sets this node's page type to \a t.
-
- \sa PageType
-*/
+/*! \fn QString Node::signature(Node::SignatureOptions options) const
-/*! \fn QString Node::signature(bool values, bool noReturnType, bool templateParams) const
-
- If this node is a FunctionNode, this function returns the function's
- signature, including default values if \a values is \c true,
- function's return type if \a noReturnType is \c false, and
- prefixed with 'template <parameter_list>' for function templates
- if templateParams is \true.
+ Specific parts of the signature are included according to flags in
+ \a options.
If this node is not a FunctionNode, this function returns plainName().
+
+ \sa FunctionNode::signature()
*/
/*! \fn const QString &Node::fileNameBase() const
@@ -1437,11 +1095,6 @@ void Node::setDeprecatedSince(const QString &sinceVersion)
of something. This function is called when the \c relates command is seen.
*/
-/*! \fn void Node::setOutputFileName(const QString &f)
- In a PageNode, this function sets the node's output file name to \a f.
- In a non-PageNode, this function does nothing.
- */
-
/*! \fn void Node::addMember(Node *node)
In a CollectionNode, this function adds \a node to the collection
node's members list. It does nothing if this node is not a CollectionNode.
@@ -1467,16 +1120,6 @@ void Node::setDeprecatedSince(const QString &sinceVersion)
data member is set to \c true.
*/
-/*! \fn void Node::getMemberNamespaces(NodeMap& out)
- If this is a CollectionNode, \a out is loaded with all the collection
- members that are namespaces.
- */
-
-/*! \fn void getMemberClasses(NodeMap& out) const { }
- If this is a CollectionNode, \a out is loaded with all the collection
- members that are classes.
- */
-
/*! \fn void Node::setDataType(const QString &dataType)
If this node is a PropertyNode or a QmlPropertyNode, its
data type data member is set to \a dataType. Otherwise,
@@ -1503,11 +1146,6 @@ void Node::setDeprecatedSince(const QString &sinceVersion)
string.
*/
-/*! \fn void Node::setNoAutoList(bool b)
- If this node is a PageNode, the node's \c {no autolist} flag is set to \a b.
- Otherwise the function does nothing.
- */
-
/*! \fn bool Node::docMustBeGenerated() const
This function is called to perform a test to decide if the node must have
documentation generated. In the Node base class, it always returns \c false.
@@ -1577,26 +1215,6 @@ void Node::setDeprecatedSince(const QString &sinceVersion)
Returns the node's name data member.
*/
-/*! \fn QString Node::nameForLists() const
- If this node is a PageNode or a HeaderNode, title() is returned.
- Otherwise name() is returned.
- */
-
-/*! \fn QString Node::outputFileName() const
- If this node is a PageNode, the name of the output file that will be
- generated for the node is returned. Otherwise an empty string is returned.
- */
-
-/*! \fn QString Node::obsoleteLink() const
- If this node is a ClassNode or a QmlTypeNode, the link to the obsolete members
- page is returned. Otherwise an empty string is returned.
- */
-
-/*! \fn void Node::setObsoleteLink(const QString &t)
- If this node is a ClassNode or a QmlTypeNode, the link to the obsolete members
- page is set to \a t. Otherwise the function does nothing.
- */
-
/*! \fn void Node::setQtVariable(const QString &v)
If this node is a CollectionNode, its QT variable is set to \a v.
Otherwise the function does nothing. I don't know what the QT variable
@@ -1615,7 +1233,7 @@ void Node::setDeprecatedSince(const QString &sinceVersion)
\c false. I don't know what the tag is used for.
*/
-/*! \fn const QMap<LinkType, QPair<QString, QString> > &Node::links() const
+/*! \fn const QMap<LinkType, std::pair<QString, QString> > &Node::links() const
Returns a reference to this node's link map. The link map should
probably be moved to the PageNode, because it contains links to the
start page, next page, previous page, and contents page, and these
@@ -1657,13 +1275,6 @@ void Node::setDeprecatedSince(const QString &sinceVersion)
\sa Doc
*/
-/*! \fn bool Node::hasDoc() const
- Returns \c true if the node has documentation, i.e. if its Doc
- data member is not empty.
-
- \sa Doc
- */
-
/*! \fn Status Node::status() const
Returns the node's status value.
@@ -1688,7 +1299,7 @@ void Node::setDeprecatedSince(const QString &sinceVersion)
/*! \fn QString Node::qmlTypeName() const
If this is a QmlPropertyNode or a FunctionNode representing a QML
- or javascript methos, this function returns the qmlTypeName() of
+ method, this function returns the qmlTypeName() of
the parent() node. Otherwise it returns the name data member.
*/
@@ -1722,7 +1333,7 @@ void Node::setDeprecatedSince(const QString &sinceVersion)
version number is not absolutely necessary.
The strings are stored in the appropriate data members for use
- when the QML or javascript module page is generated.
+ when the QML module page is generated.
*/
/*! \fn void Node::setLogicalModuleInfo(const QStringList &info)
@@ -1734,7 +1345,7 @@ void Node::setDeprecatedSince(const QString &sinceVersion)
the minor version number is not strictly necessary.
The strings are stored in the appropriate data members for use
- when the QML or javascript module page is generated. This overload
+ when the QML module page is generated. This overload
of the function is called when qdoc is reading an index file.
*/
@@ -1761,18 +1372,6 @@ void Node::setDeprecatedSince(const QString &sinceVersion)
type.
*/
-/*! \fn const QString &Node::outputSubdirectory() const
- Returns the node's output subdirector, which is the subdirectory
- of the output directory where the node's documentation file is
- written.
- */
-
-/*! \fn void Node::setOutputSubdirectory(const QString &t)
- Sets the node's output subdirectory to \a t. This is the subdirector
- of the output directory where the node's documentation file will be
- written.
- */
-
/*! \fn NodeType Node::goal(const QString &t)
When a square-bracket parameter is used in a qdoc command, this
function might be called to convert the text string \a t obtained
diff --git a/src/qdoc/node.h b/src/qdoc/qdoc/src/qdoc/node.h
index 584c8d144..3b5eb56bd 100644
--- a/src/qdoc/node.h
+++ b/src/qdoc/qdoc/src/qdoc/node.h
@@ -1,48 +1,25 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the tools applications of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#ifndef NODE_H
#define NODE_H
#include "access.h"
+#include "comparisoncategory.h"
#include "doc.h"
#include "enumitem.h"
#include "importrec.h"
#include "parameters.h"
#include "relatedclass.h"
-#include "usingclause.h"
+#include "template_declaration.h"
#include <QtCore/qdir.h>
#include <QtCore/qlist.h>
#include <QtCore/qmap.h>
-#include <QtCore/qpair.h>
#include <QtCore/qstringlist.h>
+#include <optional>
+
QT_BEGIN_NAMESPACE
class Aggregate;
@@ -61,7 +38,6 @@ class SharedCommentNode;
class Tree;
class TypedefNode;
-typedef QMap<QString, FunctionNode *> FunctionMap;
typedef QList<Node *> NodeList;
typedef QList<ClassNode *> ClassList;
typedef QList<Node *> NodeVector;
@@ -96,11 +72,7 @@ public:
QmlType,
QmlModule,
QmlProperty,
- QmlBasicType,
- JsType,
- JsModule,
- JsProperty,
- JsBasicType,
+ QmlValueType,
SharedComment,
Collection,
Proxy
@@ -109,10 +81,9 @@ public:
enum Genus : unsigned char {
DontCare = 0x0,
CPP = 0x1,
- JS = 0x2,
QML = 0x4,
DOC = 0x8,
- API = CPP | JS | QML
+ API = CPP | QML
};
enum Status : unsigned char {
@@ -130,19 +101,15 @@ public:
ThreadSafe
};
- enum LinkType : unsigned char { StartLink, NextLink, PreviousLink, ContentsLink };
-
- enum PageType : unsigned char {
- NoPageType,
- AttributionPage,
- ApiPage,
- ArticlePage,
- ExamplePage,
- HowToPage,
- OverviewPage,
- TutorialPage,
- FAQPage
+ enum SignatureOption : unsigned char {
+ SignaturePlain = 0x0,
+ SignatureDefaultValues = 0x1,
+ SignatureReturnType = 0x2,
+ SignatureTemplateParams = 0x4
};
+ Q_DECLARE_FLAGS(SignatureOptions, SignatureOption)
+
+ enum LinkType : unsigned char { StartLink, NextLink, PreviousLink, ContentsLink };
enum FlagValue { FlagValueDefault = -1, FlagValueFalse = 0, FlagValueTrue = 1 };
@@ -153,17 +120,11 @@ public:
[[nodiscard]] NodeType nodeType() const { return m_nodeType; }
[[nodiscard]] QString nodeTypeString() const;
- bool changeType(NodeType from, NodeType to);
[[nodiscard]] Genus genus() const { return m_genus; }
void setGenus(Genus t) { m_genus = t; }
static Genus getGenus(NodeType t);
- [[nodiscard]] PageType pageType() const { return m_pageType; }
- void setPageType(PageType t) { m_pageType = t; }
- void setPageType(const QString &t);
- static PageType getPageType(NodeType t);
-
[[nodiscard]] bool isActive() const { return m_status == Active; }
[[nodiscard]] bool isClass() const { return m_nodeType == Class; }
[[nodiscard]] bool isCppNode() const { return genus() == CPP; }
@@ -178,11 +139,6 @@ public:
[[nodiscard]] bool isGroup() const { return m_nodeType == Group; }
[[nodiscard]] bool isHeader() const { return m_nodeType == HeaderFile; }
[[nodiscard]] bool isIndexNode() const { return m_indexNodeFlag; }
- [[nodiscard]] bool isJsBasicType() const { return m_nodeType == JsBasicType; }
- [[nodiscard]] bool isJsModule() const { return m_nodeType == JsModule; }
- [[nodiscard]] bool isJsNode() const { return genus() == JS; }
- [[nodiscard]] bool isJsProperty() const { return m_nodeType == JsProperty; }
- [[nodiscard]] bool isJsType() const { return m_nodeType == JsType; }
[[nodiscard]] bool isModule() const { return m_nodeType == Module; }
[[nodiscard]] bool isNamespace() const { return m_nodeType == Namespace; }
[[nodiscard]] bool isPage() const { return m_nodeType == Page; }
@@ -192,11 +148,11 @@ public:
[[nodiscard]] bool isProxyNode() const { return m_nodeType == Proxy; }
[[nodiscard]] bool isPublic() const { return m_access == Access::Public; }
[[nodiscard]] bool isProtected() const { return m_access == Access::Protected; }
- [[nodiscard]] bool isQmlBasicType() const { return m_nodeType == QmlBasicType; }
+ [[nodiscard]] bool isQmlBasicType() const { return m_nodeType == QmlValueType; }
[[nodiscard]] bool isQmlModule() const { return m_nodeType == QmlModule; }
[[nodiscard]] bool isQmlNode() const { return genus() == QML; }
[[nodiscard]] bool isQmlProperty() const { return m_nodeType == QmlProperty; }
- [[nodiscard]] bool isQmlType() const { return m_nodeType == QmlType; }
+ [[nodiscard]] bool isQmlType() const { return m_nodeType == QmlType || m_nodeType == QmlValueType; }
[[nodiscard]] bool isRelatedNonmember() const { return m_relatedNonmember; }
[[nodiscard]] bool isStruct() const { return m_nodeType == Struct; }
[[nodiscard]] bool isSharedCommentNode() const { return m_nodeType == SharedComment; }
@@ -224,8 +180,6 @@ public:
[[nodiscard]] virtual bool isInternal() const;
[[nodiscard]] virtual bool isMacro() const { return false; }
[[nodiscard]] virtual bool isPageNode() const { return false; } // means "generates a doc page"
- [[nodiscard]] virtual bool isQtQuickNode() const { return false; }
- [[nodiscard]] virtual bool isReadOnly() const { return false; }
[[nodiscard]] virtual bool isRelatableType() const { return false; }
[[nodiscard]] virtual bool isMarkedReimp() const { return false; }
[[nodiscard]] virtual bool isPropertyGroup() const { return false; }
@@ -240,8 +194,7 @@ public:
QString plainFullName(const Node *relative = nullptr) const;
[[nodiscard]] QString plainSignature() const;
QString fullName(const Node *relative = nullptr) const;
- [[nodiscard]] virtual QString signature(bool, bool) const { return plainName(); }
- [[nodiscard]] virtual QString signature(bool, bool, bool) const { return plainName(); }
+ [[nodiscard]] virtual QString signature(Node::SignatureOptions) const { return plainName(); }
[[nodiscard]] const QString &fileNameBase() const { return m_fileNameBase; }
[[nodiscard]] bool hasFileNameBase() const { return !m_fileNameBase.isEmpty(); }
@@ -255,25 +208,23 @@ public:
void setSince(const QString &since);
void setPhysicalModuleName(const QString &name) { m_physicalModuleName = name; }
void setUrl(const QString &url) { m_url = url; }
- void setTemplateDecl(const QString &t) { m_templateDecl = t; }
+ void setTemplateDecl(std::optional<RelaxedTemplateDeclaration> t) { m_templateDecl = t; }
void setReconstitutedBrief(const QString &t) { m_reconstitutedBrief = t; }
void setParent(Aggregate *n) { m_parent = n; }
void setIndexNodeFlag(bool isIndexNode = true) { m_indexNodeFlag = isIndexNode; }
void setHadDoc() { m_hadDoc = true; }
+ void setComparisonCategory(const ComparisonCategory &category) { m_comparisonCategory = category; }
+ [[nodiscard]] ComparisonCategory comparisonCategory() const { return m_comparisonCategory; }
virtual void setRelatedNonmember(bool b) { m_relatedNonmember = b; }
- virtual void setOutputFileName(const QString &) {}
virtual void addMember(Node *) {}
[[nodiscard]] virtual bool hasNamespaces() const { return false; }
[[nodiscard]] virtual bool hasClasses() const { return false; }
virtual void setAbstract(bool) {}
virtual void setWrapper() {}
- virtual void getMemberNamespaces(NodeMap &) {}
- virtual void getMemberClasses(NodeMap &) const {}
virtual void setDataType(const QString &) {}
[[nodiscard]] virtual bool wasSeen() const { return false; }
virtual void appendGroupName(const QString &) {}
[[nodiscard]] virtual QString element() const { return QString(); }
- virtual void setNoAutoList(bool) {}
[[nodiscard]] virtual bool docMustBeGenerated() const { return false; }
[[nodiscard]] virtual QString title() const { return name(); }
@@ -290,25 +241,22 @@ public:
virtual void markDefault() {}
virtual void markReadOnly(bool) {}
- [[nodiscard]] bool match(const QList<int> &types) const;
[[nodiscard]] Aggregate *parent() const { return m_parent; }
[[nodiscard]] const QString &name() const { return m_name; }
- [[nodiscard]] QString physicalModuleName() const;
+ [[nodiscard]] QString physicalModuleName() const { return m_physicalModuleName; }
[[nodiscard]] QString url() const { return m_url; }
- [[nodiscard]] virtual QString nameForLists() const { return m_name; }
- [[nodiscard]] virtual QString outputFileName() const { return QString(); }
- [[nodiscard]] virtual QString obsoleteLink() const { return QString(); }
- virtual void setObsoleteLink(const QString &) {}
virtual void setQtVariable(const QString &) {}
[[nodiscard]] virtual QString qtVariable() const { return QString(); }
virtual void setQtCMakeComponent(const QString &) {}
+ virtual void setQtCMakeTargetItem(const QString &) {}
[[nodiscard]] virtual QString qtCMakeComponent() const { return QString(); }
+ [[nodiscard]] virtual QString qtCMakeTargetItem() const { return QString(); }
[[nodiscard]] virtual bool hasTag(const QString &) const { return false; }
- void setDeprecatedSince(const QString &sinceVersion);
+ void setDeprecated(const QString &sinceVersion);
[[nodiscard]] const QString &deprecatedSince() const { return m_deprecatedSince; }
- [[nodiscard]] const QMap<LinkType, QPair<QString, QString>> &links() const { return m_linkMap; }
+ [[nodiscard]] const QMap<LinkType, std::pair<QString, QString>> &links() const { return m_linkMap; }
void setLink(LinkType linkType, const QString &link, const QString &desc);
[[nodiscard]] Access access() const { return m_access; }
@@ -323,17 +271,16 @@ public:
{
return !isPrivate() && !isInternal() && !isDontDocument() && hasDoc();
}
- [[nodiscard]] bool hasDoc() const { return (m_hadDoc || !m_doc.isEmpty()); }
+ [[nodiscard]] bool hasDoc() const;
[[nodiscard]] bool hadDoc() const { return m_hadDoc; }
[[nodiscard]] Status status() const { return m_status; }
[[nodiscard]] ThreadSafeness threadSafeness() const;
[[nodiscard]] ThreadSafeness inheritedThreadSafeness() const;
[[nodiscard]] QString since() const { return m_since; }
- [[nodiscard]] const QString &templateDecl() const { return m_templateDecl; }
+ [[nodiscard]] const std::optional<RelaxedTemplateDeclaration>& templateDecl() const { return m_templateDecl; }
[[nodiscard]] const QString &reconstitutedBrief() const { return m_reconstitutedBrief; }
[[nodiscard]] bool isSharingComment() const { return (m_sharedCommentNode != nullptr); }
- [[nodiscard]] bool hasSharedDoc() const;
void setSharedCommentNode(SharedCommentNode *t) { m_sharedCommentNode = t; }
SharedCommentNode *sharedCommentNode() { return m_sharedCommentNode; }
@@ -343,16 +290,12 @@ public:
[[nodiscard]] virtual QString logicalModuleName() const { return QString(); }
[[nodiscard]] virtual QString logicalModuleVersion() const { return QString(); }
[[nodiscard]] virtual QString logicalModuleIdentifier() const { return QString(); }
- virtual void setLogicalModuleInfo(const QString &) {}
+
virtual void setLogicalModuleInfo(const QStringList &) {}
[[nodiscard]] virtual CollectionNode *logicalModule() const { return nullptr; }
virtual void setQmlModule(CollectionNode *) {}
virtual ClassNode *classNode() { return nullptr; }
virtual void setClassNode(ClassNode *) {}
- QmlTypeNode *qmlTypeNode();
- ClassNode *declarativeCppNode();
- [[nodiscard]] const QString &outputSubdirectory() const { return m_outSubDir; }
- virtual void setOutputSubdirectory(const QString &t) { m_outSubDir = t; }
[[nodiscard]] QString fullDocumentName() const;
QString qualifyCppName();
QString qualifyQmlName();
@@ -361,8 +304,6 @@ public:
static FlagValue toFlagValue(bool b);
static bool fromFlagValue(FlagValue fv, bool defaultValue);
static QString nodeTypeString(NodeType t);
- static void initialize();
- static NodeType goal(const QString &t) { return goals.value(t); }
static bool nodeNameLessThan(const Node *first, const Node *second);
protected:
@@ -373,8 +314,8 @@ private:
Genus m_genus {};
Access m_access { Access::Public };
ThreadSafeness m_safeness { UnspecifiedSafeness };
- PageType m_pageType { NoPageType };
Status m_status { Active };
+ ComparisonCategory m_comparisonCategory { ComparisonCategory::None };
bool m_indexNodeFlag : 1;
bool m_relatedNonmember : 1;
bool m_hadDoc : 1;
@@ -385,19 +326,18 @@ private:
Location m_declLocation {};
Location m_defLocation {};
Doc m_doc {};
- QMap<LinkType, QPair<QString, QString>> m_linkMap {};
+ QMap<LinkType, std::pair<QString, QString>> m_linkMap {};
QString m_fileNameBase {};
QString m_physicalModuleName {};
QString m_url {};
QString m_since {};
- QString m_templateDecl {};
+ std::optional<RelaxedTemplateDeclaration> m_templateDecl{std::nullopt};
QString m_reconstitutedBrief {};
- QString m_outSubDir {};
- static QStringMap operators;
- static QMap<QString, Node::NodeType> goals;
QString m_deprecatedSince {};
};
+Q_DECLARE_OPERATORS_FOR_FLAGS(Node::SignatureOptions)
+
QT_END_NAMESPACE
#endif
diff --git a/src/qdoc/openedlist.cpp b/src/qdoc/qdoc/src/qdoc/openedlist.cpp
index 13dc4a307..a85e45ec4 100644
--- a/src/qdoc/openedlist.cpp
+++ b/src/qdoc/qdoc/src/qdoc/openedlist.cpp
@@ -1,30 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the tools applications of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "openedlist.h"
@@ -40,7 +15,7 @@ OpenedList::OpenedList(ListStyle style) : sty(style), ini(1), nex(0) {}
OpenedList::OpenedList(const Location &location, const QString &hint) : sty(Bullet), ini(1)
{
- QRegularExpression hintSyntax("^(\\W*)([0-9]+|[A-Z]+|[a-z]+)(\\W*)$");
+ static const QRegularExpression hintSyntax("^(\\W*)([0-9]+|[A-Z]+|[a-z]+)(\\W*)$");
auto match = hintSyntax.match(hint);
if (match.hasMatch()) {
diff --git a/src/qdoc/qdoc/src/qdoc/openedlist.h b/src/qdoc/qdoc/src/qdoc/openedlist.h
new file mode 100644
index 000000000..cd0c54e40
--- /dev/null
+++ b/src/qdoc/qdoc/src/qdoc/openedlist.h
@@ -0,0 +1,47 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#ifndef OPENEDLIST_H
+#define OPENEDLIST_H
+
+#include "location.h"
+
+#include <QtCore/qstring.h>
+
+QT_BEGIN_NAMESPACE
+
+class OpenedList
+{
+public:
+ enum ListStyle { Bullet, Tag, Value, Numeric, UpperAlpha, LowerAlpha, UpperRoman, LowerRoman };
+
+ OpenedList() : sty(Bullet), ini(1), nex(0) {}
+ explicit OpenedList(ListStyle style);
+ OpenedList(const Location &location, const QString &hint);
+
+ void next() { nex++; }
+
+ [[nodiscard]] bool isStarted() const { return nex >= ini; }
+ [[nodiscard]] ListStyle style() const { return sty; }
+ [[nodiscard]] QString styleString() const;
+ [[nodiscard]] int number() const { return nex; }
+ [[nodiscard]] QString numberString() const;
+ [[nodiscard]] QString prefix() const { return pref; }
+ [[nodiscard]] QString suffix() const { return suff; }
+
+private:
+ static int fromAlpha(const QString &str);
+ static QString toRoman(int n);
+ static int fromRoman(const QString &str);
+
+ ListStyle sty;
+ int ini;
+ int nex;
+ QString pref;
+ QString suff;
+};
+Q_DECLARE_TYPEINFO(OpenedList, Q_RELOCATABLE_TYPE);
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/qdoc/pagenode.cpp b/src/qdoc/qdoc/src/qdoc/pagenode.cpp
index 42a80535f..5f01bc24f 100644
--- a/src/qdoc/pagenode.cpp
+++ b/src/qdoc/qdoc/src/qdoc/pagenode.cpp
@@ -1,30 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2020 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the tools applications of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2020 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "pagenode.h"
@@ -95,16 +70,6 @@ bool PageNode::setTitle(const QString &title)
or \c {\\namespace}, for example.
*/
-/*! \fn PageNode::PageNode(Aggregate *parent, const QString &name, PageType ptype)
- This constructor is called when the argument to the \c {\\page} command
- contains a space, which means the second word of the argument is the page type.
- It creates a PageNode that has node type Node::Page, with the specified
- \a parent and \name, and the \a ptype is that second word of the argument to
- the \c {\\page} command.
-
- \sa Node::PageType
- */
-
/*! \fn PageNode::~PageNode()
The destructor is virtual, and it does nothing.
*/
@@ -121,10 +86,6 @@ bool PageNode::setTitle(const QString &title)
\sa Aggregate.
*/
-/*! \fn QString PageNode::nameForLists() const
- Returns the title().
- */
-
/*! \fn QString PageNode::imageFileName() const
If this PageNode is an ExampleNode, the image file name
data member is returned. Otherwise an empty string is
@@ -153,12 +114,4 @@ bool PageNode::setTitle(const QString &title)
Appends \a t to the list of group names.
*/
-/*! \fn void PageNode::setOutputFileName(const QString &f)
- Sets this PageNode's output file name to \a f.
- */
-
-/*! \fn QString PageNode::outputFileName() const
- Returns this PageNode's output file name.
- */
-
QT_END_NAMESPACE
diff --git a/src/qdoc/qdoc/src/qdoc/pagenode.h b/src/qdoc/qdoc/src/qdoc/pagenode.h
new file mode 100644
index 000000000..14231bccd
--- /dev/null
+++ b/src/qdoc/qdoc/src/qdoc/pagenode.h
@@ -0,0 +1,82 @@
+// Copyright (C) 2020 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#ifndef PAGENODE_H
+#define PAGENODE_H
+
+#include "node.h"
+
+#include <QtCore/qglobal.h>
+#include <QtCore/qstring.h>
+#include <QtCore/qstringlist.h>
+
+QT_BEGIN_NAMESPACE
+
+class Aggregate;
+
+class PageNode : public Node
+{
+public:
+ PageNode(Aggregate *parent, const QString &name) : Node(Page, parent, name) {}
+ PageNode(NodeType type, Aggregate *parent, const QString &name) : Node(type, parent, name) {}
+
+ [[nodiscard]] bool isPageNode() const override { return true; }
+ [[nodiscard]] bool isTextPageNode() const override
+ {
+ return !isAggregate();
+ } // PageNode but not Aggregate
+
+ [[nodiscard]] QString title() const override { return m_title; }
+ [[nodiscard]] QString subtitle() const override { return m_subtitle; }
+ [[nodiscard]] QString fullTitle() const override;
+ bool setTitle(const QString &title) override;
+ bool setSubtitle(const QString &subtitle) override
+ {
+ m_subtitle = subtitle;
+ return true;
+ }
+ [[nodiscard]] virtual QString imageFileName() const { return QString(); }
+ virtual void setImageFileName(const QString &) {}
+
+ [[nodiscard]] bool noAutoList() const { return m_noAutoList; }
+ void setNoAutoList(bool b) { m_noAutoList = b; }
+ [[nodiscard]] const QStringList &groupNames() const { return m_groupNames; }
+ void appendGroupName(const QString &t) override { m_groupNames.append(t); }
+
+ [[nodiscard]] const PageNode *navigationParent() const { return m_navParent; }
+ void setNavigationParent(const PageNode *parent) { m_navParent = parent; }
+
+ void markAttribution() { is_attribution = true; }
+ [[nodiscard]] bool isAttribution() const { return is_attribution; }
+
+protected:
+ friend class Node;
+
+protected:
+ bool m_noAutoList { false };
+ QString m_title {};
+ QString m_subtitle {};
+ QStringList m_groupNames {};
+
+ // Marks the PageNode as being or not being an attribution.
+ // A PageNode that is an attribution represents a page that serves
+ // to present the third party software that a project uses,
+ // together with its license, link to the website of the project
+ // and so on.
+ // PageNode that are attribution are expected to be generate only
+ // for the Qt project by the QAttributionScanner, as part of the
+ // built of Qt's documentation.
+ //
+ // PageNodes that are attribution are marked primarily so that
+ // QDoc is able to generate a specialized list of attributions for
+ // a specific module through the use of the "\generatedlist"
+ // command, and behave like any other PageNode otherwise.
+ bool is_attribution{ false };
+
+private:
+ const PageNode *m_navParent { nullptr };
+};
+
+QT_END_NAMESPACE
+
+#endif // PAGENODE_H
diff --git a/src/qdoc/parameters.cpp b/src/qdoc/qdoc/src/qdoc/parameters.cpp
index 03d4d8bb2..39f88b48f 100644
--- a/src/qdoc/parameters.cpp
+++ b/src/qdoc/qdoc/src/qdoc/parameters.cpp
@@ -1,30 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the tools applications of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "parameters.h"
@@ -469,7 +444,7 @@ void Parameters::set(const QString &signature)
QStringList commaSplit = signature.split(',');
m_parameters.resize(commaSplit.size());
int i = 0;
- for (const auto &item : qAsConst(commaSplit)) {
+ for (const auto &item : std::as_const(commaSplit)) {
QStringList blankSplit = item.split(' ', Qt::SkipEmptyParts);
QString pDefault;
qsizetype defaultIdx = blankSplit.indexOf(QStringLiteral("="));
@@ -484,7 +459,7 @@ void Parameters::set(const QString &signature)
qSwap(pType, pName);
else {
int j = 0;
- while (j < pName.length() && !pName.at(j).isLetter())
+ while (j < pName.size() && !pName.at(j).isLetter())
j++;
if (j > 0) {
pType += QChar(' ') + pName.left(j);
diff --git a/src/qdoc/parameters.h b/src/qdoc/qdoc/src/qdoc/parameters.h
index f2f4d93a1..1417b0958 100644
--- a/src/qdoc/parameters.h
+++ b/src/qdoc/qdoc/src/qdoc/parameters.h
@@ -1,30 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the tools applications of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#ifndef PARAMETERS_H
#define PARAMETERS_H
diff --git a/src/qdoc/qdoc/src/qdoc/parsererror.cpp b/src/qdoc/qdoc/src/qdoc/parsererror.cpp
new file mode 100644
index 000000000..b55660572
--- /dev/null
+++ b/src/qdoc/qdoc/src/qdoc/parsererror.cpp
@@ -0,0 +1,90 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "parsererror.h"
+#include "node.h"
+#include "qdocdatabase.h"
+#include "config.h"
+#include "utilities.h"
+
+#include <QtCore/qregularexpression.h>
+
+using namespace Qt::Literals::StringLiterals;
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class FnMatchError
+ \brief Encapsulates information about \fn match error during parsing.
+*/
+
+/*!
+ \variable FnMatchError::signature
+
+ Signature for the \fn topic that failed to match.
+*/
+
+/*!
+ \relates FnMatchError
+
+ Returns \c true if any parent of a C++ function represented by
+ \a signature is documented as \\internal.
+*/
+bool isParentInternal(const QString &signature)
+{
+ const QRegularExpression scoped_fn{R"((?:\w+(?:<[^>]+>)?::)+~?\w\S*\()"};
+ auto match = scoped_fn.match(signature);
+ if (!match.isValid())
+ return false;
+
+ auto scope = match.captured().split("::"_L1);
+ scope.removeLast(); // Drop function name
+
+ for (auto &s : scope)
+ if (qsizetype pos = s.indexOf('<'); pos >= 0)
+ s.truncate(pos);
+
+ auto parent = QDocDatabase::qdocDB()->findNodeByNameAndType(scope, &Node::isCppNode);
+ if (parent && !(parent->isClassNode() || parent->isNamespace())) {
+ qCDebug(lcQdoc).noquote()
+ << "Invalid scope:" << qPrintable(parent->nodeTypeString())
+ << qPrintable(parent->fullName())
+ << "for \\fn" << qPrintable(signature);
+ return false;
+ }
+
+ while (parent) {
+ if (parent->isInternal())
+ return true;
+ parent = parent->parent();
+ }
+
+ return false;
+}
+
+/*!
+ \class ParserErrorHandler
+ \brief Processes parser errors and outputs warnings for them.
+*/
+
+/*!
+ Generates a warning specific to FnMatchError.
+
+ Warnings for internal documentation are omitted. Specifically, this
+ (omission) happens if:
+
+ \list
+ \li \c {--showinternal} command line option is \b not
+ used, and
+ \li The warning is for an \\fn that is declared
+ under a namespace/class that is documented as
+ \\internal.
+ \endlist
+*/
+void ParserErrorHandler::operator()(const FnMatchError &e) const
+{
+ if (Config::instance().showInternal() || !isParentInternal(e.signature))
+ e.location.warning("Failed to find function when parsing \\fn %1"_L1.arg(e.signature));
+}
+
+QT_END_NAMESPACE
diff --git a/src/qdoc/qdoc/src/qdoc/parsererror.h b/src/qdoc/qdoc/src/qdoc/parsererror.h
new file mode 100644
index 000000000..85435366f
--- /dev/null
+++ b/src/qdoc/qdoc/src/qdoc/parsererror.h
@@ -0,0 +1,26 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#ifndef PARSERERROR_H
+#define PARSERERROR_H
+
+#include "location.h"
+
+#include <QtCore/qstring.h>
+
+QT_BEGIN_NAMESPACE
+
+struct FnMatchError {
+ QString signature {};
+ Location location {};
+
+};
+
+struct ParserErrorHandler
+{
+ void operator()(const FnMatchError &e) const;
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/qdoc/propertynode.cpp b/src/qdoc/qdoc/src/qdoc/propertynode.cpp
index 50de3459d..6607af5bd 100644
--- a/src/qdoc/propertynode.cpp
+++ b/src/qdoc/qdoc/src/qdoc/propertynode.cpp
@@ -1,30 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the tools applications of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "propertynode.h"
@@ -47,6 +22,29 @@ PropertyNode::PropertyNode(Aggregate *parent, const QString &name) : Node(Proper
// nothing
}
+
+/*!
+ Returns a string representing an access function \a role.
+*/
+QString PropertyNode::roleName(FunctionRole role)
+{
+ switch (role) {
+ case FunctionRole::Getter:
+ return "getter";
+ case FunctionRole::Setter:
+ return "setter";
+ case FunctionRole::Resetter:
+ return "resetter";
+ case FunctionRole::Notifier:
+ return "notifier";
+ case FunctionRole::Bindable:
+ return "bindable";
+ default:
+ break;
+ }
+ return QString();
+}
+
/*!
Sets this property's \e {overridden from} property to
\a baseProperty, which indicates that this property
@@ -59,16 +57,12 @@ PropertyNode::PropertyNode(Aggregate *parent, const QString &name) : Node(Proper
*/
void PropertyNode::setOverriddenFrom(const PropertyNode *baseProperty)
{
- for (int i = 0; i < NumFunctionRoles; ++i) {
+ for (qsizetype i{0}; i < (qsizetype)FunctionRole::NumFunctionRoles; ++i) {
if (m_functions[i].isEmpty())
m_functions[i] = baseProperty->m_functions[i];
}
if (m_stored == FlagValueDefault)
m_stored = baseProperty->m_stored;
- if (m_designable == FlagValueDefault)
- m_designable = baseProperty->m_designable;
- if (m_scriptable == FlagValueDefault)
- m_scriptable = baseProperty->m_scriptable;
if (m_writable == FlagValueDefault)
m_writable = baseProperty->m_writable;
if (m_user == FlagValueDefault)
@@ -83,7 +77,7 @@ void PropertyNode::setOverriddenFrom(const PropertyNode *baseProperty)
*/
QString PropertyNode::qualifiedDataType() const
{
- if (m_propertyType != Standard || m_type.startsWith(QLatin1String("const ")))
+ if (m_propertyType != PropertyType::StandardProperty || m_type.startsWith(QLatin1String("const ")))
return m_type;
if (setters().isEmpty() && resetters().isEmpty()) {
@@ -127,16 +121,15 @@ bool PropertyNode::hasAccessFunction(const QString &name) const
}
/*!
- Returns true if function \a functionNode has role \a r for this
- property.
+ Returns the role of \a functionNode for this property.
*/
PropertyNode::FunctionRole PropertyNode::role(const FunctionNode *functionNode) const
{
- for (int i = 0; i < 4; i++) {
+ for (qsizetype i{0}; i < (qsizetype)FunctionRole::NumFunctionRoles; i++) {
if (m_functions[i].contains(const_cast<FunctionNode *>(functionNode)))
return (FunctionRole)i;
}
- return Notifier;
+ return FunctionRole::Notifier; // TODO: Figure out a better way to handle 'not found'.
}
QT_END_NAMESPACE
diff --git a/src/qdoc/propertynode.h b/src/qdoc/qdoc/src/qdoc/propertynode.h
index e7df52111..9ae59932b 100644
--- a/src/qdoc/propertynode.h
+++ b/src/qdoc/qdoc/src/qdoc/propertynode.h
@@ -1,30 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the tools applications of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#ifndef PROPERTYNODE_H
#define PROPERTYNODE_H
@@ -42,9 +17,9 @@ class Aggregate;
class PropertyNode : public Node
{
public:
- enum PropertyType { Standard, Bindable };
- enum FunctionRole { Getter, Setter, Resetter, Notifier };
- enum { NumFunctionRoles = Notifier + 1 };
+ enum class PropertyType { StandardProperty, BindableProperty };
+ enum class FunctionRole { Getter, Setter, Resetter, Notifier, Bindable, NumFunctionRoles };
+ static QString roleName(FunctionRole role);
PropertyNode(Aggregate *parent, const QString &name);
@@ -52,8 +27,6 @@ public:
void addFunction(FunctionNode *function, FunctionRole role);
void addSignal(FunctionNode *function, FunctionRole role);
void setStored(bool stored) { m_stored = toFlagValue(stored); }
- void setDesignable(bool designable) { m_designable = toFlagValue(designable); }
- void setScriptable(bool scriptable) { m_scriptable = toFlagValue(scriptable); }
void setWritable(bool writable) { m_writable = toFlagValue(writable); }
void setOverriddenFrom(const PropertyNode *baseProperty);
void setConstant() { m_const = true; }
@@ -67,10 +40,10 @@ public:
{
return m_functions[(int)role];
}
- [[nodiscard]] const NodeList &getters() const { return functions(Getter); }
- [[nodiscard]] const NodeList &setters() const { return functions(Setter); }
- [[nodiscard]] const NodeList &resetters() const { return functions(Resetter); }
- [[nodiscard]] const NodeList &notifiers() const { return functions(Notifier); }
+ [[nodiscard]] const NodeList &getters() const { return functions(FunctionRole::Getter); }
+ [[nodiscard]] const NodeList &setters() const { return functions(FunctionRole::Setter); }
+ [[nodiscard]] const NodeList &resetters() const { return functions(FunctionRole::Resetter); }
+ [[nodiscard]] const NodeList &notifiers() const { return functions(FunctionRole::Notifier); }
[[nodiscard]] bool hasAccessFunction(const QString &name) const;
FunctionRole role(const FunctionNode *functionNode) const;
[[nodiscard]] bool isStored() const { return fromFlagValue(m_stored, storedDefault()); }
@@ -81,16 +54,13 @@ public:
[[nodiscard]] const PropertyNode *overriddenFrom() const { return m_overrides; }
[[nodiscard]] bool storedDefault() const { return true; }
- [[nodiscard]] bool designableDefault() const { return !setters().isEmpty(); }
[[nodiscard]] bool writableDefault() const { return !setters().isEmpty(); }
private:
QString m_type {};
- PropertyType m_propertyType { Standard };
- NodeList m_functions[NumFunctionRoles] {};
+ PropertyType m_propertyType { PropertyType::StandardProperty };
+ NodeList m_functions[(qsizetype)FunctionRole::NumFunctionRoles] {};
FlagValue m_stored { FlagValueDefault };
- FlagValue m_designable { FlagValueDefault };
- FlagValue m_scriptable { FlagValueDefault };
FlagValue m_writable { FlagValueDefault };
FlagValue m_user { FlagValueDefault };
bool m_const { false };
@@ -113,7 +83,7 @@ inline void PropertyNode::addSignal(FunctionNode *function, FunctionRole role)
inline NodeList PropertyNode::functions() const
{
NodeList list;
- for (int i = 0; i < NumFunctionRoles; ++i)
+ for (qsizetype i{0}; i < (qsizetype)FunctionRole::NumFunctionRoles; ++i)
list += m_functions[i];
return list;
}
diff --git a/src/qdoc/proxynode.cpp b/src/qdoc/qdoc/src/qdoc/proxynode.cpp
index 3a6c159b5..49e4be34e 100644
--- a/src/qdoc/proxynode.cpp
+++ b/src/qdoc/qdoc/src/qdoc/proxynode.cpp
@@ -1,30 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2020 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the tools applications of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2020 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "proxynode.h"
diff --git a/src/qdoc/qdoc/src/qdoc/proxynode.h b/src/qdoc/qdoc/src/qdoc/proxynode.h
new file mode 100644
index 000000000..cced34892
--- /dev/null
+++ b/src/qdoc/qdoc/src/qdoc/proxynode.h
@@ -0,0 +1,23 @@
+// Copyright (C) 2020 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#ifndef PROXYNODE_H
+#define PROXYNODE_H
+
+#include "aggregate.h"
+
+#include <QtCore/qglobal.h>
+
+QT_BEGIN_NAMESPACE
+
+class ProxyNode : public Aggregate
+{
+public:
+ ProxyNode(Aggregate *parent, const QString &name);
+ [[nodiscard]] bool docMustBeGenerated() const override { return true; }
+ [[nodiscard]] bool isRelatableType() const override { return true; }
+};
+
+QT_END_NAMESPACE
+
+#endif // PROXYNODE_H
diff --git a/src/qdoc/qdoc/src/qdoc/puredocparser.cpp b/src/qdoc/qdoc/src/qdoc/puredocparser.cpp
new file mode 100644
index 000000000..c6de06a9c
--- /dev/null
+++ b/src/qdoc/qdoc/src/qdoc/puredocparser.cpp
@@ -0,0 +1,74 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "puredocparser.h"
+
+#include "qdocdatabase.h"
+#include "tokenizer.h"
+
+#include <cerrno>
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ Parses the source file identified by \a filePath and adds its
+ parsed contents to the database. The \a location is used for
+ reporting errors.
+ */
+std::vector<UntiedDocumentation> PureDocParser::parse_qdoc_file(const QString &filePath)
+{
+ QFile in(filePath);
+ if (!in.open(QIODevice::ReadOnly)) {
+ location.error(
+ QStringLiteral("Can't open source file '%1' (%2)").arg(filePath, strerror(errno)));
+ return {};
+ }
+
+ return processQdocComments(in);
+}
+
+/*!
+ This is called by parseSourceFile() to do the actual parsing
+ and tree building. It only processes qdoc comments. It skips
+ everything else.
+ */
+std::vector<UntiedDocumentation> PureDocParser::processQdocComments(QFile& input_file)
+{
+ std::vector<UntiedDocumentation> untied{};
+
+ Tokenizer tokenizer(Location{input_file.fileName()}, input_file);
+
+ const QSet<QString> &commands = CppCodeParser::topic_commands + CppCodeParser::meta_commands;
+
+ int token = tokenizer.getToken();
+ while (token != Tok_Eoi) {
+ if (token != Tok_Doc) {
+ token = tokenizer.getToken();
+ continue;
+ }
+ QString comment = tokenizer.lexeme(); // returns an entire qdoc comment.
+ Location start_loc(tokenizer.location());
+ token = tokenizer.getToken();
+
+ Doc::trimCStyleComment(start_loc, comment);
+ Location end_loc(tokenizer.location());
+
+ // Doc constructor parses the comment.
+ Doc doc(start_loc, end_loc, comment, commands, CppCodeParser::topic_commands);
+ if (doc.topicsUsed().isEmpty()) {
+ doc.location().warning(QStringLiteral("This qdoc comment contains no topic command "
+ "(e.g., '\\%1', '\\%2').")
+ .arg(COMMAND_MODULE, COMMAND_PAGE));
+ continue;
+ }
+
+ if (hasTooManyTopics(doc))
+ continue;
+
+ untied.emplace_back(UntiedDocumentation{doc, QStringList()});
+ }
+
+ return untied;
+}
+
+QT_END_NAMESPACE
diff --git a/src/qdoc/qdoc/src/qdoc/puredocparser.h b/src/qdoc/qdoc/src/qdoc/puredocparser.h
new file mode 100644
index 000000000..d3467ed92
--- /dev/null
+++ b/src/qdoc/qdoc/src/qdoc/puredocparser.h
@@ -0,0 +1,31 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#ifndef PUREDOCPARSER_H
+#define PUREDOCPARSER_H
+
+#include "cppcodeparser.h"
+
+#include <QtCore/QFile>
+
+QT_BEGIN_NAMESPACE
+
+class Location;
+
+class PureDocParser
+{
+public:
+ PureDocParser(const Location& location) : location{location} {}
+
+ std::vector<UntiedDocumentation> parse_qdoc_file(const QString& filePath);
+
+private:
+ std::vector<UntiedDocumentation> processQdocComments(QFile& input_file);
+
+private:
+ const Location& location;
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/qdoc/qdoccommandlineparser.cpp b/src/qdoc/qdoc/src/qdoc/qdoccommandlineparser.cpp
index ff9035ba0..6586056a4 100644
--- a/src/qdoc/qdoccommandlineparser.cpp
+++ b/src/qdoc/qdoc/src/qdoc/qdoccommandlineparser.cpp
@@ -1,30 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2019 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the tools applications of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2019 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "qdoccommandlineparser.h"
@@ -49,6 +24,7 @@ QDocCommandLineParser::QDocCommandLineParser()
noLinkErrorsOption(QStringList() << QStringLiteral("no-link-errors")),
autoLinkErrorsOption(QStringList() << QStringLiteral("autolink-errors")),
debugOption(QStringList() << QStringLiteral("debug")),
+ atomsDumpOption("atoms-dump"),
prepareOption(QStringList() << QStringLiteral("prepare")),
generateOption(QStringList() << QStringLiteral("generate")),
logProgressOption(QStringList() << QStringLiteral("log-progress")),
@@ -61,87 +37,84 @@ QDocCommandLineParser::QDocCommandLineParser()
timestampsOption(QStringList() << QStringLiteral("timestamps")),
useDocBookExtensions(QStringList() << QStringLiteral("docbook-extensions"))
{
- setApplicationDescription(QCoreApplication::translate("qdoc", "Qt documentation generator"));
+ setApplicationDescription(QStringLiteral("Qt documentation generator"));
addHelpOption();
addVersionOption();
setSingleDashWordOptionMode(QCommandLineParser::ParseAsLongOptions);
- addPositionalArgument("file1.qdocconf ...", QCoreApplication::translate("qdoc", "Input files"));
+ addPositionalArgument("file1.qdocconf ...", QStringLiteral("Input files"));
- defineOption.setDescription(QCoreApplication::translate(
- "qdoc", "Define the argument as a macro while parsing sources"));
+ defineOption.setDescription(
+ QStringLiteral("Define the argument as a macro while parsing sources"));
defineOption.setValueName(QStringLiteral("macro[=def]"));
addOption(defineOption);
- dependsOption.setDescription(QCoreApplication::translate("qdoc", "Specify dependent modules"));
+ dependsOption.setDescription(QStringLiteral("Specify dependent modules"));
dependsOption.setValueName(QStringLiteral("module"));
addOption(dependsOption);
- highlightingOption.setDescription(QCoreApplication::translate(
- "qdoc", "Turn on syntax highlighting (makes qdoc run slower)"));
+ highlightingOption.setDescription(
+ QStringLiteral("Turn on syntax highlighting (makes qdoc run slower)"));
addOption(highlightingOption);
- showInternalOption.setDescription(
- QCoreApplication::translate("qdoc", "Include content marked internal"));
+ showInternalOption.setDescription(QStringLiteral("Include content marked internal"));
addOption(showInternalOption);
redirectDocumentationToDevNullOption.setDescription(
- QCoreApplication::translate("qdoc",
- "Save all documentation content to /dev/null. Useful if "
- "someone is interested in qdoc errors only."));
+ QStringLiteral("Save all documentation content to /dev/null. "
+ " Useful if someone is interested in qdoc errors only."));
addOption(redirectDocumentationToDevNullOption);
- noExamplesOption.setDescription(
- QCoreApplication::translate("qdoc", "Do not generate documentation for examples"));
+ noExamplesOption.setDescription(QStringLiteral("Do not generate documentation for examples"));
addOption(noExamplesOption);
- indexDirOption.setDescription(QCoreApplication::translate(
- "qdoc", "Specify a directory where QDoc should search for index files to load"));
+ indexDirOption.setDescription(
+ QStringLiteral("Specify a directory where QDoc should search for index files to load"));
indexDirOption.setValueName(QStringLiteral("dir"));
addOption(indexDirOption);
- installDirOption.setDescription(QCoreApplication::translate(
- "qdoc",
+ installDirOption.setDescription(QStringLiteral(
"Specify the directory where the output will be after running \"make install\""));
installDirOption.setValueName(QStringLiteral("dir"));
addOption(installDirOption);
- outputDirOption.setDescription(QCoreApplication::translate(
- "qdoc", "Specify output directory, overrides setting in qdocconf file"));
+ outputDirOption.setDescription(
+ QStringLiteral("Specify output directory, overrides setting in qdocconf file"));
outputDirOption.setValueName(QStringLiteral("dir"));
addOption(outputDirOption);
- outputFormatOption.setDescription(QCoreApplication::translate(
- "qdoc", "Specify output format, overrides setting in qdocconf file"));
+ outputFormatOption.setDescription(
+ QStringLiteral("Specify output format, overrides setting in qdocconf file"));
outputFormatOption.setValueName(QStringLiteral("format"));
addOption(outputFormatOption);
noLinkErrorsOption.setDescription(
- QCoreApplication::translate("qdoc", "Do not print link errors (i.e. missing targets)"));
+ QStringLiteral("Do not print link errors (i.e. missing targets)"));
addOption(noLinkErrorsOption);
- autoLinkErrorsOption.setDescription(
- QCoreApplication::translate("qdoc", "Show errors when automatic linking fails"));
+ autoLinkErrorsOption.setDescription(QStringLiteral("Show errors when automatic linking fails"));
addOption(autoLinkErrorsOption);
- debugOption.setDescription(QCoreApplication::translate("qdoc", "Enable debug output"));
+ debugOption.setDescription(QStringLiteral("Enable debug output"));
addOption(debugOption);
- prepareOption.setDescription(QCoreApplication::translate(
- "qdoc", "Run qdoc only to generate an index file, not the docs"));
+ atomsDumpOption.setDescription(QStringLiteral(
+ "Shows a human-readable form of the intermediate result of parsing a block-comment."));
+ addOption(atomsDumpOption);
+
+ prepareOption.setDescription(
+ QStringLiteral("Run qdoc only to generate an index file, not the docs"));
addOption(prepareOption);
- generateOption.setDescription(QCoreApplication::translate(
- "qdoc", "Run qdoc to read the index files and generate the docs"));
+ generateOption.setDescription(
+ QStringLiteral("Run qdoc to read the index files and generate the docs"));
addOption(generateOption);
- logProgressOption.setDescription(
- QCoreApplication::translate("qdoc", "Log progress on stderr."));
+ logProgressOption.setDescription(QStringLiteral("Log progress on stderr."));
addOption(logProgressOption);
- singleExecOption.setDescription(
- QCoreApplication::translate("qdoc", "Run qdoc once over all the qdoc conf files."));
+ singleExecOption.setDescription(QStringLiteral("Run qdoc once over all the qdoc conf files."));
addOption(singleExecOption);
includePathOption.setFlags(QCommandLineOption::ShortOptionStyle);
@@ -152,12 +125,11 @@ QDocCommandLineParser::QDocCommandLineParser()
frameworkOption.setFlags(QCommandLineOption::ShortOptionStyle);
addOption(frameworkOption);
- timestampsOption.setDescription(
- QCoreApplication::translate("qdoc", "Timestamp each qdoc log line."));
+ timestampsOption.setDescription(QStringLiteral("Timestamp each qdoc log line."));
addOption(timestampsOption);
- useDocBookExtensions.setDescription(QCoreApplication::translate(
- "qdoc", "Use the DocBook Library extensions for metadata."));
+ useDocBookExtensions.setDescription(
+ QStringLiteral("Use the DocBook Library extensions for metadata."));
addOption(useDocBookExtensions);
}
@@ -201,5 +173,5 @@ void QDocCommandLineParser::process(const QStringList &arguments)
QCommandLineParser::process(allArguments);
if (isSet(singleExecOption) && isSet(indexDirOption))
- qDebug("WARNING: -indexdir option ignored: Index files are not used in single-exec mode.");
+ qCWarning(lcQdoc) << "Warning: -indexdir option ignored: Index files are not used in single-exec mode.";
}
diff --git a/src/qdoc/qdoc/src/qdoc/qdoccommandlineparser.h b/src/qdoc/qdoc/src/qdoc/qdoccommandlineparser.h
new file mode 100644
index 000000000..57b36b582
--- /dev/null
+++ b/src/qdoc/qdoc/src/qdoc/qdoccommandlineparser.h
@@ -0,0 +1,28 @@
+// Copyright (C) 2019 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#ifndef QDOCCOMMANDLINEPARSER_H
+#define QDOCCOMMANDLINEPARSER_H
+
+#include <QtCore/qcommandlineparser.h>
+
+QT_BEGIN_NAMESPACE
+
+struct QDocCommandLineParser : public QCommandLineParser
+{
+ QDocCommandLineParser();
+ void process(const QStringList &arguments);
+
+ QCommandLineOption defineOption, dependsOption, highlightingOption;
+ QCommandLineOption showInternalOption, redirectDocumentationToDevNullOption;
+ QCommandLineOption noExamplesOption, indexDirOption, installDirOption;
+ QCommandLineOption outputDirOption, outputFormatOption;
+ QCommandLineOption noLinkErrorsOption, autoLinkErrorsOption, debugOption, atomsDumpOption;
+ QCommandLineOption prepareOption, generateOption, logProgressOption, singleExecOption;
+ QCommandLineOption includePathOption, includePathSystemOption, frameworkOption;
+ QCommandLineOption timestampsOption, useDocBookExtensions;
+};
+
+QT_END_NAMESPACE
+
+#endif // QDOCCOMMANDLINEPARSER_H
diff --git a/src/qdoc/qdocdatabase.cpp b/src/qdoc/qdoc/src/qdoc/qdocdatabase.cpp
index 3d3bc20ba..57e88fbde 100644
--- a/src/qdoc/qdocdatabase.cpp
+++ b/src/qdoc/qdoc/src/qdoc/qdocdatabase.cpp
@@ -1,30 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the tools applications of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "qdocdatabase.h"
@@ -36,9 +11,11 @@
#include "tree.h"
#include <QtCore/qregularexpression.h>
+#include <stack>
QT_BEGIN_NAMESPACE
+using namespace Qt::StringLiterals;
static NodeMultiMap emptyNodeMultiMap_;
/*!
@@ -97,18 +74,6 @@ QDocForest::~QDocForest()
}
/*!
- Increments the forest's current tree index. If the current
- tree index is still within the forest, the function returns
- the root node of the current tree. Otherwise it returns \nullptr.
- */
-NamespaceNode *QDocForest::nextRoot()
-{
- ++m_currentIndex;
- return (m_currentIndex < searchOrder().size() ? searchOrder()[m_currentIndex]->root()
- : nullptr);
-}
-
-/*!
Initializes the forest prior to a traversal and
returns a pointer to the primary tree. If the
forest is empty, it returns \nullptr.
@@ -151,7 +116,7 @@ void QDocForest::setPrimaryTree(const QString &t)
m_primaryTree = findTree(T);
m_forest.remove(T);
if (m_primaryTree == nullptr)
- qDebug() << "ERROR: Could not set primary tree to:" << t;
+ qCCritical(lcQdoc) << "Error: Could not set primary tree to" << t;
}
/*!
@@ -297,6 +262,10 @@ void QDocForest::newPrimaryTree(const QString &module)
other trees, which are all index trees. With relative set
to 0, the starting point for each index tree is the root
of the index tree.
+
+ If \a targetPath is resolved successfully but it refers to
+ a \\section title, continue the search, keeping the section
+ title as a fallback if no higher-priority targets are found.
*/
const Node *QDocForest::findNodeForTarget(QStringList &targetPath, const Node *relative,
Node::Genus genus, QString &ref)
@@ -310,13 +279,20 @@ const Node *QDocForest::findNodeForTarget(QStringList &targetPath, const Node *r
if (!targetPath.isEmpty())
target = targetPath.takeFirst();
+ TargetRec::TargetType type = TargetRec::Unknown;
+ const Node *tocNode = nullptr;
for (const auto *tree : searchOrder()) {
- const Node *n = tree->findNodeForTarget(entityPath, target, relative, flags, genus, ref);
- if (n)
- return n;
+ const Node *n = tree->findNodeForTarget(entityPath, target, relative, flags, genus, ref, &type);
+ if (n) {
+ // Targets referring to non-section titles are returned immediately
+ if (type != TargetRec::Contents)
+ return n;
+ if (!tocNode)
+ tocNode = n;
+ }
relative = nullptr;
}
- return nullptr;
+ return tocNode;
}
/*!
@@ -360,6 +336,7 @@ NodeMultiMap QDocDatabase::s_qmlTypes;
NodeMultiMap QDocDatabase::s_examples;
NodeMultiMapMap QDocDatabase::s_newClassMaps;
NodeMultiMapMap QDocDatabase::s_newQmlTypeMaps;
+NodeMultiMapMap QDocDatabase::s_newEnumValueMaps;
NodeMultiMapMap QDocDatabase::s_newSinceMaps;
/*!
@@ -420,12 +397,9 @@ void QDocDatabase::destroyQdocDB()
\note Do not add QML basic types into this list as it will
break linking to those types.
-
- Also calls Node::initialize() to initialize the search goal map.
*/
void QDocDatabase::initializeDB()
{
- Node::initialize();
s_typeNodeMap.insert("accepted", nullptr);
s_typeNodeMap.insert("actionPerformed", nullptr);
s_typeNodeMap.insert("activated", nullptr);
@@ -477,8 +451,6 @@ void QDocDatabase::initializeDB()
s_typeNodeMap.insert("initialized", nullptr);
s_typeNodeMap.insert("isLoaded", nullptr);
s_typeNodeMap.insert("item", nullptr);
- s_typeNodeMap.insert("jsdict", nullptr);
- s_typeNodeMap.insert("jsobject", nullptr);
s_typeNodeMap.insert("key", nullptr);
s_typeNodeMap.insert("keysequence", nullptr);
s_typeNodeMap.insert("listViewClicked", nullptr);
@@ -583,12 +555,6 @@ void QDocDatabase::initializeDB()
QML module nodes in the primary tree.
*/
-/*!
- \fn const CNMap &QDocDatabase::jsModules()
- Returns a const reference to the collection of all
- JovaScript module nodes in the primary tree.
-*/
-
/*! \fn CollectionNode *QDocDatabase::findGroup(const QString &name)
Find the group node named \a name and return a pointer
to it. If a matching node is not found, add a new group
@@ -607,18 +573,6 @@ void QDocDatabase::initializeDB()
and the new module node is marked \e{not seen}.
*/
-/*! \fn CollectionNode *QDocDatabase::findQmlModule(const QString &name, bool javaScript)
- Find the QML module node named \a name and return a pointer
- to it. If a matching node is not found, add a new QML module
- node named \a name and return a pointer to that one.
-
- If \a javaScript is set, the return collection must be a
- JavaScript module.
-
- If a new QML or JavaScript module node is added, its parent
- is the tree root, and the new node is marked \e{not seen}.
- */
-
/*! \fn CollectionNode *QDocDatabase::addGroup(const QString &name)
Looks up the group named \a name in the primary tree. If
a match is found, a pointer to the node is returned.
@@ -643,14 +597,6 @@ void QDocDatabase::initializeDB()
node is returned.
*/
-/*! \fn CollectionNode *QDocDatabase::addJsModule(const QString &name)
- Looks up the JavaScript module named \a name in the primary
- tree. If a match is found, a pointer to the node is returned.
- Otherwise, a new JavaScript module node named \a name is
- created and inserted into the collection, and the pointer to
- that node is returned.
- */
-
/*! \fn CollectionNode *QDocDatabase::addToGroup(const QString &name, Node *node)
Looks up the group node named \a name in the collection
of all group nodes. If a match is not found, a new group
@@ -675,96 +621,61 @@ void QDocDatabase::initializeDB()
list. The parent of \a node is not changed by this function.
*/
-/*! \fn Collection *QDocDatabase::addToJsModule(const QString &name, Node *node)
- Looks up the JavaScript module named \a name. If it isn't there,
- create it. Then append \a node to the JavaScript module's member
- list. The parent of \a node is not changed by this function.
- */
-
-/*!
- Looks up the QML type node identified by the qualified Qml
- type \a name and returns a pointer to the QML type node.
+/*! \fn QmlTypeNode *QDocDatabase::findQmlType(const QString &name)
+ Returns the QML type node identified by the qualified
+ QML type \a name, or \c nullptr if no type was found.
*/
-QmlTypeNode *QDocDatabase::findQmlType(const QString &name)
-{
- QmlTypeNode *qcn = m_forest.lookupQmlType(name);
- if (qcn)
- return qcn;
- return nullptr;
-}
/*!
- Looks up the QML type node identified by the Qml module id
- \a qmid and QML type \a name and returns a pointer to the
- QML type node. The key is \a qmid + "::" + \a name.
+ Returns the QML type node identified by the QML module id
+ \a qmid and QML type \a name, or \c nullptr if no type
+ was found.
- If the QML module id is empty, it looks up the QML type by
+ If the QML module id is empty, looks up the QML type by
\a name only.
*/
QmlTypeNode *QDocDatabase::findQmlType(const QString &qmid, const QString &name)
{
if (!qmid.isEmpty()) {
- QString t = qmid + "::" + name;
- QmlTypeNode *qcn = m_forest.lookupQmlType(t);
- if (qcn)
+ if (auto *qcn = m_forest.lookupQmlType(qmid + u"::"_s + name); qcn)
return qcn;
}
QStringList path(name);
- Node *n = m_forest.findNodeByNameAndType(path, &Node::isQmlType);
- if (n && (n->isQmlType() || n->isJsType()))
- return static_cast<QmlTypeNode *>(n);
- return nullptr;
+ return static_cast<QmlTypeNode *>(m_forest.findNodeByNameAndType(path, &Node::isQmlType));
}
/*!
- Looks up the QML basic type node identified by the Qml module id
- \a qmid and QML basic type \a name and returns a pointer to the
- QML basic type node. The key is \a qmid + "::" + \a name.
-
- If the QML module id is empty, it looks up the QML basic type by
- \a name only.
+ Returns the QML type node identified by the QML module id
+ constructed from the strings in the import \a record and the
+ QML type \a name. Returns \c nullptr if no type was not found.
*/
-Aggregate *QDocDatabase::findQmlBasicType(const QString &qmid, const QString &name)
+QmlTypeNode *QDocDatabase::findQmlType(const ImportRec &record, const QString &name)
{
- if (!qmid.isEmpty()) {
- QString t = qmid + "::" + name;
- Aggregate *a = m_forest.lookupQmlBasicType(t);
- if (a)
- return a;
+ if (!record.isEmpty()) {
+ const QString qmName = record.m_importUri.isEmpty() ?
+ record.m_moduleName : record.m_importUri;
+ const QStringList dotSplit{name.split(QLatin1Char('.'))};
+ for (const auto &namePart : dotSplit) {
+ if (auto *qcn = m_forest.lookupQmlType(qmName + u"::"_s + namePart); qcn)
+ return qcn;
+ }
}
-
- QStringList path(name);
- Node *n = m_forest.findNodeByNameAndType(path, &Node::isQmlBasicType);
- if (n && n->isQmlBasicType())
- return static_cast<Aggregate *>(n);
return nullptr;
}
/*!
- Looks up the QML type node identified by the Qml module id
- constructed from the strings in the \a import record and the
- QML type \a name and returns a pointer to the QML type node.
- If a QML type node is not found, 0 is returned.
- */
-QmlTypeNode *QDocDatabase::findQmlType(const ImportRec &import, const QString &name)
+ Returns the QML node identified by the QML module id \a qmid
+ and \a name, searching in the primary tree only. If \a qmid
+ is an empty string, searches for the node using name only.
+
+ Returns \c nullptr if no node was found.
+*/
+QmlTypeNode *QDocDatabase::findQmlTypeInPrimaryTree(const QString &qmid, const QString &name)
{
- if (!import.isEmpty()) {
- QStringList dotSplit;
- dotSplit = name.split(QLatin1Char('.'));
- QString qmName;
- if (import.m_importUri.isEmpty())
- qmName = import.m_moduleName;
- else
- qmName = import.m_importUri;
- for (const auto &namePart : dotSplit) {
- QString qualifiedName = qmName + "::" + namePart;
- QmlTypeNode *qcn = m_forest.lookupQmlType(qualifiedName);
- if (qcn)
- return qcn;
- }
- }
- return nullptr;
+ if (!qmid.isEmpty())
+ return primaryTree()->lookupQmlType(qmid + u"::"_s + name);
+ return static_cast<QmlTypeNode *>(primaryTreeRoot()->findChildNode(name, Node::QML, TypesOnly));
}
/*!
@@ -776,165 +687,130 @@ QmlTypeNode *QDocDatabase::findQmlType(const ImportRec &import, const QString &n
*/
void QDocDatabase::processForest()
{
- Tree *t = m_forest.firstTree();
- while (t) {
- findAllClasses(t->root());
- findAllFunctions(t->root());
- findAllObsoleteThings(t->root());
- findAllLegaleseTexts(t->root());
- findAllSince(t->root());
- findAllAttributions(t->root());
- t->setTreeHasBeenAnalyzed();
- t = m_forest.nextTree();
- }
+ processForest(&QDocDatabase::findAllClasses);
+ processForest(&QDocDatabase::findAllFunctions);
+ processForest(&QDocDatabase::findAllObsoleteThings);
+ processForest(&QDocDatabase::findAllLegaleseTexts);
+ processForest(&QDocDatabase::findAllSince);
+ processForest(&QDocDatabase::findAllAttributions);
resolveNamespaces();
}
/*!
This function calls \a func for each tree in the forest,
- but only if Tree::treeHasBeenAnalyzed() returns false for
- the tree. In this way, when running qdoc in \e singleExec
- mode, each tree is analyzed in turn, and its classes and
- types are added to the appropriate node maps.
+ ensuring that \a func is called only once per tree.
+
+ \sa processForest()
*/
-void QDocDatabase::processForest(void (QDocDatabase::*func)(Aggregate *))
+void QDocDatabase::processForest(FindFunctionPtr func)
{
Tree *t = m_forest.firstTree();
while (t) {
- if (!t->treeHasBeenAnalyzed()) {
+ if (!m_completedFindFunctions.values(t).contains(func)) {
(this->*(func))(t->root());
+ m_completedFindFunctions.insert(t, func);
}
t = m_forest.nextTree();
}
}
/*!
- Constructs the collection of legalese texts, if it has not
- already been constructed, and returns a reference to it.
+ Returns a reference to the collection of legalese texts.
*/
TextToNodeMap &QDocDatabase::getLegaleseTexts()
{
- if (m_legaleseTexts.isEmpty())
- processForest(&QDocDatabase::findAllLegaleseTexts);
+ processForest(&QDocDatabase::findAllLegaleseTexts);
return m_legaleseTexts;
}
/*!
- Construct the data structures for obsolete things, if they
- have not already been constructed. Returns a reference to
- the map of C++ classes with obsolete members.
+ Returns a reference to the map of C++ classes with obsolete members.
*/
NodeMultiMap &QDocDatabase::getClassesWithObsoleteMembers()
{
- if (s_obsoleteClasses.isEmpty() && s_obsoleteQmlTypes.isEmpty())
- processForest(&QDocDatabase::findAllObsoleteThings);
+ processForest(&QDocDatabase::findAllObsoleteThings);
return s_classesWithObsoleteMembers;
}
/*!
- Construct the data structures for obsolete things, if they
- have not already been constructed. Returns a reference to
- the map of obsolete QML types.
+ Returns a reference to the map of obsolete QML types.
*/
NodeMultiMap &QDocDatabase::getObsoleteQmlTypes()
{
- if (s_obsoleteClasses.isEmpty() && s_obsoleteQmlTypes.isEmpty())
- processForest(&QDocDatabase::findAllObsoleteThings);
+ processForest(&QDocDatabase::findAllObsoleteThings);
return s_obsoleteQmlTypes;
}
/*!
- Construct the data structures for obsolete things, if they
- have not already been constructed. Returns a reference to
- the map of QML types with obsolete members.
+ Returns a reference to the map of QML types with obsolete members.
*/
NodeMultiMap &QDocDatabase::getQmlTypesWithObsoleteMembers()
{
- if (s_obsoleteClasses.isEmpty() && s_obsoleteQmlTypes.isEmpty())
- processForest(&QDocDatabase::findAllObsoleteThings);
+ processForest(&QDocDatabase::findAllObsoleteThings);
return s_qmlTypesWithObsoleteMembers;
}
/*!
- Construct the data structures for QML basic types, if they
- have not already been constructed. Returns a reference to
- the map of QML basic types.
+ Returns a reference to the map of QML basic types.
*/
-NodeMultiMap &QDocDatabase::getQmlBasicTypes()
+NodeMultiMap &QDocDatabase::getQmlValueTypes()
{
- if (s_cppClasses.isEmpty() && s_qmlBasicTypes.isEmpty())
- processForest(&QDocDatabase::findAllClasses);
+ processForest(&QDocDatabase::findAllClasses);
return s_qmlBasicTypes;
}
/*!
- Construct the data structures for QML types, if they
- have not already been constructed. Returns a reference to
- the multimap of QML types.
+ Returns a reference to the multimap of QML types.
*/
NodeMultiMap &QDocDatabase::getQmlTypes()
{
- if (s_cppClasses.isEmpty() && s_qmlTypes.isEmpty())
- processForest(&QDocDatabase::findAllClasses);
+ processForest(&QDocDatabase::findAllClasses);
return s_qmlTypes;
}
/*!
- Construct the data structures for examples, if they
- have not already been constructed. Returns a reference to
- the multimap of example nodes.
+ Returns a reference to the multimap of example nodes.
*/
NodeMultiMap &QDocDatabase::getExamples()
{
- if (s_cppClasses.isEmpty() && s_examples.isEmpty())
- processForest(&QDocDatabase::findAllClasses);
+ processForest(&QDocDatabase::findAllClasses);
return s_examples;
}
/*!
- Construct the data structures for attributions, if they
- have not already been constructed. Returns a reference to
- the multimap of attribution nodes.
+ Returns a reference to the multimap of attribution nodes.
*/
NodeMultiMap &QDocDatabase::getAttributions()
{
- if (m_attributions.isEmpty())
- processForest(&QDocDatabase::findAllAttributions);
+ processForest(&QDocDatabase::findAllAttributions);
return m_attributions;
}
/*!
- Construct the data structures for obsolete things, if they
- have not already been constructed. Returns a reference to
- the map of obsolete C++ clases.
+ Returns a reference to the map of obsolete C++ clases.
*/
NodeMultiMap &QDocDatabase::getObsoleteClasses()
{
- if (s_obsoleteClasses.isEmpty() && s_obsoleteQmlTypes.isEmpty())
- processForest(&QDocDatabase::findAllObsoleteThings);
+ processForest(&QDocDatabase::findAllObsoleteThings);
return s_obsoleteClasses;
}
/*!
- Construct the C++ class data structures, if they have not
- already been constructed. Returns a reference to the map
- of all C++ classes.
+ Returns a reference to the map of all C++ classes.
*/
NodeMultiMap &QDocDatabase::getCppClasses()
{
- if (s_cppClasses.isEmpty() && s_qmlTypes.isEmpty())
- processForest(&QDocDatabase::findAllClasses);
+ processForest(&QDocDatabase::findAllClasses);
return s_cppClasses;
}
/*!
- Construct the function index data structure and return it.
- This data structure is used to output the function index page.
+ Returns the function index. This data structure is used to
+ output the function index page.
*/
NodeMapMap &QDocDatabase::getFunctionIndex()
{
- if (m_functionIndex.isEmpty())
- processForest(&QDocDatabase::findAllFunctions);
+ processForest(&QDocDatabase::findAllFunctions);
return m_functionIndex;
}
@@ -980,12 +856,9 @@ void QDocDatabase::findAllLegaleseTexts(Aggregate *node)
*/
const NodeMultiMap &QDocDatabase::getClassMap(const QString &key)
{
- if (s_newSinceMaps.isEmpty() && s_newClassMaps.isEmpty() && s_newQmlTypeMaps.isEmpty())
- processForest(&QDocDatabase::findAllSince);
+ processForest(&QDocDatabase::findAllSince);
auto it = s_newClassMaps.constFind(key);
- if (it != s_newClassMaps.constEnd())
- return it.value();
- return emptyNodeMultiMap_;
+ return (it != s_newClassMaps.constEnd()) ? it.value() : emptyNodeMultiMap_;
}
/*!
@@ -995,12 +868,9 @@ const NodeMultiMap &QDocDatabase::getClassMap(const QString &key)
*/
const NodeMultiMap &QDocDatabase::getQmlTypeMap(const QString &key)
{
- if (s_newSinceMaps.isEmpty() && s_newClassMaps.isEmpty() && s_newQmlTypeMaps.isEmpty())
- processForest(&QDocDatabase::findAllSince);
+ processForest(&QDocDatabase::findAllSince);
auto it = s_newQmlTypeMaps.constFind(key);
- if (it != s_newQmlTypeMaps.constEnd())
- return it.value();
- return emptyNodeMultiMap_;
+ return (it != s_newQmlTypeMaps.constEnd()) ? it.value() : emptyNodeMultiMap_;
}
/*!
@@ -1010,12 +880,9 @@ const NodeMultiMap &QDocDatabase::getQmlTypeMap(const QString &key)
*/
const NodeMultiMap &QDocDatabase::getSinceMap(const QString &key)
{
- if (s_newSinceMaps.isEmpty() && s_newClassMaps.isEmpty() && s_newQmlTypeMaps.isEmpty())
- processForest(&QDocDatabase::findAllSince);
+ processForest(&QDocDatabase::findAllSince);
auto it = s_newSinceMaps.constFind(key);
- if (it != s_newSinceMaps.constEnd())
- return it.value();
- return emptyNodeMultiMap_;
+ return (it != s_newSinceMaps.constEnd()) ? it.value() : emptyNodeMultiMap_;
}
/*!
@@ -1030,6 +897,7 @@ void QDocDatabase::resolveStuff()
// order matters
primaryTree()->resolveBaseClasses(primaryTreeRoot());
primaryTree()->resolvePropertyOverriddenFromPtrs(primaryTreeRoot());
+ primaryTreeRoot()->resolveRelates();
primaryTreeRoot()->normalizeOverloads();
primaryTree()->markDontDocumentNodes();
primaryTree()->removePrivateAndInternalBases(primaryTreeRoot());
@@ -1038,14 +906,14 @@ void QDocDatabase::resolveStuff()
primaryTreeRoot()->resolveQmlInheritance();
primaryTree()->resolveTargets(primaryTreeRoot());
primaryTree()->resolveCppToQmlLinks();
- primaryTree()->resolveUsingClauses();
+ primaryTree()->resolveSince(*primaryTreeRoot());
}
if (config.singleExec() && config.generating()) {
primaryTree()->resolveBaseClasses(primaryTreeRoot());
primaryTree()->resolvePropertyOverriddenFromPtrs(primaryTreeRoot());
primaryTreeRoot()->resolveQmlInheritance();
primaryTree()->resolveCppToQmlLinks();
- primaryTree()->resolveUsingClauses();
+ primaryTree()->resolveSince(*primaryTreeRoot());
}
if (!config.preparing()) {
resolveNamespaces();
@@ -1091,7 +959,7 @@ void QDocDatabase::resolveNamespaces()
if (!m_namespaceIndex.isEmpty())
return;
- bool linkErrors = !Config::instance().getBool(CONFIG_NOLINKERRORS);
+ bool linkErrors = !Config::instance().get(CONFIG_NOLINKERRORS).asBool();
NodeMultiMap namespaceMultimap;
Tree *t = m_forest.firstTree();
while (t) {
@@ -1119,8 +987,8 @@ void QDocDatabase::resolveNamespaces()
if (nsNode->hadDoc() && nsNode != ns) {
ns->doc().location().warning(
QStringLiteral("Namespace %1 documented more than once")
- .arg(nsNode->name()));
- nsNode->doc().location().warning(QStringLiteral("...also seen here"));
+ .arg(nsNode->name()), QStringLiteral("also seen here: %1")
+ .arg(nsNode->doc().location().toString()));
}
}
} else if (!indexNamespace) {
@@ -1220,7 +1088,7 @@ const FunctionNode *QDocDatabase::findFunctionNode(const QString &target, const
{
QString signature;
QString function = target;
- qsizetype length = target.length();
+ qsizetype length = target.size();
if (function.endsWith("()"))
function.chop(2);
if (function.endsWith(QChar(')'))) {
@@ -1306,7 +1174,7 @@ void QDocDatabase::readIndexes(const QStringList &indexFiles)
if (!isLoaded(fn))
filesToRead << file;
else
- qDebug() << "This index file is already in memory:" << file;
+ qCCritical(lcQdoc) << "Index file" << file << "is already in memory.";
}
QDocIndexFiles::qdocIndexFiles()->readIndexes(filesToRead);
}
@@ -1326,36 +1194,31 @@ void QDocDatabase::generateIndex(const QString &fileName, const QString &url, co
}
/*!
- Find a node of the specified \a type that is reached with
- the specified \a path qualified with the name of one of the
- open namespaces (might not be any open ones). If the node
- is found in an open namespace, prefix \a path with the name
- of the open namespace and "::" and return a pointer to the
- node. Otherwise return \c nullptr.
-
- This function only searches in the current primary tree.
- */
-Node *QDocDatabase::findNodeInOpenNamespace(QStringList &path, bool (Node::*isMatch)() const)
+ Returns the collection node representing the module that \a relative
+ node belongs to, or \c nullptr if there is no such module in the
+ primary tree.
+*/
+const CollectionNode *QDocDatabase::getModuleNode(const Node *relative)
{
- if (path.isEmpty())
+ Node::NodeType moduleType{Node::Module};
+ QString moduleName;
+ switch (relative->genus())
+ {
+ case Node::CPP:
+ moduleType = Node::Module;
+ moduleName = relative->physicalModuleName();
+ break;
+ case Node::QML:
+ moduleType = Node::QmlModule;
+ moduleName = relative->logicalModuleName();
+ break;
+ default:
return nullptr;
- Node *n = nullptr;
- if (!m_openNamespaces.isEmpty()) {
- const auto &openNamespaces = m_openNamespaces;
- for (const QString &t : openNamespaces) {
- QStringList p;
- if (t != path[0])
- p = t.split("::") + path;
- else
- p = path;
- n = primaryTree()->findNodeByNameAndType(p, isMatch);
- if (n) {
- path = p;
- break;
- }
- }
}
- return n;
+ if (moduleName.isEmpty())
+ return nullptr;
+
+ return primaryTree()->getCollection(moduleName, moduleType);
}
/*!
@@ -1378,7 +1241,7 @@ void QDocDatabase::mergeCollections(Node::NodeType type, CNMap &cnm, const Node
}
if (cnmm.isEmpty())
return;
- QRegularExpression singleDigit("\\b([0-9])\\b");
+ static const QRegularExpression singleDigit("\\b([0-9])\\b");
const QStringList keys = cnmm.uniqueKeys();
for (const auto &key : keys) {
const QList<CollectionNode *> values = cnmm.values(key);
@@ -1393,8 +1256,8 @@ void QDocDatabase::mergeCollections(Node::NodeType type, CNMap &cnm, const Node
if (values.size() > 1) {
for (CollectionNode *value : values) {
if (value != n) {
- // Allow multiple (major) versions of QML/JS modules
- if ((n->isQmlModule() || n->isJsModule())
+ // Allow multiple (major) versions of QML modules
+ if ((n->isQmlModule())
&& n->logicalModuleIdentifier() != value->logicalModuleIdentifier()) {
if (value->wasSeen() && value != relative
&& !value->members().isEmpty())
@@ -1420,7 +1283,7 @@ void QDocDatabase::mergeCollections(Node::NodeType type, CNMap &cnm, const Node
and type as \a c and merges their members into the
members list of \a c.
- For QML and JS modules, only nodes with matching
+ For QML modules, only nodes with matching
module identifiers are merged to avoid merging
modules with different (major) versions.
*/
@@ -1429,16 +1292,121 @@ void QDocDatabase::mergeCollections(CollectionNode *c)
if (c == nullptr)
return;
+ // REMARK: This form of merging is usually called during the
+ // generation phase om-the-fly when a source-of-truth collection
+ // is required.
+ // In practice, this means a collection could be merged many, many
+ // times during the lifetime of a generation.
+ // To avoid repeating the merging process each time, which could
+ // be time consuming, we use a small flag that is set directly on
+ // the collection to bail-out early.
+ //
+ // The merging process is only meaningful for collections when the
+ // collection references are spread troughout multiple projects.
+ // The part of information that exists in other project is read
+ // before the generation phase, such that when the generation
+ // phase comes, we already have all the information we need for
+ // merging such that we can consider all version of a certain
+ // collection node immutable, making the caching inherently
+ // correct at any point of the generation.
+ //
+ // This implies that this operation is unsafe if it is performed
+ // before all the index files are loaded.
+ // Indeed, this is a prerequisite, with the current structure, to
+ // perform this optmization.
+ //
+ // At the current time, this is true and is expected not to
+ // change.
+ //
+ // Do note that this is not applied to the other overload of
+ // mergeCollections as we cannot as safely ensure its consistency
+ // and, as the result of the merging depends on multiple
+ // parameters, it would require an actual memoization of the call.
+ //
+ // Note that this is a defensive optimization and we are assuming
+ // that it is effective based on heuristical data. As this is
+ // expected to disappear, at least in its current form, in the
+ // future, a more thorough analysis was not performed.
+ if (c->isMerged()) {
+ return;
+ }
+
for (auto *tree : searchOrder()) {
CollectionNode *cn = tree->getCollection(c->name(), c->nodeType());
if (cn && cn != c) {
- if ((cn->isQmlModule() || cn->isJsModule())
+ if ((cn->isQmlModule())
&& cn->logicalModuleIdentifier() != c->logicalModuleIdentifier())
continue;
+
for (auto *node : cn->members())
c->addMember(node);
+
+ // REMARK: The merging process is performed to ensure that
+ // references to the collection in external projects are
+ // taken into account before consuming the collection.
+ //
+ // This works by having QDoc construct empty collections
+ // as soon as a reference to a collection is encountered
+ // and filling details later on when its definition is
+ // found.
+ //
+ // This initially-empty collection is always saved to the
+ // primaryTree and it is the collection that is directly
+ // accessible to consumers during the generation process.
+ //
+ // Nonetheless, when the definition for the collection is
+ // not in the same project as the one that is being
+ // compiled, its details will never be filled in.
+ //
+ // Indeed, the details will live in the index file for the
+ // project where the collection is defined, if any, and
+ // the node for it, which has complete information, will
+ // live in some non-primaryTree.
+ //
+ // The merging process itself is used by consumers during
+ // the generation process because they access the
+ // primaryTree version of the collection expecting a
+ // source-of-truth.
+ // To ensure that this is the case for usages that
+ // requires linking, we need to merge not only the members
+ // of the collection that reside in external versions of
+ // the collection; but some of the data that reside in the
+ // definition of the collection intself, namely the title
+ // and the url.
+ //
+ // A collection that contains the data of a definition is
+ // always marked as seen, hence we use that to discern
+ // whether we are working with a placeholder node or not,
+ // and fill in the data if we encounter a node that
+ // represents a definition.
+ //
+ // The way in which QDoc works implies that collection are
+ // globally scoped between projects.
+ // The repetition of the definition for the same
+ // collection is warned for as a duplicate documentation,
+ // such that we can expect a single valid source of truth
+ // for a given collection in each project.
+ // It is currently unknown if this warning is applicable
+ // when the repeated collection is defined in two
+ // different projects.
+ //
+ // As QDoc implicitly would not correctly support this
+ // case, we assume that only one declaration exists for
+ // each collection, such that the first encoutered one
+ // must be the source of truth and that there is no need
+ // to copy any data after the first copy is performed.
+ // KLUDGE: Note that this process is done as a hackish
+ // solution to QTBUG-104237 and should not be considered
+ // final or dependable.
+ if (!c->wasSeen() && cn->wasSeen()) {
+ c->markSeen();
+ c->setTitle(cn->title());
+ c->setUrl(cn->url());
+ }
}
}
+
+ c->markMerged();
}
/*!
@@ -1474,7 +1442,7 @@ const Node *QDocDatabase::findNodeForAtom(const Atom *a, const Node *relative, Q
else if (first.endsWith(QChar(')'))) {
QString signature;
QString function = first;
- qsizetype length = first.length();
+ qsizetype length = first.size();
if (function.endsWith("()"))
function.chop(2);
if (function.endsWith(QChar(')'))) {
@@ -1519,9 +1487,13 @@ const Node *QDocDatabase::findNodeForAtom(const Atom *a, const Node *relative, Q
}
/*!
- Updates navigation (previous/next page links) for pages listed
- in the TOC, specified by the \c navigation.toctitles configuration
- variable.
+ Updates navigation (previous/next page links and the navigation parent)
+ for pages listed in the TOC, specified by the \c navigation.toctitles
+ configuration variable.
+
+ if \c navigation.toctitles.inclusive is \c true, include also the TOC
+ page(s) themselves as a 'root' item in the navigation bar (breadcrumbs)
+ that are generated for HTML output.
*/
void QDocDatabase::updateNavigation()
{
@@ -1529,54 +1501,179 @@ void QDocDatabase::updateNavigation()
QList<Tree *> searchOrder = this->searchOrder();
setLocalSearch();
- const auto tocTitles =
- Config::instance().getStringList(CONFIG_NAVIGATION +
- Config::dot +
- CONFIG_TOCTITLES);
+ const QString configVar = CONFIG_NAVIGATION +
+ Config::dot +
+ CONFIG_TOCTITLES;
+
+ // TODO: [direct-configuration-access]
+ // The configuration is currently a singleton with some generally
+ // global mutable state.
+ //
+ // Accessing the data in this form complicates testing and
+ // requires tests that inhibit any test parallelization, as the
+ // tests are not self contained.
+ //
+ // This should be generally avoived. Possibly, we should strive
+ // for Config to be a POD type that generally is scoped to
+ // main and whose data is destructured into dependencies when
+ // the dependencies are constructed.
+ bool inclusive{Config::instance().get(
+ configVar + Config::dot + CONFIG_INCLUSIVE).asBool()};
+
+ // TODO: [direct-configuration-access]
+ const auto tocTitles{Config::instance().get(configVar).asStringList()};
for (const auto &tocTitle : tocTitles) {
- if (const auto tocPage = findNodeForTarget(tocTitle, nullptr)) {
+ if (const auto candidateTarget = findNodeForTarget(tocTitle, nullptr); candidateTarget && candidateTarget->isPageNode()) {
+ auto tocPage{static_cast<const PageNode*>(candidateTarget)};
+
Text body = tocPage->doc().body();
+
auto *atom = body.firstAtom();
- std::pair<Node *, Atom *> prev { nullptr, nullptr };
+
+ std::pair<PageNode *, Atom *> prev { nullptr, nullptr };
+
+ std::stack<const PageNode *> tocStack;
+ tocStack.push(inclusive ? tocPage : nullptr);
+
bool inItem = false;
+
+ // TODO: Understand how much we use this form of looping over atoms.
+ // If it is used a few times we might consider providing
+ // an iterator for Text to make use of a simpler
+ // range-for loop.
while (atom) {
switch (atom->type()) {
- case Atom::ListItemLeft:
- inItem = true;
- break;
- case Atom::ListItemRight:
- inItem = false;
- break;
- case Atom::Link: {
- if (!inItem)
+ case Atom::ListItemLeft:
+ // Not known if we're going to have a link, push a temporary
+ tocStack.push(nullptr);
+ inItem = true;
break;
- QString ref;
- auto page = const_cast<Node *>(findNodeForAtom(atom, nullptr, ref));
- if (page && page->isPageNode()) {
+ case Atom::ListItemRight:
+ tocStack.pop();
+ inItem = false;
+ break;
+ case Atom::Link: {
+ if (!inItem)
+ break;
+
+ // TODO: [unnecessary-output-parameter]
+ // We currently need an lvalue string to
+ // pass to findNodeForAtom, as the
+ // outparameter ref.
+ //
+ // Apart from the general problems with output
+ // parameters, we shouldn't be forced to
+ // instanciate an unnecessary object at call
+ // site.
+ //
+ // Understand what the correct way to avoid this is.
+ // This requires changes to findNodeForAtom
+ // and should be addressed in the context of
+ // revising that method.
+ QString unused{};
+ // TODO: Having to const cast is really a code
+ // smell and could result in undefined
+ // behavior in some specific cases (e.g point
+ // to something that is actually const).
+ //
+ // We should understand how to sequence the
+ // code so that we have access to mutable data
+ // when we need it and "freeze" the data
+ // afterwards.
+ //
+ // If it we expect this form of mutability at
+ // this point we should expose a non-const API
+ // for the database, possibly limited to a
+ // very specific scope of execution.
+ //
+ // Understand what the correct sequencing for
+ // this processing is and revise this part.
+ auto candidatePage = const_cast<Node *>(findNodeForAtom(atom, nullptr, unused));
+ if (!candidatePage || !candidatePage->isPageNode()) break;
+
+ auto page{static_cast<PageNode*>(candidatePage)};
+
+ // ignore self-references
+ if (page == prev.first) break;
+
if (prev.first) {
- prev.first->setLink(Node::NextLink,
- page->title(),
- atom->linkText());
- page->setLink(Node::PreviousLink,
- prev.first->title(),
- prev.second->linkText());
+ prev.first->setLink(
+ Node::NextLink,
+ page->title(),
+ // TODO: [possible-assertion-failure][imprecise-types][atoms-link]
+ // As with other structures in QDoc we
+ // are able to call methods that are
+ // valid only on very specific states.
+ //
+ // For some of those calls we have
+ // some defensive programming measures
+ // that allow us to at least identify
+ // the error during debugging, while
+ // for others this may currently hide
+ // some logic error.
+ //
+ // To avoid those cases, we should
+ // strive to move those cases to a
+ // compilation error, requiring a
+ // statically analyzable state that
+ // represents the current model.
+ //
+ // This would ensure that those
+ // lingering class of bugs are
+ // eliminated completely, forces a
+ // more explicit codebase where the
+ // current capabilities do not depend
+ // on runtime values might not be
+ // generally visible, and does not
+ // require us to incur into the
+ // required state, which may be rare,
+ // simplifying our abilities to
+ // evaluate all possible states.
+ //
+ // For linking atoms, LinkAtom is
+ // available and might be a good
+ // enough solution to move linkText
+ // to.
+ atom->linkText()
+ );
+ page->setLink(
+ Node::PreviousLink,
+ prev.first->title(),
+ // TODO: [possible-assertion-failure][imprecise-types][atoms-link]
+ prev.second->linkText()
+ );
}
+
+ if (page == tocPage)
+ break;
+
+ // Find the navigation parent from the stack; we may have null pointers
+ // for non-link list items, so skip those.
+ qsizetype popped = 0;
+ while (tocStack.size() > 1 && !tocStack.top()) {
+ tocStack.pop();
+ ++popped;
+ }
+
+ page->setNavigationParent(tocStack.empty() ? nullptr : tocStack.top());
+
+ while (--popped > 0)
+ tocStack.push(nullptr);
+
+ tocStack.push(page);
prev = { page, atom };
}
- }
- break;
- default:
- break;
+ break;
+ default:
+ break;
}
- if (atom == body.lastAtom())
- break;
atom = atom->next();
}
} else {
- Config::instance().lastLocation().warning(
- QStringLiteral("Failed to find table of contents with title '%1'")
+ Config::instance().get(configVar).location()
+ .warning(QStringLiteral("Failed to find table of contents with title '%1'")
.arg(tocTitle));
}
}
diff --git a/src/qdoc/qdocdatabase.h b/src/qdoc/qdoc/src/qdoc/qdocdatabase.h
index 76d4b63a7..df2b4135c 100644
--- a/src/qdoc/qdocdatabase.h
+++ b/src/qdoc/qdoc/src/qdoc/qdocdatabase.h
@@ -1,30 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the tools applications of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#ifndef QDOCDATABASE_H
#define QDOCDATABASE_H
@@ -64,7 +39,6 @@ private:
}
~QDocForest();
- NamespaceNode *nextRoot();
Tree *firstTree();
Tree *nextTree();
Tree *primaryTree() { return m_primaryTree; }
@@ -173,15 +147,6 @@ private:
return nullptr;
}
- Aggregate *lookupQmlBasicType(const QString &name)
- {
- for (const auto *tree : searchOrder()) {
- Aggregate *a = tree->lookupQmlBasicType(name);
- if (a)
- return a;
- }
- return nullptr;
- }
void clearSearchOrder() { m_searchOrder.clear(); }
void newPrimaryTree(const QString &module);
void setPrimaryTree(const QString &t);
@@ -204,17 +169,17 @@ public:
static void destroyQdocDB();
~QDocDatabase() = default;
+ using FindFunctionPtr = void (QDocDatabase::*)(Aggregate *);
+
Tree *findTree(const QString &t) { return m_forest.findTree(t); }
const CNMap &groups() { return primaryTree()->groups(); }
const CNMap &modules() { return primaryTree()->modules(); }
const CNMap &qmlModules() { return primaryTree()->qmlModules(); }
- const CNMap &jsModules() { return primaryTree()->jsModules(); }
CollectionNode *addGroup(const QString &name) { return primaryTree()->addGroup(name); }
CollectionNode *addModule(const QString &name) { return primaryTree()->addModule(name); }
CollectionNode *addQmlModule(const QString &name) { return primaryTree()->addQmlModule(name); }
- CollectionNode *addJsModule(const QString &name) { return primaryTree()->addJsModule(name); }
CollectionNode *addToGroup(const QString &name, Node *node)
{
@@ -228,18 +193,17 @@ public:
{
return primaryTree()->addToQmlModule(name, node);
}
- CollectionNode *addToJsModule(const QString &name, Node *node)
- {
- return primaryTree()->addToJsModule(name, node);
- }
void addExampleNode(ExampleNode *n) { primaryTree()->addExampleNode(n); }
ExampleNodeMap &exampleNodeMap() { return primaryTree()->exampleNodeMap(); }
- QmlTypeNode *findQmlType(const QString &name);
+ QmlTypeNode *findQmlType(const QString &name)
+ {
+ return m_forest.lookupQmlType(name);
+ }
QmlTypeNode *findQmlType(const QString &qmid, const QString &name);
QmlTypeNode *findQmlType(const ImportRec &import, const QString &name);
- Aggregate *findQmlBasicType(const QString &qmid, const QString &name);
+ QmlTypeNode *findQmlTypeInPrimaryTree(const QString &qmid, const QString &name);
static NodeMultiMap &obsoleteClasses() { return s_obsoleteClasses; }
static NodeMultiMap &obsoleteQmlTypes() { return s_obsoleteQmlTypes; }
@@ -251,6 +215,7 @@ public:
static NodeMultiMap &examples() { return s_examples; }
static NodeMultiMapMap &newClassMaps() { return s_newClassMaps; }
static NodeMultiMapMap &newQmlTypeMaps() { return s_newQmlTypeMaps; }
+ static NodeMultiMapMap &newEnumValueMaps() { return s_newEnumValueMaps; }
static NodeMultiMapMap &newSinceMaps() { return s_newSinceMaps; }
private:
@@ -271,7 +236,7 @@ public:
NodeMultiMap &getObsoleteQmlTypes();
NodeMultiMap &getQmlTypesWithObsoleteMembers();
NodeMultiMap &getNamespaces();
- NodeMultiMap &getQmlBasicTypes();
+ NodeMultiMap &getQmlValueTypes();
NodeMultiMap &getQmlTypes();
NodeMultiMap &getExamples();
NodeMultiMap &getAttributions();
@@ -298,7 +263,6 @@ public:
{
return primaryTree()->findRelatesNode(path);
}
- Node *findNodeInOpenNamespace(QStringList &path, bool (Node::*)() const);
/*******************************************************************/
/*****************************************************************************
@@ -329,6 +293,8 @@ public:
{
return m_forest.getCollectionNode(name, type);
}
+ const CollectionNode *getModuleNode(const Node *relative);
+
FunctionNode *findFunctionNodeForTag(const QString &tag)
{
return primaryTree()->findFunctionNodeForTag(tag);
@@ -364,12 +330,8 @@ public:
void generateIndex(const QString &fileName, const QString &url, const QString &title,
Generator *g);
- void clearOpenNamespaces() { m_openNamespaces.clear(); }
- void insertOpenNamespace(const QString &path) { m_openNamespaces.insert(path); }
void processForest();
- // Try to make this function private.
- QDocForest &forest() { return m_forest; }
NamespaceNode *primaryTreeRoot() { return m_forest.primaryTreeRoot(); }
void newPrimaryTree(const QString &module) { m_forest.newPrimaryTree(module); }
void setPrimaryTree(const QString &t) { m_forest.setPrimaryTree(t); }
@@ -390,12 +352,7 @@ public:
private:
friend class Tree;
- const Node *findNode(const QStringList &path, const Node *relative, int findFlags,
- Node::Genus genus)
- {
- return m_forest.findNode(path, relative, findFlags, genus);
- }
- void processForest(void (QDocDatabase::*)(Aggregate *));
+ void processForest(FindFunctionPtr func);
bool isLoaded(const QString &t) { return m_forest.isLoaded(t); }
static void initializeDB();
@@ -420,6 +377,7 @@ private:
static NodeMultiMap s_examples;
static NodeMultiMapMap s_newClassMaps;
static NodeMultiMapMap s_newQmlTypeMaps;
+ static NodeMultiMapMap s_newEnumValueMaps;
static NodeMultiMapMap s_newSinceMaps;
QString m_version {};
@@ -429,7 +387,7 @@ private:
NodeMultiMap m_attributions {};
NodeMapMap m_functionIndex {};
TextToNodeMap m_legaleseTexts {};
- QSet<QString> m_openNamespaces {};
+ QMultiHash<Tree*, FindFunctionPtr> m_completedFindFunctions {};
};
QT_END_NAMESPACE
diff --git a/src/qdoc/qdocindexfiles.cpp b/src/qdoc/qdoc/src/qdoc/qdocindexfiles.cpp
index 3ff4ecb8c..74d27ed3c 100644
--- a/src/qdoc/qdocindexfiles.cpp
+++ b/src/qdoc/qdoc/src/qdoc/qdocindexfiles.cpp
@@ -1,30 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the tools applications of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "qdocindexfiles.h"
@@ -32,6 +7,7 @@
#include "atom.h"
#include "classnode.h"
#include "collectionnode.h"
+#include "comparisoncategory.h"
#include "config.h"
#include "enumnode.h"
#include "examplenode.h"
@@ -59,7 +35,8 @@ enum QDocAttr {
QDocAttrFile,
QDocAttrImage,
QDocAttrDocument,
- QDocAttrExternalPage
+ QDocAttrExternalPage,
+ QDocAttrAttribution
};
static Node *root_ = nullptr;
@@ -79,7 +56,7 @@ QDocIndexFiles *QDocIndexFiles::s_qdocIndexFiles = nullptr;
QDocIndexFiles::QDocIndexFiles() : m_gen(nullptr)
{
m_qdb = QDocDatabase::qdocDB();
- m_storeLocationInfo = Config::instance().getBool(CONFIG_LOCATIONINFO);
+ m_storeLocationInfo = Config::instance().get(CONFIG_LOCATIONINFO).asBool();
}
/*!
@@ -146,15 +123,16 @@ void QDocIndexFiles::readIndexFile(const QString &path)
QXmlStreamAttributes attrs = reader.attributes();
- // Generate a relative URL between the install dir and the index file
- // when the -installdir command line option is set.
- QString indexUrl;
- if (Config::installDir.isEmpty()) {
- indexUrl = attrs.value(QLatin1String("url")).toString();
- } else {
- // Use a fake directory, since we will copy the output to a sub directory of
- // installDir when using "make install". This is just for a proper relative path.
- // QDir installDir(path.section('/', 0, -3) + "/outputdir");
+ QString indexUrl {attrs.value(QLatin1String("url")).toString()};
+
+ // Decide how we link to nodes loaded from this index file:
+ // If building a set that will be installed AND the URL of
+ // the dependency is identical to ours, assume that also
+ // the dependent html files are available under the same
+ // directory tree. Otherwise, link using the full index URL.
+ if (!Config::installDir.isEmpty() && indexUrl == Config::instance().get(CONFIG_URL).asString()) {
+ // Generate a relative URL between the install dir and the index file
+ // when the -installdir command line option is set.
QDir installDir(path.section('/', 0, -3) + '/' + Generator::outputSubdir());
indexUrl = installDir.relativeFilePath(path).section('/', 0, -2);
}
@@ -251,7 +229,7 @@ void QDocIndexFiles::readIndexSection(QXmlStreamReader &reader, Node *current,
QString bases = attributes.value(QLatin1String("bases")).toString();
if (!bases.isEmpty())
m_basesList.append(
- QPair<ClassNode *, QString>(static_cast<ClassNode *>(node), bases));
+ std::pair<ClassNode *, QString>(static_cast<ClassNode *>(node), bases));
}
if (!indexUrl.isEmpty())
location = Location(indexUrl + QLatin1Char('/') + name.toLower() + ".html");
@@ -271,8 +249,10 @@ void QDocIndexFiles::readIndexSection(QXmlStreamReader &reader, Node *current,
location = Location(indexUrl + QLatin1Char('/') + name);
else if (!indexUrl.isNull())
location = Location(name);
- } else if (elementName == QLatin1String("qmlclass")) {
- auto *qmlTypeNode = new QmlTypeNode(parent, name);
+ } else if (elementName == QLatin1String("qmlclass") || elementName == QLatin1String("qmlvaluetype")
+ || elementName == QLatin1String("qmlbasictype")) {
+ auto *qmlTypeNode = new QmlTypeNode(parent, name,
+ elementName == QLatin1String("qmlclass") ? Node::QmlType : Node::QmlValueType);
qmlTypeNode->setTitle(attributes.value(QLatin1String("title")).toString());
QString logicalModuleName = attributes.value(QLatin1String("qml-module-name")).toString();
if (!logicalModuleName.isEmpty())
@@ -292,49 +272,6 @@ void QDocIndexFiles::readIndexSection(QXmlStreamReader &reader, Node *current,
else if (!indexUrl.isNull())
location = Location(name);
node = qmlTypeNode;
- } else if (elementName == QLatin1String("jstype")) {
- auto *qmlTypeNode = new QmlTypeNode(parent, name);
- qmlTypeNode->setGenus(Node::JS);
- qmlTypeNode->setTitle(attributes.value(QLatin1String("title")).toString());
- QString logicalModuleName = attributes.value(QLatin1String("js-module-name")).toString();
- if (!logicalModuleName.isEmpty())
- m_qdb->addToQmlModule(logicalModuleName, qmlTypeNode);
- bool abstract = false;
- if (attributes.value(QLatin1String("abstract")) == QLatin1String("true"))
- abstract = true;
- qmlTypeNode->setAbstract(abstract);
- QString qmlFullBaseName = attributes.value(QLatin1String("js-base-type")).toString();
- if (!qmlFullBaseName.isEmpty()) {
- qmlTypeNode->setQmlBaseName(qmlFullBaseName);
- }
- if (attributes.hasAttribute(QLatin1String("location")))
- name = attributes.value("location").toString();
- if (!indexUrl.isEmpty())
- location = Location(indexUrl + QLatin1Char('/') + name);
- else if (!indexUrl.isNull())
- location = Location(name);
- node = qmlTypeNode;
- } else if (elementName == QLatin1String("qmlbasictype")) {
- auto *qbtn = new QmlBasicTypeNode(parent, name);
- qbtn->setTitle(attributes.value(QLatin1String("title")).toString());
- if (attributes.hasAttribute(QLatin1String("location")))
- name = attributes.value("location").toString();
- if (!indexUrl.isEmpty())
- location = Location(indexUrl + QLatin1Char('/') + name);
- else if (!indexUrl.isNull())
- location = Location(name);
- node = qbtn;
- } else if (elementName == QLatin1String("jsbasictype")) {
- auto *qbtn = new QmlBasicTypeNode(parent, name);
- qbtn->setGenus(Node::JS);
- qbtn->setTitle(attributes.value(QLatin1String("title")).toString());
- if (attributes.hasAttribute(QLatin1String("location")))
- name = attributes.value("location").toString();
- if (!indexUrl.isEmpty())
- location = Location(indexUrl + QLatin1Char('/') + name);
- else if (!indexUrl.isNull())
- location = Location(name);
- node = qbtn;
} else if (elementName == QLatin1String("qmlproperty")) {
QString type = attributes.value(QLatin1String("type")).toString();
bool attached = false;
@@ -348,18 +285,6 @@ void QDocIndexFiles::readIndexSection(QXmlStreamReader &reader, Node *current,
if (attributes.value(QLatin1String("required")) == QLatin1String("true"))
qmlPropertyNode->setRequired();
node = qmlPropertyNode;
- } else if (elementName == QLatin1String("jsproperty")) {
- QString type = attributes.value(QLatin1String("type")).toString();
- bool attached = false;
- if (attributes.value(QLatin1String("attached")) == QLatin1String("true"))
- attached = true;
- bool readonly = false;
- if (attributes.value(QLatin1String("writable")) == QLatin1String("false"))
- readonly = true;
- auto *qmlPropertyNode = new QmlPropertyNode(parent, name, type, attached);
- qmlPropertyNode->setGenus(Node::JS);
- qmlPropertyNode->markReadOnly(readonly);
- node = qmlPropertyNode;
} else if (elementName == QLatin1String("group")) {
auto *collectionNode = m_qdb->addGroup(name);
collectionNode->setTitle(attributes.value(QLatin1String("title")).toString());
@@ -375,21 +300,10 @@ void QDocIndexFiles::readIndexSection(QXmlStreamReader &reader, Node *current,
collectionNode->markSeen();
node = collectionNode;
} else if (elementName == QLatin1String("qmlmodule")) {
- QString t = attributes.value(QLatin1String("qml-module-name")).toString();
- auto *collectionNode = m_qdb->addQmlModule(t);
- QStringList info;
- info << t << attributes.value(QLatin1String("qml-module-version")).toString();
- collectionNode->setLogicalModuleInfo(info);
- collectionNode->setTitle(attributes.value(QLatin1String("title")).toString());
- collectionNode->setSubtitle(attributes.value(QLatin1String("subtitle")).toString());
- if (attributes.value(QLatin1String("seen")) == QLatin1String("true"))
- collectionNode->markSeen();
- node = collectionNode;
- } else if (elementName == QLatin1String("jsmodule")) {
- QString t = attributes.value(QLatin1String("js-module-name")).toString();
- auto *collectionNode = m_qdb->addJsModule(t);
- QStringList info;
- info << t << attributes.value(QLatin1String("js-module-version")).toString();
+ auto *collectionNode = m_qdb->addQmlModule(name);
+ const QStringList info = QStringList()
+ << name
+ << QString(attributes.value(QLatin1String("qml-module-version")).toString());
collectionNode->setLogicalModuleInfo(info);
collectionNode->setTitle(attributes.value(QLatin1String("title")).toString());
collectionNode->setSubtitle(attributes.value(QLatin1String("subtitle")).toString());
@@ -398,26 +312,19 @@ void QDocIndexFiles::readIndexSection(QXmlStreamReader &reader, Node *current,
node = collectionNode;
} else if (elementName == QLatin1String("page")) {
QDocAttr subtype = QDocAttrNone;
- Node::PageType ptype = Node::NoPageType;
QString attr = attributes.value(QLatin1String("subtype")).toString();
if (attr == QLatin1String("attribution")) {
- subtype = QDocAttrDocument;
- ptype = Node::AttributionPage;
+ subtype = QDocAttrAttribution;
} else if (attr == QLatin1String("example")) {
subtype = QDocAttrExample;
- ptype = Node::ExamplePage;
} else if (attr == QLatin1String("file")) {
subtype = QDocAttrFile;
- ptype = Node::NoPageType;
} else if (attr == QLatin1String("image")) {
subtype = QDocAttrImage;
- ptype = Node::NoPageType;
} else if (attr == QLatin1String("page")) {
subtype = QDocAttrDocument;
- ptype = Node::ArticlePage;
} else if (attr == QLatin1String("externalpage")) {
subtype = QDocAttrExternalPage;
- ptype = Node::ArticlePage;
} else
goto done;
@@ -436,8 +343,11 @@ void QDocIndexFiles::readIndexSection(QXmlStreamReader &reader, Node *current,
pageNode = new ExampleNode(parent, name);
else if (subtype == QDocAttrExternalPage)
pageNode = new ExternalPageNode(parent, name);
- else
- pageNode = new PageNode(parent, name, ptype);
+ else {
+ pageNode = new PageNode(parent, name);
+ if (subtype == QDocAttrAttribution) pageNode->markAttribution();
+ }
+
pageNode->setTitle(attributes.value(QLatin1String("title")).toString());
if (attributes.hasAttribute(QLatin1String("location")))
@@ -461,9 +371,10 @@ void QDocIndexFiles::readIndexSection(QXmlStreamReader &reader, Node *current,
while (reader.readNextStartElement()) {
QXmlStreamAttributes childAttributes = reader.attributes();
if (reader.name() == QLatin1String("value")) {
-
EnumItem item(childAttributes.value(QLatin1String("name")).toString(),
- childAttributes.value(QLatin1String("value")).toString());
+ childAttributes.value(QLatin1String("value")).toString(),
+ childAttributes.value(QLatin1String("since")).toString()
+ );
enumNode->addItem(item);
} else if (reader.name() == QLatin1String("keyword")) {
insertTarget(TargetRec::Keyword, childAttributes, enumNode);
@@ -490,7 +401,9 @@ void QDocIndexFiles::readIndexSection(QXmlStreamReader &reader, Node *current,
auto *propNode = new PropertyNode(parent, name);
node = propNode;
if (attributes.value(QLatin1String("bindable")) == QLatin1String("true"))
- propNode->setPropertyType(PropertyNode::Bindable);
+ propNode->setPropertyType(PropertyNode::PropertyType::BindableProperty);
+
+ propNode->setWritable(attributes.value(QLatin1String("writable")) != QLatin1String("false"));
if (!indexUrl.isEmpty())
location = Location(indexUrl + QLatin1Char('/') + parent->name().toLower() + ".html");
@@ -506,13 +419,26 @@ void QDocIndexFiles::readIndexSection(QXmlStreamReader &reader, Node *current,
if (attributes.value(QLatin1String("attached")) == QLatin1String("true"))
attached = true;
auto *fn = new FunctionNode(metaness, parent, name, attached);
+
+ fn->setReturnType(attributes.value(QLatin1String("type")).toString());
+
if (fn->isCppNode()) {
- fn->setReturnType(attributes.value(QLatin1String("type")).toString());
fn->setVirtualness(attributes.value(QLatin1String("virtual")).toString());
fn->setConst(attributes.value(QLatin1String("const")) == QLatin1String("true"));
fn->setStatic(attributes.value(QLatin1String("static")) == QLatin1String("true"));
fn->setFinal(attributes.value(QLatin1String("final")) == QLatin1String("true"));
fn->setOverride(attributes.value(QLatin1String("override")) == QLatin1String("true"));
+
+ if (attributes.value(QLatin1String("explicit")) == QLatin1String("true"))
+ fn->markExplicit();
+
+ if (attributes.value(QLatin1String("constexpr")) == QLatin1String("true"))
+ fn->markConstexpr();
+
+ if (attributes.value(QLatin1String("noexcept")) == QLatin1String("true")) {
+ fn->markNoexcept(attributes.value("noexcept_expression").toString());
+ }
+
qsizetype refness = attributes.value(QLatin1String("refness")).toUInt();
if (refness == 1)
fn->setRef(true);
@@ -528,28 +454,29 @@ void QDocIndexFiles::readIndexSection(QXmlStreamReader &reader, Node *current,
fn->setOverloadNumber(attributes.value(QLatin1String("overload-number")).toUInt());
else
fn->setOverloadNumber(0);
- /*
- Note: The "signature" attribute was written to the
- index file, but it is not read back in. That is ok
- because we reconstruct the parameter list and the
- return type, from which the signature was built in
- the first place and from which it can be rebuilt.
- */
- while (reader.readNextStartElement()) {
- QXmlStreamAttributes childAttributes = reader.attributes();
- if (reader.name() == QLatin1String("parameter")) {
- // Do not use the default value for the parameter; it is not
- // required, and has been known to cause problems.
- QString type = childAttributes.value(QLatin1String("type")).toString();
- QString name = childAttributes.value(QLatin1String("name")).toString();
- fn->parameters().append(type, name);
- } else if (reader.name() == QLatin1String("keyword")) {
- insertTarget(TargetRec::Keyword, childAttributes, fn);
- } else if (reader.name() == QLatin1String("target")) {
- insertTarget(TargetRec::Target, childAttributes, fn);
- }
- reader.skipCurrentElement();
+ }
+
+ /*
+ Note: The "signature" attribute was written to the
+ index file, but it is not read back in. That is ok
+ because we reconstruct the parameter list and the
+ return type, from which the signature was built in
+ the first place and from which it can be rebuilt.
+ */
+ while (reader.readNextStartElement()) {
+ QXmlStreamAttributes childAttributes = reader.attributes();
+ if (reader.name() == QLatin1String("parameter")) {
+ // Do not use the default value for the parameter; it is not
+ // required, and has been known to cause problems.
+ QString type = childAttributes.value(QLatin1String("type")).toString();
+ QString name = childAttributes.value(QLatin1String("name")).toString();
+ fn->parameters().append(type, name);
+ } else if (reader.name() == QLatin1String("keyword")) {
+ insertTarget(TargetRec::Keyword, childAttributes, fn);
+ } else if (reader.name() == QLatin1String("target")) {
+ insertTarget(TargetRec::Target, childAttributes, fn);
}
+ reader.skipCurrentElement();
}
node = fn;
@@ -618,6 +545,9 @@ void QDocIndexFiles::readIndexSection(QXmlStreamReader &reader, Node *current,
} else
node->setThreadSafeness(Node::UnspecifiedSafeness);
+ const QString category = attributes.value(QLatin1String("comparison_category")).toString();
+ node->setComparisonCategory(comparisonCategoryFromString(category.toStdString()));
+
QString status = attributes.value(QLatin1String("status")).toString();
// TODO: "obsolete" is kept for backward compatibility, remove in the near future
if (status == QLatin1String("obsolete") || status == QLatin1String("deprecated"))
@@ -664,7 +594,6 @@ void QDocIndexFiles::readIndexSection(QXmlStreamReader &reader, Node *current,
Doc doc(location, location, QString(), emptySet, emptySet); // placeholder
node->setDoc(doc);
node->setIndexNodeFlag(); // Important: This node came from an index file.
- node->setOutputSubdirectory(m_project.toLower());
QString briefAttr = attributes.value(QLatin1String("brief")).toString();
if (!briefAttr.isEmpty()) {
node->setReconstitutedBrief(briefAttr);
@@ -728,7 +657,7 @@ void QDocIndexFiles::insertTarget(TargetRec::TargetType type,
*/
void QDocIndexFiles::resolveIndex()
{
- for (const auto &pair : qAsConst(m_basesList)) {
+ for (const auto &pair : std::as_const(m_basesList)) {
const QStringList bases = pair.second.split(QLatin1Char(','));
for (const auto &base : bases) {
QStringList basePath = base.split(QString("::"));
@@ -824,6 +753,38 @@ bool QDocIndexFiles::adoptRelatedNode(Aggregate *adoptiveParent, int index)
}
/*!
+ Write canonicalized versions of \\target and \\keyword identifiers
+ that appear in the documentation of \a node into the index using
+ \a writer, so that they can be used as link targets in external
+ documentation sets.
+*/
+void QDocIndexFiles::writeTargets(QXmlStreamWriter &writer, Node *node)
+{
+ if (node->doc().hasTargets()) {
+ for (const Atom *target : std::as_const(node->doc().targets())) {
+ const QString &title = target->string();
+ const QString &name{Utilities::asAsciiPrintable(title)};
+ writer.writeStartElement("target");
+ writer.writeAttribute("name", node->isExternalPage() ? title : name);
+ if (name != title)
+ writer.writeAttribute("title", title);
+ writer.writeEndElement(); // target
+ }
+ }
+ if (node->doc().hasKeywords()) {
+ for (const Atom *keyword : std::as_const(node->doc().keywords())) {
+ const QString &title = keyword->string();
+ const QString &name{Utilities::asAsciiPrintable(title)};
+ writer.writeStartElement("keyword");
+ writer.writeAttribute("name", name);
+ if (name != title)
+ writer.writeAttribute("title", title);
+ writer.writeEndElement(); // keyword
+ }
+ }
+}
+
+/*!
Generate the index section with the given \a writer for the \a node
specified, returning true if an element was written, and returning
false if an element is not written.
@@ -870,29 +831,14 @@ bool QDocIndexFiles::generateIndexSection(QXmlStreamWriter &writer, Node *node,
nodeName = "header";
break;
case Node::QmlType:
- nodeName = "qmlclass";
- if (node->logicalModule() != nullptr)
- logicalModuleName = node->logicalModule()->logicalModuleName();
+ case Node::QmlValueType:
+ nodeName = (node->nodeType() == Node::QmlType) ? "qmlclass" : "qmlvaluetype";
+ logicalModuleName = node->logicalModuleName();
baseNameAttr = "qml-base-type";
moduleNameAttr = "qml-module-name";
moduleVerAttr = "qml-module-version";
qmlFullBaseName = node->qmlFullBaseName();
break;
- case Node::JsType:
- nodeName = "jstype";
- baseNameAttr = "js-base-type";
- moduleNameAttr = "js-module-name";
- moduleVerAttr = "js-module-version";
- if (node->logicalModule() != nullptr)
- logicalModuleName = node->logicalModule()->logicalModuleName();
- qmlFullBaseName = node->qmlFullBaseName();
- break;
- case Node::QmlBasicType:
- nodeName = "qmlbasictype";
- break;
- case Node::JsBasicType:
- nodeName = "jsbasictype";
- break;
case Node::Page:
case Node::Example:
case Node::ExternalPage:
@@ -911,13 +857,6 @@ bool QDocIndexFiles::generateIndexSection(QXmlStreamWriter &writer, Node *node,
logicalModuleName = node->logicalModuleName();
logicalModuleVersion = node->logicalModuleVersion();
break;
- case Node::JsModule:
- nodeName = "jsmodule";
- moduleNameAttr = "js-module-name";
- moduleVerAttr = "js-module-version";
- logicalModuleName = node->logicalModuleName();
- logicalModuleVersion = node->logicalModuleVersion();
- break;
case Node::Enum:
nodeName = "enum";
break;
@@ -935,14 +874,11 @@ bool QDocIndexFiles::generateIndexSection(QXmlStreamWriter &writer, Node *node,
if (!node->isPropertyGroup())
return false;
// Add an entry for property groups so that they can be linked to
- nodeName = node->genus() == Node::QML ? "qmlproperty" : "jsproperty";
+ nodeName = "qmlproperty";
break;
case Node::QmlProperty:
nodeName = "qmlproperty";
break;
- case Node::JsProperty:
- nodeName = "jsproperty";
- break;
case Node::Proxy:
nodeName = "proxy";
break;
@@ -965,12 +901,10 @@ bool QDocIndexFiles::generateIndexSection(QXmlStreamWriter &writer, Node *node,
writer.writeAttribute("name", objName);
- // Write module and base type info for QML/JS types
+ // Write module and base type info for QML types
if (!moduleNameAttr.isEmpty()) {
if (!logicalModuleName.isEmpty())
writer.writeAttribute(moduleNameAttr, logicalModuleName);
- else
- writer.writeAttribute(moduleNameAttr, node->name());
if (!logicalModuleVersion.isEmpty())
writer.writeAttribute(moduleVerAttr, logicalModuleVersion);
}
@@ -985,9 +919,9 @@ bool QDocIndexFiles::generateIndexSection(QXmlStreamWriter &writer, Node *node,
href = m_gen->fullDocumentLocation(node);
} else
href = node->name();
- if (node->isQmlNode() || node->isJsNode()) {
+ if (node->isQmlNode()) {
Aggregate *p = node->parent();
- if (p && (p->isQmlType() || p->isJsType()) && p->isAbstract())
+ if (p && p->isQmlType() && p->isAbstract())
href.clear();
}
if (!href.isEmpty())
@@ -1045,6 +979,8 @@ bool QDocIndexFiles::generateIndexSection(QXmlStreamWriter &writer, Node *node,
writer.writeAttribute("module", node->physicalModuleName());
if (!brief.isEmpty())
writer.writeAttribute("brief", brief);
+ if (auto category = node->comparisonCategory(); category != ComparisonCategory::None)
+ writer.writeAttribute("comparison_category", comparisonCategoryAsString(category));
} break;
case Node::HeaderFile: {
const auto *headerNode = static_cast<const HeaderNode *>(node);
@@ -1063,7 +999,7 @@ bool QDocIndexFiles::generateIndexSection(QXmlStreamWriter &writer, Node *node,
if (!brief.isEmpty())
writer.writeAttribute("brief", brief);
} break;
- case Node::JsType:
+ case Node::QmlValueType:
case Node::QmlType: {
const auto *qmlTypeNode = static_cast<const QmlTypeNode *>(node);
writer.writeAttribute("title", qmlTypeNode->title());
@@ -1075,23 +1011,12 @@ bool QDocIndexFiles::generateIndexSection(QXmlStreamWriter &writer, Node *node,
case Node::Page:
case Node::Example:
case Node::ExternalPage: {
- /*
- Page nodes (anything that generates a doc page)
- no longer have a subtype. Some of the subtypes
- (Example, External, and Header) have been promoted
- to be node types. They have become subclasses of
- PageNode or, in the case of Header, a subclass of
- Aggregate. The processing for other subtypes that
- have not (yet) been promoted to be node types is
- determined by the PageType enum.
- */
if (node->isExample())
writer.writeAttribute("subtype", "example");
else if (node->isExternalPage())
writer.writeAttribute("subtype", "externalpage");
else
- writer.writeAttribute("subtype",
- (node->pageType() == Node::AttributionPage) ? "attribution" : "page");
+ writer.writeAttribute("subtype", (static_cast<PageNode*>(node)->isAttribution() ? "attribution" : "page"));
const auto *pageNode = static_cast<const PageNode *>(node);
writer.writeAttribute("title", pageNode->title());
@@ -1102,7 +1027,6 @@ bool QDocIndexFiles::generateIndexSection(QXmlStreamWriter &writer, Node *node,
} break;
case Node::Group:
case Node::Module:
- case Node::JsModule:
case Node::QmlModule: {
const auto *collectionNode = static_cast<const CollectionNode *>(node);
writer.writeAttribute("seen", collectionNode->wasSeen() ? "true" : "false");
@@ -1114,12 +1038,11 @@ bool QDocIndexFiles::generateIndexSection(QXmlStreamWriter &writer, Node *node,
if (!brief.isEmpty())
writer.writeAttribute("brief", brief);
} break;
- case Node::JsProperty:
case Node::QmlProperty: {
auto *qmlPropertyNode = static_cast<QmlPropertyNode *>(node);
writer.writeAttribute("type", qmlPropertyNode->dataType());
writer.writeAttribute("attached", qmlPropertyNode->isAttached() ? "true" : "false");
- writer.writeAttribute("writable", qmlPropertyNode->isWritable() ? "true" : "false");
+ writer.writeAttribute("writable", qmlPropertyNode->isReadOnly() ? "false" : "true");
if (qmlPropertyNode->isRequired())
writer.writeAttribute("required", "true");
if (!brief.isEmpty())
@@ -1128,45 +1051,21 @@ bool QDocIndexFiles::generateIndexSection(QXmlStreamWriter &writer, Node *node,
case Node::Property: {
const auto *propertyNode = static_cast<const PropertyNode *>(node);
- if (propertyNode->propertyType() == PropertyNode::Bindable)
+ if (propertyNode->propertyType() == PropertyNode::PropertyType::BindableProperty)
writer.writeAttribute("bindable", "true");
+ if (!propertyNode->isWritable())
+ writer.writeAttribute("writable", "false");
+
if (!brief.isEmpty())
writer.writeAttribute("brief", brief);
- const auto &getters = propertyNode->getters();
- for (const auto *fnNode : getters) {
- if (fnNode) {
- const auto *functionNode = static_cast<const FunctionNode *>(fnNode);
- writer.writeStartElement("getter");
- writer.writeAttribute("name", functionNode->name());
- writer.writeEndElement(); // getter
- }
- }
- const auto &setters = propertyNode->setters();
- for (const auto *fnNode : setters) {
- if (fnNode) {
- const auto *functionNode = static_cast<const FunctionNode *>(fnNode);
- writer.writeStartElement("setter");
- writer.writeAttribute("name", functionNode->name());
- writer.writeEndElement(); // setter
- }
- }
- const auto &resetters = propertyNode->resetters();
- for (const auto *fnNode : resetters) {
- if (fnNode) {
- const auto *functionNode = static_cast<const FunctionNode *>(fnNode);
- writer.writeStartElement("resetter");
- writer.writeAttribute("name", functionNode->name());
- writer.writeEndElement(); // resetter
- }
- }
- const auto &notifiers = propertyNode->notifiers();
- for (const auto *fnNode : notifiers) {
- if (fnNode) {
- const auto *functionNode = static_cast<const FunctionNode *>(fnNode);
- writer.writeStartElement("notifier");
- writer.writeAttribute("name", functionNode->name());
- writer.writeEndElement(); // notifier
+ // Property access function names
+ for (qsizetype i{0}; i < (qsizetype)PropertyNode::FunctionRole::NumFunctionRoles; ++i) {
+ auto role{(PropertyNode::FunctionRole)i};
+ for (const auto *fnNode : propertyNode->functions(role)) {
+ writer.writeStartElement(PropertyNode::roleName(role));
+ writer.writeAttribute("name", fnNode->name());
+ writer.writeEndElement();
}
}
} break;
@@ -1188,6 +1087,8 @@ bool QDocIndexFiles::generateIndexSection(QXmlStreamWriter &writer, Node *node,
writer.writeStartElement("value");
writer.writeAttribute("name", item.name());
writer.writeAttribute("value", item.value());
+ if (!item.since().isEmpty())
+ writer.writeAttribute("since", item.since());
writer.writeEndElement(); // value
}
} break;
@@ -1204,48 +1105,7 @@ bool QDocIndexFiles::generateIndexSection(QXmlStreamWriter &writer, Node *node,
break;
}
- /*
- For our pages, we canonicalize the target, keyword and content
- item names so that they can be used by qdoc for other sets of
- documentation.
-
- The reason we do this here is that we don't want to ruin
- externally composed indexes, containing non-qdoc-style target names
- when reading in indexes.
-
- targets and keywords are now allowed in any node, not just inner nodes.
- */
-
- if (node->doc().hasTargets()) {
- bool external = false;
- if (node->isExternalPage())
- external = true;
- const auto &targets = node->doc().targets();
- for (const Atom *target : targets) {
- const QString &title = target->string();
- QString name = Doc::canonicalTitle(title);
- writer.writeStartElement("target");
- if (!external)
- writer.writeAttribute("name", name);
- else
- writer.writeAttribute("name", title);
- if (name != title)
- writer.writeAttribute("title", title);
- writer.writeEndElement(); // target
- }
- }
- if (node->doc().hasKeywords()) {
- const auto &keywords = node->doc().keywords();
- for (const Atom *keyword : keywords) {
- const QString &title = keyword->string();
- QString name = Doc::canonicalTitle(title);
- writer.writeStartElement("keyword");
- writer.writeAttribute("name", name);
- if (name != title)
- writer.writeAttribute("title", title);
- writer.writeEndElement(); // keyword
- }
- }
+ writeTargets(writer, node);
/*
Some nodes have a table of contents. For these, we close
@@ -1261,7 +1121,7 @@ bool QDocIndexFiles::generateIndexSection(QXmlStreamWriter &writer, Node *node,
int level = node->doc().tableOfContentsLevels()[i];
QString title = Text::sectionHeading(item).toString();
writer.writeStartElement("contents");
- writer.writeAttribute("name", Doc::canonicalTitle(title));
+ writer.writeAttribute("name", Tree::refForAtom(item));
writer.writeAttribute("title", title);
writer.writeAttribute("level", QString::number(level));
writer.writeEndElement(); // contents
@@ -1313,14 +1173,17 @@ bool QDocIndexFiles::generateIndexSection(QXmlStreamWriter &writer, Node *node,
*/
void QDocIndexFiles::generateFunctionSection(QXmlStreamWriter &writer, FunctionNode *fn)
{
- QString objName = fn->name();
+ if (fn->isInternal() && !Config::instance().showInternal())
+ return;
+
+ const QString objName = fn->name();
writer.writeStartElement("function");
writer.writeAttribute("name", objName);
- QString fullName = fn->fullDocumentName();
+ const QString fullName = fn->fullDocumentName();
if (fullName != objName)
writer.writeAttribute("fullname", fullName);
- QString href = m_gen->fullDocumentLocation(fn);
+ const QString href = m_gen->fullDocumentLocation(fn);
if (!href.isEmpty())
writer.writeAttribute("href", href);
if (fn->threadSafeness() != Node::UnspecifiedSafeness)
@@ -1343,14 +1206,30 @@ void QDocIndexFiles::generateFunctionSection(QXmlStreamWriter &writer, FunctionN
if (!fn->since().isEmpty())
writer.writeAttribute("since", fn->since());
- QString brief = fn->doc().trimmedBriefText(fn->name()).toString();
+ const QString brief = fn->doc().trimmedBriefText(fn->name()).toString();
writer.writeAttribute("meta", fn->metanessString());
if (fn->isCppNode()) {
- writer.writeAttribute("virtual", fn->virtualness());
- writer.writeAttribute("const", fn->isConst() ? "true" : "false");
- writer.writeAttribute("static", fn->isStatic() ? "true" : "false");
- writer.writeAttribute("final", fn->isFinal() ? "true" : "false");
- writer.writeAttribute("override", fn->isOverride() ? "true" : "false");
+ if (!fn->isNonvirtual())
+ writer.writeAttribute("virtual", fn->virtualness());
+
+ if (fn->isConst())
+ writer.writeAttribute("const", "true");
+ if (fn->isStatic())
+ writer.writeAttribute("static", "true");
+ if (fn->isFinal())
+ writer.writeAttribute("final", "true");
+ if (fn->isOverride())
+ writer.writeAttribute("override", "true");
+ if (fn->isExplicit())
+ writer.writeAttribute("explicit", "true");
+ if (fn->isConstexpr())
+ writer.writeAttribute("constexpr", "true");
+
+ if (auto noexcept_info = fn->getNoexcept()) {
+ writer.writeAttribute("noexcept", "true");
+ if (!(*noexcept_info).isEmpty()) writer.writeAttribute("noexcept_expression", *noexcept_info);
+ }
+
/*
This ensures that for functions that have overloads,
the first function written is the one that is not an
@@ -1374,38 +1253,40 @@ void QDocIndexFiles::generateFunctionSection(QXmlStreamWriter &writer, FunctionN
writer.writeAttribute("associated-property",
associatedProperties.join(QLatin1Char(',')));
}
- writer.writeAttribute("type", fn->returnType());
+ }
+
+ const auto return_type = fn->returnType();
+ if (!return_type.isEmpty())
+ writer.writeAttribute("type", return_type);
+
+ if (fn->isCppNode()) {
if (!brief.isEmpty())
writer.writeAttribute("brief", brief);
+
/*
- Note: The "signature" attribute is written to the
- index file, but it is not read back in by qdoc. However,
- we need it for the webxml generator.
+ Note: The "signature" attribute is written to the
+ index file, but it is not read back in by qdoc. However,
+ we need it for the webxml generator.
*/
- QString signature = fn->signature(false, false);
- // 'const' is already part of FunctionNode::signature()
- if (fn->isFinal())
- signature += " final";
- if (fn->isOverride())
- signature += " override";
- if (fn->isPureVirtual())
- signature += " = 0";
+ const QString signature = appendAttributesToSignature(fn);
writer.writeAttribute("signature", signature);
QStringList groups = m_qdb->groupNamesForNode(fn);
if (!groups.isEmpty())
writer.writeAttribute("groups", groups.join(QLatin1Char(',')));
+ }
- for (int i = 0; i < fn->parameters().count(); ++i) {
- const Parameter &parameter = fn->parameters().at(i);
- writer.writeStartElement("parameter");
- writer.writeAttribute("type", parameter.type());
- writer.writeAttribute("name", parameter.name());
- writer.writeAttribute("default", parameter.defaultValue());
- writer.writeEndElement(); // parameter
- }
+ for (int i = 0; i < fn->parameters().count(); ++i) {
+ const Parameter &parameter = fn->parameters().at(i);
+ writer.writeStartElement("parameter");
+ writer.writeAttribute("type", parameter.type());
+ writer.writeAttribute("name", parameter.name());
+ writer.writeAttribute("default", parameter.defaultValue());
+ writer.writeEndElement(); // parameter
}
+ writeTargets(writer, fn);
+
// Append to the section if the callback object was set
if (post_)
post_->append(writer, fn);
@@ -1414,35 +1295,49 @@ void QDocIndexFiles::generateFunctionSection(QXmlStreamWriter &writer, FunctionN
}
/*!
- This function outputs a <function> element to the index file
- for each FunctionNode in \a aggregate using the \a writer.
+ \internal
+
+ Constructs the signature to be written to an index file for the function
+ represented by FunctionNode \a fn.
+
+ 'const' is already part of FunctionNode::signature(), which forms the basis
+ for the signature returned by this method. The method adds, where
+ applicable, the C++ keywords "final", "override", or "= 0", to the
+ signature carried by the FunctionNode itself.
+ */
+QString QDocIndexFiles::appendAttributesToSignature(const FunctionNode *fn) const noexcept
+{
+ QString signature = fn->signature(Node::SignatureReturnType);
+
+ if (fn->isFinal())
+ signature += " final";
+ if (fn->isOverride())
+ signature += " override";
+ if (fn->isPureVirtual())
+ signature += " = 0";
+
+ return signature;
+}
+
+/*!
+ Outputs a <function> element to the index for each FunctionNode in
+ an \a aggregate, using \a writer.
The \a aggregate has a function map that contains all the
- function nodes indexed by function name. But the map is not
- used as a multimap, so if the \a aggregate contains multiple
- functions with the same name, only one of those functions is
- in the function map index. The others are linked to that
- function using the next overload pointer.
-
- So this function generates a <function> element for a function
- followed by a function element for each of its overloads. If a
- <function> element represents an overload, it has an \c overload
- attribute set to \c true and an \c {overload-number} attribute
- set to the function's overload number. If the <function>
- element does not represent an overload, the <function> element
- has neither of these attributes.
+ function nodes (a vector of overloads) indexed by function
+ name.
+
+ If a function element represents an overload, it has an
+ \c overload attribute set to \c true and an \c {overload-number}
+ attribute set to the function's overload number.
*/
void QDocIndexFiles::generateFunctionSections(QXmlStreamWriter &writer, Aggregate *aggregate)
{
- FunctionMap &functionMap = aggregate->functionMap();
- if (!functionMap.isEmpty()) {
- for (auto it = functionMap.begin(); it != functionMap.end(); ++it) {
- FunctionNode *fn = it.value();
- while (fn) {
- if (!fn->isInternal() || Config::instance().showInternal())
- generateFunctionSection(writer, fn);
- fn = fn->nextOverload();
+ for (auto functions : std::as_const(aggregate->functionMap())) {
+ std::for_each(functions.begin(), functions.end(),
+ [this,&writer](FunctionNode *fn) {
+ generateFunctionSection(writer, fn);
}
- }
+ );
}
}
@@ -1457,8 +1352,7 @@ void QDocIndexFiles::generateIndexSections(QXmlStreamWriter &writer, Node *node,
Note that groups, modules, and QML modules are written
after all the other nodes.
*/
- if (node->isCollectionNode() || node->isGroup() || node->isModule() || node->isQmlModule()
- || node->isJsModule())
+ if (node->isCollectionNode() || node->isGroup() || node->isModule() || node->isQmlModule())
return;
if (node->isInternal() && !Config::instance().showInternal())
@@ -1506,14 +1400,6 @@ void QDocIndexFiles::generateIndexSections(QXmlStreamWriter &writer, Node *node,
writer.writeEndElement();
}
}
-
- const CNMap &jsModules = m_qdb->jsModules();
- if (!jsModules.isEmpty()) {
- for (auto it = jsModules.constBegin(); it != jsModules.constEnd(); ++it) {
- if (generateIndexSection(writer, it.value(), post))
- writer.writeEndElement();
- }
- }
}
writer.writeEndElement();
@@ -1546,7 +1432,7 @@ void QDocIndexFiles::generateIndex(const QString &fileName, const QString &url,
writer.writeAttribute("url", url);
writer.writeAttribute("title", title);
writer.writeAttribute("version", m_qdb->version());
- writer.writeAttribute("project", Config::instance().getString(CONFIG_PROJECT));
+ writer.writeAttribute("project", Config::instance().get(CONFIG_PROJECT).asString());
root_ = m_qdb->primaryTreeRoot();
if (!root_->tree()->indexTitle().isEmpty())
diff --git a/src/qdoc/qdocindexfiles.h b/src/qdoc/qdoc/src/qdoc/qdocindexfiles.h
index 242c8675b..2225aa576 100644
--- a/src/qdoc/qdocindexfiles.h
+++ b/src/qdoc/qdoc/src/qdoc/qdocindexfiles.h
@@ -1,30 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the tools applications of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#ifndef QDOCINDEXFILES_H
#define QDOCINDEXFILES_H
@@ -71,6 +46,7 @@ private:
void resolveIndex();
int indexForNode(Node *node);
bool adoptRelatedNode(Aggregate *adoptiveParent, int index);
+ void writeTargets(QXmlStreamWriter &writer, Node *node);
void generateIndex(const QString &fileName, const QString &url, const QString &title,
Generator *g);
@@ -80,13 +56,14 @@ private:
IndexSectionWriter *post = nullptr);
void generateIndexSections(QXmlStreamWriter &writer, Node *node,
IndexSectionWriter *post = nullptr);
+ QString appendAttributesToSignature(const FunctionNode *fn) const noexcept;
private:
static QDocIndexFiles *s_qdocIndexFiles;
QDocDatabase *m_qdb {};
Generator *m_gen {};
QString m_project;
- QList<QPair<ClassNode *, QString>> m_basesList;
+ QList<std::pair<ClassNode *, QString>> m_basesList;
NodeList m_relatedNodes;
bool m_storeLocationInfo;
};
diff --git a/src/qdoc/qmlcodemarker.cpp b/src/qdoc/qdoc/src/qdoc/qmlcodemarker.cpp
index 75070e52a..30dec979e 100644
--- a/src/qdoc/qmlcodemarker.cpp
+++ b/src/qdoc/qdoc/src/qdoc/qmlcodemarker.cpp
@@ -1,30 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the tools applications of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "qmlcodemarker.h"
@@ -35,13 +10,11 @@
#include "qmlmarkupvisitor.h"
#include "text.h"
-#ifndef QT_NO_DECLARATIVE
-# include <private/qqmljsast_p.h>
-# include <private/qqmljsastfwd_p.h>
-# include <private/qqmljsengine_p.h>
-# include <private/qqmljslexer_p.h>
-# include <private/qqmljsparser_p.h>
-#endif
+#include <private/qqmljsast_p.h>
+#include <private/qqmljsastfwd_p.h>
+#include <private/qqmljsengine_p.h>
+#include <private/qqmljslexer_p.h>
+#include <private/qqmljsparser_p.h>
QT_BEGIN_NAMESPACE
@@ -50,7 +23,11 @@ QT_BEGIN_NAMESPACE
*/
bool QmlCodeMarker::recognizeCode(const QString &code)
{
-#ifndef QT_NO_DECLARATIVE
+ // Naive pre-check; starts with an import statement or 'CamelCase {'
+ static const QRegularExpression regExp(QStringLiteral("^\\s*(import |([A-Z][a-z0-9]*)+\\s?{)"));
+ if (!regExp.match(code).hasMatch())
+ return false;
+
QQmlJS::Engine engine;
QQmlJS::Lexer lexer(&engine);
QQmlJS::Parser parser(&engine);
@@ -60,10 +37,6 @@ bool QmlCodeMarker::recognizeCode(const QString &code)
lexer.setCode(newCode, 1);
return parser.parse();
-#else
- Q_UNUSED(code);
- return false;
-#endif
}
/*!
@@ -100,7 +73,7 @@ QString QmlCodeMarker::markedUpCode(const QString &code, const Node *relative,
/*!
Constructs and returns the marked up name for the \a node.
- If the node is any kind of QML or JS function (a method,
+ If the node is any kind of QML function (a method,
signal, or handler), "()" is appended to the marked up name.
*/
QString QmlCodeMarker::markedUpName(const Node *node)
@@ -111,31 +84,14 @@ QString QmlCodeMarker::markedUpName(const Node *node)
return name;
}
-QString QmlCodeMarker::markedUpIncludes(const QStringList &includes)
-{
- QString code;
-
- for (const auto &include : includes)
- code += "import " + include + QLatin1Char('\n');
-
- Location location;
- return addMarkUp(code, nullptr, location);
-}
-
-QString QmlCodeMarker::functionBeginRegExp(const QString &funcName)
+QString QmlCodeMarker::markedUpInclude(const QString &include)
{
- return QLatin1Char('^') + QRegularExpression::escape("function " + funcName) + QLatin1Char('$');
-}
-
-QString QmlCodeMarker::functionEndRegExp(const QString & /* funcName */)
-{
- return "^\\}$";
+ return addMarkUp("import " + include, nullptr, Location{});
}
QString QmlCodeMarker::addMarkUp(const QString &code, const Node * /* relative */,
const Location &location)
{
-#ifndef QT_NO_DECLARATIVE
QQmlJS::Engine engine;
QQmlJS::Lexer lexer(&engine);
@@ -167,25 +123,13 @@ QString QmlCodeMarker::addMarkUp(const QString &code, const Node * /* relative *
}
return output;
-#else
- Q_UNUSED(code);
- location.warning("QtDeclarative not installed; cannot parse QML or JS.");
- return QString();
-#endif
}
-#ifndef QT_NO_DECLARATIVE
/*
Copied and pasted from
src/declarative/qml/qqmlscriptparser.cpp.
*/
-static void replaceWithSpace(QString &str, int idx, int n)
-{
- QChar *data = str.data() + idx;
- const QChar space(QLatin1Char(' '));
- for (int ii = 0; ii < n; ++ii)
- *data++ = space;
-}
+void replaceWithSpace(QString &str, int idx, int n); // qmlcodeparser.cpp
/*
Copied and pasted from
@@ -227,6 +171,5 @@ QList<QQmlJS::SourceLocation> QmlCodeMarker::extractPragmas(QString &script)
}
return removed;
}
-#endif
QT_END_NAMESPACE
diff --git a/src/qdoc/qdoc/src/qdoc/qmlcodemarker.h b/src/qdoc/qdoc/src/qdoc/qmlcodemarker.h
new file mode 100644
index 000000000..64a1f7c9f
--- /dev/null
+++ b/src/qdoc/qdoc/src/qdoc/qmlcodemarker.h
@@ -0,0 +1,38 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#ifndef QMLCODEMARKER_H
+#define QMLCODEMARKER_H
+
+#include "cppcodemarker.h"
+
+#include <private/qqmljsastfwd_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QmlCodeMarker : public CppCodeMarker
+{
+public:
+ QmlCodeMarker() = default;
+ ~QmlCodeMarker() override = default;
+
+ bool recognizeCode(const QString &code) override;
+ bool recognizeExtension(const QString &ext) override;
+ bool recognizeLanguage(const QString &language) override;
+ [[nodiscard]] Atom::AtomType atomType() const override;
+ QString markedUpCode(const QString &code, const Node *relative,
+ const Location &location) override;
+
+ QString markedUpName(const Node *node) override;
+ QString markedUpInclude(const QString &include) override;
+
+ /* Copied from src/declarative/qml/qdeclarativescriptparser.cpp */
+ QList<QQmlJS::SourceLocation> extractPragmas(QString &script);
+
+private:
+ QString addMarkUp(const QString &code, const Node *relative, const Location &location);
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/qdoc/qdoc/src/qdoc/qmlcodeparser.cpp b/src/qdoc/qdoc/src/qdoc/qmlcodeparser.cpp
new file mode 100644
index 000000000..fadd7c307
--- /dev/null
+++ b/src/qdoc/qdoc/src/qdoc/qmlcodeparser.cpp
@@ -0,0 +1,143 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "qmlcodeparser.h"
+
+#include "node.h"
+#include "qmlvisitor.h"
+#include "utilities.h"
+
+#include <private/qqmljsast_p.h>
+
+#include <qdebug.h>
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ Returns "QML".
+ */
+QString QmlCodeParser::language()
+{
+ return "QML";
+}
+
+/*!
+ Returns a string list containing "*.qml". This is the only
+ file type parsed by the QMLN parser.
+ */
+QStringList QmlCodeParser::sourceFileNameFilter()
+{
+ return QStringList() << "*.qml";
+}
+
+/*!
+ Parses the source file at \a filePath and inserts the contents
+ into the database. The \a location is used for error reporting.
+
+ If it can't open the file at \a filePath, it reports an error
+ and returns without doing anything.
+ */
+void QmlCodeParser::parseSourceFile(const Location &location, const QString &filePath, CppCodeParser&)
+{
+ static const QSet<QString> topic_commands{
+ COMMAND_VARIABLE, COMMAND_QMLCLASS, COMMAND_QMLTYPE, COMMAND_QMLPROPERTY,
+ COMMAND_QMLPROPERTYGROUP, COMMAND_QMLATTACHEDPROPERTY, COMMAND_QMLSIGNAL,
+ COMMAND_QMLATTACHEDSIGNAL, COMMAND_QMLMETHOD, COMMAND_QMLATTACHEDMETHOD,
+ COMMAND_QMLVALUETYPE, COMMAND_QMLBASICTYPE,
+ };
+
+ QFile in(filePath);
+ if (!in.open(QIODevice::ReadOnly)) {
+ location.error(QStringLiteral("Cannot open QML file '%1'").arg(filePath));
+ return;
+ }
+
+ QString document = in.readAll();
+ in.close();
+
+ QString newCode = document;
+ extractPragmas(newCode);
+
+ QQmlJS::Engine engine{};
+ QQmlJS::Lexer lexer{&engine};
+ lexer.setCode(newCode, 1);
+
+ QQmlJS::Parser parser{&engine};
+
+ if (parser.parse()) {
+ QQmlJS::AST::UiProgram *ast = parser.ast();
+ QmlDocVisitor visitor(filePath, newCode, &engine, topic_commands + CodeParser::common_meta_commands,
+ topic_commands);
+ QQmlJS::AST::Node::accept(ast, &visitor);
+ if (visitor.hasError())
+ Location(filePath).warning("Could not analyze QML file, output is incomplete.");
+ }
+ const auto &messages = parser.diagnosticMessages();
+ for (const auto &msg : messages) {
+ qCDebug(lcQdoc, "%s: %d: %d: QML syntax error: %s", qUtf8Printable(filePath),
+ msg.loc.startLine, msg.loc.startColumn, qUtf8Printable(msg.message));
+ }
+}
+
+/*!
+ Copy and paste from src/declarative/qml/qdeclarativescriptparser.cpp.
+ This function blanks out the section of the \a str beginning at \a idx
+ and running for \a n characters.
+*/
+void replaceWithSpace(QString &str, int idx, int n) // Also used in qmlcodemarker.cpp.
+{
+ QChar *data = str.data() + idx;
+ const QChar space(QLatin1Char(' '));
+ for (int ii = 0; ii < n; ++ii)
+ *data++ = space;
+}
+
+/*!
+ Copy & paste from src/declarative/qml/qdeclarativescriptparser.cpp,
+ then modified to return no values.
+
+ Searches for ".pragma <value>" declarations within \a script.
+ Currently supported pragmas are: library
+*/
+void QmlCodeParser::extractPragmas(QString &script)
+{
+ const QString pragma(QLatin1String("pragma"));
+
+ QQmlJS::Lexer l(nullptr);
+ l.setCode(script, 0);
+
+ int token = l.lex();
+
+ while (true) {
+ if (token != QQmlJSGrammar::T_DOT)
+ return;
+
+ int startOffset = l.tokenOffset();
+ int startLine = l.tokenStartLine();
+
+ token = l.lex();
+
+ if (token != QQmlJSGrammar::T_IDENTIFIER || l.tokenStartLine() != startLine
+ || script.mid(l.tokenOffset(), l.tokenLength()) != pragma)
+ return;
+
+ token = l.lex();
+
+ if (token != QQmlJSGrammar::T_IDENTIFIER || l.tokenStartLine() != startLine)
+ return;
+
+ QString pragmaValue = script.mid(l.tokenOffset(), l.tokenLength());
+ int endOffset = l.tokenLength() + l.tokenOffset();
+
+ token = l.lex();
+ if (l.tokenStartLine() == startLine)
+ return;
+
+ if (pragmaValue == QLatin1String("library"))
+ replaceWithSpace(script, startOffset, endOffset - startOffset);
+ else
+ return;
+ }
+}
+
+QT_END_NAMESPACE
diff --git a/src/qdoc/qdoc/src/qdoc/qmlcodeparser.h b/src/qdoc/qdoc/src/qdoc/qmlcodeparser.h
new file mode 100644
index 000000000..dff493be4
--- /dev/null
+++ b/src/qdoc/qdoc/src/qdoc/qmlcodeparser.h
@@ -0,0 +1,38 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#ifndef QMLCODEPARSER_H
+#define QMLCODEPARSER_H
+
+#include "codeparser.h"
+
+#include <QtCore/qset.h>
+
+#include <private/qqmljsengine_p.h>
+#include <private/qqmljslexer_p.h>
+#include <private/qqmljsparser_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class Node;
+class QString;
+
+class QmlCodeParser : public CodeParser
+{
+public:
+ QmlCodeParser() = default;
+ ~QmlCodeParser() override = default;
+
+ void initializeParser() override {}
+ void terminateParser() override {}
+ QString language() override;
+ QStringList sourceFileNameFilter() override;
+ void parseSourceFile(const Location &location, const QString &filePath, CppCodeParser&) override;
+
+ /* Copied from src/declarative/qml/qdeclarativescriptparser.cpp */
+ void extractPragmas(QString &script);
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/qdoc/qmlmarkupvisitor.cpp b/src/qdoc/qdoc/src/qdoc/qmlmarkupvisitor.cpp
index 65c3955dc..31adb838d 100644
--- a/src/qdoc/qmlmarkupvisitor.cpp
+++ b/src/qdoc/qdoc/src/qdoc/qmlmarkupvisitor.cpp
@@ -1,44 +1,16 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the tools applications of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "qmlmarkupvisitor.h"
#include <QtCore/qglobal.h>
#include <QtCore/qstringlist.h>
-#ifndef QT_NO_DECLARATIVE
-# include <private/qqmljsast_p.h>
-# include <private/qqmljsengine_p.h>
-#endif
+#include <private/qqmljsast_p.h>
+#include <private/qqmljsengine_p.h>
QT_BEGIN_NAMESPACE
-#ifndef QT_NO_DECLARATIVE
QmlMarkupVisitor::QmlMarkupVisitor(const QString &source,
const QList<QQmlJS::SourceLocation> &pragmas,
QQmlJS::Engine *engine)
@@ -53,7 +25,7 @@ QmlMarkupVisitor::QmlMarkupVisitor(const QString &source,
int i = 0;
int j = 0;
const QList<QQmlJS::SourceLocation> comments = engine->comments();
- while (i < comments.size() && j < pragmas.length()) {
+ while (i < comments.size() && j < pragmas.size()) {
if (comments[i].offset < pragmas[j].offset) {
m_extraTypes.append(Comment);
m_extraLocations.append(comments[i]);
@@ -71,7 +43,7 @@ QmlMarkupVisitor::QmlMarkupVisitor(const QString &source,
++i;
}
- while (j < pragmas.length()) {
+ while (j < pragmas.size()) {
m_extraTypes.append(Pragma);
m_extraLocations.append(pragmas[j]);
++j;
@@ -87,7 +59,7 @@ static const QString squot = QLatin1String("&quot;");
QString QmlMarkupVisitor::protect(const QString &str)
{
- qsizetype n = str.length();
+ qsizetype n = str.size();
QString marked;
marked.reserve(n * 2 + 30);
const QChar *data = str.constData();
@@ -114,8 +86,8 @@ QString QmlMarkupVisitor::protect(const QString &str)
QString QmlMarkupVisitor::markedUpCode()
{
- if (int(m_cursor) < m_source.length())
- addExtra(m_cursor, m_source.length());
+ if (int(m_cursor) < m_source.size())
+ addExtra(m_cursor, m_source.size());
return m_output;
}
@@ -127,7 +99,7 @@ bool QmlMarkupVisitor::hasError() const
void QmlMarkupVisitor::addExtra(quint32 start, quint32 finish)
{
- if (m_extraIndex >= m_extraLocations.length()) {
+ if (m_extraIndex >= m_extraLocations.size()) {
QString extra = m_source.mid(start, finish - start);
if (extra.trimmed().isEmpty())
m_output += extra;
@@ -138,7 +110,7 @@ void QmlMarkupVisitor::addExtra(quint32 start, quint32 finish)
return;
}
- while (m_extraIndex < m_extraLocations.length()) {
+ while (m_extraIndex < m_extraLocations.size()) {
if (m_extraTypes[m_extraIndex] == Comment) {
if (m_extraLocations[m_extraIndex].offset - 2 >= start)
break;
@@ -150,7 +122,7 @@ void QmlMarkupVisitor::addExtra(quint32 start, quint32 finish)
}
quint32 i = start;
- while (i < finish && m_extraIndex < m_extraLocations.length()) {
+ while (i < finish && m_extraIndex < m_extraLocations.size()) {
quint32 j = m_extraLocations[m_extraIndex].offset - 2;
if (i <= j && j < finish) {
if (i < j)
@@ -251,9 +223,9 @@ void QmlMarkupVisitor::endVisit(QQmlJS::AST::UiImport *uiimport)
bool QmlMarkupVisitor::visit(QQmlJS::AST::UiPublicMember *member)
{
if (member->type == QQmlJS::AST::UiPublicMember::Property) {
- addVerbatim(member->defaultToken);
- addVerbatim(member->readonlyToken);
- addVerbatim(member->propertyToken);
+ addVerbatim(member->defaultToken());
+ addVerbatim(member->readonlyToken());
+ addVerbatim(member->propertyToken());
addVerbatim(member->typeModifierToken);
addMarkedUpToken(member->typeToken, QLatin1String("type"));
addMarkedUpToken(member->identifierToken, QLatin1String("name"));
@@ -263,10 +235,10 @@ bool QmlMarkupVisitor::visit(QQmlJS::AST::UiPublicMember *member)
else if (member->statement)
QQmlJS::AST::Node::accept(member->statement, this);
} else {
- addVerbatim(member->propertyToken);
+ addVerbatim(member->propertyToken());
addVerbatim(member->typeModifierToken);
addMarkedUpToken(member->typeToken, QLatin1String("type"));
- // addVerbatim(member->identifierToken);
+ // addVerbatim(member->identifierToken());
QQmlJS::AST::Node::accept(member->parameters, this);
}
addVerbatim(member->semicolonToken);
@@ -819,6 +791,4 @@ void QmlMarkupVisitor::throwRecursionDepthError()
m_hasRecursionDepthError = true;
}
-#endif
-
QT_END_NAMESPACE
diff --git a/src/qdoc/qmlmarkupvisitor.h b/src/qdoc/qdoc/src/qdoc/qmlmarkupvisitor.h
index 936d217a1..a19636a67 100644
--- a/src/qdoc/qmlmarkupvisitor.h
+++ b/src/qdoc/qdoc/src/qdoc/qmlmarkupvisitor.h
@@ -1,30 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the tools applications of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#ifndef QMLMARKUPVISITOR_H
#define QMLMARKUPVISITOR_H
@@ -34,9 +9,8 @@
#include <QtCore/qstring.h>
-#ifndef QT_NO_DECLARATIVE
-# include <private/qqmljsastvisitor_p.h>
-# include <private/qqmljsengine_p.h>
+#include <private/qqmljsastvisitor_p.h>
+#include <private/qqmljsengine_p.h>
QT_BEGIN_NAMESPACE
@@ -163,5 +137,3 @@ Q_DECLARE_TYPEINFO(QmlMarkupVisitor::ExtraType, Q_PRIMITIVE_TYPE);
QT_END_NAMESPACE
#endif
-
-#endif
diff --git a/src/qdoc/qdoc/src/qdoc/qmlpropertynode.cpp b/src/qdoc/qdoc/src/qdoc/qmlpropertynode.cpp
new file mode 100644
index 000000000..335b7d870
--- /dev/null
+++ b/src/qdoc/qdoc/src/qdoc/qmlpropertynode.cpp
@@ -0,0 +1,175 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "qmlpropertynode.h"
+
+#include "classnode.h"
+#include "propertynode.h"
+#include "enumnode.h"
+
+#include <utility>
+#include "qdocdatabase.h"
+#include "utilities.h"
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ Constructor for the QML property node.
+ */
+QmlPropertyNode::QmlPropertyNode(Aggregate *parent, const QString &name, QString type,
+ bool attached)
+ : Node(QmlProperty, parent, name),
+ m_type(std::move(type)),
+ m_attached(attached)
+{
+ if (m_type == "alias")
+ m_isAlias = true;
+ if (name.startsWith("__"))
+ setStatus(Internal);
+}
+
+/*!
+ \fn bool QmlPropertyNode::isReadOnly() const
+
+ Returns \c true if this QML property node is marked as a
+ read-only property.
+*/
+
+/*!
+ \fn const EnumNode *QmlPropertyNode::enumNode() const
+
+ Returns the node representing the C++ enumeration associated
+ with this property, or \nullptr.
+*/
+
+/*!
+ Returns the prefix to use for documentated enumerators from
+ the associated C++ enum for this property.
+*/
+const QString &QmlPropertyNode::enumPrefix() const
+{
+ return !m_enumNode.second.isEmpty() ?
+ m_enumNode.second : parent()->name();
+}
+
+/*!
+ Locates the node specified by \a path and sets it as the C++ enumeration
+ associated with this property.
+
+ \a registeredQmlName is used as the prefix in the generated enum value
+ documentation.
+
+ \note The target EnumNode is searched under the primary tree only.
+
+ Returns \c true on success.
+*/
+bool QmlPropertyNode::setEnumNode(const QString &path, const QString &registeredQmlName)
+{
+ m_enumNode.first = static_cast<EnumNode*>(
+ QDocDatabase::qdocDB()->primaryTree()->findNodeByNameAndType(path.split("::"), &Node::isEnumType)
+ );
+ m_enumNode.second = registeredQmlName;
+ return m_enumNode.first != nullptr;
+}
+
+/*!
+ Returns \c true if this QML property or attached property is
+ read-only. If the read-only status is not set explicitly
+ using the \\readonly command, QDoc attempts to resolve it
+ from the associated C++ class instantiated by the QML type
+ that this property belongs to.
+
+ \note Depending on how the QML type is implemented, this
+ information may not be available to QDoc. If so, add a debug
+ line but do not treat it as a warning.
+ */
+bool QmlPropertyNode::isReadOnly()
+{
+ if (m_readOnly != FlagValueDefault)
+ return fromFlagValue(m_readOnly, false);
+
+ // Find the parent QML type node
+ auto *parent{this->parent()};
+ while (parent && !(parent->isQmlType()))
+ parent = parent->parent();
+
+ bool readonly{false};
+ if (auto qcn = static_cast<QmlTypeNode *>(parent); qcn && qcn->classNode()) {
+ if (auto propertyNode = findCorrespondingCppProperty(); propertyNode)
+ readonly = !propertyNode->isWritable();
+ else
+ qCDebug(lcQdoc).nospace()
+ << qPrintable(defLocation().toString())
+ << ": Automatic resolution of QML property attributes failed for "
+ << name()
+ << " (Q_PROPERTY not found in the C++ class hierarchy known to QDoc. "
+ << "Likely, the type is replaced with a private implementation.)";
+ }
+ markReadOnly(readonly);
+ return readonly;
+}
+
+/*!
+ Returns \c true if this QML property is marked with \required or the
+ corresponding C++ property uses the REQUIRED keyword.
+*/
+bool QmlPropertyNode::isRequired()
+{
+ if (m_required != FlagValueDefault)
+ return fromFlagValue(m_required, false);
+
+ PropertyNode *pn = findCorrespondingCppProperty();
+ return pn != nullptr && pn->isRequired();
+}
+
+/*!
+ Returns a pointer this QML property's corresponding C++
+ property, if it has one.
+ */
+PropertyNode *QmlPropertyNode::findCorrespondingCppProperty()
+{
+ PropertyNode *pn;
+ Node *n = parent();
+ while (n && !(n->isQmlType()))
+ n = n->parent();
+ if (n) {
+ auto *qcn = static_cast<QmlTypeNode *>(n);
+ ClassNode *cn = qcn->classNode();
+ if (cn) {
+ /*
+ If there is a dot in the property name, first
+ find the C++ property corresponding to the QML
+ property group.
+ */
+ QStringList dotSplit = name().split(QChar('.'));
+ pn = cn->findPropertyNode(dotSplit[0]);
+ if (pn) {
+ /*
+ Now find the C++ property corresponding to
+ the QML property in the QML property group,
+ <group>.<property>.
+ */
+ if (dotSplit.size() > 1) {
+ QStringList path(extractClassName(pn->qualifiedDataType()));
+ Node *nn = QDocDatabase::qdocDB()->findClassNode(path);
+ if (nn) {
+ auto *cn = static_cast<ClassNode *>(nn);
+ PropertyNode *pn2 = cn->findPropertyNode(dotSplit[1]);
+ /*
+ If found, return the C++ property
+ corresponding to the QML property.
+ Otherwise, return the C++ property
+ corresponding to the QML property
+ group.
+ */
+ return (pn2 ? pn2 : pn);
+ }
+ } else
+ return pn;
+ }
+ }
+ }
+ return nullptr;
+}
+
+QT_END_NAMESPACE
diff --git a/src/qdoc/qmlpropertynode.h b/src/qdoc/qdoc/src/qdoc/qmlpropertynode.h
index 07f2c7f43..f966949c1 100644
--- a/src/qdoc/qmlpropertynode.h
+++ b/src/qdoc/qdoc/src/qdoc/qmlpropertynode.h
@@ -1,30 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the tools applications of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#ifndef QMLPROPERTYNODE_H
#define QMLPROPERTYNODE_H
@@ -45,20 +20,18 @@ public:
void setDataType(const QString &dataType) override { m_type = dataType; }
void setStored(bool stored) { m_stored = toFlagValue(stored); }
void setDefaultValue(const QString &value) { m_defaultValue = value; }
- void setDesignable(bool designable) { m_designable = toFlagValue(designable); }
void setRequired() { m_required = toFlagValue(true); }
+ bool setEnumNode(const QString &path, const QString &registeredQmlName);
[[nodiscard]] const QString &dataType() const { return m_type; }
[[nodiscard]] const QString &defaultValue() const { return m_defaultValue; }
- [[nodiscard]] bool isReadOnlySet() const { return (readOnly_ != FlagValueDefault); }
[[nodiscard]] bool isStored() const { return fromFlagValue(m_stored, true); }
- bool isWritable();
bool isRequired();
[[nodiscard]] bool isDefault() const override { return m_isDefault; }
- [[nodiscard]] bool isReadOnly() const override { return fromFlagValue(readOnly_, false); }
+ [[nodiscard]] bool isReadOnly() const { return fromFlagValue(m_readOnly, false); }
+ [[nodiscard]] bool isReadOnly();
[[nodiscard]] bool isAlias() const override { return m_isAlias; }
[[nodiscard]] bool isAttached() const override { return m_attached; }
- [[nodiscard]] bool isQtQuickNode() const override { return parent()->isQtQuickNode(); }
[[nodiscard]] QString qmlTypeName() const override { return parent()->qmlTypeName(); }
[[nodiscard]] QString logicalModuleName() const override
{
@@ -73,9 +46,11 @@ public:
return parent()->logicalModuleIdentifier();
}
[[nodiscard]] QString element() const override { return parent()->name(); }
+ [[nodiscard]] const EnumNode *enumNode() const { return m_enumNode.first; }
+ [[nodiscard]] const QString &enumPrefix() const;
void markDefault() override { m_isDefault = true; }
- void markReadOnly(bool flag) override { readOnly_ = toFlagValue(flag); }
+ void markReadOnly(bool flag) override { m_readOnly = toFlagValue(flag); }
private:
PropertyNode *findCorrespondingCppProperty();
@@ -84,12 +59,12 @@ private:
QString m_type {};
QString m_defaultValue {};
FlagValue m_stored { FlagValueDefault };
- FlagValue m_designable { FlagValueDefault };
bool m_isAlias { false };
bool m_isDefault { false };
bool m_attached {};
- FlagValue readOnly_ { FlagValueDefault };
+ FlagValue m_readOnly { FlagValueDefault };
FlagValue m_required { FlagValueDefault };
+ std::pair<EnumNode *, QString> m_enumNode { nullptr, {} };
};
QT_END_NAMESPACE
diff --git a/src/qdoc/qmltypenode.cpp b/src/qdoc/qdoc/src/qdoc/qmltypenode.cpp
index ffa0eb47a..4285f9b6e 100644
--- a/src/qdoc/qmltypenode.cpp
+++ b/src/qdoc/qdoc/src/qdoc/qmltypenode.cpp
@@ -1,30 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the tools applications of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "qmltypenode.h"
#include "collectionnode.h"
@@ -37,14 +12,15 @@ QT_BEGIN_NAMESPACE
QMultiMap<const Node *, Node *> QmlTypeNode::s_inheritedBy;
/*!
- Constructs a Qml type node or a Js type node depending on
- the value of \a type, which is Node::QmlType by default,
- but which can also be Node::JsType. The new node has the
- given \a parent and \a name.
+ Constructs a Qml type.
+
+ The new node has the given \a parent, name \a name, and a specific node
+ \a type. Valid types are Node::QmlType and Node::QmlValueType.
*/
-QmlTypeNode::QmlTypeNode(Aggregate *parent, const QString &name, NodeType type)
+QmlTypeNode::QmlTypeNode(Aggregate *parent, const QString &name, Node::NodeType type)
: Aggregate(type, parent, name)
{
+ Q_ASSERT(type == Node::QmlType || type == Node::QmlValueType);
setTitle(name);
}
@@ -151,8 +127,8 @@ void QmlTypeNode::resolveInheritance(NodeMap &previousSearches)
auto *base = static_cast<QmlTypeNode *>(previousSearches.value(m_qmlBaseName));
if (!previousSearches.contains(m_qmlBaseName)) {
- for (const auto &import : qAsConst(m_importList)) {
- base = QDocDatabase::qdocDB()->findQmlType(import, m_qmlBaseName);
+ for (const auto &imp : std::as_const(m_importList)) {
+ base = QDocDatabase::qdocDB()->findQmlType(imp, m_qmlBaseName);
if (base)
break;
}
@@ -174,16 +150,4 @@ void QmlTypeNode::resolveInheritance(NodeMap &previousSearches)
}
}
-/*!
- Constructs either a Qml basic type node or a Javascript
- basic type node, depending on the value pf \a type, which
- must be either Node::QmlBasicType or Node::JsBasicType.
- The new node has the given \a parent and \a name.
- */
-QmlBasicTypeNode::QmlBasicTypeNode(Aggregate *parent, const QString &name, Node::NodeType type)
- : Aggregate(type, parent, name)
-{
- setTitle(name);
-}
-
QT_END_NAMESPACE
diff --git a/src/qdoc/qmltypenode.h b/src/qdoc/qdoc/src/qdoc/qmltypenode.h
index 612564dc4..d7cd5ff2b 100644
--- a/src/qdoc/qmltypenode.h
+++ b/src/qdoc/qdoc/src/qdoc/qmltypenode.h
@@ -1,30 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the tools applications of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#ifndef QMLTYPENODE_H
#define QMLTYPENODE_H
@@ -46,12 +21,8 @@ typedef QList<ImportRec> ImportList;
class QmlTypeNode : public Aggregate
{
public:
- QmlTypeNode(Aggregate *parent, const QString &name, NodeType type = QmlType);
+ QmlTypeNode(Aggregate *parent, const QString &name, Node::NodeType type);
[[nodiscard]] bool isFirstClassAggregate() const override { return true; }
- [[nodiscard]] bool isQtQuickNode() const override
- {
- return (logicalModuleName() == QLatin1String("QtQuick"));
- }
ClassNode *classNode() override { return m_classNode; }
void setClassNode(ClassNode *cn) override { m_classNode = cn; }
[[nodiscard]] bool isAbstract() const override { return m_abstract; }
@@ -60,8 +31,6 @@ public:
void setWrapper() override { m_wrapper = true; }
[[nodiscard]] bool isInternal() const override { return (status() == Internal); }
[[nodiscard]] QString qmlFullBaseName() const override;
- [[nodiscard]] QString obsoleteLink() const override { return m_obsoleteLink; }
- void setObsoleteLink(const QString &t) override { m_obsoleteLink = t; }
[[nodiscard]] QString logicalModuleName() const override;
[[nodiscard]] QString logicalModuleVersion() const override;
[[nodiscard]] QString logicalModuleIdentifier() const override;
@@ -73,7 +42,6 @@ public:
void setQmlBaseName(const QString &name) { m_qmlBaseName = name; }
[[nodiscard]] QmlTypeNode *qmlBaseNode() const override { return m_qmlBaseNode; }
void resolveInheritance(NodeMap &previousSearches);
- [[nodiscard]] bool cppClassRequired() const { return m_classNodeRequired; }
static void addInheritedBy(const Node *base, Node *sub);
static void subclasses(const Node *base, NodeList &subs);
static void terminate();
@@ -84,23 +52,14 @@ public:
private:
bool m_abstract { false };
- bool m_classNodeRequired { false };
bool m_wrapper { false };
ClassNode *m_classNode { nullptr };
QString m_qmlBaseName {};
- QString m_obsoleteLink {};
CollectionNode *m_logicalModule { nullptr };
QmlTypeNode *m_qmlBaseNode { nullptr };
ImportList m_importList {};
};
-class QmlBasicTypeNode : public Aggregate
-{
-public:
- QmlBasicTypeNode(Aggregate *parent, const QString &name, NodeType type = QmlBasicType);
- [[nodiscard]] bool isFirstClassAggregate() const override { return true; }
-};
-
QT_END_NAMESPACE
#endif // QMLTYPENODE_H
diff --git a/src/qdoc/qmlvisitor.cpp b/src/qdoc/qdoc/src/qdoc/qmlvisitor.cpp
index 9c46ac661..d6ecf1986 100644
--- a/src/qdoc/qmlvisitor.cpp
+++ b/src/qdoc/qdoc/src/qdoc/qmlvisitor.cpp
@@ -1,30 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the tools applications of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "qmlvisitor.h"
@@ -36,19 +11,19 @@
#include "qdocdatabase.h"
#include "qmlpropertynode.h"
#include "tokenizer.h"
+#include "utilities.h"
#include <QtCore/qdebug.h>
#include <QtCore/qfileinfo.h>
#include <QtCore/qglobal.h>
-#ifndef QT_NO_DECLARATIVE
-# include <private/qqmljsast_p.h>
-# include <private/qqmljsengine_p.h>
-#endif
+#include <private/qqmljsast_p.h>
+#include <private/qqmljsengine_p.h>
QT_BEGIN_NAMESPACE
-#ifndef QT_NO_DECLARATIVE
+using namespace Qt::StringLiterals;
+
/*!
The constructor stores all the parameters in local data members.
*/
@@ -119,89 +94,96 @@ private:
/*!
Finds the nearest unused qdoc comment above the QML entity
- represented by the \a node and processes the qdoc commands
+ represented by a \a node and processes the qdoc commands
in that comment. The processed documentation is stored in
- the \a node.
+ \a node.
+
+ If \a node is a \c nullptr and there is a valid comment block,
+ the QML module identifier (\inqmlmodule argument) is used
+ for searching an existing QML type node. If an existing node
+ is not found, constructs a new QmlTypeNode instance.
- If a qdoc comment is found for \a location, true is returned.
- If a comment is not found there, false is returned.
+ Returns a pointer to the QmlTypeNode instance if one was
+ found or constructed. Otherwise, returns a pointer to the \a
+ node that was passed as an argument.
*/
-bool QmlDocVisitor::applyDocumentation(QQmlJS::SourceLocation location, Node *node)
+Node *QmlDocVisitor::applyDocumentation(QQmlJS::SourceLocation location, Node *node)
{
QQmlJS::SourceLocation loc = precedingComment(location.begin());
+ Location comment_loc(m_filePath);
+
+ // No preceding comment; construct a new QML type if
+ // needed.
+ if (!loc.isValid()) {
+ if (!node)
+ node = new QmlTypeNode(m_current, m_name, Node::QmlType);
+ comment_loc.setLineNo(location.startLine);
+ node->setLocation(comment_loc);
+ return node;
+ }
+
+ QString source = m_document.mid(loc.offset + 1, loc.length - 1);
+ comment_loc.setLineNo(loc.startLine);
+ comment_loc.setColumnNo(loc.startColumn);
+
+ Doc doc(comment_loc, comment_loc, source, m_commands, m_topics);
+ const TopicList &topicsUsed = doc.topicsUsed();
+ NodeList nodes;
+ if (!node) {
+ QString qmid;
+ if (auto args = doc.metaCommandArgs(COMMAND_INQMLMODULE); !args.isEmpty())
+ qmid = args.first().first;
+ node = QDocDatabase::qdocDB()->findQmlTypeInPrimaryTree(qmid, m_name);
+ if (!node) {
+ node = new QmlTypeNode(m_current, m_name, Node::QmlType);
+ node->setLocation(comment_loc);
+ }
+ }
- if (loc.isValid()) {
- QString source = m_document.mid(loc.offset, loc.length);
- Location start(m_filePath);
- start.setLineNo(loc.startLine);
- start.setColumnNo(loc.startColumn);
- Location finish(m_filePath);
- finish.setLineNo(loc.startLine);
- finish.setColumnNo(loc.startColumn);
-
- Doc doc(start, finish, source.mid(1), m_commands, m_topics);
- const TopicList &topicsUsed = doc.topicsUsed();
- NodeList nodes;
- Node *nodePassedIn = node;
- Aggregate *parent = nodePassedIn->parent();
- node->setDoc(doc);
- nodes.append(node);
- if (!topicsUsed.empty()) {
- for (int i = 0; i < topicsUsed.size(); ++i) {
- QString topic = topicsUsed.at(i).m_topic;
- if (!topic.startsWith(QLatin1String("qml"))
- && !topic.startsWith(QLatin1String("js")))
- continue; // maybe a qdoc warning here? mws 18/07/18
- QString args = topicsUsed.at(i).m_args;
- if (topic == COMMAND_JSTYPE) {
- node->changeType(Node::QmlType, Node::JsType);
- } else if (topic.endsWith(QLatin1String("property"))) {
- QmlPropArgs qpa;
- if (splitQmlPropertyArg(doc, args, qpa)) {
- if (qpa.m_name == nodePassedIn->name()) {
- if (nodePassedIn->isAlias())
- nodePassedIn->setDataType(qpa.m_type);
- } else {
- bool isAttached = topic.contains(QLatin1String("attached"));
- QmlPropertyNode *n = parent->hasQmlProperty(qpa.m_name, isAttached);
- if (n == nullptr)
- n = new QmlPropertyNode(parent, qpa.m_name, qpa.m_type, isAttached);
- n->setLocation(doc.location());
- n->setDoc(doc);
- n->markReadOnly(nodePassedIn->isReadOnly());
- if (nodePassedIn->isDefault())
- n->markDefault();
- if (isAttached)
- n->markReadOnly(0);
- if ((topic == COMMAND_JSPROPERTY)
- || (topic == COMMAND_JSATTACHEDPROPERTY))
- n->changeType(Node::QmlProperty, Node::JsProperty);
- nodes.append(n);
- }
- } else
- qDebug() << " FAILED TO PARSE QML OR JS PROPERTY:" << topic << args;
- } else if (topic.endsWith(QLatin1String("method")) || topic == COMMAND_QMLSIGNAL) {
- if (node->isFunction()) {
- auto *fn = static_cast<FunctionNode *>(node);
- QmlSignatureParser qsp(fn, args, doc.location());
- if (topic == COMMAND_JSMETHOD || topic == COMMAND_JSATTACHEDMETHOD)
- fn->changeMetaness(FunctionNode::QmlMethod, FunctionNode::JsMethod);
+ auto *parent{node->parent()};
+ node->setDoc(doc);
+ nodes.append(node);
+ if (!topicsUsed.empty()) {
+ for (int i = 0; i < topicsUsed.size(); ++i) {
+ QString topic = topicsUsed.at(i).m_topic;
+ QString args = topicsUsed.at(i).m_args;
+ if (topic.endsWith(QLatin1String("property"))) {
+ auto *qmlProperty = static_cast<QmlPropertyNode *>(node);
+ QmlPropArgs qpa;
+ if (splitQmlPropertyArg(doc, args, qpa)) {
+ if (qpa.m_name == node->name()) {
+ if (qmlProperty->isAlias())
+ qmlProperty->setDataType(qpa.m_type);
+ } else {
+ bool isAttached = topic.contains(QLatin1String("attached"));
+ QmlPropertyNode *n = parent->hasQmlProperty(qpa.m_name, isAttached);
+ if (n == nullptr)
+ n = new QmlPropertyNode(parent, qpa.m_name, qpa.m_type, isAttached);
+ n->setLocation(doc.location());
+ n->setDoc(doc);
+ // Use the const-overload of QmlPropertyNode::isReadOnly() as there's
+ // no associated C++ property to resolve the read-only status from
+ n->markReadOnly(const_cast<const QmlPropertyNode *>(qmlProperty)->isReadOnly()
+ && !isAttached);
+ if (qmlProperty->isDefault())
+ n->markDefault();
+ nodes.append(n);
}
+ } else
+ qCDebug(lcQdoc) << "Failed to parse QML property:" << topic << args;
+ } else if (topic.endsWith(QLatin1String("method")) || topic == COMMAND_QMLSIGNAL) {
+ if (node->isFunction()) {
+ auto *fn = static_cast<FunctionNode *>(node);
+ QmlSignatureParser qsp(fn, args, doc.location());
}
}
}
- for (const auto &node : nodes)
- applyMetacommands(loc, node, doc);
- m_usedComments.insert(loc.offset);
- if (doc.isEmpty()) {
- return false;
- }
- return true;
}
- Location codeLoc(m_filePath);
- codeLoc.setLineNo(location.startLine);
- node->setLocation(codeLoc);
- return false;
+ for (const auto &node : nodes)
+ applyMetacommands(loc, node, doc);
+
+ m_usedComments.insert(loc.offset);
+ return node;
}
QmlSignatureParser::QmlSignatureParser(FunctionNode *func, const QString &signature,
@@ -414,25 +396,23 @@ void QmlDocVisitor::applyMetacommands(QQmlJS::SourceLocation, Node *node, Doc &d
{
QDocDatabase *qdb = QDocDatabase::qdocDB();
QSet<QString> metacommands = doc.metaCommandsUsed();
- if (metacommands.count() > 0) {
+ if (metacommands.size() > 0) {
metacommands.subtract(m_topics);
- for (const auto &command : qAsConst(metacommands)) {
+ for (const auto &command : std::as_const(metacommands)) {
const ArgList args = doc.metaCommandArgs(command);
if ((command == COMMAND_QMLABSTRACT) || (command == COMMAND_ABSTRACT)) {
- if (node->isQmlType() || node->isJsType()) {
+ if (node->isQmlType()) {
node->setAbstract(true);
}
} else if (command == COMMAND_DEPRECATED) {
- node->setStatus(Node::Deprecated);
- if (!args[0].second.isEmpty())
- node->setDeprecatedSince(args[0].second);
- } else if ((command == COMMAND_INQMLMODULE) || (command == COMMAND_INJSMODULE)) {
+ node->setDeprecated(args[0].second);
+ } else if (command == COMMAND_INQMLMODULE) {
qdb->addToQmlModule(args[0].first, node);
} else if (command == COMMAND_QMLINHERITS) {
if (node->name() == args[0].first)
doc.location().warning(
QStringLiteral("%1 tries to inherit itself").arg(args[0].first));
- else if (node->isQmlType() || node->isJsType()) {
+ else if (node->isQmlType()) {
auto *qmlType = static_cast<QmlTypeNode *>(node);
qmlType->setQmlBaseName(args[0].first);
}
@@ -448,6 +428,14 @@ void QmlDocVisitor::applyMetacommands(QQmlJS::SourceLocation, Node *node, Doc &d
}
} else if (command == COMMAND_QMLDEFAULT) {
node->markDefault();
+ } else if (command == COMMAND_QMLENUMERATORSFROM) {
+ if (!node->isQmlProperty()) {
+ doc.location().warning("Ignored '\\%1', applies only to '\\%2'"_L1
+ .arg(command, COMMAND_QMLPROPERTY));
+ } else if (!static_cast<QmlPropertyNode*>(node)->setEnumNode(args[0].first, args[0].second)) {
+ doc.location().warning("Failed to find C++ enumeration '%2' passed to \\%1"_L1
+ .arg(command, args[0].first), "Use \\value commands instead"_L1);
+ }
} else if (command == COMMAND_QMLREADONLY) {
node->markReadOnly(1);
} else if (command == COMMAND_QMLREQUIRED) {
@@ -499,29 +487,24 @@ QString QmlDocVisitor::getFullyQualifiedId(QQmlJS::AST::UiQualifiedId *id)
to test whether we are at the public API level. The public level
is level 1.
- Note that this visit() function creates the qdoc object node as a
- QmlType. If it is actually a JsType, this fact is discovered when
- the qdoc comment is applied to the node. The node's typet is then
- changed to JsType.
+ Defers the construction of a QmlTypeNode instance to
+ applyDocumentation(), by passing \c nullptr as the second
+ argument.
*/
bool QmlDocVisitor::visit(QQmlJS::AST::UiObjectDefinition *definition)
{
QString type = getFullyQualifiedId(definition->qualifiedTypeNameId);
m_nestingLevel++;
-
if (m_current->isNamespace()) {
- QmlTypeNode *component = nullptr;
- Node *candidate = m_current->findChildNode(m_name, Node::QML);
- if (candidate != nullptr)
- component = static_cast<QmlTypeNode *>(candidate);
- else
- component = new QmlTypeNode(m_current, m_name);
- component->setTitle(m_name);
- component->setImportList(m_importList);
+ auto component = applyDocumentation(definition->firstSourceLocation(), nullptr);
+ Q_ASSERT(component);
+ auto *qmlTypeNode = static_cast<QmlTypeNode *>(component);
+ if (!component->doc().isEmpty())
+ qmlTypeNode->setQmlBaseName(type);
+ qmlTypeNode->setTitle(m_name);
+ qmlTypeNode->setImportList(m_importList);
m_importList.clear();
- if (applyDocumentation(definition->firstSourceLocation(), component))
- component->setQmlBaseName(type);
- m_current = component;
+ m_current = qmlTypeNode;
}
return true;
@@ -545,7 +528,7 @@ bool QmlDocVisitor::visit(QQmlJS::AST::UiImport *import)
{
QString name = m_document.mid(import->fileNameToken.offset, import->fileNameToken.length);
if (name[0] == '\"')
- name = name.mid(1, name.length() - 2);
+ name = name.mid(1, name.size() - 2);
QString version;
if (import->version) {
const auto start = import->version->firstSourceLocation().begin();
@@ -581,17 +564,7 @@ bool QmlDocVisitor::visit(QQmlJS::AST::UiArrayBinding *)
void QmlDocVisitor::endVisit(QQmlJS::AST::UiArrayBinding *) {}
-template<typename T>
-QString qualifiedIdToString(T node);
-
-template<>
-QString qualifiedIdToString(QStringView node)
-{
- return node.toString();
-}
-
-template<>
-QString qualifiedIdToString(QQmlJS::AST::UiQualifiedId *node)
+static QString qualifiedIdToString(QQmlJS::AST::UiQualifiedId *node)
{
QString s;
@@ -617,17 +590,15 @@ bool QmlDocVisitor::visit(QQmlJS::AST::UiPublicMember *member)
}
switch (member->type) {
case QQmlJS::AST::UiPublicMember::Signal: {
- if (m_current->isQmlType() || m_current->isJsType()) {
+ if (m_current->isQmlType()) {
auto *qmlType = static_cast<QmlTypeNode *>(m_current);
if (qmlType) {
FunctionNode::Metaness metaness = FunctionNode::QmlSignal;
- if (qmlType->isJsType())
- metaness = FunctionNode::JsSignal;
QString name = member->name.toString();
auto *newSignal = new FunctionNode(metaness, m_current, name);
Parameters &parameters = newSignal->parameters();
for (QQmlJS::AST::UiParameterList *it = member->parameters; it; it = it->next) {
- const QString type = qualifiedIdToString(it->type);
+ const QString type = it->type ? it->type->toString() : QString();
if (!type.isEmpty() && !it->name.isEmpty())
parameters.append(type, it->name.toString());
}
@@ -638,17 +609,17 @@ bool QmlDocVisitor::visit(QQmlJS::AST::UiPublicMember *member)
}
case QQmlJS::AST::UiPublicMember::Property: {
QString type = qualifiedIdToString(member->memberType);
- if (m_current->isQmlType() || m_current->isJsType()) {
+ if (m_current->isQmlType()) {
auto *qmlType = static_cast<QmlTypeNode *>(m_current);
if (qmlType) {
QString name = member->name.toString();
QmlPropertyNode *qmlPropNode = qmlType->hasQmlProperty(name);
if (qmlPropNode == nullptr)
qmlPropNode = new QmlPropertyNode(qmlType, name, type, false);
- qmlPropNode->markReadOnly(member->isReadonlyMember);
- if (member->isDefaultMember)
+ qmlPropNode->markReadOnly(member->isReadonly());
+ if (member->isDefaultMember())
qmlPropNode->markDefault();
- if (member->requiredToken.isValid())
+ if (member->requiredToken().isValid())
qmlPropNode->setRequired();
applyDocumentation(member->firstSourceLocation(), qmlPropNode);
}
@@ -683,9 +654,7 @@ bool QmlDocVisitor::visit(QQmlJS::AST::FunctionDeclaration *fd)
{
if (m_nestingLevel <= 1) {
FunctionNode::Metaness metaness = FunctionNode::QmlMethod;
- if (m_current->isJsType())
- metaness = FunctionNode::JsMethod;
- else if (!m_current->isQmlType())
+ if (!m_current->isQmlType())
return true;
QString name = fd->name.toString();
auto *method = new FunctionNode(metaness, m_current, name);
@@ -757,6 +726,4 @@ bool QmlDocVisitor::hasError() const
return hasRecursionDepthError;
}
-#endif
-
QT_END_NAMESPACE
diff --git a/src/qdoc/qmlvisitor.h b/src/qdoc/qdoc/src/qdoc/qmlvisitor.h
index 2d2e80add..201dc0e61 100644
--- a/src/qdoc/qmlvisitor.h
+++ b/src/qdoc/qdoc/src/qdoc/qmlvisitor.h
@@ -1,30 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the tools applications of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#ifndef QMLVISITOR_H
#define QMLVISITOR_H
@@ -34,10 +9,8 @@
#include <QtCore/qstring.h>
-#ifndef QT_NO_DECLARATIVE
-# include <private/qqmljsastvisitor_p.h>
-# include <private/qqmljsengine_p.h>
-#endif
+#include <private/qqmljsastvisitor_p.h>
+#include <private/qqmljsengine_p.h>
QT_BEGIN_NAMESPACE
@@ -59,7 +32,6 @@ struct QmlPropArgs
}
};
-#ifndef QT_NO_DECLARATIVE
class QmlDocVisitor : public QQmlJS::AST::Visitor
{
public:
@@ -98,7 +70,7 @@ public:
private:
QString getFullyQualifiedId(QQmlJS::AST::UiQualifiedId *id);
[[nodiscard]] QQmlJS::SourceLocation precedingComment(quint32 offset) const;
- bool applyDocumentation(QQmlJS::SourceLocation location, Node *node);
+ Node *applyDocumentation(QQmlJS::SourceLocation location, Node *node);
void applyMetacommands(QQmlJS::SourceLocation location, Node *node, Doc &doc);
bool splitQmlPropertyArg(const Doc &doc, const QString &arg, QmlPropArgs &qpa);
@@ -115,7 +87,6 @@ private:
Aggregate *m_current { nullptr };
bool hasRecursionDepthError { false };
};
-#endif
QT_END_NAMESPACE
diff --git a/src/qdoc/quoter.cpp b/src/qdoc/qdoc/src/qdoc/quoter.cpp
index 6692572b3..37799a9e9 100644
--- a/src/qdoc/quoter.cpp
+++ b/src/qdoc/qdoc/src/qdoc/quoter.cpp
@@ -1,30 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the tools applications of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "quoter.h"
@@ -80,7 +55,7 @@ QStringList Quoter::splitLines(const QString &line)
static void trimWhiteSpace(QString &str)
{
enum { Normal, MetAlnum, MetSpace } state = Normal;
- const qsizetype n = str.length();
+ const qsizetype n = str.size();
int j = -1;
QChar *d = str.data();
@@ -166,7 +141,7 @@ void Quoter::quoteFromFile(const QString &userFriendlyFilePath, const QString &p
m_plainLines = splitLines(plainCode);
m_markedLines = splitLines(markedCode);
- if (m_markedLines.count() != m_plainLines.count()) {
+ if (m_markedLines.size() != m_plainLines.size()) {
m_codeLocation.warning(
QStringLiteral("Something is wrong with qdoc's handling of marked code"));
m_markedLines = m_plainLines;
@@ -214,7 +189,7 @@ QString Quoter::quoteSnippet(const Location &docLocation, const QString &identif
while (!m_plainLines.isEmpty()) {
if (match(docLocation, delimiter, m_plainLines.first())) {
QString startLine = getLine();
- while (indent < startLine.length() && startLine[indent] == QLatin1Char(' '))
+ while (indent < startLine.size() && startLine[indent] == QLatin1Char(' '))
indent++;
break;
}
@@ -285,7 +260,7 @@ QString Quoter::getLine(int unindent)
QString t = m_markedLines.takeFirst();
int i = 0;
- while (i < unindent && i < t.length() && t[i] == QLatin1Char(' '))
+ while (i < unindent && i < t.size() && t[i] == QLatin1Char(' '))
i++;
t = t.mid(i);
@@ -298,12 +273,12 @@ bool Quoter::match(const Location &docLocation, const QString &pattern0, const Q
{
QString str = line;
while (str.endsWith(QLatin1Char('\n')))
- str.truncate(str.length() - 1);
+ str.truncate(str.size() - 1);
QString pattern = pattern0;
if (pattern.startsWith(QLatin1Char('/')) && pattern.endsWith(QLatin1Char('/'))
- && pattern.length() > 2) {
- QRegularExpression rx(pattern.mid(1, pattern.length() - 2));
+ && pattern.size() > 2) {
+ QRegularExpression rx(pattern.mid(1, pattern.size() - 2));
if (!m_silent && !rx.isValid()) {
docLocation.warning(
QStringLiteral("Invalid regular expression '%1'").arg(rx.pattern()));
diff --git a/src/qdoc/quoter.h b/src/qdoc/qdoc/src/qdoc/quoter.h
index 33231ceb7..087e9ffd2 100644
--- a/src/qdoc/quoter.h
+++ b/src/qdoc/qdoc/src/qdoc/quoter.h
@@ -1,30 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the tools applications of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#ifndef QUOTER_H
#define QUOTER_H
diff --git a/src/qdoc/relatedclass.cpp b/src/qdoc/qdoc/src/qdoc/relatedclass.cpp
index 2ea3a038e..3eea8df92 100644
--- a/src/qdoc/relatedclass.cpp
+++ b/src/qdoc/qdoc/src/qdoc/relatedclass.cpp
@@ -1,30 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the tools applications of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "relatedclass.h"
diff --git a/src/qdoc/qdoc/src/qdoc/relatedclass.h b/src/qdoc/qdoc/src/qdoc/relatedclass.h
new file mode 100644
index 000000000..9ca3b849a
--- /dev/null
+++ b/src/qdoc/qdoc/src/qdoc/relatedclass.h
@@ -0,0 +1,34 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#ifndef RELATEDCLASS_H
+#define RELATEDCLASS_H
+
+#include "access.h"
+
+#include <QtCore/qstring.h>
+#include <QtCore/qstringlist.h>
+
+#include <utility>
+
+QT_BEGIN_NAMESPACE
+
+class ClassNode;
+
+struct RelatedClass
+{
+ RelatedClass() = default;
+ // constructor for resolved base class
+ RelatedClass(Access access, ClassNode *node) : m_access(access), m_node(node) {}
+ // constructor for unresolved base class
+ RelatedClass(Access access, QStringList path) : m_access(access), m_path(std::move(path)) { }
+ [[nodiscard]] bool isPrivate() const;
+
+ Access m_access {};
+ ClassNode *m_node { nullptr };
+ QStringList m_path {};
+};
+
+QT_END_NAMESPACE
+
+#endif // RELATEDCLASS_H
diff --git a/src/qdoc/sections.cpp b/src/qdoc/qdoc/src/qdoc/sections.cpp
index 1c685cef0..bf066c08e 100644
--- a/src/qdoc/sections.cpp
+++ b/src/qdoc/qdoc/src/qdoc/sections.cpp
@@ -1,30 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the tools applications of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "sections.h"
@@ -46,17 +21,96 @@
QT_BEGIN_NAMESPACE
-static bool sectionsInitialized = false;
-QList<Section> Sections::s_stdSummarySections(7, Section(Section::Summary, Section::Active));
-QList<Section> Sections::s_stdDetailsSections(7, Section(Section::Details, Section::Active));
-QList<Section> Sections::s_stdCppClassSummarySections(18,
- Section(Section::Summary, Section::Active));
-QList<Section> Sections::s_stdCppClassDetailsSections(6,
- Section(Section::Details, Section::Active));
-QList<Section> Sections::s_sinceSections(15, Section(Section::Details, Section::Active));
-QList<Section> Sections::s_allMembers(1, Section(Section::AllMembers, Section::Active));
-QList<Section> Sections::s_stdQmlTypeSummarySections(7, Section(Section::Summary, Section::Active));
-QList<Section> Sections::s_stdQmlTypeDetailsSections(7, Section(Section::Details, Section::Active));
+QList<Section> Sections::s_stdSummarySections {
+ { "Namespaces", "namespace", "namespaces", "", Section::Summary },
+ { "Classes", "class", "classes", "", Section::Summary },
+ { "Types", "type", "types", "", Section::Summary },
+ { "Variables", "variable", "variables", "", Section::Summary },
+ { "Static Variables", "static variable", "static variables", "", Section::Summary },
+ { "Functions", "function", "functions", "", Section::Summary },
+ { "Macros", "macro", "macros", "", Section::Summary },
+};
+
+QList<Section> Sections::s_stdDetailsSections {
+ { "Namespaces", "namespace", "namespaces", "nmspace", Section::Details },
+ { "Classes", "class", "classes", "classes", Section::Details },
+ { "Type Documentation", "type", "types", "types", Section::Details },
+ { "Variable Documentation", "variable", "variables", "vars", Section::Details },
+ { "Static Variables", "static variable", "static variables", QString(), Section::Details },
+ { "Function Documentation", "function", "functions", "func", Section::Details },
+ { "Macro Documentation", "macro", "macros", "macros", Section::Details },
+};
+
+QList<Section> Sections::s_stdCppClassSummarySections {
+ { "Public Types", "public type", "public types", "", Section::Summary },
+ { "Properties", "property", "properties", "", Section::Summary },
+ { "Public Functions", "public function", "public functions", "", Section::Summary },
+ { "Public Slots", "public slot", "public slots", "", Section::Summary },
+ { "Signals", "signal", "signals", "", Section::Summary },
+ { "Public Variables", "public variable", "public variables", "", Section::Summary },
+ { "Static Public Members", "static public member", "static public members", "", Section::Summary },
+ { "Protected Types", "protected type", "protected types", "", Section::Summary },
+ { "Protected Functions", "protected function", "protected functions", "", Section::Summary },
+ { "Protected Slots", "protected slot", "protected slots", "", Section::Summary },
+ { "Protected Variables", "protected type", "protected variables", "", Section::Summary },
+ { "Static Protected Members", "static protected member", "static protected members", "", Section::Summary },
+ { "Private Types", "private type", "private types", "", Section::Summary },
+ { "Private Functions", "private function", "private functions", "", Section::Summary },
+ { "Private Slots", "private slot", "private slots", "", Section::Summary },
+ { "Static Private Members", "static private member", "static private members", "", Section::Summary },
+ { "Related Non-Members", "related non-member", "related non-members", "", Section::Summary },
+ { "Macros", "macro", "macros", "", Section::Summary },
+};
+
+QList<Section> Sections::s_stdCppClassDetailsSections {
+ { "Member Type Documentation", "member", "members", "types", Section::Details },
+ { "Property Documentation", "member", "members", "prop", Section::Details },
+ { "Member Function Documentation", "member", "members", "func", Section::Details },
+ { "Member Variable Documentation", "member", "members", "vars", Section::Details },
+ { "Related Non-Members", "member", "members", "relnonmem", Section::Details },
+ { "Macro Documentation", "member", "members", "macros", Section::Details },
+};
+
+QList<Section> Sections::s_stdQmlTypeSummarySections {
+ { "Properties", "property", "properties", "", Section::Summary },
+ { "Attached Properties", "attached property", "attached properties", "", Section::Summary },
+ { "Signals", "signal", "signals", "", Section::Summary },
+ { "Signal Handlers", "signal handler", "signal handlers", "", Section::Summary },
+ { "Attached Signals", "attached signal", "attached signals", "", Section::Summary },
+ { "Methods", "method", "methods", "", Section::Summary },
+ { "Attached Methods", "attached method", "attached methods", "", Section::Summary },
+};
+
+QList<Section> Sections::s_stdQmlTypeDetailsSections {
+ { "Property Documentation", "member", "members", "qmlprop", Section::Details },
+ { "Attached Property Documentation", "member", "members", "qmlattprop", Section::Details },
+ { "Signal Documentation", "signal", "signals", "qmlsig", Section::Details },
+ { "Signal Handler Documentation", "signal handler", "signal handlers", "qmlsighan", Section::Details },
+ { "Attached Signal Documentation", "signal", "signals", "qmlattsig", Section::Details },
+ { "Method Documentation", "member", "members", "qmlmeth", Section::Details },
+ { "Attached Method Documentation", "member", "members", "qmlattmeth", Section::Details },
+};
+
+QList<Section> Sections::s_sinceSections {
+ { "New Namespaces", "", "", "", Section::Details },
+ { "New Classes", "", "", "", Section::Details },
+ { "New Member Functions", "", "", "", Section::Details },
+ { "New Functions in Namespaces", "", "", "", Section::Details },
+ { "New Global Functions", "", "", "", Section::Details },
+ { "New Macros", "", "", "", Section::Details },
+ { "New Enum Types", "", "", "", Section::Details },
+ { "New Enum Values", "", "", "", Section::Details },
+ { "New Type Aliases", "", "", "", Section::Details },
+ { "New Properties", "", "", "", Section::Details },
+ { "New Variables", "", "", "", Section::Details },
+ { "New QML Types", "", "", "", Section::Details },
+ { "New QML Properties", "", "", "", Section::Details },
+ { "New QML Signals", "", "", "", Section::Details },
+ { "New QML Signal Handlers", "", "", "", Section::Details },
+ { "New QML Methods", "", "", "", Section::Details },
+};
+
+QList<Section> Sections::s_allMembers{ { "", "member", "members", "", Section::AllMembers } };
/*!
\class Section
@@ -64,13 +118,6 @@ QList<Section> Sections::s_stdQmlTypeDetailsSections(7, Section(Section::Details
*/
/*!
- \fn Section::Section(Style style, Status status)
-
- The constructor used when the \a style and \a status must
- be provided.
- */
-
-/*!
The destructor must delete the members of collections
when the members are allocated on the heap.
*/
@@ -87,44 +134,23 @@ Section::~Section()
*/
void Section::clear()
{
- m_memberMap.clear();
- m_obsoleteMemberMap.clear();
m_reimplementedMemberMap.clear();
- if (!m_classMapList.isEmpty()) {
- for (ClassMap *classMap : m_classMapList) {
- classMap = nullptr;
- delete classMap;
- }
- m_classMapList.clear();
- }
- m_keys.clear();
- m_obsoleteKeys.clear();
m_members.clear();
m_obsoleteMembers.clear();
m_reimplementedMembers.clear();
m_inheritedMembers.clear();
- if (!m_classKeysNodesList.isEmpty()) {
- for (ClassKeysNodes *classKeysNode : m_classKeysNodesList) {
- classKeysNode = nullptr;
- delete classKeysNode;
- }
- m_classKeysNodesList.clear();
- }
+ m_classNodesList.clear();
m_aggregate = nullptr;
}
/*!
Construct a name for the \a node that can be used for sorting
- a set of nodes into equivalence classes. If \a name is provided,
- start with that name. Itherwise start with the name in \a node.
+ a set of nodes into equivalence classes.
*/
-QString Section::sortName(const Node *node, const QString *name)
+QString sortName(const Node *node)
{
- QString nodeName;
- if (name != nullptr)
- nodeName = *name;
- else
- nodeName = node->name();
+ QString nodeName{node->name()};
+
int numDigits = 0;
for (qsizetype i = nodeName.size() - 1; i > 0; --i) {
if (nodeName.at(i).digitValue() == -1)
@@ -138,32 +164,36 @@ QString Section::sortName(const Node *node, const QString *name)
nodeName.insert(nodeName.size() - numDigits - 1, QLatin1Char('0'));
}
- if (node->isFunction()) {
+ if (node->isClassNode())
+ return QLatin1Char('A') + nodeName;
+
+ if (node->isFunction(Node::CPP)) {
const auto *fn = static_cast<const FunctionNode *>(node);
- if (fn->isCppFunction()) {
- QString sortNo;
- if (fn->isSomeCtor())
- sortNo = QLatin1String("C");
- else if (fn->isDtor())
- sortNo = QLatin1String("D");
- else if (nodeName.startsWith(QLatin1String("operator")) && nodeName.length() > 8
- && !nodeName[8].isLetterOrNumber())
- sortNo = QLatin1String("F");
- else
- sortNo = QLatin1String("E");
- return sortNo + nodeName + QLatin1Char(' ') + QString::number(fn->overloadNumber(), 36);
- }
- if (fn->isQmlMethod() || fn->isQmlSignal() || fn->isQmlSignalHandler())
- return QLatin1Char('E') + nodeName;
- if (fn->isJsMethod() || fn->isJsSignal() || fn->isJsSignalHandler())
- return QLatin1Char('E') + nodeName;
+ QString sortNo;
+ if (fn->isCtor())
+ sortNo = QLatin1String("C");
+ else if (fn->isCCtor())
+ sortNo = QLatin1String("D");
+ else if (fn->isMCtor())
+ sortNo = QLatin1String("E");
+ else if (fn->isDtor())
+ sortNo = QLatin1String("F");
+ else if (nodeName.startsWith(QLatin1String("operator")) && nodeName.size() > 8
+ && !nodeName[8].isLetterOrNumber())
+ sortNo = QLatin1String("H");
+ else
+ sortNo = QLatin1String("G");
+
+ return sortNo + nodeName + QLatin1Char(' ') + QString::number(fn->overloadNumber(), 36);
}
- if (node->isClassNode())
- return QLatin1Char('A') + nodeName;
+
+ if (node->isFunction(Node::QML))
+ return QLatin1Char('E') + nodeName + QLatin1Char(' ') +
+ QString::number(static_cast<const FunctionNode*>(node)->overloadNumber(), 36);
if (node->isProperty() || node->isVariable())
- return QLatin1Char('E') + nodeName;
+ return QLatin1Char('G') + nodeName;
return QLatin1Char('B') + nodeName;
}
@@ -179,7 +209,7 @@ void Section::insert(Node *node)
if (!node->isRelatedNonmember()) {
Aggregate *p = node->parent();
if (!p->isNamespace() && p != m_aggregate) {
- if ((!p->isQmlType() && !p->isJsType()) || !p->isAbstract())
+ if (!p->isQmlType() || !p->isAbstract())
inherited = true;
}
}
@@ -202,18 +232,15 @@ void Section::insert(Node *node)
if (!irrelevant) {
QString key = sortName(node);
if (node->isDeprecated()) {
- m_obsoleteMemberMap.insert(key, node);
+ m_obsoleteMembers.push_back(node);
} else {
- if (!inherited)
- m_memberMap.insert(key, node);
- else if (m_style == AllMembers) {
- if (!m_memberMap.contains(key))
- m_memberMap.insert(key, node);
- }
+ if (!inherited || m_style == AllMembers)
+ m_members.push_back(node);
+
if (inherited && (node->parent()->isClassNode() || node->parent()->isNamespace())) {
if (m_inheritedMembers.isEmpty()
|| m_inheritedMembers.last().first != node->parent()) {
- QPair<Aggregate *, int> p(node->parent(), 0);
+ std::pair<Aggregate *, int> p(node->parent(), 0);
m_inheritedMembers.append(p);
}
m_inheritedMembers.last().second++;
@@ -225,8 +252,7 @@ void Section::insert(Node *node)
/*!
Returns \c true if the \a node is a reimplemented member
function of the current class. If true, the \a node is
- inserted into the reimplemented member map. The test is
- performed only when the section status is \e Active. True
+ inserted into the reimplemented member map. True
is returned only if \a node is inserted into the map.
That is, false is returned if the \a node is already in
the map.
@@ -235,7 +261,7 @@ bool Section::insertReimplementedMember(Node *node)
{
if (!node->isPrivate() && !node->isRelatedNonmember()) {
const auto *fn = static_cast<const FunctionNode *>(node);
- if (!fn->overridesThis().isEmpty() && (m_status == Active)) {
+ if (!fn->overridesThis().isEmpty()) {
if (fn->parent() == m_aggregate) {
QString key = sortName(fn);
if (!m_reimplementedMemberMap.contains(key)) {
@@ -249,51 +275,56 @@ bool Section::insertReimplementedMember(Node *node)
}
/*!
- Allocate a new ClassMap on the heap for the \a aggregate
- node, append it to the list of class maps, and return a
- pointer to the new class map.
- */
-ClassMap *Section::newClassMap(const Aggregate *aggregate)
-{
- auto *classMap = new ClassMap;
- classMap->first = static_cast<const QmlTypeNode *>(aggregate);
- m_classMapList.append(classMap);
- return classMap;
-}
-
-/*!
- Add node \a n to the \a classMap and to the member map.
- */
-void Section::add(ClassMap *classMap, Node *n)
-{
- QString key = n->name();
- key = sortName(n, &key);
- m_memberMap.insert(key, n);
- classMap->second.insert(key, n);
-}
-
-/*!
If this section is not empty, convert its maps to sequential
structures for better traversal during doc generation.
*/
void Section::reduce()
{
- if (!isEmpty()) {
- m_keys = m_memberMap.keys();
- m_members = m_memberMap.values().toVector();
- m_reimplementedMembers = m_reimplementedMemberMap.values().toVector();
- for (int i = 0; i < m_classMapList.size(); i++) {
- ClassMap *cm = m_classMapList[i];
- auto *ckn = new ClassKeysNodes;
- ckn->first = cm->first;
- ckn->second.second = cm->second.values().toVector();
- ckn->second.first = cm->second.keys();
- m_classKeysNodesList.append(ckn);
- }
- }
- if (!m_obsoleteMemberMap.isEmpty()) {
- m_obsoleteKeys = m_obsoleteMemberMap.keys();
- m_obsoleteMembers = m_obsoleteMemberMap.values().toVector();
+ // TODO:TEMPORARY:INTERMEDITATE: Section uses a series of maps
+ // to internally manage the categorization of the various members
+ // of an aggregate. It further uses a secondary "flattened"
+ // (usually vector) version that is later used by consumers of a
+ // Section content.
+ //
+ // One of the uses of those maps is that of ordering, by using
+ // keys generated with `sortName`.
+ // Nonetheless, this is the only usage that comes from the keys,
+ // as they are neither necessary nor used outside of the internal
+ // code for Section.
+ //
+ // Hence, the codebase is moving towards removing the maps in
+ // favor of building a flattened, consumer ready, version of the
+ // categorization directly, cutting the intermediate conversion
+ // step.
+ //
+ // To do so while keeping as much of the old behavior as possible,
+ // we provide a sorting for the flattened version that is based on
+ // `sortName`, as the previous ordering was.
+ //
+ // This acts as a relatively heavy pessimization, as `sortName`,
+ // used as a comparator, can be called multiple times for each
+ // Node, while before it would have been called almost-once.
+ //
+ // Instead of fixing this issue, by for example caching the
+ // sortName of each Node instance, we temporarily keep the
+ // pessimization while the various maps are removed.
+ //
+ // When all the maps are removed, we can remove `sortName`, which
+ // produces strings to use as key requiring a few allocations and
+ // expensive operations, with an actual comparator function, which
+ // should be more lightweight and more than offset the
+ // multiple-calls.
+ static auto node_less_than = [](const Node* left, const Node* right) {
+ return sortName(left) < sortName(right);
+ };
+
+ std::stable_sort(m_members.begin(), m_members.end(), node_less_than);
+ std::stable_sort(m_obsoleteMembers.begin(), m_obsoleteMembers.end(), node_less_than);
+
+ m_reimplementedMembers = m_reimplementedMemberMap.values().toVector();
+
+ for (auto &cn : m_classNodesList) {
+ std::stable_sort(cn.second.begin(), cn.second.end(), node_less_than);
}
}
@@ -312,7 +343,6 @@ void Section::reduce()
*/
Sections::Sections(Aggregate *aggregate) : m_aggregate(aggregate)
{
- initSections();
initAggregate(s_allMembers, m_aggregate);
switch (m_aggregate->nodeType()) {
case Node::Class:
@@ -322,10 +352,8 @@ Sections::Sections(Aggregate *aggregate) : m_aggregate(aggregate)
initAggregate(s_stdCppClassDetailsSections, m_aggregate);
buildStdCppClassRefPageSections();
break;
- case Node::JsType:
- case Node::JsBasicType:
case Node::QmlType:
- case Node::QmlBasicType:
+ case Node::QmlValueType:
initAggregate(s_stdQmlTypeSummarySections, m_aggregate);
initAggregate(s_stdQmlTypeDetailsSections, m_aggregate);
buildStdQmlTypeRefPageSections();
@@ -347,14 +375,12 @@ Sections::Sections(Aggregate *aggregate) : m_aggregate(aggregate)
*/
Sections::Sections(const NodeMultiMap &nsmap) : m_aggregate(nullptr)
{
- initSections();
if (nsmap.isEmpty())
return;
SectionVector &sections = sinceSections();
for (auto it = nsmap.constBegin(); it != nsmap.constEnd(); ++it) {
Node *node = it.value();
switch (node->nodeType()) {
- case Node::JsType:
case Node::QmlType:
sections[SinceQmlTypes].appendMember(node);
break;
@@ -366,27 +392,29 @@ Sections::Sections(const NodeMultiMap &nsmap) : m_aggregate(nullptr)
case Node::Union:
sections[SinceClasses].appendMember(node);
break;
- case Node::Enum:
- sections[SinceEnumTypes].appendMember(node);
+ case Node::Enum: {
+ // The map can contain an enum node with \since, or an enum node
+ // with \value containing a since-clause. In the latter case,
+ // key() is an empty string.
+ if (!it.key().isEmpty())
+ sections[SinceEnumTypes].appendMember(node);
+ else
+ sections[SinceEnumValues].appendMember(node);
break;
+ }
case Node::Typedef:
- sections[SinceTypedefs].appendMember(node);
- break;
case Node::TypeAlias:
sections[SinceTypeAliases].appendMember(node);
break;
case Node::Function: {
const auto *fn = static_cast<const FunctionNode *>(node);
switch (fn->metaness()) {
- case FunctionNode::JsSignal:
case FunctionNode::QmlSignal:
sections[SinceQmlSignals].appendMember(node);
break;
- case FunctionNode::JsSignalHandler:
case FunctionNode::QmlSignalHandler:
sections[SinceQmlSignalHandlers].appendMember(node);
break;
- case FunctionNode::JsMethod:
case FunctionNode::QmlMethod:
sections[SinceQmlMethods].appendMember(node);
break;
@@ -418,7 +446,6 @@ Sections::Sections(const NodeMultiMap &nsmap) : m_aggregate(nullptr)
case Node::Variable:
sections[SinceVariables].appendMember(node);
break;
- case Node::JsProperty:
case Node::QmlProperty:
sections[SinceQmlProperties].appendMember(node);
break;
@@ -445,10 +472,8 @@ Sections::~Sections()
clear(stdCppClassDetailsSections());
allMembersSection().clear();
break;
- case Node::JsType:
- case Node::JsBasicType:
case Node::QmlType:
- case Node::QmlBasicType:
+ case Node::QmlValueType:
clear(stdQmlTypeSummarySections());
clear(stdQmlTypeDetailsSections());
allMembersSection().clear();
@@ -475,119 +500,6 @@ void Sections::initAggregate(SectionVector &v, Aggregate *aggregate)
}
/*!
- This function is called once to initialize all the instances
- of QList<Section>. The lists have already been constructed
- with the correct number of Section entries in each. Each Section
- entry has already been constructed with the correct values of
- Style and Status for the list it is in. This function adds the
- correct text strings to each section in each vector.
- */
-void Sections::initSections()
-{
- if (sectionsInitialized)
- return;
- sectionsInitialized = true;
-
- s_allMembers[0].init("member", "members");
- {
- QList<Section> &v = s_stdCppClassSummarySections;
- v[0].init("Public Types", "public type", "public types");
- v[1].init("Properties", "property", "properties");
- v[2].init("Public Functions", "public function", "public functions");
- v[3].init("Public Slots", "public slot", "public slots");
- v[4].init("Signals", "signal", "signals");
- v[5].init("Public Variables", "public variable", "public variables");
- v[6].init("Static Public Members", "static public member", "static public members");
- v[7].init("Protected Types", "protected type", "protected types");
- v[8].init("Protected Functions", "protected function", "protected functions");
- v[9].init("Protected Slots", "protected slot", "protected slots");
- v[10].init("Protected Variables", "protected type", "protected variables");
- v[11].init("Static Protected Members", "static protected member",
- "static protected members");
- v[12].init("Private Types", "private type", "private types");
- v[13].init("Private Functions", "private function", "private functions");
- v[14].init("Private Slots", "private slot", "private slots");
- v[15].init("Static Private Members", "static private member", "static private members");
- v[16].init("Related Non-Members", "related non-member", "related non-members");
- v[17].init("Macros", "macro", "macros");
- }
-
- {
- QList<Section> &v = s_stdCppClassDetailsSections;
- v[0].init("Member Type Documentation", "types", "member", "members");
- v[1].init("Property Documentation", "prop", "member", "members");
- v[2].init("Member Function Documentation", "func", "member", "members");
- v[3].init("Member Variable Documentation", "vars", "member", "members");
- v[4].init("Related Non-Members", "relnonmem", "member", "members");
- v[5].init("Macro Documentation", "macros", "member", "members");
- }
-
- {
- QList<Section> &v = s_stdSummarySections;
- v[0].init("Namespaces", "namespace", "namespaces");
- v[1].init("Classes", "class", "classes");
- v[2].init("Types", "type", "types");
- v[3].init("Variables", "variable", "variables");
- v[4].init("Static Variables", "static variable", "static variables");
- v[5].init("Functions", "function", "functions");
- v[6].init("Macros", "macro", "macros");
- }
-
- {
- QList<Section> &v = s_stdDetailsSections;
- v[0].init("Namespaces", "nmspace", "namespace", "namespaces");
- v[1].init("Classes", "classes", "class", "classes");
- v[2].init("Type Documentation", "types", "type", "types");
- v[3].init("Variable Documentation", "vars", "variable", "variables");
- v[4].init("Static Variables", QString(), "static variable", "static variables");
- v[5].init("Function Documentation", "func", "function", "functions");
- v[6].init("Macro Documentation", "macros", "macro", "macros");
- }
-
- {
- QList<Section> &v = s_sinceSections;
- v[SinceNamespaces].init(" New Namespaces");
- v[SinceClasses].init(" New Classes");
- v[SinceMemberFunctions].init(" New Member Functions");
- v[SinceNamespaceFunctions].init(" New Functions in Namespaces");
- v[SinceGlobalFunctions].init(" New Global Functions");
- v[SinceMacros].init(" New Macros");
- v[SinceEnumTypes].init(" New Enum Types");
- v[SinceTypedefs].init(" New Typedefs");
- v[SinceTypeAliases].init(" New Type Aliases");
- v[SinceProperties].init(" New Properties");
- v[SinceVariables].init(" New Variables");
- v[SinceQmlTypes].init(" New QML Types");
- v[SinceQmlProperties].init(" New QML Properties");
- v[SinceQmlSignals].init(" New QML Signals");
- v[SinceQmlSignalHandlers].init(" New QML Signal Handlers");
- v[SinceQmlMethods].init(" New QML Methods");
- }
-
- {
- QList<Section> &v = s_stdQmlTypeSummarySections;
- v[0].init("Properties", "property", "properties");
- v[1].init("Attached Properties", "attached property", "attached properties");
- v[2].init("Signals", "signal", "signals");
- v[3].init("Signal Handlers", "signal handler", "signal handlers");
- v[4].init("Attached Signals", "attached signal", "attached signals");
- v[5].init("Methods", "method", "methods");
- v[6].init("Attached Methods", "attached method", "attached methods");
- }
-
- {
- QList<Section> &v = s_stdQmlTypeDetailsSections;
- v[0].init("Property Documentation", "qmlprop", "member", "members");
- v[1].init("Attached Property Documentation", "qmlattprop", "member", "members");
- v[2].init("Signal Documentation", "qmlsig", "signal", "signals");
- v[3].init("Signal Handler Documentation", "qmlsighan", "signal handler", "signal handlers");
- v[4].init("Attached Signal Documentation", "qmlattsig", "signal", "signals");
- v[5].init("Method Documentation", "qmlmeth", "member", "members");
- v[6].init("Attached Method Documentation", "qmlattmeth", "member", "members");
- }
-}
-
-/*!
Reset each Section in vector \a v to its initialized state.
*/
void Sections::clear(QList<Section> &v)
@@ -649,7 +561,7 @@ void Sections::stdRefPageSwitch(SectionVector &v, Node *n, Node *t)
return;
case Node::SharedComment: {
auto *scn = static_cast<SharedCommentNode *>(t);
- if (!scn->doc().isEmpty() && scn->collective().count())
+ if (!scn->doc().isEmpty() && scn->collective().size())
stdRefPageSwitch(
v, scn,
scn->collective().first()); // TODO: warn about mixed node types in collective?
@@ -814,7 +726,7 @@ void Sections::distributeNodeInDetailsVector(SectionVector &dv, Node *n)
if (n->isSharedCommentNode() && n->hasDoc()) {
auto *scn = static_cast<SharedCommentNode *>(n);
- if (scn->collective().count())
+ if (scn->collective().size())
t = scn->collective().first(); // TODO: warn about mixed node types in collective?
}
@@ -863,11 +775,11 @@ void Sections::distributeQmlNodeInDetailsVector(SectionVector &dv, Node *n)
return;
}
auto *scn = static_cast<SharedCommentNode *>(n);
- if (scn->collective().count())
+ if (scn->collective().size())
t = scn->collective().first(); // TODO: warn about mixed node types in collective?
}
- if (t->isQmlProperty() || t->isJsProperty()) {
+ if (t->isQmlProperty()) {
auto *pn = static_cast<QmlPropertyNode *>(t);
if (pn->isAttached())
dv[QmlAttachedProperties].insert(n);
@@ -875,14 +787,14 @@ void Sections::distributeQmlNodeInDetailsVector(SectionVector &dv, Node *n)
dv[QmlProperties].insert(n);
} else if (t->isFunction()) {
auto *fn = static_cast<FunctionNode *>(t);
- if (fn->isQmlSignal() || fn->isJsSignal()) {
+ if (fn->isQmlSignal()) {
if (fn->isAttached())
dv[QmlAttachedSignals].insert(n);
else
dv[QmlSignals].insert(n);
- } else if (fn->isQmlSignalHandler() || fn->isJsSignalHandler()) {
+ } else if (fn->isQmlSignalHandler()) {
dv[QmlSignalHandlers].insert(n);
- } else if (fn->isQmlMethod() || fn->isJsMethod()) {
+ } else if (fn->isQmlMethod()) {
if (fn->isAttached())
dv[QmlAttachedMethods].insert(n);
else
@@ -900,7 +812,7 @@ void Sections::distributeQmlNodeInSummaryVector(SectionVector &sv, Node *n, bool
{
if (n->isSharingComment() && !sharing)
return;
- if (n->isQmlProperty() || n->isJsProperty()) {
+ if (n->isQmlProperty()) {
auto *pn = static_cast<QmlPropertyNode *>(n);
if (pn->isAttached())
sv[QmlAttachedProperties].insert(pn);
@@ -908,14 +820,14 @@ void Sections::distributeQmlNodeInSummaryVector(SectionVector &sv, Node *n, bool
sv[QmlProperties].insert(pn);
} else if (n->isFunction()) {
auto *fn = static_cast<FunctionNode *>(n);
- if (fn->isQmlSignal() || fn->isJsSignal()) {
+ if (fn->isQmlSignal()) {
if (fn->isAttached())
sv[QmlAttachedSignals].insert(fn);
else
sv[QmlSignals].insert(fn);
- } else if (fn->isQmlSignalHandler() || fn->isJsSignalHandler()) {
+ } else if (fn->isQmlSignalHandler()) {
sv[QmlSignalHandlers].insert(fn);
- } else if (fn->isQmlMethod() || fn->isJsMethod()) {
+ } else if (fn->isQmlMethod()) {
if (fn->isAttached())
sv[QmlAttachedMethods].insert(fn);
else
@@ -950,16 +862,12 @@ void Sections::buildStdCppClassRefPageSections()
SectionVector &summarySections = stdCppClassSummarySections();
SectionVector &detailsSections = stdCppClassDetailsSections();
Section &allMembers = allMembersSection();
- bool documentAll = true;
- if (m_aggregate->parent() && !m_aggregate->name().isEmpty() && !m_aggregate->hasDoc())
- documentAll = false;
+
for (auto it = m_aggregate->constBegin(); it != m_aggregate->constEnd(); ++it) {
Node *n = *it;
if (!n->isPrivate() && !n->isProperty() && !n->isRelatedNonmember()
&& !n->isSharedCommentNode())
allMembers.insert(n);
- if (!documentAll && !n->hasDoc())
- continue;
distributeNodeInSummaryVector(summarySections, n);
distributeNodeInDetailsVector(detailsSections, n);
@@ -980,8 +888,6 @@ void Sections::buildStdCppClassRefPageSections()
if (!n->isPrivate() && !n->isProperty() && !n->isRelatedNonmember()
&& !n->isSharedCommentNode())
allMembers.insert(n);
- if (!documentAll && !n->hasDoc())
- continue;
}
pushBaseClasses(stack, cn);
}
@@ -996,15 +902,15 @@ void Sections::buildStdCppClassRefPageSections()
*/
void Sections::buildStdQmlTypeRefPageSections()
{
- ClassMap *classMap = nullptr;
+ ClassNodes *classNodes = nullptr;
SectionVector &summarySections = stdQmlTypeSummarySections();
SectionVector &detailsSections = stdQmlTypeDetailsSections();
Section &allMembers = allMembersSection();
const Aggregate *qtn = m_aggregate;
while (qtn) {
- if (!qtn->isAbstract() || !classMap)
- classMap = allMembers.newClassMap(qtn);
+ if (!qtn->isAbstract() || !classNodes)
+ classNodes = &allMembers.classNodesList().emplace_back(static_cast<const QmlTypeNode*>(qtn), NodeVector{});
for (const auto n : qtn->childNodes()) {
if (n->isInternal())
continue;
@@ -1015,7 +921,9 @@ void Sections::buildStdQmlTypeRefPageSections()
m_aggregate->findChildren(n->name(), candidates);
if (std::any_of(candidates.cbegin(), candidates.cend(), [&n](const Node *c) {
if (c->nodeType() == n->nodeType()) {
- if (!n->isFunction() || static_cast<const FunctionNode*>(n)->compare(c, false))
+ if (!n->isFunction() ||
+ compare(static_cast<const FunctionNode *>(n),
+ static_cast<const FunctionNode *>(c)) == 0)
return true;
}
return false;
@@ -1024,8 +932,11 @@ void Sections::buildStdQmlTypeRefPageSections()
}
}
- if (!n->isSharedCommentNode() || n->isPropertyGroup())
- allMembers.add(classMap, n);
+ if (!n->isSharedCommentNode() || n->isPropertyGroup()) {
+ allMembers.insert(n);
+ classNodes->second.push_back(n);
+ }
+
if (qtn == m_aggregate || qtn->isAbstract()) {
distributeQmlNodeInSummaryVector(summarySections, n);
@@ -1057,7 +968,7 @@ bool Sections::hasObsoleteMembers(SectionPtrVector *summary_spv,
const SectionVector *sections = nullptr;
if (m_aggregate->isClassNode())
sections = &stdCppClassSummarySections();
- else if (m_aggregate->isQmlType() || m_aggregate->isQmlBasicType())
+ else if (m_aggregate->isQmlType())
sections = &stdQmlTypeSummarySections();
else
sections = &stdSummarySections();
@@ -1067,7 +978,7 @@ bool Sections::hasObsoleteMembers(SectionPtrVector *summary_spv,
}
if (m_aggregate->isClassNode())
sections = &stdCppClassDetailsSections();
- else if (m_aggregate->isQmlType() || m_aggregate->isQmlBasicType())
+ else if (m_aggregate->isQmlType())
sections = &stdQmlTypeDetailsSections();
else
sections = &stdDetailsSections();
diff --git a/src/qdoc/sections.h b/src/qdoc/qdoc/src/qdoc/sections.h
index e09ebbd0c..70c73a91c 100644
--- a/src/qdoc/sections.h
+++ b/src/qdoc/qdoc/src/qdoc/sections.h
@@ -1,146 +1,78 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the tools applications of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#ifndef SECTIONS_H
#define SECTIONS_H
#include "node.h"
-#include <QtCore/qpair.h>
-
QT_BEGIN_NAMESPACE
class Aggregate;
-typedef QMultiMap<QString, Node *> MemberMap; // the string is the member signature
-typedef QPair<const QmlTypeNode *, MemberMap> ClassMap; // the node is the QML type
-typedef QList<ClassMap *> ClassMapList;
-
-typedef QPair<QStringList, NodeVector> KeysAndNodes;
-typedef QPair<const QmlTypeNode *, KeysAndNodes> ClassKeysNodes;
-typedef QList<ClassKeysNodes *> ClassKeysNodesList;
+typedef std::pair<const QmlTypeNode *, NodeVector> ClassNodes;
+typedef QList<ClassNodes> ClassNodesList;
class Section
{
public:
enum Style { Summary, Details, AllMembers, Accessors };
- enum Status { Obsolete, Active };
public:
- Section() : m_style(Details), m_status(Active), m_aggregate(nullptr) { }
- Section(Style style, Status status) : m_style(style), m_status(status), m_aggregate(nullptr) {};
- ~Section();
+ Section(
+ QString title, QString singular, QString plural,
+ QString divclass, Style style
+ ) : m_title{title}, m_singular{singular}, m_plural{plural},
+ m_divClass{divclass}, m_style{style}
+ {}
- void init(const QString &title) { m_title = title; }
- void init(const QString &singular, const QString &plural)
- {
- m_singular = singular;
- m_plural = plural;
- }
- void init(const QString &title, const QString &singular, const QString &plural)
- {
- m_title = title;
- m_divClass.clear();
- m_singular = singular;
- m_plural = plural;
- }
- void init(const QString &title, const QString &divClass, const QString &singular,
- const QString &plural)
- {
- m_title = title;
- m_divClass = divClass;
- m_singular = singular;
- m_plural = plural;
- }
+ ~Section();
void insert(Node *node);
- void insert(const QString &key, Node *node) { m_memberMap.insert(key, node); }
bool insertReimplementedMember(Node *node);
- ClassMap *newClassMap(const Aggregate *aggregate);
- void add(ClassMap *classMap, Node *n);
void appendMember(Node *node) { m_members.append(node); }
void clear();
void reduce();
[[nodiscard]] bool isEmpty() const
{
- return (m_memberMap.isEmpty() && m_inheritedMembers.isEmpty()
- && m_reimplementedMemberMap.isEmpty() && m_classMapList.isEmpty());
+ return (m_members.isEmpty() && m_inheritedMembers.isEmpty()
+ && m_reimplementedMemberMap.isEmpty() && m_classNodesList.isEmpty());
}
[[nodiscard]] Style style() const { return m_style; }
- [[nodiscard]] Status status() const { return m_status; }
[[nodiscard]] const QString &title() const { return m_title; }
[[nodiscard]] const QString &divClass() const { return m_divClass; }
[[nodiscard]] const QString &singular() const { return m_singular; }
[[nodiscard]] const QString &plural() const { return m_plural; }
- [[nodiscard]] const QStringList &keys() const { return m_keys; }
- [[nodiscard]] const QStringList &keys(Status t) const
- {
- return (t == Obsolete ? m_obsoleteKeys : m_keys);
- }
[[nodiscard]] const NodeVector &members() const { return m_members; }
[[nodiscard]] const NodeVector &reimplementedMembers() const { return m_reimplementedMembers; }
- [[nodiscard]] const QList<QPair<Aggregate *, int>> &inheritedMembers() const
+ [[nodiscard]] const QList<std::pair<Aggregate *, int>> &inheritedMembers() const
{
return m_inheritedMembers;
}
- ClassKeysNodesList &classKeysNodesList() { return m_classKeysNodesList; }
+ ClassNodesList &classNodesList() { return m_classNodesList; }
[[nodiscard]] const NodeVector &obsoleteMembers() const { return m_obsoleteMembers; }
void appendMembers(const NodeVector &nv) { m_members.append(nv); }
[[nodiscard]] const Aggregate *aggregate() const { return m_aggregate; }
void setAggregate(Aggregate *t) { m_aggregate = t; }
private:
- QString sortName(const Node *node, const QString *name = nullptr);
-
-private:
- Style m_style {};
- Status m_status {};
QString m_title {};
- QString m_divClass {};
QString m_singular {};
QString m_plural {};
+ QString m_divClass {};
+ Style m_style {};
Aggregate *m_aggregate { nullptr };
- QStringList m_keys {};
- QStringList m_obsoleteKeys {};
NodeVector m_members {};
NodeVector m_obsoleteMembers {};
NodeVector m_reimplementedMembers {};
- QList<QPair<Aggregate *, int>> m_inheritedMembers {};
- ClassKeysNodesList m_classKeysNodesList {};
+ QList<std::pair<Aggregate *, int>> m_inheritedMembers {};
+ ClassNodesList m_classNodesList {};
- QMultiMap<QString, Node *> m_memberMap {};
- QMultiMap<QString, Node *> m_obsoleteMemberMap {};
QMultiMap<QString, Node *> m_reimplementedMemberMap {};
- ClassMapList m_classMapList {};
};
typedef QList<Section> SectionVector;
@@ -184,23 +116,23 @@ public:
SinceEnumTypes = 6,
StdMacros = 6,
QmlAttachedMethods = 6,
+ SinceEnumValues = 7,
ProtectedTypes = 7,
- SinceTypedefs = 7,
- SinceTypeAliases = 7,
+ SinceTypeAliases = 8,
ProtectedFunctions = 8,
- SinceProperties = 8,
+ SinceProperties = 9,
ProtectedSlots = 9,
- SinceVariables = 9,
+ SinceVariables = 10,
ProtectedVariables = 10,
- SinceQmlTypes = 10,
+ SinceQmlTypes = 11,
StaticProtectedMembers = 11,
- SinceQmlProperties = 11,
+ SinceQmlProperties = 12,
PrivateTypes = 12,
- SinceQmlSignals = 12,
+ SinceQmlSignals = 13,
PrivateFunctions = 13,
- SinceQmlSignalHandlers = 13,
+ SinceQmlSignalHandlers = 14,
PrivateSlots = 14,
- SinceQmlMethods = 14,
+ SinceQmlMethods = 15,
StaticPrivateMembers = 15,
RelatedNonmembers = 16,
Macros = 17
@@ -210,7 +142,6 @@ public:
explicit Sections(const NodeMultiMap &nsmap);
~Sections();
- void initSections();
void clear(SectionVector &v);
void reduce(SectionVector &v);
void buildStdRefPageSections();
diff --git a/src/qdoc/qdoc/src/qdoc/sharedcommentnode.cpp b/src/qdoc/qdoc/src/qdoc/sharedcommentnode.cpp
new file mode 100644
index 000000000..05204d1f3
--- /dev/null
+++ b/src/qdoc/qdoc/src/qdoc/sharedcommentnode.cpp
@@ -0,0 +1,56 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "sharedcommentnode.h"
+
+#include "aggregate.h"
+#include "functionnode.h"
+#include "qmltypenode.h"
+
+QT_BEGIN_NAMESPACE
+
+SharedCommentNode::SharedCommentNode(QmlTypeNode *parent, int count, QString &group)
+ : Node(Node::SharedComment, parent, group)
+{
+ m_collective.reserve(count);
+}
+
+/*!
+ Searches the shared comment node's member nodes for function
+ nodes. Each function node's overload flag is set.
+ */
+void SharedCommentNode::setOverloadFlags()
+{
+ for (auto *node : m_collective) {
+ if (node->isFunction())
+ static_cast<FunctionNode *>(node)->setOverloadFlag();
+ }
+}
+
+/*!
+ Clone this node on the heap and make the clone a child of
+ \a parent.
+
+ Returns the pointer to the clone.
+ */
+Node *SharedCommentNode::clone(Aggregate *parent)
+{
+ auto *scn = new SharedCommentNode(*this); // shallow copy
+ scn->setParent(nullptr);
+ parent->addChild(scn);
+
+ return scn;
+}
+
+/*!
+ Sets the related nonmember flag in this node and in each
+ node in the shared comment's collective to \a value.
+ */
+void SharedCommentNode::setRelatedNonmember(bool value)
+{
+ Node::setRelatedNonmember(value);
+ for (auto *node : m_collective)
+ node->setRelatedNonmember(value);
+}
+
+QT_END_NAMESPACE
diff --git a/src/qdoc/qdoc/src/qdoc/sharedcommentnode.h b/src/qdoc/qdoc/src/qdoc/sharedcommentnode.h
new file mode 100644
index 000000000..d319d7c59
--- /dev/null
+++ b/src/qdoc/qdoc/src/qdoc/sharedcommentnode.h
@@ -0,0 +1,51 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#ifndef SHAREDCOMMENTNODE_H
+#define SHAREDCOMMENTNODE_H
+
+#include "node.h"
+
+#include <QtCore/qglobal.h>
+#include <QtCore/qlist.h>
+
+QT_BEGIN_NAMESPACE
+
+class Aggregate;
+class QmlTypeNode;
+
+class SharedCommentNode : public Node
+{
+public:
+ explicit SharedCommentNode(Node *node) : Node(Node::SharedComment, node->parent(), QString())
+ {
+ m_collective.reserve(1);
+ append(node);
+ }
+ SharedCommentNode(QmlTypeNode *parent, int count, QString &group);
+ ~SharedCommentNode() override { m_collective.clear(); }
+
+ [[nodiscard]] bool isPropertyGroup() const override
+ {
+ return !name().isEmpty() && !m_collective.isEmpty() && (m_collective.at(0)->isQmlProperty());
+ }
+ [[nodiscard]] qsizetype count() const { return m_collective.size(); }
+ void append(Node *node)
+ {
+ m_collective.append(node);
+ node->setSharedCommentNode(this);
+ setGenus(node->genus());
+ }
+ void sort() { std::sort(m_collective.begin(), m_collective.end(), Node::nodeNameLessThan); }
+ [[nodiscard]] const QList<Node *> &collective() const { return m_collective; }
+ void setOverloadFlags();
+ void setRelatedNonmember(bool value) override;
+ Node *clone(Aggregate *parent) override;
+
+private:
+ QList<Node *> m_collective {};
+};
+
+QT_END_NAMESPACE
+
+#endif // SHAREDCOMMENTNODE_H
diff --git a/src/qdoc/qdoc/src/qdoc/singleton.h b/src/qdoc/qdoc/src/qdoc/singleton.h
new file mode 100644
index 000000000..085a5ea64
--- /dev/null
+++ b/src/qdoc/qdoc/src/qdoc/singleton.h
@@ -0,0 +1,32 @@
+// Copyright (C) 2020 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+#ifndef SINGLETON_H
+#define SINGLETON_H
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class Singleton
+ \internal
+
+ Class template for singleton objects in QDoc.
+ */
+template<typename T>
+class Singleton
+{
+public:
+ Singleton(const Singleton &) = delete;
+ Singleton &operator=(const Singleton &) = delete;
+ static T &instance()
+ {
+ static T s_instance {};
+ return s_instance;
+ }
+
+protected:
+ Singleton() = default;
+};
+
+QT_END_NAMESPACE
+
+#endif // SINGLETON_H
diff --git a/src/qdoc/qdoc/src/qdoc/sourcefileparser.h b/src/qdoc/qdoc/src/qdoc/sourcefileparser.h
new file mode 100644
index 000000000..95bc71627
--- /dev/null
+++ b/src/qdoc/qdoc/src/qdoc/sourcefileparser.h
@@ -0,0 +1,82 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#pragma once
+
+#include "clangcodeparser.h"
+#include "puredocparser.h"
+
+#include <variant>
+
+#include <QString>
+#include <QFileInfo>
+
+struct CppSourceFile {};
+struct QDocSourceFile {};
+struct JsSourceFile {};
+struct UnknownSourceFile {};
+
+using SourceFileTag = std::variant<CppSourceFile, QDocSourceFile, JsSourceFile, UnknownSourceFile>;
+using TaggedSourceFile = std::pair<QString, SourceFileTag>;
+
+inline TaggedSourceFile tag_source_file(const QString& path) {
+ static QStringList cpp_file_extensions{
+ "c++", "cc", "cpp", "cxx", "mm"
+ };
+ static QStringList qdoc_file_extensions{ "qdoc" };
+ static QStringList javascript_file_extensions{ "js" };
+
+ QString extension{QFileInfo(path).suffix()};
+
+ if (cpp_file_extensions.contains(extension)) return TaggedSourceFile{path, CppSourceFile{}};
+ else if (qdoc_file_extensions.contains(extension)) return TaggedSourceFile{path, QDocSourceFile{}};
+ else if (javascript_file_extensions.contains(extension)) return TaggedSourceFile{path, JsSourceFile{}};
+
+ return TaggedSourceFile{path, UnknownSourceFile{}};
+}
+
+class SourceFileParser {
+public:
+ struct ParseResult {
+ std::vector<UntiedDocumentation> untied;
+ std::vector<TiedDocumentation> tied;
+ };
+
+public:
+ SourceFileParser(ClangCodeParser& clang_parser, PureDocParser& pure_parser)
+ : cpp_file_parser(clang_parser),
+ pure_file_parser(pure_parser)
+ {}
+
+ ParseResult operator()(const TaggedSourceFile& source) {
+ if (std::holds_alternative<CppSourceFile>(source.second))
+ return (*this)(source.first, std::get<CppSourceFile>(source.second));
+ else if (std::holds_alternative<QDocSourceFile>(source.second))
+ return (*this)(source.first, std::get<QDocSourceFile>(source.second));
+ else if (std::holds_alternative<JsSourceFile>(source.second))
+ return (*this)(source.first, std::get<JsSourceFile>(source.second));
+ else if (std::holds_alternative<UnknownSourceFile>(source.second))
+ return {{}, {}};
+
+ Q_UNREACHABLE();
+ }
+
+private:
+ ParseResult operator()(const QString& path, CppSourceFile) {
+ auto [untied, tied] = cpp_file_parser.parse_cpp_file(path);
+
+ return {untied, tied};
+ }
+
+ ParseResult operator()(const QString& path, QDocSourceFile) {
+ return {pure_file_parser.parse_qdoc_file(path), {}};
+ }
+
+ ParseResult operator()(const QString& path, JsSourceFile) {
+ return {pure_file_parser.parse_qdoc_file(path), {}};
+ }
+
+private:
+ ClangCodeParser& cpp_file_parser;
+ PureDocParser& pure_file_parser;
+};
diff --git a/src/qdoc/tagfilewriter.cpp b/src/qdoc/qdoc/src/qdoc/tagfilewriter.cpp
index 25535bc9c..7b6b496ca 100644
--- a/src/qdoc/tagfilewriter.cpp
+++ b/src/qdoc/qdoc/src/qdoc/tagfilewriter.cpp
@@ -1,30 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2020 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the tools applications of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2020 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "tagfilewriter.h"
@@ -75,7 +50,6 @@ void TagFileWriter::generateTagFileCompounds(QXmlStreamWriter &writer, const Agg
case Node::Struct:
case Node::Union:
case Node::QmlType:
- case Node::JsType:
kind = "class";
break;
default:
@@ -99,7 +73,7 @@ void TagFileWriter::generateTagFileCompounds(QXmlStreamWriter &writer, const Agg
if (node->isClassNode()) {
writer.writeTextElement("name", node->fullDocumentName());
- writer.writeTextElement("filename", m_generator->fullDocumentLocation(node, false));
+ writer.writeTextElement("filename", m_generator->fullDocumentLocation(node));
// Classes contain information about their base classes.
const auto *classNode = static_cast<const ClassNode *>(node);
@@ -118,7 +92,7 @@ void TagFileWriter::generateTagFileCompounds(QXmlStreamWriter &writer, const Agg
generateTagFileCompounds(writer, aggregate);
} else {
writer.writeTextElement("name", node->fullDocumentName());
- writer.writeTextElement("filename", m_generator->fullDocumentLocation(node, false));
+ writer.writeTextElement("filename", m_generator->fullDocumentLocation(node));
// Recurse to write all members.
generateTagFileMembers(writer, aggregate);
@@ -136,7 +110,8 @@ void TagFileWriter::generateTagFileCompounds(QXmlStreamWriter &writer, const Agg
*/
void TagFileWriter::generateTagFileMembers(QXmlStreamWriter &writer, const Aggregate *parent)
{
- const auto &childNodes = parent->childNodes();
+ auto childNodes = parent->childNodes();
+ std::sort(childNodes.begin(), childNodes.end(), Node::nodeNameLessThan);
for (const auto *node : childNodes) {
if (!node->url().isNull())
continue;
@@ -227,10 +202,10 @@ void TagFileWriter::generateTagFileMembers(QXmlStreamWriter &writer, const Aggre
writer.writeTextElement("name", objName);
const QStringList pieces =
- m_generator->fullDocumentLocation(node, false).split(QLatin1Char('#'));
+ m_generator->fullDocumentLocation(node).split(QLatin1Char('#'));
writer.writeTextElement("anchorfile", pieces[0]);
writer.writeTextElement("anchor", pieces[1]);
- QString signature = functionNode->signature(false, false);
+ QString signature = functionNode->signature(Node::SignatureReturnType);
signature = signature.mid(signature.indexOf(QChar('('))).trimmed();
if (functionNode->isConst())
signature += " const";
@@ -249,7 +224,7 @@ void TagFileWriter::generateTagFileMembers(QXmlStreamWriter &writer, const Aggre
writer.writeAttribute("type", propertyNode->dataType());
writer.writeTextElement("name", objName);
const QStringList pieces =
- m_generator->fullDocumentLocation(node, false).split(QLatin1Char('#'));
+ m_generator->fullDocumentLocation(node).split(QLatin1Char('#'));
writer.writeTextElement("anchorfile", pieces[0]);
writer.writeTextElement("anchor", pieces[1]);
writer.writeTextElement("arglist", QString());
@@ -260,7 +235,7 @@ void TagFileWriter::generateTagFileMembers(QXmlStreamWriter &writer, const Aggre
const auto *enumNode = static_cast<const EnumNode *>(node);
writer.writeTextElement("name", objName);
const QStringList pieces =
- m_generator->fullDocumentLocation(node, false).split(QLatin1Char('#'));
+ m_generator->fullDocumentLocation(node).split(QLatin1Char('#'));
writer.writeTextElement("anchorfile", pieces[0]);
writer.writeTextElement("anchor", pieces[1]);
writer.writeEndElement(); // member
@@ -284,7 +259,7 @@ void TagFileWriter::generateTagFileMembers(QXmlStreamWriter &writer, const Aggre
writer.writeAttribute("type", QString());
writer.writeTextElement("name", objName);
const QStringList pieces =
- m_generator->fullDocumentLocation(node, false).split(QLatin1Char('#'));
+ m_generator->fullDocumentLocation(node).split(QLatin1Char('#'));
writer.writeTextElement("anchorfile", pieces[0]);
writer.writeTextElement("anchor", pieces[1]);
writer.writeTextElement("arglist", QString());
diff --git a/src/qdoc/qdoc/src/qdoc/tagfilewriter.h b/src/qdoc/qdoc/src/qdoc/tagfilewriter.h
new file mode 100644
index 000000000..40a1ed399
--- /dev/null
+++ b/src/qdoc/qdoc/src/qdoc/tagfilewriter.h
@@ -0,0 +1,33 @@
+// Copyright (C) 2020 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#ifndef TAGFILEWRITER_H
+#define TAGFILEWRITER_H
+
+#include <QtCore/qxmlstream.h>
+
+QT_BEGIN_NAMESPACE
+
+class Aggregate;
+class Generator;
+class QDocDatabase;
+
+class TagFileWriter
+{
+public:
+ TagFileWriter();
+ ~TagFileWriter() = default;
+
+ void generateTagFile(const QString &fileName, Generator *generator);
+
+private:
+ void generateTagFileCompounds(QXmlStreamWriter &writer, const Aggregate *inner);
+ void generateTagFileMembers(QXmlStreamWriter &writer, const Aggregate *inner);
+
+ QDocDatabase *m_qdb { nullptr };
+ Generator *m_generator { nullptr };
+};
+
+QT_END_NAMESPACE
+
+#endif // TAGFILEWRITER_H
diff --git a/src/qdoc/qdoc/src/qdoc/template_declaration.h b/src/qdoc/qdoc/src/qdoc/template_declaration.h
new file mode 100644
index 000000000..a0aade682
--- /dev/null
+++ b/src/qdoc/qdoc/src/qdoc/template_declaration.h
@@ -0,0 +1,502 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#pragma once
+
+#include <algorithm>
+#include <cstdint>
+#include <functional>
+#include <numeric>
+#include <optional>
+#include <string>
+#include <vector>
+
+#include <QString>
+
+/*
+ * Represents a general declaration that has a form that can be
+ * described by a type, name and initializer triplet, or any such form
+ * that can be described by zero or more of those same parts.
+ *
+ * For example, it can be used to represent a C++ variable declaration
+ * such as:
+ *
+ * std::vector<int> foo = { 1, 2, 3 };
+ *
+ * Where `std::vector<int>` is the type, `foo` is the name and `{ 1, 2,
+ * 3 }` is the initializer.
+ *
+ * Similarly, it can be used to represent a non-type template parameter
+ * declaration, such as the `foo` parameter in:
+ *
+ * template<int foo = 10>
+ *
+ * Where `int` is the type, `foo` is the name and `10` is the
+ * initializer.
+ *
+ * An instance can be used to represent less information dense elements
+ * by setting one or more of the fields as the empty string.
+ *
+ * For example, a template type parameter such as `T` in:
+ *
+ * template<typename T = int>
+ *
+ * Can be represented by an instance that has an empty string as the
+ * type, `T` as the name and `int` as the initializer.
+ *
+ * In general, it can be used to represent any such element that has
+ * zero or more of the three components, albeit, in QDoc, it is
+ * specifically intended to be used to represent various C++
+ * declarations.
+ *
+ * All three fields are lowered stringified version of the original
+ * declaration, so that the type should be used at the end of a
+ * pipeline where the semantic property of the represented code are not
+ * required.
+ */
+struct ValuedDeclaration
+{
+ struct PrintingPolicy
+ {
+ bool include_type = true;
+ bool include_name = true;
+ bool include_initializer = true;
+ };
+
+ std::string type;
+ std::string name;
+ std::string initializer;
+
+ // KLUDGE: Workaround for
+ // https://stackoverflow.com/questions/53408962/try-to-understand-compiler-error-message-default-member-initializer-required-be
+ static PrintingPolicy default_printing_policy() { return PrintingPolicy{}; }
+
+ /*
+ * Constructs and returns a human-readable representation of this
+ * declaration.
+ *
+ * The constructed string is formatted so that as to rebuild a
+ * possible version of the C++ code that is modeled by an instance
+ * of this type.
+ *
+ * Each component participates in the human-presentable version if
+ * it they are not the empty string.
+ *
+ * The "type" and "name" component participate with their literal
+ * representation.
+ *
+ * The "iniitlalizer" components contributes an equal symbol,
+ * followed by a space followed by the literal representation of
+ * the component.
+ *
+ * The component contributes in an ordered way, with "type"
+ * contributing first, "name" contributing second and
+ * "initializer" contributing last.
+ *
+ * Each contribution is separated by a space if the component that
+ * comes before it, if any, has contributed to the human-readable
+ * representation.
+ *
+ * For example, an instance of this type that has "type" component
+ * "int", "name" component "foo" and "iniitializer" component
+ * "100", would be represented as:
+ *
+ * int foo = 100
+ *
+ * Where "int" is the "type" component contribution, "foo" is the
+ * "name" component contribution and "= 100" is the "initializer"
+ * component contribution.
+ * Each of those contribution is separated by a space, as each
+ * "preceding" component has contributed to the representation.
+ *
+ * If we provide a similar instance with, for example, the "type"
+ * and "name" components as the empty string, then the
+ * representation would be "= 100", which is the "initializer"
+ * component contribution, the only component that is not the
+ * empty string.
+ *
+ * The policy argument allows to treat certain components as if
+ * they were the empty string.
+ *
+ * For example, given an instance of this type that has "type"
+ * component "double", "name" component "bar" and "iniitializer"
+ * component "10.2", its human-readable representation would be
+ * "double bar = 10.2".
+ *
+ * If the representation of that same instance was obtained by
+ * using a policy that excludes the "name" component, then that
+ * representation would be "double = 10.2", which is equivalent
+ * to the representation of an instance that is the same as the
+ * orginal one with the "name" component as the empty string.
+ */
+ inline std::string to_std_string(PrintingPolicy policy = default_printing_policy()) const
+ {
+ std::string s{};
+
+ if (!type.empty() && policy.include_type)
+ s += (s.empty() ? "" : " ") + type;
+
+ if (!name.empty() && policy.include_name)
+ s += (s.empty() ? "" : " ") + name;
+
+ if (!initializer.empty() && policy.include_initializer)
+ s += (s.empty() ? "= " : " = ") + initializer;
+
+ return s;
+ }
+};
+
+struct RelaxedTemplateParameter;
+
+struct TemplateDeclarationStorage
+{
+ std::vector<RelaxedTemplateParameter> parameters;
+
+ inline std::string to_std_string() const;
+};
+
+/*
+ * Represents a C++ template parameter.
+ *
+ * The model used by this representation is a slighly simplified
+ * model.
+ *
+ * In the model, template parameters are one of:
+ *
+ * - A type template parameter.
+ * - A non type template parameter.
+ * - A template template parameter.
+ *
+ * Furthermore, each parameter can:
+ *
+ * - Be a parameter pack.
+ * - Carry an additional template declaration (as a template template
+ * parameter would).
+ * - Have no declared type.
+ * - Have no declared name.
+ * - Have no declared initializer.
+ *
+ * Due to this simplified model certain incorrect parameters can be
+ * represented.
+ *
+ * For example, it might be possible to represent a parameter pack
+ * that has a default initializer, a non-type template parameter that
+ * has no type or a template template parameter that carries no
+ * template declaration.
+ *
+ * The model further elides some of the semantic that might be carried
+ * by a parameter.
+ * For example, the model has no specific concept for template
+ * constraints.
+ *
+ * Template parameters can be represented as instances of the type.
+ *
+ * For example, a type template parameter `typename T` can be
+ * represented as the following instance:
+ *
+ * RelaxedTemplateParameter{
+ * RelaxedTemplateParameter::Kind::TypeTemplateParameter,
+ * false,
+ * {
+ * "",
+ * "T",
+ * ""
+ * },
+ * {}
+ * };
+ *
+ * And a non-type template parameter pack "int... Args" as:
+ *
+ * RelaxedTemplateParameter{
+ * RelaxedTemplateParameter::Kind::NonTypeTemplateParameter,
+ * true,
+ * {
+ * "int",
+ * "Args",
+ * ""
+ * },
+ * {}
+ * };
+ *
+ * Due to the relaxed constraint and the representable incorrect
+ * parameters, the type is intended to be used for data that is
+ * already validated and known to be correct, such as data that is
+ * extracted from Clang.
+ */
+struct RelaxedTemplateParameter
+{
+ enum class Kind : std::uint8_t {
+ TypeTemplateParameter,
+ NonTypeTemplateParameter,
+ TemplateTemplateParameter
+ };
+
+ Kind kind;
+ bool is_parameter_pack;
+ ValuedDeclaration valued_declaration;
+ std::optional<TemplateDeclarationStorage> template_declaration;
+
+ /*
+ * Constructs and returns a human-readable representation of this
+ * parameter.
+ *
+ * The constructed string is formatted so that as to rebuild a
+ * possible version of the C++ code that is modeled by an instance
+ * of this type.
+ *
+ * The format of the representation varies based on the "kind" of
+ * the parameter.
+ *
+ * - A "TypeTemplateParameter", is constructed as the
+ * concatenation of the literal "typename", followed by the
+ * literal "..." if the parameter is a pack, followed by the
+ * human-readable representaion of "valued_declaration".
+ *
+ * If the human-readable representation of
+ * "valued_declaration" is not the empty string, it is
+ * preceded by a space when it contributes to the
+ * representation.
+ *
+ * For example, the C++ type template parameter "typename Foo
+ * = int", would be represented by the instance:
+ *
+ * RelaxedTemplateParameter{
+ * RelaxedTemplateParameter::Kind::TypeTemplateParameter,
+ * false,
+ * {
+ * "",
+ * "Foo",
+ * "int"
+ * },
+ * {}
+ * };
+ *
+ * And its representation would be:
+ *
+ * typename Foo = int
+ *
+ * Where "typename" is the added literal and "Foo = int" is
+ * the representation for "valued_declaration", with a space
+ * in-between the two contributions.
+ *
+ * - A "NonTypeTemplateParameter", is constructed by the
+ * contribution of the "type" compoment of "valued_declaration",
+ * followed by the literal "..." if the parameter is a pack,
+ * followed by the human-presentable version of
+ * "valued_declaration" without its "type" component
+ * contribution.
+ *
+ * If the contribution of the "type" component of
+ * "valued_declaration" is not empty, the next contribution is
+ * preceded by a space.
+ *
+ * For example, the C++ non-type template parameter "int...
+ * SIZE", would be represented by the instance:
+ *
+ *
+ * RelaxedTemplateParameter{
+ * RelaxedTemplateParameter::Kind::NonTypeTemplateParameter,
+ * true,
+ * {
+ * "int",
+ * "SIZE",
+ * ""
+ * },
+ * {}
+ * };
+ *
+ * And its representation would be:
+ *
+ * int... SIZE
+ *
+ * Where "int" is the "type" component contribution of
+ * "valued_declaration", "..." is the added literal due to
+ * the parameter being a pack and " SIZE" being the
+ * human-readable representation of "valued_declaration"
+ * without its "type" component contribution, preceded by a
+ * space.
+ *
+ * - A "TemplateTemplateParameter", is constructed by the
+ * contribution of the human-presentable representation of
+ * "template_declaration", followed by the representation of
+ * this parameter if it was a "TypeTemplateParameter", with a
+ * space between the two contributions if the
+ * human-presentable representation of "template_declaration"
+ * is not empty.
+ *
+ * For example, the C++ template template template parameter
+ * "template<typename> T", would be represented by the
+ * instance:
+ *
+ *
+ * RelaxedTemplateParameter{
+ * RelaxedTemplateParameter::Kind::TemplateTemplateParameter,
+ * false,
+ * {
+ * "",
+ * "T",
+ * ""
+ * },
+ * {
+ * RelaxedTemplateParameter{
+ * RelaxedTemplateParameter::Kind::TypeTemplateParameter,
+ * false,
+ * {
+ * "",
+ * "",
+ * ""
+ * },
+ * {}
+ * }
+ * }
+ * };
+ *
+ * And its representation would be:
+ *
+ * template <typename> typename T
+ *
+ * Where "template <typename>" human-presentable version of
+ * "template_declaration" and "typename T" is the
+ * human-presentable version of this parameter if it was a
+ * type template parameter.
+ *
+ * With a space between the two contributions.
+ */
+ inline std::string to_std_string() const
+ {
+ switch (kind) {
+ // TODO: This can probably be moved under the template
+ // template parameter case and reused through a fallback.
+ case Kind::TypeTemplateParameter: {
+ std::string valued_declaration_string = valued_declaration.to_std_string();
+
+ return std::string("typename") + (is_parameter_pack ? "..." : "")
+ + (valued_declaration_string.empty() ? "" : " ") + valued_declaration_string;
+ }
+ case Kind::NonTypeTemplateParameter: {
+ std::string type_string = valued_declaration.type + (is_parameter_pack ? "..." : "");
+
+ return type_string + (type_string.empty() ? "" : " ")
+ + valued_declaration.to_std_string(
+ ValuedDeclaration::PrintingPolicy{ false, true, true });
+ }
+ case Kind::TemplateTemplateParameter: {
+ std::string valued_declaration_string = valued_declaration.to_std_string();
+
+ return (template_declaration ? (*template_declaration).to_std_string() + " " : "")
+ + "typename" + (is_parameter_pack ? "..." : "")
+ + (valued_declaration_string.empty() ? "" : " ") + valued_declaration_string;
+ }
+ default:
+ return "";
+ }
+ }
+};
+
+/*
+ * Represents a C++ template declaration as a collection of template
+ * parameters.
+ *
+ * The parameters for the declaration follow the same relaxed rules as
+ * `RelaxedTemplateParameter` and inherit the possibility of
+ * representing incorrect declarations.
+ *
+ * Due to the relaxed constraint and the representable incorrect
+ * parameters, the type is intended to be used for data that is
+ * already validated and known to be correct, such as data that is
+ * extracted from Clang.
+ */
+struct RelaxedTemplateDeclaration : TemplateDeclarationStorage
+{
+ inline QString to_qstring() const { return QString::fromStdString(to_std_string()); }
+};
+
+/*
+ * Constructs and returns a human-readable representation of this
+ * declaration.
+ *
+ * The constructed string is formatted so as to rebuild a
+ * possible version of the C++ code that is modeled by an instance
+ * of this type.
+ *
+ * The representation of a declaration is constructed by the literal
+ * "template <", followed by the human-presentable version of each
+ * parameter in "parameters", with a comma and a space between each
+ * parameter, followed by a closing literal ">".
+ *
+ * For example, the empty declaration is represented as "template <>".
+ *
+ * While a template declaration that has a type template parameter
+ * "Foo" with initializer "int" and a non-type template parameter pack
+ * with type "int" and name "S" would be represented as:
+ *
+ * template <typename Foo = int, int... S>
+ */
+inline std::string TemplateDeclarationStorage::to_std_string() const
+{
+ if (parameters.empty())
+ return "template <>";
+
+ return "template <"
+ + std::accumulate(std::next(parameters.cbegin()), parameters.cend(),
+ parameters.front().to_std_string(),
+ [](auto &&acc, const RelaxedTemplateParameter &parameter) {
+ return acc + ", " + parameter.to_std_string();
+ })
+ + ">";
+}
+
+/*
+ * Returns true if the two template declaration represented by left
+ * and right are substitutable.
+ *
+ * QDoc uses a simplified model for template declarations and,
+ * similarly, uses a simplified model of "substitutability".
+ *
+ * Two declarations are substitutable if:
+ *
+ * - They have the same amount of parameters
+ * - For each pair of parameters with the same postion:
+ * - They have the same kind
+ * - They are both parameter packs or both are not parameter packs
+ * - If they are non-type template parameters then they have the same type
+ * - If they are both template template parameters then they both
+ * carry an additional template declaration and the additional
+ * template declarations are substitutable
+ *
+ * This means that in the simplified models, we generally ignore default arguments, name and such.
+ *
+ * This model does not follow the way C++ performs disambiguation but
+ * should be enough to handle most cases in the documentation.
+ */
+inline bool are_template_declarations_substitutable(const TemplateDeclarationStorage& left, const TemplateDeclarationStorage& right) {
+ static auto are_template_parameters_substitutable = [](const RelaxedTemplateParameter& left, const RelaxedTemplateParameter& right) {
+ if (left.kind != right.kind) return false;
+ if (left.is_parameter_pack != right.is_parameter_pack) return false;
+
+ if (left.kind == RelaxedTemplateParameter::Kind::NonTypeTemplateParameter &&
+ (left.valued_declaration.type != right.valued_declaration.type))
+ return false;
+
+ if (left.kind == RelaxedTemplateParameter::Kind::TemplateTemplateParameter) {
+ if (!left.template_declaration && right.template_declaration) return false;
+ if (left.template_declaration && !right.template_declaration) return false;
+
+ if (left.template_declaration && right.template_declaration)
+ return are_template_declarations_substitutable(*left.template_declaration, *right.template_declaration);
+ }
+
+ return true;
+ };
+
+ const auto& left_parameters = left.parameters;
+ const auto& right_parameters = right.parameters;
+
+ if (left_parameters.size() != right_parameters.size()) return false;
+
+ return std::transform_reduce(left_parameters.cbegin(), left_parameters.cend(), right_parameters.cbegin(),
+ true,
+ std::logical_and<bool>{},
+ are_template_parameters_substitutable
+ );
+}
diff --git a/src/qdoc/text.cpp b/src/qdoc/qdoc/src/qdoc/text.cpp
index 60dcabd5f..d3401ce68 100644
--- a/src/qdoc/text.cpp
+++ b/src/qdoc/qdoc/src/qdoc/text.cpp
@@ -1,30 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the tools applications of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "text.h"
@@ -217,19 +192,64 @@ Text Text::sectionHeading(const Atom *sectionLeft)
return Text();
}
+/*!
+ Prints a human-readable version of the contained atoms to stderr.
+
+ The output is formatted as a linear list of atoms, with each atom
+ being on its own line.
+
+ Each atom is represented by its type and its stringified-contents,
+ if any, with a space between the two.
+
+ Indentation is used to emphasize the possible block-level
+ relationship between consecutive atoms, increasing after a
+ "Left" atom and decreasing just before a "Right" atom.
+
+ For example, if this `Text` represented the block-comment
+ containing the text:
+
+ \c {\l {somelink} {This is a link}}
+
+ Then the human-readable output would look like the following:
+
+ \badcode
+ ParaLeft
+ Link "somelink"
+ FormattingLeft "link"
+ String "This is a link"
+ FormattingRight "link"
+ String
+ ParaRight
+ \endcode
+ */
void Text::dump() const
{
+ constexpr int minimum_indentation_level { 1 };
+ int indentation_level { minimum_indentation_level };
+ int indentation_width { 4 };
+
const Atom *atom = firstAtom();
while (atom != nullptr) {
QString str = atom->string();
str.replace("\\", "\\\\");
str.replace("\"", "\\\"");
str.replace("\n", "\\n");
- str.replace(QRegularExpression(R"([^ -~])"), "?");
+ static const QRegularExpression re(R"([^ -~])");
+ str.replace(re, "?");
if (!str.isEmpty())
str = " \"" + str + QLatin1Char('"');
- fprintf(stderr, " %-15s%s\n", atom->typeString().toLatin1().data(),
- str.toLatin1().data());
+
+ QString atom_type = atom->typeString();
+ if (atom_type.contains("Right"))
+ indentation_level = std::max(minimum_indentation_level, indentation_level - 1);
+
+ fprintf(stderr, "%s%s%s\n",
+ QString(indentation_level * indentation_width, ' ').toLatin1().data(),
+ atom_type.toLatin1().data(), str.toLatin1().data());
+
+ if (atom_type.contains("Left"))
+ indentation_level += 1;
+
atom = atom->next();
}
}
@@ -283,4 +303,39 @@ int Text::compare(const Text &text1, const Text &text2)
}
}
+/*!
+ \internal
+
+ \brief Splits the current Text from \a start to end into a new Text object.
+
+ Returns a new Text from the first Atom in this Text of atom type \a start.
+ */
+Text Text::splitAtFirst(Atom::AtomType start) {
+ if (m_first == nullptr)
+ return {};
+
+ Atom *previous = nullptr;
+ Atom *current = m_first;
+
+ while (current != nullptr) {
+ if (current->type() == start)
+ break;
+ previous = current;
+ current = current->next();
+ }
+
+ if (!current)
+ return {};
+
+ Text splitText = Text(current, m_last);
+
+ // Reset this Text's first and last atom pointers based on
+ // whether all or part of the content was extracted.
+ m_first = previous ? m_first : nullptr;
+ if (m_last = previous; m_last)
+ m_last->setNext(nullptr);
+
+ return splitText;
+}
+
QT_END_NAMESPACE
diff --git a/src/qdoc/text.h b/src/qdoc/qdoc/src/qdoc/text.h
index f1942ffbd..d42143909 100644
--- a/src/qdoc/text.h
+++ b/src/qdoc/qdoc/src/qdoc/text.h
@@ -1,30 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the tools applications of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#ifndef TEXT_H
#define TEXT_H
@@ -67,7 +42,17 @@ public:
static Text sectionHeading(const Atom *sectionBegin);
static int compare(const Text &text1, const Text &text2);
+ [[nodiscard]] Text splitAtFirst(Atom::AtomType start);
+
private:
+ Text(Atom *start, Atom *end) : m_first(start), m_last(end)
+ {
+ Q_ASSERT(m_first != nullptr);
+ Q_ASSERT(m_last != nullptr);
+
+ m_last->setNext(nullptr);
+ }
+
Atom *m_first { nullptr };
Atom *m_last { nullptr };
};
diff --git a/src/qdoc/tokenizer.cpp b/src/qdoc/qdoc/src/qdoc/tokenizer.cpp
index 1f9476d45..bfbfc53c5 100644
--- a/src/qdoc/tokenizer.cpp
+++ b/src/qdoc/qdoc/src/qdoc/tokenizer.cpp
@@ -1,30 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the tools applications of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "tokenizer.h"
@@ -491,7 +466,7 @@ int Tokenizer::getToken()
}
}
- if (m_preprocessorSkipping.count() > 1) {
+ if (m_preprocessorSkipping.size() > 1) {
m_tokLoc.warning(QStringLiteral("Expected #endif before end of file"));
// clear it out or we get an infinite loop!
while (!m_preprocessorSkipping.isEmpty()) {
@@ -507,16 +482,17 @@ int Tokenizer::getToken()
void Tokenizer::initialize()
{
Config &config = Config::instance();
- QString versionSym = config.getString(CONFIG_VERSIONSYM);
-
- QString sourceEncoding = config.getString(CONFIG_SOURCEENCODING);
- if (sourceEncoding.isEmpty())
- sourceEncoding = QLatin1String("UTF-8");
- sourceDecoder = QStringDecoder(sourceEncoding.toUtf8());
- if (!sourceDecoder.isValid()) {
- qWarning() << "Source encoding" << sourceEncoding << "is not supported. Using UTF-8.";
- sourceDecoder = QStringDecoder::Utf8;
+ QString versionSym = config.get(CONFIG_VERSIONSYM).asString();
+ const QLatin1String defaultEncoding("UTF-8");
+
+ QString sourceEncoding = config.get(CONFIG_SOURCEENCODING).asString(defaultEncoding);
+ if (!QStringConverter::encodingForName(sourceEncoding.toUtf8().constData())) {
+ Location().warning(QStringLiteral("Source encoding '%1' not supported, using '%2' as default.")
+ .arg(sourceEncoding, defaultEncoding));
+ sourceEncoding = defaultEncoding;
}
+ sourceDecoder = QStringDecoder(sourceEncoding.toUtf8().constData());
+ Q_ASSERT(sourceDecoder.isValid());
comment = new QRegularExpression("/(?:\\*.*\\*/|/.*\n|/[^\n]*$)", QRegularExpression::InvertedGreedinessOption);
versionX = new QRegularExpression("$cannot possibly match^");
@@ -525,10 +501,11 @@ void Tokenizer::initialize()
+ ")[ \t]+\"([^\"]*)\"[ \t]*$");
definedX = new QRegularExpression("^defined ?\\(?([A-Z_0-9a-z]+) ?\\)?$");
- QStringList d = config.getStringList(CONFIG_DEFINES);
+ QStringList d{config.get(CONFIG_DEFINES).asStringList()};
d += "qdoc";
defines = new QRegularExpression(QRegularExpression::anchoredPattern(d.join('|')));
- falsehoods = new QRegularExpression(QRegularExpression::anchoredPattern(config.getStringList(CONFIG_FALSEHOODS).join('|')));
+ falsehoods = new QRegularExpression(QRegularExpression::anchoredPattern(
+ config.get(CONFIG_FALSEHOODS).asStringList().join('|')));
/*
The keyword hash table is always cleared before any words are inserted.
@@ -539,16 +516,18 @@ void Tokenizer::initialize()
ignoredTokensAndDirectives = new QHash<QByteArray, bool>;
- const QStringList tokens =
- config.getStringList(LANGUAGE_CPP + Config::dot + CONFIG_IGNORETOKENS);
+ const QStringList tokens{config.get(LANGUAGE_CPP
+ + Config::dot
+ + CONFIG_IGNORETOKENS).asStringList()};
for (const auto &token : tokens) {
const QByteArray tb = token.toLatin1();
ignoredTokensAndDirectives->insert(tb, false);
insertKwordIntoHash(tb.data(), -1);
}
- const QStringList directives =
- config.getStringList(LANGUAGE_CPP + Config::dot + CONFIG_IGNOREDIRECTIVES);
+ const QStringList directives{config.get(LANGUAGE_CPP
+ + Config::dot
+ + CONFIG_IGNOREDIRECTIVES).asStringList()};
for (const auto &directive : directives) {
const QByteArray db = directive.toLatin1();
ignoredTokensAndDirectives->insert(db, true);
@@ -757,7 +736,7 @@ bool Tokenizer::isTrue(const QString &condition)
X && Y || Z // the or
(X || Y) && Z // the and
*/
- for (int i = 0; i < condition.length() - 1; i++) {
+ for (int i = 0; i < condition.size() - 1; i++) {
QChar ch = condition[i];
if (ch == QChar('(')) {
parenDepth++;
@@ -787,7 +766,7 @@ bool Tokenizer::isTrue(const QString &condition)
if (t[0] == QChar('!'))
return !isTrue(t.mid(1));
if (t[0] == QChar('(') && t.endsWith(QChar(')')))
- return isTrue(t.mid(1, t.length() - 2));
+ return isTrue(t.mid(1, t.size() - 2));
auto match = definedX->match(t);
if (match.hasMatch())
diff --git a/src/qdoc/tokenizer.h b/src/qdoc/qdoc/src/qdoc/tokenizer.h
index a7b1728fb..d5669dfb7 100644
--- a/src/qdoc/tokenizer.h
+++ b/src/qdoc/qdoc/src/qdoc/tokenizer.h
@@ -1,30 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the tools applications of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#ifndef TOKENIZER_H
#define TOKENIZER_H
@@ -140,6 +115,8 @@ private:
inline int getChar()
{
+ using namespace Qt::StringLiterals;
+
if (m_ch == EOF)
return EOF;
if (m_lexLen < yyLexBufSize - 1) {
@@ -147,8 +124,8 @@ private:
m_lex[m_lexLen] = '\0';
} else if (!token_too_long_warning_was_issued) {
location().warning(
- u"The content is too long.\n"_qs,
- u"The maximum amount of characters for this content is %1.\n"_qs.arg(yyLexBufSize) +
+ u"The content is too long.\n"_s,
+ u"The maximum amount of characters for this content is %1.\n"_s.arg(yyLexBufSize) +
"Consider splitting it or reducing its size."
);
diff --git a/src/qdoc/qdoc/src/qdoc/topic.h b/src/qdoc/qdoc/src/qdoc/topic.h
new file mode 100644
index 000000000..1f9646864
--- /dev/null
+++ b/src/qdoc/qdoc/src/qdoc/topic.h
@@ -0,0 +1,29 @@
+// Copyright (C) 2020 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+#ifndef TOPIC_H
+#define TOPIC_H
+
+QT_BEGIN_NAMESPACE
+
+struct Topic
+{
+public:
+ Topic() = default;
+ Topic(QString &t, QString a) : m_topic(t), m_args(std::move(a)) { }
+ ~Topic() = default;
+
+ [[nodiscard]] bool isEmpty() const { return m_topic.isEmpty(); }
+ void clear()
+ {
+ m_topic.clear();
+ m_args.clear();
+ }
+
+ QString m_topic {};
+ QString m_args {};
+};
+typedef QList<Topic> TopicList;
+
+QT_END_NAMESPACE
+
+#endif // TOPIC_H
diff --git a/src/qdoc/tree.cpp b/src/qdoc/qdoc/src/qdoc/tree.cpp
index 5b73bd717..5c8f7d6d1 100644
--- a/src/qdoc/tree.cpp
+++ b/src/qdoc/qdoc/src/qdoc/tree.cpp
@@ -1,30 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the tools applications of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "tree.h"
@@ -39,7 +14,6 @@
#include "qdocdatabase.h"
#include "text.h"
#include "typedefnode.h"
-#include "usingclause.h"
QT_BEGIN_NAMESPACE
@@ -69,8 +43,7 @@ QT_BEGIN_NAMESPACE
as it appears in the qdocconf file.
*/
Tree::Tree(const QString &camelCaseModuleName, QDocDatabase *qdb)
- : m_treeHasBeenAnalyzed(false),
- m_camelCaseModuleName(camelCaseModuleName),
+ : m_camelCaseModuleName(camelCaseModuleName),
m_physicalModuleName(camelCaseModuleName.toLower()),
m_qdb(qdb),
m_root(nullptr, QString())
@@ -80,24 +53,19 @@ Tree::Tree(const QString &camelCaseModuleName, QDocDatabase *qdb)
}
/*!
- Destroys the Tree. The root node is a data member
- of this object, so its destructor is called. The
- destructor of each child node is called, and these
- destructors are recursive. Thus the entire tree is
- destroyed.
+ Destroys the Tree.
There are two maps of targets, keywords, and contents.
- One map is indexed by ref, the other by title. The ref
- is just the canonical form of the title. Both maps
+ One map is indexed by ref, the other by title. Both maps
use the same set of TargetRec objects as the values,
- so the destructor only deletes the values from one of
- the maps. Then it clears both maps.
+ so we only need to delete the values from one of them.
+
+ The Node instances themselves are destroyed by the root
+ node's (\c m_root) destructor.
*/
Tree::~Tree()
{
- for (auto i = m_nodesByTargetRef.begin(); i != m_nodesByTargetRef.end(); ++i) {
- delete i.value();
- }
+ qDeleteAll(m_nodesByTargetRef);
m_nodesByTargetRef.clear();
m_nodesByTargetTitle.clear();
}
@@ -252,6 +220,10 @@ void Tree::resolvePropertyOverriddenFromPtrs(Aggregate *n)
}
/*!
+ Resolves access functions associated with each PropertyNode stored
+ in \c m_unresolvedPropertyMap, and adds them into the property node.
+ This allows the property node to list the access functions when
+ generating their documentation.
*/
void Tree::resolveProperties()
{
@@ -259,10 +231,11 @@ void Tree::resolveProperties()
propEntry != m_unresolvedPropertyMap.constEnd(); ++propEntry) {
PropertyNode *property = propEntry.key();
Aggregate *parent = property->parent();
- QString getterName = (*propEntry)[PropertyNode::Getter];
- QString setterName = (*propEntry)[PropertyNode::Setter];
- QString resetterName = (*propEntry)[PropertyNode::Resetter];
- QString notifierName = (*propEntry)[PropertyNode::Notifier];
+ QString getterName = (*propEntry)[PropertyNode::FunctionRole::Getter];
+ QString setterName = (*propEntry)[PropertyNode::FunctionRole::Setter];
+ QString resetterName = (*propEntry)[PropertyNode::FunctionRole::Resetter];
+ QString notifierName = (*propEntry)[PropertyNode::FunctionRole::Notifier];
+ QString bindableName = (*propEntry)[PropertyNode::FunctionRole::Bindable];
for (auto it = parent->constBegin(); it != parent->constEnd(); ++it) {
if ((*it)->isFunction()) {
@@ -270,13 +243,15 @@ void Tree::resolveProperties()
if (function->access() == property->access()
&& (function->status() == property->status() || function->doc().isEmpty())) {
if (function->name() == getterName) {
- property->addFunction(function, PropertyNode::Getter);
+ property->addFunction(function, PropertyNode::FunctionRole::Getter);
} else if (function->name() == setterName) {
- property->addFunction(function, PropertyNode::Setter);
+ property->addFunction(function, PropertyNode::FunctionRole::Setter);
} else if (function->name() == resetterName) {
- property->addFunction(function, PropertyNode::Resetter);
+ property->addFunction(function, PropertyNode::FunctionRole::Resetter);
} else if (function->name() == notifierName) {
- property->addSignal(function, PropertyNode::Notifier);
+ property->addSignal(function, PropertyNode::FunctionRole::Notifier);
+ } else if (function->name() == bindableName) {
+ property->addFunction(function, PropertyNode::FunctionRole::Bindable);
}
}
}
@@ -304,39 +279,63 @@ void Tree::resolveCppToQmlLinks()
const NodeList &children = m_root.childNodes();
for (auto *child : children) {
- if (child->isQmlType() || child->isJsType()) {
+ if (child->isQmlType()) {
auto *qcn = static_cast<QmlTypeNode *>(child);
auto *cn = const_cast<ClassNode *>(qcn->classNode());
if (cn)
- cn->setQmlElement(qcn);
+ cn->insertQmlNativeType(qcn);
}
}
}
/*!
- For each C++ class node, resolve any \c using clauses
- that appeared in the class declaration.
+ For each \a aggregate, recursively set the \\since version based on
+ \\since information from the associated physical or logical module.
+ That is, C++ and QML types inherit the \\since of their module,
+ unless that command is explicitly used in the type documentation.
- For type aliases, resolve the aliased node.
- */
-void Tree::resolveUsingClauses(Aggregate *parent)
+ In addition, resolve the since information for individual enum
+ values.
+*/
+void Tree::resolveSince(Aggregate &aggregate)
{
- if (!parent)
- parent = &m_root;
- for (auto *child : parent->childNodes()) {
- if (child->isClassNode()) {
- auto *cn = static_cast<ClassNode *>(child);
- QList<UsingClause> &usingClauses = cn->usingClauses();
- for (auto &usingClause : usingClauses) {
- if (usingClause.node() == nullptr) {
- const Node *n = m_qdb->findFunctionNode(usingClause.signature(), cn, Node::CPP);
- if (n != nullptr)
- usingClause.setNode(n);
- }
- }
+ for (auto *child : aggregate.childNodes()) {
+ // Order matters; resolve since-clauses in enum values
+ // first as EnumNode is not an Aggregate
+ if (child->isEnumType())
+ resolveEnumValueSince(static_cast<EnumNode&>(*child));
+ if (!child->isAggregate())
+ continue;
+ if (!child->since().isEmpty())
+ continue;
+
+ if (const auto collectionNode = m_qdb->getModuleNode(child))
+ child->setSince(collectionNode->since());
+
+ resolveSince(static_cast<Aggregate&>(*child));
+ }
+}
+
+/*!
+ Resolve since information for values of enum node \a en.
+
+ Enum values are not derived from Node, but they can have
+ 'since' information associated with them. Since-strings
+ for each enum item are initially stored in the Doc
+ instance of EnumNode as SinceTag atoms; parse the doc
+ and store them into each EnumItem.
+*/
+void Tree::resolveEnumValueSince(EnumNode &en)
+{
+ const QStringList enumItems{en.doc().enumItemNames()};
+ const Atom *atom = en.doc().body().firstAtom();
+ while ((atom = atom->find(Atom::ListTagLeft))) {
+ if (atom = atom->next(); !atom)
+ break;
+ if (auto val = atom->string(); enumItems.contains(val)) {
+ if (atom = atom->next(); atom && atom->next(Atom::SinceTagLeft))
+ en.setSince(val, atom->next()->next()->string());
}
- if (child->genus() == Node::CPP && child->isAggregate())
- resolveUsingClauses(static_cast<Aggregate *>(child));
}
}
@@ -441,83 +440,90 @@ Node *Tree::findNodeRecursive(const QStringList &path, int pathIndex, const Node
begins at the root.
The \a flags can indicate whether to search base classes and/or
- the enum values in enum types. \a genus can be a further restriction
- on what kind of node is an acceptible match, i.e. CPP or QML.
-
- If a matching node is found, \a ref is an output parameter that
- is set to the HTML reference to use for the link.
+ the enum values in enum types. \a genus further restricts
+ the type of nodes to match, i.e. CPP or QML.
+
+ If a matching node is found, \a ref is set to the HTML fragment
+ identifier to use for the link. On return, the optional
+ \a targetType parameter contains the type of the resolved
+ target; section title (Contents), \\target, \\keyword, or other
+ (Unknown).
*/
const Node *Tree::findNodeForTarget(const QStringList &path, const QString &target,
const Node *start, int flags, Node::Genus genus,
- QString &ref) const
+ QString &ref, TargetRec::TargetType *targetType) const
{
const Node *node = nullptr;
- if ((genus == Node::DontCare) || (genus == Node::DOC)) {
- node = findPageNodeByTitle(path.at(0));
- if (node) {
- if (!target.isEmpty()) {
- ref = getRef(target, node);
- if (ref.isEmpty())
- node = nullptr;
- }
- if (node)
+
+ // Retrieves and sets ref from target for Node n.
+ // Returns n on valid (or empty) target, or nullptr on an invalid target.
+ auto set_ref_from_target = [this, &ref, &target](const Node *n) -> const Node* {
+ if (!target.isEmpty()) {
+ if (ref = getRef(target, n); ref.isEmpty())
+ return nullptr;
+ }
+ return n;
+ };
+
+ if (genus == Node::DontCare || genus == Node::DOC) {
+ if (node = findPageNodeByTitle(path.at(0)); node) {
+ if (node = set_ref_from_target(node); node)
return node;
}
}
- node = findUnambiguousTarget(path.join(QLatin1String("::")), genus, ref);
- if (node) {
- if (!target.isEmpty()) {
- ref = getRef(target, node);
- if (ref.isEmpty())
- node = nullptr;
+ const TargetRec *result = findUnambiguousTarget(path.join(QLatin1String("::")), genus);
+ if (result) {
+ ref = result->m_ref;
+ if (node = set_ref_from_target(result->m_node); node) {
+ // Delay returning references to section titles as we
+ // may find a better match below
+ if (result->m_type != TargetRec::Contents) {
+ if (targetType)
+ *targetType = result->m_type;
+ return node;
+ }
+ ref.clear();
}
- if (node)
- return node;
}
- const Node *current = start;
- if (current == nullptr)
- current = root();
-
+ const Node *current = start ? start : root();
/*
If the path contains one or two double colons ("::"),
- check first to see if the first two path strings refer
- to a QML element. If they do, path[0] will be the QML
- module identifier, and path[1] will be the QML type.
- If the answer is yes, the reference identifies a QML
- type node.
+ check if the first two path elements refer to a QML type.
+ If so, path[0] is QML module identifier, and path[1] is
+ the type.
*/
int path_idx = 0;
- if (((genus == Node::QML) || (genus == Node::DontCare)) && (path.size() >= 2)
- && !path[0].isEmpty()) {
- QmlTypeNode *qcn = lookupQmlType(QString(path[0] + "::" + path[1]));
- if (qcn) {
+ if ((genus == Node::QML || genus == Node::DontCare)
+ && path.size() >= 2 && !path[0].isEmpty()) {
+ if (auto *qcn = lookupQmlType(path.sliced(0, 2).join(QLatin1String("::"))); qcn) {
current = qcn;
- if (path.size() == 2) {
- if (!target.isEmpty()) {
- ref = getRef(target, current);
- if (!ref.isEmpty())
- return current;
- return nullptr;
- } else
- return current;
- }
+ // No further elements in the path, return the type
+ if (path.size() == 2)
+ return set_ref_from_target(qcn);
path_idx = 2;
}
}
- while (current != nullptr) {
- if (current->isAggregate()) { // Should this be isPageNode() ???
- const Node *node =
- matchPathAndTarget(path, path_idx, target, current, flags, genus, ref);
- if (node)
- return node;
+ while (current) {
+ if (current->isAggregate()) {
+ if (const Node *match = matchPathAndTarget(
+ path, path_idx, target, current, flags, genus, ref);
+ match != nullptr)
+ return match;
}
current = current->parent();
path_idx = 0;
}
- return nullptr;
+
+ if (node && result) {
+ // Fall back to previously found section title
+ ref = result->m_ref;
+ if (targetType)
+ *targetType = result->m_type;
+ }
+ return node;
}
/*!
@@ -564,7 +570,7 @@ const Node *Tree::matchPathAndTarget(const QStringList &path, int idx, const QSt
if (node->isAggregate()) {
NodeVector nodes;
static_cast<const Aggregate *>(node)->findChildren(name, nodes);
- for (const auto *child : qAsConst(nodes)) {
+ for (const auto *child : std::as_const(nodes)) {
if (genus != Node::DontCare && !(genus & child->genus()))
continue;
const Node *t = matchPathAndTarget(path, idx + 1, target, child, flags, genus, ref);
@@ -712,7 +718,7 @@ QString Tree::getRef(const QString &target, const Node *node) const
++it;
} while (it != m_nodesByTargetTitle.constEnd() && it.key() == target);
}
- QString key = Doc::canonicalTitle(target);
+ QString key = Utilities::asAsciiPrintable(target);
it = m_nodesByTargetRef.constFind(key);
if (it != m_nodesByTargetRef.constEnd()) {
do {
@@ -739,135 +745,166 @@ void Tree::insertTarget(const QString &name, const QString &title, TargetRec::Ta
}
/*!
+ \internal
+
+ \a root is the root node of the tree to resolve targets for. This function
+ traverses the tree starting from the root node and processes each child
+ node. If the child node is an aggregate node, this function is called
+ recursively on the child node.
*/
void Tree::resolveTargets(Aggregate *root)
{
for (auto *child : root->childNodes()) {
- if (child->isTextPageNode()) {
- auto *node = static_cast<PageNode *>(child);
- QString key = node->title();
- if (!key.isEmpty()) {
- if (key.contains(QChar(' ')))
- key = Doc::canonicalTitle(key);
- QList<PageNode *> nodes = m_pageNodesByTitle.values(key);
- bool alreadyThere = false;
- if (!nodes.empty()) {
- for (const auto &node_ : nodes) {
- if (node_->isExternalPage()) {
- if (node->name() == node_->name()) {
- alreadyThere = true;
- break;
- }
- }
- }
- }
- if (!alreadyThere)
- m_pageNodesByTitle.insert(key, node);
- }
- }
+ addToPageNodeByTitleMap(child);
+ populateTocSectionTargetMap(child);
+ addKeywordsToTargetMaps(child);
+ addTargetsToTargetMap(child);
- if (child->doc().hasTableOfContents()) {
- const QList<Atom *> &toc = child->doc().tableOfContents();
- for (Atom *i : toc) {
- QString ref = refForAtom(i);
- QString title = Text::sectionHeading(i).toString();
- if (!ref.isEmpty() && !title.isEmpty()) {
- QString key = Doc::canonicalTitle(title);
- auto *target = new TargetRec(ref, TargetRec::Contents, child, 3);
- m_nodesByTargetRef.insert(key, target);
- m_nodesByTargetTitle.insert(title, target);
- }
- }
- }
- if (child->doc().hasKeywords()) {
- const QList<Atom *> &keywords = child->doc().keywords();
- for (Atom *i : keywords) {
- QString ref = refForAtom(i);
- QString title = i->string();
- if (!ref.isEmpty() && !title.isEmpty()) {
- auto *target = new TargetRec(ref, TargetRec::Keyword, child, 1);
- m_nodesByTargetRef.insert(Doc::canonicalTitle(title), target);
- m_nodesByTargetTitle.insert(title, target);
- }
- }
- }
- if (child->doc().hasTargets()) {
- const QList<Atom *> &targets = child->doc().targets();
- for (Atom *i : targets) {
- QString ref = refForAtom(i);
- QString title = i->string();
- if (!ref.isEmpty() && !title.isEmpty()) {
- QString key = Doc::canonicalTitle(title);
- auto *target = new TargetRec(ref, TargetRec::Target, child, 2);
- m_nodesByTargetRef.insert(key, target);
- m_nodesByTargetTitle.insert(title, target);
- }
- }
- }
if (child->isAggregate())
resolveTargets(static_cast<Aggregate *>(child));
}
}
/*!
- This function searches for a \a target anchor node. If it
- finds one, it sets \a ref and returns the found node.
+ \internal
+
+ Updates the target maps for targets associated with the given \a node.
*/
-const Node *Tree::findUnambiguousTarget(const QString &target, Node::Genus genus,
- QString &ref) const
-{
- int numBestTargets = 0;
- TargetRec *bestTarget = nullptr;
- QList<TargetRec *> bestTargetList;
+void Tree::addTargetsToTargetMap(Node *node) {
+ if (!node || !node->doc().hasTargets())
+ return;
- QString key = target;
- for (auto it = m_nodesByTargetTitle.find(key); it != m_nodesByTargetTitle.constEnd(); ++it) {
- if (it.key() != key)
- break;
- TargetRec *candidate = it.value();
- if ((genus == Node::DontCare) || (genus & candidate->genus())) {
- if (!bestTarget || (candidate->m_priority < bestTarget->m_priority)) {
- bestTarget = candidate;
- bestTargetList.clear();
- bestTargetList.append(candidate);
- numBestTargets = 1;
- } else if (candidate->m_priority == bestTarget->m_priority) {
- bestTargetList.append(candidate);
- ++numBestTargets;
- }
+ for (Atom *i : std::as_const(node->doc().targets())) {
+ const QString ref = refForAtom(i);
+ const QString title = i->string();
+ if (!ref.isEmpty() && !title.isEmpty()) {
+ QString key = Utilities::asAsciiPrintable(title);
+ auto *target = new TargetRec(ref, TargetRec::Target, node, 2);
+ m_nodesByTargetRef.insert(key, target);
+ m_nodesByTargetTitle.insert(title, target);
}
}
- if (bestTarget) {
- ref = bestTarget->m_ref;
- return bestTarget->m_node;
- }
+}
- numBestTargets = 0;
- bestTarget = nullptr;
- key = Doc::canonicalTitle(target);
- for (auto it = m_nodesByTargetRef.find(key); it != m_nodesByTargetRef.constEnd(); ++it) {
- if (it.key() != key)
- break;
- TargetRec *candidate = it.value();
- if ((genus == Node::DontCare) || (genus & candidate->genus())) {
- if (!bestTarget || (candidate->m_priority < bestTarget->m_priority)) {
- bestTarget = candidate;
- bestTargetList.clear();
- bestTargetList.append(candidate);
- numBestTargets = 1;
- } else if (candidate->m_priority == bestTarget->m_priority) {
- bestTargetList.append(candidate);
- ++numBestTargets;
- }
+/*!
+ \internal
+
+ Updates the target maps for keywords associated with the given \a node.
+ */
+void Tree::addKeywordsToTargetMaps(Node *node) {
+ if (!node->doc().hasKeywords())
+ return;
+
+ for (Atom *i : std::as_const(node->doc().keywords())) {
+ QString ref = refForAtom(i);
+ QString title = i->string();
+ if (!ref.isEmpty() && !title.isEmpty()) {
+ auto *target = new TargetRec(ref, TargetRec::Keyword, node, 1);
+ m_nodesByTargetRef.insert(Utilities::asAsciiPrintable(title), target);
+ m_nodesByTargetTitle.insert(title, target);
}
}
- if (bestTarget) {
- ref = bestTarget->m_ref;
- return bestTarget->m_node;
+}
+
+/*!
+ \internal
+
+ Populates the map of targets for each section in the table of contents for
+ the given \a node while ensuring that each target has a unique reference.
+ */
+void Tree::populateTocSectionTargetMap(Node *node) {
+ if (!node || !node->doc().hasTableOfContents())
+ return;
+
+ QStack<Atom *> tocLevels;
+ QSet<QString> anchors;
+
+ qsizetype index = 0;
+
+ for (Atom *atom: std::as_const(node->doc().tableOfContents())) {
+ while (!tocLevels.isEmpty() && tocLevels.top()->string().toInt() >= atom->string().toInt())
+ tocLevels.pop();
+
+ tocLevels.push(atom);
+
+ QString ref = refForAtom(atom);
+ const QString &title = Text::sectionHeading(atom).toString();
+ if (ref.isEmpty() || title.isEmpty())
+ continue;
+
+ if (anchors.contains(ref)) {
+ QStringList refParts;
+ for (const auto tocLevel : tocLevels)
+ refParts << refForAtom(tocLevel);
+
+ refParts << QString::number(index);
+ ref = refParts.join(QLatin1Char('-'));
+ }
+
+ anchors.insert(ref);
+ if (atom->next(Atom::SectionHeadingLeft))
+ atom->next()->append(ref);
+ ++index;
+
+ const QString &key = Utilities::asAsciiPrintable(title);
+ auto *target = new TargetRec(ref, TargetRec::Contents, node, 3);
+ m_nodesByTargetRef.insert(key, target);
+ m_nodesByTargetTitle.insert(title, target);
}
+}
- ref.clear();
- return nullptr;
+/*!
+ \internal
+
+ Checks if the \a node's title is registered in the page nodes by title map.
+ If not, it stores the page node in the map.
+ */
+void Tree::addToPageNodeByTitleMap(Node *node) {
+ if (!node || !node->isTextPageNode())
+ return;
+
+ auto *pageNode = static_cast<PageNode *>(node);
+ QString key = pageNode->title();
+ if (key.isEmpty())
+ return;
+
+ if (key.contains(QChar(' ')))
+ key = Utilities::asAsciiPrintable(key);
+ const QList<PageNode *> nodes = m_pageNodesByTitle.values(key);
+
+ bool alreadyThere = std::any_of(nodes.cbegin(), nodes.cend(), [&](const auto &knownNode) {
+ return knownNode->isExternalPage() && knownNode->name() == pageNode->name();
+ });
+
+ if (!alreadyThere)
+ m_pageNodesByTitle.insert(key, pageNode);
+}
+
+/*!
+ Searches for a \a target anchor, matching the given \a genus, and returns
+ the associated TargetRec instance.
+ */
+const TargetRec *Tree::findUnambiguousTarget(const QString &target, Node::Genus genus) const
+{
+ auto findBestCandidate = [&](const TargetMap &tgtMap, const QString &key) {
+ TargetRec *best = nullptr;
+ auto [it, end] = tgtMap.equal_range(key);
+ while (it != end) {
+ TargetRec *candidate = it.value();
+ if ((genus == Node::DontCare) || (genus & candidate->genus())) {
+ if (!best || (candidate->m_priority < best->m_priority))
+ best = candidate;
+ }
+ ++it;
+ }
+ return best;
+ };
+
+ TargetRec *bestTarget = findBestCandidate(m_nodesByTargetTitle, target);
+ if (!bestTarget)
+ bestTarget = findBestCandidate(m_nodesByTargetRef, Utilities::asAsciiPrintable(target));
+
+ return bestTarget;
}
/*!
@@ -877,7 +914,7 @@ const PageNode *Tree::findPageNodeByTitle(const QString &title) const
{
PageNodeMultiMap::const_iterator it;
if (title.contains(QChar(' ')))
- it = m_pageNodesByTitle.constFind(Doc::canonicalTitle(title));
+ it = m_pageNodesByTitle.constFind(Utilities::asAsciiPrintable(title));
else
it = m_pageNodesByTitle.constFind(title);
if (it != m_pageNodesByTitle.constEnd()) {
@@ -908,17 +945,27 @@ const PageNode *Tree::findPageNodeByTitle(const QString &title) const
/*!
Returns a canonical title for the \a atom, if the \a atom
- is a SectionLeft or a Target.
+ is a SectionLeft, SectionHeadingLeft, Keyword, or Target.
*/
QString Tree::refForAtom(const Atom *atom)
{
- if (atom) {
- if (atom->type() == Atom::SectionLeft)
- return Doc::canonicalTitle(Text::sectionHeading(atom).toString());
- if ((atom->type() == Atom::Target) || (atom->type() == Atom::Keyword))
- return Doc::canonicalTitle(atom->string());
+ Q_ASSERT(atom);
+
+ switch (atom->type()) {
+ case Atom::SectionLeft:
+ atom = atom->next();
+ [[fallthrough]];
+ case Atom::SectionHeadingLeft:
+ if (atom->count() == 2)
+ return atom->string(1);
+ return Utilities::asAsciiPrintable(Text::sectionHeading(atom).toString());
+ case Atom::Target:
+ [[fallthrough]];
+ case Atom::Keyword:
+ return Utilities::asAsciiPrintable(atom->string());
+ default:
+ return {};
}
- return QString();
}
/*!
@@ -952,8 +999,6 @@ CNMap *Tree::getCollectionMap(Node::NodeType type)
return &m_modules;
case Node::QmlModule:
return &m_qmlModules;
- case Node::JsModule:
- return &m_jsModules;
default:
break;
}
@@ -978,12 +1023,11 @@ CollectionNode *Tree::getCollection(const QString &name, Node::NodeType type)
}
/*!
- Find the group, module, QML module, or JavaScript module
- named \a name and return a pointer to that collection node.
- \a type specifies which kind of collection node you want.
- If a collection node with the specified \a name and \a type
- is not found, a new one is created, and the pointer to the
- new one is returned.
+ Find the group, module, or QML module named \a name and return a
+ pointer to that collection node. \a type specifies which kind of
+ collection node you want. If a collection node with the specified \a
+ name and \a type is not found, a new one is created, and the pointer
+ to the new one is returned.
If a new collection node is created, its parent is the tree
root, and the new collection node is marked \e{not seen}.
@@ -1033,15 +1077,6 @@ CollectionNode *Tree::findCollection(const QString &name, Node::NodeType type)
and the new node is marked \e{not seen}.
*/
-/*! \fn CollectionNode *Tree::findJsModule(const QString &name)
- Find the JavaScript module named \a name and return a pointer
- to it. If a matching node is not found, add a new JavaScript
- module node named \a name and return a pointer to that one.
-
- If a new JavaScript module node is added, its parent is the
- tree root, and the new node is marked \e{not seen}.
- */
-
/*! \fn CollectionNode *Tree::addGroup(const QString &name)
Looks up the group node named \a name in the collection
of all group nodes. If a match is found, a pointer to the
@@ -1066,14 +1101,6 @@ CollectionNode *Tree::findCollection(const QString &name, Node::NodeType type)
to that node is returned.
*/
-/*! \fn CollectionNode *Tree::addJsModule(const QString &name)
- Looks up the JavaScript module node named \a name in the collection
- of all JavaScript module nodes. If a match is found, a pointer to the
- node is returned. Otherwise, a new JavaScrpt module node named \a name
- is created and inserted into the collection, and the pointer
- to that node is returned.
- */
-
/*!
Looks up the group node named \a name in the collection
of all group nodes. If a match is not found, a new group
@@ -1140,37 +1167,6 @@ CollectionNode *Tree::addToQmlModule(const QString &name, Node *node)
}
/*!
- Looks up the QML module named \a name. If it isn't there,
- create it. Then append \a node to the QML module's member
- list. The parent of \a node is not changed by this function.
- Returns the pointer to the QML module node.
- */
-CollectionNode *Tree::addToJsModule(const QString &name, Node *node)
-{
- QStringList qmid;
- QStringList dotSplit;
- QStringList blankSplit = name.split(QLatin1Char(' '));
- qmid.append(blankSplit[0]);
- if (blankSplit.size() > 1) {
- qmid.append(blankSplit[0] + blankSplit[1]);
- dotSplit = blankSplit[1].split(QLatin1Char('.'));
- qmid.append(blankSplit[0] + dotSplit[0]);
- }
-
- CollectionNode *cn = findJsModule(blankSplit[0]);
- cn->addMember(node);
- node->setQmlModule(cn);
- if (node->isJsType()) {
- QmlTypeNode *n = static_cast<QmlTypeNode *>(node);
- for (int i = 0; i < qmid.size(); ++i) {
- QString key = qmid[i] + "::" + node->name();
- insertQmlType(key, n);
- }
- }
- return cn;
-}
-
-/*!
If the QML type map does not contain \a key, insert node
\a n with the specified \a key.
*/
@@ -1183,7 +1179,7 @@ void Tree::insertQmlType(const QString &key, QmlTypeNode *n)
/*!
Finds the function node with the specifried name \a path that
also has the specified \a parameters and returns a pointer to
- the the first matching function node if one is found.
+ the first matching function node if one is found.
This function begins searching the tree at \a relative for
the \l {FunctionNode} {function node} identified by \a path
@@ -1201,7 +1197,7 @@ const FunctionNode *Tree::findFunctionNode(const QStringList &path, const Parame
if (qcn == nullptr) {
QStringList p(path[1]);
Node *n = findNodeByNameAndType(p, &Node::isQmlType);
- if ((n != nullptr) && (n->isQmlType() || n->isJsType()))
+ if ((n != nullptr) && n->isQmlType())
qcn = static_cast<QmlTypeNode *>(n);
}
if (qcn != nullptr)
diff --git a/src/qdoc/tree.h b/src/qdoc/qdoc/src/qdoc/tree.h
index 850055eaf..d0bed25d6 100644
--- a/src/qdoc/tree.h
+++ b/src/qdoc/qdoc/src/qdoc/tree.h
@@ -1,30 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the tools applications of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#ifndef TREE_H
#define TREE_H
@@ -49,10 +24,10 @@ class QDocDatabase;
struct TargetRec
{
public:
- enum TargetType { Unknown, Target, Keyword, Contents, Class, Function, Page, Subtitle };
+ enum TargetType { Unknown, Target, Keyword, Contents };
TargetRec(QString name, TargetRec::TargetType type, Node *node, int priority)
- : m_node(node), m_ref(std::move(name)), m_priority(priority)
+ : m_node(node), m_ref(std::move(name)), m_type(type), m_priority(priority)
{
// Discard the dedicated ref for keywords - they always
// link to the top of the QDoc comment they appear in
@@ -65,20 +40,14 @@ public:
Node *m_node { nullptr };
QString m_ref {};
+ TargetType m_type {};
int m_priority {};
};
-struct TargetLoc
-{
-public:
- TargetLoc() = default;
-};
-
typedef QMultiMap<QString, TargetRec *> TargetMap;
typedef QMultiMap<QString, PageNode *> PageNodeMultiMap;
typedef QMap<QString, QmlTypeNode *> QmlTypeMap;
typedef QMultiMap<QString, const ExampleNode *> ExampleNodeMap;
-typedef QList<TargetLoc *> TargetList;
class Tree
{
@@ -93,6 +62,9 @@ private: // Note the constructor and destructor are private.
~Tree();
public: // Of necessity, a few public functions remain.
+ [[nodiscard]] Node *findNodeByNameAndType(const QStringList &path,
+ bool (Node::*isMatch)() const) const;
+
[[nodiscard]] const QString &camelCaseModuleName() const { return m_camelCaseModuleName; }
[[nodiscard]] const QString &physicalModuleName() const { return m_physicalModuleName; }
[[nodiscard]] const QString &indexFileName() const { return m_indexFileName; }
@@ -102,6 +74,7 @@ public: // Of necessity, a few public functions remain.
void appendProxy(ProxyNode *t) { m_proxies.append(t); }
void addToDontDocumentMap(QString &arg);
void markDontDocumentNodes();
+ static QString refForAtom(const Atom *atom);
private: // The rest of the class is private.
Aggregate *findAggregate(const QString &name);
@@ -113,7 +86,8 @@ private: // The rest of the class is private.
Node *findNodeRecursive(const QStringList &path, int pathIndex, const Node *start,
bool (Node::*)() const) const;
const Node *findNodeForTarget(const QStringList &path, const QString &target, const Node *node,
- int flags, Node::Genus genus, QString &ref) const;
+ int flags, Node::Genus genus, QString &ref,
+ TargetRec::TargetType *targetType = nullptr) const;
const Node *matchPathAndTarget(const QStringList &path, int idx, const QString &target,
const Node *node, int flags, Node::Genus genus,
QString &ref) const;
@@ -121,15 +95,18 @@ private: // The rest of the class is private.
const Node *findNode(const QStringList &path, const Node *relative, int flags,
Node::Genus genus) const;
- [[nodiscard]] Node *findNodeByNameAndType(const QStringList &path,
- bool (Node::*isMatch)() const) const;
Aggregate *findRelatesNode(const QStringList &path);
const Node *findEnumNode(const Node *node, const Node *aggregate, const QStringList &path, int offset) const;
QString getRef(const QString &target, const Node *node) const;
void insertTarget(const QString &name, const QString &title, TargetRec::TargetType type,
Node *node, int priority);
void resolveTargets(Aggregate *root);
- const Node *findUnambiguousTarget(const QString &target, Node::Genus genus, QString &ref) const;
+ void addToPageNodeByTitleMap(Node *node);
+ void populateTocSectionTargetMap(Node *node);
+ void addKeywordsToTargetMaps(Node *node);
+ void addTargetsToTargetMap(Node *node);
+
+ const TargetRec *findUnambiguousTarget(const QString &target, Node::Genus genus) const;
[[nodiscard]] const PageNode *findPageNodeByTitle(const QString &title) const;
void addPropertyFunction(PropertyNode *property, const QString &funcName,
@@ -138,19 +115,18 @@ private: // The rest of the class is private.
void resolvePropertyOverriddenFromPtrs(Aggregate *n);
void resolveProperties();
void resolveCppToQmlLinks();
- void resolveUsingClauses(Aggregate *parent = nullptr);
+ void resolveSince(Aggregate &aggregate);
+ void resolveEnumValueSince(EnumNode &en);
void removePrivateAndInternalBases(NamespaceNode *rootNode);
NamespaceNode *root() { return &m_root; }
[[nodiscard]] const NamespaceNode *root() const { return &m_root; }
ClassList allBaseClasses(const ClassNode *classe) const;
- QString refForAtom(const Atom *atom);
CNMap *getCollectionMap(Node::NodeType type);
[[nodiscard]] const CNMap &groups() const { return m_groups; }
[[nodiscard]] const CNMap &modules() const { return m_modules; }
[[nodiscard]] const CNMap &qmlModules() const { return m_qmlModules; }
- [[nodiscard]] const CNMap &jsModules() const { return m_jsModules; }
CollectionNode *getCollection(const QString &name, Node::NodeType type);
CollectionNode *findCollection(const QString &name, Node::NodeType type);
@@ -161,41 +137,28 @@ private: // The rest of the class is private.
{
return findCollection(name, Node::QmlModule);
}
- CollectionNode *findJsModule(const QString &name)
- {
- return findCollection(name, Node::JsModule);
- }
CollectionNode *addGroup(const QString &name) { return findGroup(name); }
CollectionNode *addModule(const QString &name) { return findModule(name); }
CollectionNode *addQmlModule(const QString &name) { return findQmlModule(name); }
- CollectionNode *addJsModule(const QString &name) { return findJsModule(name); }
CollectionNode *addToGroup(const QString &name, Node *node);
CollectionNode *addToModule(const QString &name, Node *node);
CollectionNode *addToQmlModule(const QString &name, Node *node);
- CollectionNode *addToJsModule(const QString &name, Node *node);
[[nodiscard]] QmlTypeNode *lookupQmlType(const QString &name) const
{
return m_qmlTypeMap.value(name);
}
- [[nodiscard]] Aggregate *lookupQmlBasicType(const QString &name) const
- {
- return m_qmlTypeMap.value(name);
- }
void insertQmlType(const QString &key, QmlTypeNode *n);
void addExampleNode(ExampleNode *n) { m_exampleNodeMap.insert(n->title(), n); }
ExampleNodeMap &exampleNodeMap() { return m_exampleNodeMap; }
void setIndexFileName(const QString &t) { m_indexFileName = t; }
- [[nodiscard]] bool treeHasBeenAnalyzed() const { return m_treeHasBeenAnalyzed; }
- void setTreeHasBeenAnalyzed() { m_treeHasBeenAnalyzed = true; }
FunctionNode *findFunctionNodeForTag(const QString &tag, Aggregate *parent = nullptr);
FunctionNode *findMacroNode(const QString &t, const Aggregate *parent = nullptr);
private:
- bool m_treeHasBeenAnalyzed {};
QString m_camelCaseModuleName {};
QString m_physicalModuleName {};
QString m_indexFileName {};
@@ -209,7 +172,6 @@ private:
CNMap m_groups {};
CNMap m_modules {};
CNMap m_qmlModules {};
- CNMap m_jsModules {};
QmlTypeMap m_qmlTypeMap {};
ExampleNodeMap m_exampleNodeMap {};
NodeList m_proxies {};
diff --git a/src/qdoc/qdoc/src/qdoc/typedefnode.cpp b/src/qdoc/qdoc/src/qdoc/typedefnode.cpp
new file mode 100644
index 000000000..997e570d3
--- /dev/null
+++ b/src/qdoc/qdoc/src/qdoc/typedefnode.cpp
@@ -0,0 +1,55 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "typedefnode.h"
+
+#include "aggregate.h"
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class TypedefNode
+ */
+
+/*!
+ */
+void TypedefNode::setAssociatedEnum(const EnumNode *enume)
+{
+ m_associatedEnum = enume;
+}
+
+/*!
+ Clone this node on the heap and make the clone a child of
+ \a parent.
+
+ Returns the pointer to the clone.
+ */
+Node *TypedefNode::clone(Aggregate *parent)
+{
+ auto *tn = new TypedefNode(*this); // shallow copy
+ tn->setParent(nullptr);
+ parent->addChild(tn);
+
+ return tn;
+}
+
+/*!
+ \class TypeAliasNode
+ */
+
+/*!
+ Clone this node on the heap and make the clone a child of
+ \a parent.
+
+ Returns the pointer to the clone.
+ */
+Node *TypeAliasNode::clone(Aggregate *parent)
+{
+ auto *tan = new TypeAliasNode(*this); // shallow copy
+ tan->setParent(nullptr);
+ parent->addChild(tan);
+
+ return tan;
+}
+
+QT_END_NAMESPACE
diff --git a/src/qdoc/qdoc/src/qdoc/typedefnode.h b/src/qdoc/qdoc/src/qdoc/typedefnode.h
new file mode 100644
index 000000000..839cd6a0b
--- /dev/null
+++ b/src/qdoc/qdoc/src/qdoc/typedefnode.h
@@ -0,0 +1,54 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#ifndef TYPEDEFNODE_H
+#define TYPEDEFNODE_H
+
+#include "enumnode.h"
+#include "node.h"
+
+#include <QtCore/qglobal.h>
+#include <QtCore/qstring.h>
+
+QT_BEGIN_NAMESPACE
+
+class Aggregate;
+
+class TypedefNode : public Node
+{
+public:
+ TypedefNode(Aggregate *parent, const QString &name, NodeType type = Typedef)
+ : Node(type, parent, name)
+ {
+ }
+
+ bool hasAssociatedEnum() const { return m_associatedEnum != nullptr; }
+ const EnumNode *associatedEnum() const { return m_associatedEnum; }
+ Node *clone(Aggregate *parent) override;
+
+private:
+ void setAssociatedEnum(const EnumNode *t);
+
+ friend class EnumNode;
+
+ const EnumNode *m_associatedEnum { nullptr };
+};
+
+class TypeAliasNode : public TypedefNode
+{
+public:
+ TypeAliasNode(Aggregate *parent, const QString &name, const QString &aliasedType)
+ : TypedefNode(parent, name, NodeType::TypeAlias), m_aliasedType(aliasedType)
+ {
+ }
+
+ const QString &aliasedType() const { return m_aliasedType; }
+ Node *clone(Aggregate *parent) override;
+
+private:
+ QString m_aliasedType {};
+};
+
+QT_END_NAMESPACE
+
+#endif // TYPEDEFNODE_H
diff --git a/src/qdoc/utilities.cpp b/src/qdoc/qdoc/src/qdoc/utilities.cpp
index f82282b9b..2825804d6 100644
--- a/src/qdoc/utilities.cpp
+++ b/src/qdoc/qdoc/src/qdoc/utilities.cpp
@@ -1,32 +1,9 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the tools applications of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include <QtCore/qprocess.h>
+#include <QCryptographicHash>
+#include "location.h"
#include "utilities.h"
QT_BEGIN_NAMESPACE
@@ -104,6 +81,77 @@ QString comma(qsizetype wordPosition, qsizetype numberOfWords)
}
/*!
+ \brief Returns an ascii-printable representation of \a str.
+
+ Replace non-ascii-printable characters in \a str from a subset of such
+ characters. The subset includes alphanumeric (alnum) characters
+ ([a-zA-Z0-9]), space, punctuation characters, and common symbols. Non-alnum
+ characters in this subset are replaced by a single hyphen. Leading,
+ trailing, and consecutive hyphens are removed, such that the resulting
+ string does not start or end with a hyphen. All characters are converted to
+ lowercase.
+
+ If any character in \a str is non-latin, or latin and not found in the
+ aforementioned subset (e.g. 'ß', 'å', or 'ö'), a hash of \a str is appended
+ to the final string.
+
+ Returns a string that is normalized for use where ascii-printable strings
+ are required, such as file names or fragment identifiers in URLs.
+
+ The implementation is equivalent to:
+
+ \code
+ name.replace(QRegularExpression("[^A-Za-z0-9]+"), " ");
+ name = name.simplified();
+ name.replace(QLatin1Char(' '), QLatin1Char('-'));
+ name = name.toLower();
+ \endcode
+
+ However, it has been measured to be approximately four times faster.
+*/
+QString asAsciiPrintable(const QString &str)
+{
+ auto legal_ascii = [](const uint value) {
+ const uint start_ascii_subset{ 32 };
+ const uint end_ascii_subset{ 126 };
+
+ return value >= start_ascii_subset && value <= end_ascii_subset;
+ };
+
+ QString result;
+ bool begun = false;
+ bool has_non_alnum_content{ false };
+
+ for (const auto &c : str) {
+ char16_t u = c.unicode();
+ if (!legal_ascii(u))
+ has_non_alnum_content = true;
+ if (u >= 'A' && u <= 'Z')
+ u += 'a' - 'A';
+ if ((u >= 'a' && u <= 'z') || (u >= '0' && u <= '9')) {
+ result += QLatin1Char(u);
+ begun = true;
+ } else if (begun) {
+ result += QLatin1Char('-');
+ begun = false;
+ }
+ }
+ if (result.endsWith(QLatin1Char('-')))
+ result.chop(1);
+
+ if (has_non_alnum_content) {
+ auto title_hash = QString::fromLocal8Bit(
+ QCryptographicHash::hash(str.toUtf8(), QCryptographicHash::Md5).toHex());
+ title_hash.truncate(8);
+ if (!result.isEmpty())
+ result.append(QLatin1Char('-'));
+ result.append(title_hash);
+ }
+
+ return result;
+}
+
+/*!
\internal
*/
static bool runProcess(const QString &program, const QStringList &arguments,
diff --git a/src/qdoc/qdoc/src/qdoc/utilities.h b/src/qdoc/qdoc/src/qdoc/utilities.h
new file mode 100644
index 000000000..0d485f650
--- /dev/null
+++ b/src/qdoc/qdoc/src/qdoc/utilities.h
@@ -0,0 +1,28 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#ifndef UTILITIES_H
+#define UTILITIES_H
+
+#include <QtCore/qstring.h>
+#include <QtCore/qloggingcategory.h>
+
+QT_BEGIN_NAMESPACE
+
+Q_DECLARE_LOGGING_CATEGORY(lcQdoc)
+Q_DECLARE_LOGGING_CATEGORY(lcQdocClang)
+
+namespace Utilities {
+void startDebugging(const QString &message);
+void stopDebugging(const QString &message);
+bool debugging();
+
+QString separator(qsizetype wordPosition, qsizetype numberOfWords);
+QString comma(qsizetype wordPosition, qsizetype numberOfWords);
+QString asAsciiPrintable(const QString &name);
+QStringList getInternalIncludePaths(const QString &compiler);
+}
+
+QT_END_NAMESPACE
+
+#endif // UTILITIES_H
diff --git a/src/qdoc/qdoc/src/qdoc/variablenode.cpp b/src/qdoc/qdoc/src/qdoc/variablenode.cpp
new file mode 100644
index 000000000..11c8363f3
--- /dev/null
+++ b/src/qdoc/qdoc/src/qdoc/variablenode.cpp
@@ -0,0 +1,23 @@
+// Copyright (C) 2020 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "variablenode.h"
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ Clone this node on the heap and make the clone a child of
+ \a parent.
+
+ Returns a pointer to the clone.
+ */
+Node *VariableNode::clone(Aggregate *parent)
+{
+ auto *vn = new VariableNode(*this); // shallow copy
+ vn->setParent(nullptr);
+ parent->addChild(vn);
+
+ return vn;
+}
+
+QT_END_NAMESPACE
diff --git a/src/qdoc/qdoc/src/qdoc/variablenode.h b/src/qdoc/qdoc/src/qdoc/variablenode.h
new file mode 100644
index 000000000..7db1252fe
--- /dev/null
+++ b/src/qdoc/qdoc/src/qdoc/variablenode.h
@@ -0,0 +1,44 @@
+// Copyright (C) 2020 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#ifndef VARIABLENODE_H
+#define VARIABLENODE_H
+
+#include "aggregate.h"
+#include "node.h"
+
+#include <QtCore/qglobal.h>
+#include <QtCore/qstring.h>
+
+QT_BEGIN_NAMESPACE
+
+class VariableNode : public Node
+{
+public:
+ VariableNode(Aggregate *parent, const QString &name);
+
+ void setLeftType(const QString &leftType) { m_leftType = leftType; }
+ void setRightType(const QString &rightType) { m_rightType = rightType; }
+ void setStatic(bool b) { m_static = b; }
+
+ [[nodiscard]] const QString &leftType() const { return m_leftType; }
+ [[nodiscard]] const QString &rightType() const { return m_rightType; }
+ [[nodiscard]] QString dataType() const { return m_leftType + m_rightType; }
+ [[nodiscard]] bool isStatic() const override { return m_static; }
+ Node *clone(Aggregate *parent) override;
+
+private:
+ QString m_leftType {};
+ QString m_rightType {};
+ bool m_static { false };
+};
+
+inline VariableNode::VariableNode(Aggregate *parent, const QString &name)
+ : Node(Variable, parent, name)
+{
+ setGenus(Node::CPP);
+}
+
+QT_END_NAMESPACE
+
+#endif // VARIABLENODE_H
diff --git a/src/qdoc/webxmlgenerator.cpp b/src/qdoc/qdoc/src/qdoc/webxmlgenerator.cpp
index 2f8f772b5..c2cc38161 100644
--- a/src/qdoc/webxmlgenerator.cpp
+++ b/src/qdoc/qdoc/src/qdoc/webxmlgenerator.cpp
@@ -1,30 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the tools applications of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "webxmlgenerator.h"
@@ -36,13 +11,18 @@
#include "propertynode.h"
#include "qdocdatabase.h"
#include "quoter.h"
+#include "utilities.h"
#include <QtCore/qxmlstream.h>
QT_BEGIN_NAMESPACE
+using namespace Qt::StringLiterals;
+
static CodeMarker *marker_ = nullptr;
+WebXMLGenerator::WebXMLGenerator(FileResolver& file_resolver) : HtmlGenerator(file_resolver) {}
+
void WebXMLGenerator::initializeGenerator()
{
HtmlGenerator::initializeGenerator();
@@ -50,7 +30,7 @@ void WebXMLGenerator::initializeGenerator()
void WebXMLGenerator::terminateGenerator()
{
- Generator::terminateGenerator();
+ HtmlGenerator::terminateGenerator();
}
QString WebXMLGenerator::format()
@@ -119,35 +99,34 @@ void WebXMLGenerator::generatePageNode(PageNode *pn, CodeMarker * /* marker */)
endSubPage();
}
-void WebXMLGenerator::generateExampleFilePage(const Node *en, const QString &file,
- CodeMarker * /* marker */)
+void WebXMLGenerator::generateExampleFilePage(const Node *en, ResolvedFile resolved_file, CodeMarker* /* marker */)
{
+ // TODO: [generator-insufficient-structural-abstraction]
+
QByteArray data;
QXmlStreamWriter writer(&data);
writer.setAutoFormatting(true);
- beginFilePage(en, linkForExampleFile(file, "webxml"));
+ beginSubPage(en, linkForExampleFile(resolved_file.get_query(), "webxml"));
writer.writeStartDocument();
writer.writeStartElement("WebXML");
writer.writeStartElement("document");
writer.writeStartElement("page");
- writer.writeAttribute("name", file);
- writer.writeAttribute("href", linkForExampleFile(file));
- QString title = exampleFileTitle(static_cast<const ExampleNode *>(en), file);
+ writer.writeAttribute("name", resolved_file.get_query());
+ writer.writeAttribute("href", linkForExampleFile(resolved_file.get_query()));
+ const QString title = exampleFileTitle(static_cast<const ExampleNode *>(en), resolved_file.get_query());
writer.writeAttribute("title", title);
writer.writeAttribute("fulltitle", title);
- writer.writeAttribute("subtitle", file);
+ writer.writeAttribute("subtitle", resolved_file.get_query());
writer.writeStartElement("description");
- if (Config::instance().getBool(CONFIG_LOCATIONINFO)) {
- QString userFriendlyFilePath; // unused
- writer.writeAttribute("path",
- Doc::resolveFile(en->doc().location(), file, &userFriendlyFilePath));
+ if (Config::instance().get(CONFIG_LOCATIONINFO).asBool()) {
+ writer.writeAttribute("path", resolved_file.get_path());
writer.writeAttribute("line", "0");
writer.writeAttribute("column", "0");
}
Quoter quoter;
- Doc::quoteFromFile(en->doc().location(), quoter, file);
+ Doc::quoteFromFile(en->doc().location(), quoter, resolved_file);
QString code = quoter.quoteTo(en->location(), QString(), QString());
writer.writeTextElement("code", trimmedTrailing(code, QString(), QString()));
@@ -158,7 +137,7 @@ void WebXMLGenerator::generateExampleFilePage(const Node *en, const QString &fil
writer.writeEndDocument();
out() << data;
- endFilePage();
+ endSubPage();
}
void WebXMLGenerator::generateIndexSections(QXmlStreamWriter &writer, Node *node)
@@ -179,7 +158,7 @@ void WebXMLGenerator::append(QXmlStreamWriter &writer, Node *node)
Q_ASSERT(marker_);
writer.writeStartElement("description");
- if (Config::instance().getBool(CONFIG_LOCATIONINFO)) {
+ if (Config::instance().get(CONFIG_LOCATIONINFO).asBool()) {
writer.writeAttribute("path", node->doc().location().filePath());
writer.writeAttribute("line", QString::number(node->doc().location().lineNo()));
writer.writeAttribute("column", QString::number(node->doc().location().columnNo()));
@@ -199,8 +178,7 @@ void WebXMLGenerator::append(QXmlStreamWriter &writer, Node *node)
writer.writeAttribute("level", "1");
writer.writeCharacters("Namespaces");
writer.writeEndElement(); // heading
- NodeMap namespaces;
- cnn->getMemberNamespaces(namespaces);
+ NodeMap namespaces{cnn->getMembers(Node::Namespace)};
generateAnnotatedList(writer, node, namespaces);
writer.writeEndElement(); // section
}
@@ -210,8 +188,7 @@ void WebXMLGenerator::append(QXmlStreamWriter &writer, Node *node)
writer.writeAttribute("level", "1");
writer.writeCharacters("Classes");
writer.writeEndElement(); // heading
- NodeMap classes;
- cnn->getMemberClasses(classes);
+ NodeMap classes{cnn->getMembers([](const Node *n){ return n->isClassNode(); })};
generateAnnotatedList(writer, node, classes);
writer.writeEndElement(); // section
}
@@ -298,28 +275,30 @@ const Atom *WebXMLGenerator::addAtomElements(QXmlStreamWriter &writer, const Ato
if (cn)
generateAnnotatedList(writer, relative, cn->members());
} break;
- case Atom::AutoLink:
+ case Atom::AutoLink: {
+ const Node *node{nullptr};
+ QString link{};
+
if (!m_inLink && !m_inSectionHeading) {
- const Node *node = nullptr;
- QString link = getAutoLink(atom, relative, &node, Node::API);
+ link = getAutoLink(atom, relative, &node, Node::API);
+
if (!link.isEmpty() && node && node->isDeprecated()
&& relative->parent() != node && !relative->isDeprecated()) {
link.clear();
}
- if (node) {
- startLink(writer, atom, node, link);
- if (m_inLink) {
- writer.writeCharacters(atom->string());
- writer.writeEndElement(); // link
- m_inLink = false;
- }
- } else {
- writer.writeCharacters(atom->string());
- }
- } else {
- writer.writeCharacters(atom->string());
}
+
+ startLink(writer, atom, node, link);
+
+ writer.writeCharacters(atom->string());
+
+ if (m_inLink) {
+ writer.writeEndElement(); // link
+ m_inLink = false;
+ }
+
break;
+ }
case Atom::BaseName:
break;
case Atom::BriefLeft:
@@ -385,31 +364,11 @@ const Atom *WebXMLGenerator::addAtomElements(QXmlStreamWriter &writer, const Ato
keepQuoting = true;
break;
-#ifdef QDOC_QML
- case Atom::Qml:
- if (!m_hasQuotingInformation)
- writer.writeTextElement(
- "qml", trimmedTrailing(plainCode(atom->string()), QString(), QString()));
- else
- keepQuoting = true;
-#endif
case Atom::CodeBad:
writer.writeTextElement("badcode",
trimmedTrailing(plainCode(atom->string()), QString(), QString()));
break;
- case Atom::CodeNew:
- writer.writeTextElement("para", "you can rewrite it as");
- writer.writeTextElement("newcode",
- trimmedTrailing(plainCode(atom->string()), QString(), QString()));
- break;
-
- case Atom::CodeOld:
- writer.writeTextElement("para", "For example, if you have code like");
- writer.writeTextElement("oldcode",
- trimmedTrailing(plainCode(atom->string()), QString(), QString()));
- break;
-
case Atom::CodeQuoteArgument:
if (m_quoting) {
if (quoteCommand == "dots") {
@@ -497,6 +456,8 @@ const Atom *WebXMLGenerator::addAtomElements(QXmlStreamWriter &writer, const Ato
writer.writeEndElement();
else if (atom->string() == ATOM_FORMATTING_INDEX)
writer.writeEndElement();
+ else if (atom->string() == ATOM_FORMATTING_TRADEMARK && appendTrademark(atom))
+ writer.writeCharacters(QChar(0x2122)); // 'TM' symbol
}
if (m_inLink) {
writer.writeEndElement(); // link
@@ -509,20 +470,56 @@ const Atom *WebXMLGenerator::addAtomElements(QXmlStreamWriter &writer, const Ato
writer.writeAttribute("contents", atom->string());
writer.writeEndElement();
break;
- case Atom::Image:
- writer.writeStartElement("image");
- writer.writeAttribute("href", imageFileName(relative, atom->string()));
- writer.writeEndElement();
- setImageFileName(relative, atom->string());
- break;
- case Atom::InlineImage:
- writer.writeStartElement("inlineimage");
- writer.writeAttribute("href", imageFileName(relative, atom->string()));
- writer.writeEndElement();
- setImageFileName(relative, atom->string());
+ // TODO: The other generators treat inlineimage and image
+ // simultaneously as the diffirences aren't big. It should be
+ // possible to do the same for webxmlgenerator instead of
+ // repeating the code.
+
+ // TODO: [generator-insufficient-structural-abstraction]
+ case Atom::Image: {
+ auto maybe_resolved_file{file_resolver.resolve(atom->string())};
+ if (!maybe_resolved_file) {
+ // TODO: [uncentralized-admonition][failed-resolve-file]
+ relative->location().warning(QStringLiteral("Missing image: %1").arg(atom->string()));
+ } else {
+ ResolvedFile file{*maybe_resolved_file};
+ QString file_name{QFileInfo{file.get_path()}.fileName()};
+
+ // TODO: [uncentralized-output-directory-structure]
+ Config::copyFile(relative->doc().location(), file.get_path(), file_name, outputDir() + QLatin1String("/images"));
+
+ writer.writeStartElement("image");
+ // TODO: [uncentralized-output-directory-structure]
+ writer.writeAttribute("href", "images/" + file_name);
+ writer.writeEndElement();
+ // TODO: [uncentralized-output-directory-structure]
+ setImageFileName(relative, "images/" + file_name);
+ }
break;
+ }
+ // TODO: [generator-insufficient-structural-abstraction]
+ case Atom::InlineImage: {
+ auto maybe_resolved_file{file_resolver.resolve(atom->string())};
+ if (!maybe_resolved_file) {
+ // TODO: [uncentralized-admonition][failed-resolve-file]
+ relative->location().warning(QStringLiteral("Missing image: %1").arg(atom->string()));
+ } else {
+ ResolvedFile file{*maybe_resolved_file};
+ QString file_name{QFileInfo{file.get_path()}.fileName()};
+ // TODO: [uncentralized-output-directory-structure]
+ Config::copyFile(relative->doc().location(), file.get_path(), file_name, outputDir() + QLatin1String("/images"));
+
+ writer.writeStartElement("inlineimage");
+ // TODO: [uncentralized-output-directory-structure]
+ writer.writeAttribute("href", "images/" + file_name);
+ writer.writeEndElement();
+ // TODO: [uncentralized-output-directory-structure]
+ setImageFileName(relative, "images/" + file_name);
+ }
+ break;
+ }
case Atom::ImageText:
break;
@@ -618,10 +615,12 @@ const Atom *WebXMLGenerator::addAtomElements(QXmlStreamWriter &writer, const Ato
case Atom::Nop:
break;
+ case Atom::CaptionLeft:
case Atom::ParaLeft:
writer.writeStartElement("para");
break;
+ case Atom::CaptionRight:
case Atom::ParaRight:
writer.writeEndElement(); // para
break;
@@ -640,7 +639,8 @@ const Atom *WebXMLGenerator::addAtomElements(QXmlStreamWriter &writer, const Ato
case Atom::SectionLeft:
writer.writeStartElement("section");
- writer.writeAttribute("id", Doc::canonicalTitle(Text::sectionHeading(atom).toString()));
+ writer.writeAttribute("id",
+ Utilities::asAsciiPrintable(Text::sectionHeading(atom).toString()));
break;
case Atom::SectionRight:
@@ -681,9 +681,22 @@ const Atom *WebXMLGenerator::addAtomElements(QXmlStreamWriter &writer, const Ato
if (m_quoting) {
const QString &location = atom->string();
writer.writeAttribute("location", location);
- const QString resolved = Doc::resolveFile(Location(), location);
- if (!resolved.isEmpty())
- writer.writeAttribute("path", resolved);
+ auto maybe_resolved_file{file_resolver.resolve(location)};
+ // const QString resolved = Doc::resolveFile(Location(), location);
+ if (maybe_resolved_file)
+ writer.writeAttribute("path", (*maybe_resolved_file).get_path());
+ else {
+ // TODO: [uncetnralized-admonition][failed-resolve-file]
+ QString details = std::transform_reduce(
+ file_resolver.get_search_directories().cbegin(),
+ file_resolver.get_search_directories().cend(),
+ u"Searched directories:"_s,
+ std::plus(),
+ [](const DirectoryPath &directory_path) -> QString { return u' ' + directory_path.value(); }
+ );
+
+ relative->location().warning(u"Cannot find file to quote from: %1"_s.arg(location), details);
+ }
}
break;
@@ -732,7 +745,7 @@ const Atom *WebXMLGenerator::addAtomElements(QXmlStreamWriter &writer, const Ato
case Atom::Target:
writer.writeStartElement("target");
- writer.writeAttribute("name", Doc::canonicalTitle(atom->string()));
+ writer.writeAttribute("name", Utilities::asAsciiPrintable(atom->string()));
writer.writeEndElement();
break;
@@ -775,7 +788,7 @@ void WebXMLGenerator::startLink(QXmlStreamWriter &writer, const Atom *atom, cons
break;
case Node::Example: {
const auto *en = static_cast<const ExampleNode *>(node);
- QString fileTitle = exampleFileTitle(en, atom->string());
+ const QString fileTitle = atom ? exampleFileTitle(en, atom->string()) : QString();
if (!fileTitle.isEmpty()) {
writer.writeAttribute("page", fileTitle);
break;
@@ -809,7 +822,7 @@ void WebXMLGenerator::endLink(QXmlStreamWriter &writer)
void WebXMLGenerator::generateRelations(QXmlStreamWriter &writer, const Node *node)
{
if (node && !node->links().empty()) {
- QPair<QString, QString> anchorPair;
+ std::pair<QString, QString> anchorPair;
const Node *linkNode;
for (auto it = node->links().cbegin(); it != node->links().cend(); ++it) {
diff --git a/src/qdoc/webxmlgenerator.h b/src/qdoc/qdoc/src/qdoc/webxmlgenerator.h
index b5119891b..7065670b2 100644
--- a/src/qdoc/webxmlgenerator.h
+++ b/src/qdoc/qdoc/src/qdoc/webxmlgenerator.h
@@ -1,30 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the tools applications of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#ifndef WEBXMLGENERATOR_H
#define WEBXMLGENERATOR_H
@@ -43,7 +18,7 @@ class Aggregate;
class WebXMLGenerator : public HtmlGenerator, public IndexSectionWriter
{
public:
- WebXMLGenerator() = default;
+ WebXMLGenerator(FileResolver& file_resolver);
void initializeGenerator() override;
void terminateGenerator() override;
@@ -56,7 +31,7 @@ protected:
void generateCppReferencePage(Aggregate *aggregate, CodeMarker *marker) override;
void generatePageNode(PageNode *pn, CodeMarker *marker) override;
void generateDocumentation(Node *node) override;
- void generateExampleFilePage(const Node *en, const QString &file, CodeMarker *marker) override;
+ void generateExampleFilePage(const Node *en, ResolvedFile file, CodeMarker *marker = nullptr) override;
[[nodiscard]] QString fileExtension() const override;
virtual const Atom *addAtomElements(QXmlStreamWriter &writer, const Atom *atom,
@@ -74,8 +49,6 @@ private:
void endLink(QXmlStreamWriter &writer);
QString fileBase(const Node *node) const override;
- bool m_inLink { false };
- bool m_inSectionHeading { false };
bool m_hasQuotingInformation { false };
QString quoteCommand {};
QScopedPointer<QXmlStreamWriter> currentWriter {};
diff --git a/src/qdoc/xmlgenerator.cpp b/src/qdoc/qdoc/src/qdoc/xmlgenerator.cpp
index a83a25143..ffad5259d 100644
--- a/src/qdoc/xmlgenerator.cpp
+++ b/src/qdoc/qdoc/src/qdoc/xmlgenerator.cpp
@@ -1,31 +1,6 @@
-/****************************************************************************
-**
-** Copyright (C) 2019 Thibaut Cuvelier
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the tools applications of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2019 Thibaut Cuvelier
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "xmlgenerator.h"
@@ -35,15 +10,20 @@
#include "qdocdatabase.h"
#include "typedefnode.h"
+using namespace Qt::Literals::StringLiterals;
+
QT_BEGIN_NAMESPACE
+const QRegularExpression XmlGenerator::m_funcLeftParen(QStringLiteral("^\\S+(\\(.*\\))"));
+
+XmlGenerator::XmlGenerator(FileResolver& file_resolver) : Generator(file_resolver) {}
+
/*!
- Do not display \brief for QML/JS types, document and collection nodes
+ Do not display \brief for QML types, document and collection nodes
*/
bool XmlGenerator::hasBrief(const Node *node)
{
- return !(node->isQmlType() || node->isPageNode() || node->isCollectionNode()
- || node->isJsType());
+ return !(node->isQmlType() || node->isPageNode() || node->isCollectionNode());
}
/*!
@@ -61,6 +41,27 @@ bool XmlGenerator::isThreeColumnEnumValueTable(const Atom *atom)
}
/*!
+ Determines whether the list atom should be shown with just one column (value).
+ */
+bool XmlGenerator::isOneColumnValueTable(const Atom *atom)
+{
+ if (atom->type() != Atom::ListLeft || atom->string() != ATOM_LIST_VALUE)
+ return false;
+
+ while (atom && atom->type() != Atom::ListTagRight)
+ atom = atom->next();
+
+ if (atom) {
+ if (!matchAhead(atom, Atom::ListItemLeft))
+ return false;
+ if (!atom->next())
+ return false;
+ return matchAhead(atom->next(), Atom::ListItemRight);
+ }
+ return false;
+}
+
+/*!
Header offset depending on the type of the node
*/
int XmlGenerator::hOffset(const Node *node)
@@ -73,7 +74,7 @@ int XmlGenerator::hOffset(const Node *node)
case Node::Module:
return 2;
case Node::QmlModule:
- case Node::QmlBasicType:
+ case Node::QmlValueType:
case Node::QmlType:
case Node::Page:
case Node::Group:
@@ -94,22 +95,21 @@ int XmlGenerator::hOffset(const Node *node)
*/
void XmlGenerator::rewritePropertyBrief(const Atom *atom, const Node *relative)
{
- if (relative->nodeType() == Node::Property || relative->nodeType() == Node::Variable) {
- atom = atom->next();
- if (atom && atom->type() == Atom::String) {
- QString firstWord =
- atom->string().toLower().section(' ', 0, 0, QString::SectionSkipEmpty);
- if (firstWord == QLatin1String("the") || firstWord == QLatin1String("a")
- || firstWord == QLatin1String("an") || firstWord == QLatin1String("whether")
- || firstWord == QLatin1String("which")) {
- QString str = QLatin1String("This ")
- + QLatin1String(relative->nodeType() == Node::Property ? "property"
- : "variable")
- + QLatin1String(" holds ") + atom->string().left(1).toLower()
- + atom->string().mid(1);
- const_cast<Atom *>(atom)->setString(str);
- }
- }
+ if (relative->nodeType() != Node::Property && relative->nodeType() != Node::Variable)
+ return;
+ atom = atom->next();
+ if (!atom || atom->type() != Atom::String)
+ return;
+
+ const QString firstWord =
+ atom->string().toLower().section(' ', 0, 0, QString::SectionSkipEmpty);
+ const QStringList words{ "the", "a", "an", "whether", "which" };
+ if (words.contains(firstWord)) {
+ QString str = QLatin1String("This ")
+ + QLatin1String(relative->nodeType() == Node::Property ? "property" : "variable")
+ + QLatin1String(" holds ") + atom->string().left(1).toLower()
+ + atom->string().mid(1);
+ const_cast<Atom *>(atom)->setString(str);
}
}
@@ -121,8 +121,6 @@ Node::NodeType XmlGenerator::typeFromString(const Atom *atom)
const auto &name = atom->string();
if (name.startsWith(QLatin1String("qml")))
return Node::QmlModule;
- else if (name.startsWith(QLatin1String("js")))
- return Node::JsModule;
else if (name.startsWith(QLatin1String("groups")))
return Node::Group;
else
@@ -149,16 +147,16 @@ void XmlGenerator::setImageFileName(const Node *relative, const QString &fileNam
returns the content of the list entry \a atom (first member of the pair).
It also returns the number of items to skip ahead (second member of the pair).
*/
-QPair<QString, int> XmlGenerator::getAtomListValue(const Atom *atom)
+std::pair<QString, int> XmlGenerator::getAtomListValue(const Atom *atom)
{
const Atom *lookAhead = atom->next();
if (!lookAhead)
- return QPair<QString, int>(QString(), 1);
+ return std::pair<QString, int>(QString(), 1);
QString t = lookAhead->string();
lookAhead = lookAhead->next();
if (!lookAhead || lookAhead->type() != Atom::ListTagRight)
- return QPair<QString, int>(QString(), 1);
+ return std::pair<QString, int>(QString(), 1);
lookAhead = lookAhead->next();
int skipAhead;
@@ -173,7 +171,7 @@ QPair<QString, int> XmlGenerator::getAtomListValue(const Atom *atom)
} else {
skipAhead = 1;
}
- return QPair<QString, int>(t, skipAhead);
+ return std::pair<QString, int>(t, skipAhead);
}
/*!
@@ -182,7 +180,7 @@ QPair<QString, int> XmlGenerator::getAtomListValue(const Atom *atom)
the attribute for this table (either "generic" or
"borderless").
*/
-QPair<QString, QString> XmlGenerator::getTableWidthAttr(const Atom *atom)
+std::pair<QString, QString> XmlGenerator::getTableWidthAttr(const Atom *atom)
{
QString p0, p1;
QString attr = "generic";
@@ -204,7 +202,21 @@ QPair<QString, QString> XmlGenerator::getTableWidthAttr(const Atom *atom)
else if (p1.contains(QLatin1Char('%')))
width = p1;
}
- return QPair<QString, QString>(width, attr);
+
+ // Many times, in the documentation, there is a space before the % sign:
+ // this breaks the parsing logic above.
+ if (width == QLatin1String("%")) {
+ // The percentage is typically stored in p0, parse it as an int.
+ bool ok = false;
+ int widthPercentage = p0.toInt(&ok);
+ if (ok) {
+ width = QString::number(widthPercentage) + "%";
+ } else {
+ width = {};
+ }
+ }
+
+ return {width, attr};
}
/*!
@@ -214,21 +226,23 @@ QPair<QString, QString> XmlGenerator::getTableWidthAttr(const Atom *atom)
To ensure unicity throughout the document, this method
uses the \a refMap cache.
*/
-QString XmlGenerator::registerRef(const QString &ref)
+QString XmlGenerator::registerRef(const QString &ref, bool xmlCompliant)
{
- QString clean = Generator::cleanRef(ref);
+ QString cleanRef = Generator::cleanRef(ref, xmlCompliant);
for (;;) {
- QString &prevRef = refMap[clean.toLower()];
+ QString &prevRef = refMap[cleanRef.toLower()];
if (prevRef.isEmpty()) {
+ // This reference has never been met before for this document: register it.
prevRef = ref;
break;
} else if (prevRef == ref) {
+ // This exact same reference was already found. This case typically occurs within refForNode.
break;
}
- clean += QLatin1Char('x');
+ cleanRef += QLatin1Char('x');
}
- return clean;
+ return cleanRef;
}
/*!
@@ -254,15 +268,12 @@ QString XmlGenerator::refForNode(const Node *node)
case Node::Function: {
const auto fn = static_cast<const FunctionNode *>(node);
switch (fn->metaness()) {
- case FunctionNode::JsSignal:
case FunctionNode::QmlSignal:
ref = fn->name() + "-signal";
break;
- case FunctionNode::JsSignalHandler:
case FunctionNode::QmlSignalHandler:
ref = fn->name() + "-signal-handler";
break;
- case FunctionNode::JsMethod:
case FunctionNode::QmlMethod:
ref = fn->name() + "-method";
if (fn->overloadNumber() != 0)
@@ -270,7 +281,7 @@ QString XmlGenerator::refForNode(const Node *node)
break;
default:
if (fn->hasOneAssociatedProperty() && fn->doc().isEmpty()) {
- return refForNode(fn->firstAssociatedProperty());
+ return refForNode(fn->associatedProperties()[0]);
} else {
ref = fn->name();
if (fn->overloadNumber() != 0)
@@ -283,7 +294,6 @@ QString XmlGenerator::refForNode(const Node *node)
if (!node->isPropertyGroup())
break;
} Q_FALLTHROUGH();
- case Node::JsProperty:
case Node::QmlProperty:
if (node->isAttached())
ref = node->name() + "-attached-prop";
@@ -322,8 +332,7 @@ QString XmlGenerator::linkForNode(const Node *node, const Node *relative)
return QString();
QString fn = fileName(node);
- if (node->parent() && (node->parent()->isQmlType() || node->parent()->isJsType())
- && node->parent()->isAbstract()) {
+ if (node->parent() && node->parent()->isQmlType() && node->parent()->isAbstract()) {
if (Generator::qmlTypeContext()) {
if (Generator::qmlTypeContext()->inherits(node->parent())) {
fn = fileName(Generator::qmlTypeContext());
@@ -348,20 +357,13 @@ QString XmlGenerator::linkForNode(const Node *node, const Node *relative)
}
/*
- If the output is going to subdirectories, then if the
- two nodes will be output to different directories, then
- the link must go up to the parent directory and then
- back down into the other subdirectory.
+ If the output is going to subdirectories, the
+ two nodes have different output directories if 'node'
+ was read from index.
*/
if (relative && (node != relative)) {
- if (useOutputSubdirs() && !node->isExternalPage()
- && node->outputSubdirectory() != relative->outputSubdirectory()) {
- if (link.startsWith(QString(node->outputSubdirectory() + QLatin1Char('/')))) {
- link.prepend(QString("../"));
- } else {
- link.prepend(QString("../" + node->outputSubdirectory() + QLatin1Char('/')));
- }
- }
+ if (useOutputSubdirs() && !node->isExternalPage() && node->isIndexNode())
+ link.prepend("../%1/"_L1.arg(node->tree()->physicalModuleName()));
}
return link;
}
@@ -433,9 +435,9 @@ QString XmlGenerator::getAutoLink(const Atom *atom, const Node *relative, const
return link;
}
-QPair<QString, QString> XmlGenerator::anchorForNode(const Node *node)
+std::pair<QString, QString> XmlGenerator::anchorForNode(const Node *node)
{
- QPair<QString, QString> anchorPair;
+ std::pair<QString, QString> anchorPair;
anchorPair.first = Generator::fileName(node);
if (node->isTextPageNode())
diff --git a/src/qdoc/qdoc/src/qdoc/xmlgenerator.h b/src/qdoc/qdoc/src/qdoc/xmlgenerator.h
new file mode 100644
index 000000000..5f7ba67fd
--- /dev/null
+++ b/src/qdoc/qdoc/src/qdoc/xmlgenerator.h
@@ -0,0 +1,54 @@
+// Copyright (C) 2019 Thibaut Cuvelier
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#ifndef XMLGENERATOR_H
+#define XMLGENERATOR_H
+
+#include "node.h"
+#include "generator.h"
+#include "filesystem/fileresolver.h"
+
+#include <QtCore/qmap.h>
+#include <QtCore/qstring.h>
+
+QT_BEGIN_NAMESPACE
+
+class XmlGenerator : public Generator
+{
+public:
+ explicit XmlGenerator(FileResolver& file_resolver);
+
+protected:
+ QHash<QString, QString> refMap;
+
+ static bool hasBrief(const Node *node);
+ static bool isThreeColumnEnumValueTable(const Atom *atom);
+ static bool isOneColumnValueTable(const Atom *atom);
+ static int hOffset(const Node *node);
+
+ static void rewritePropertyBrief(const Atom *atom, const Node *relative);
+ static Node::NodeType typeFromString(const Atom *atom);
+ static void setImageFileName(const Node *relative, const QString &fileName);
+ static std::pair<QString, int> getAtomListValue(const Atom *atom);
+ static std::pair<QString, QString> getTableWidthAttr(const Atom *atom);
+
+ QString registerRef(const QString &ref, bool xmlCompliant = false);
+ QString refForNode(const Node *node);
+ QString linkForNode(const Node *node, const Node *relative);
+ QString getLink(const Atom *atom, const Node *relative, const Node **node);
+ QString getAutoLink(const Atom *atom, const Node *relative, const Node **node,
+ Node::Genus = Node::DontCare);
+
+ std::pair<QString, QString> anchorForNode(const Node *node);
+
+ static QString targetType(const Node *node);
+
+protected:
+ static const QRegularExpression m_funcLeftParen;
+ const Node *m_linkNode { nullptr };
+};
+
+QT_END_NAMESPACE
+
+#endif // XMLGENERATOR_H
diff --git a/src/qdoc/qdoc/tests/CMakeLists.txt b/src/qdoc/qdoc/tests/CMakeLists.txt
new file mode 100644
index 000000000..143837517
--- /dev/null
+++ b/src/qdoc/qdoc/tests/CMakeLists.txt
@@ -0,0 +1,6 @@
+add_subdirectory(qdoc)
+add_subdirectory(config)
+add_subdirectory(qdoccommandlineparser)
+add_subdirectory(utilities)
+add_subdirectory(generatedoutput)
+add_subdirectory(validateqdocoutputfiles)
diff --git a/src/qdoc/qdoc/tests/config/CMakeLists.txt b/src/qdoc/qdoc/tests/config/CMakeLists.txt
new file mode 100644
index 000000000..ef483291d
--- /dev/null
+++ b/src/qdoc/qdoc/tests/config/CMakeLists.txt
@@ -0,0 +1,24 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+#####################################################################
+## tst_config Test:
+#####################################################################
+
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_config LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
+qt_internal_add_test(tst_config
+ SOURCES
+ ${CMAKE_CURRENT_LIST_DIR}/tst_config.cpp
+
+ ${CMAKE_CURRENT_LIST_DIR}/../../src/qdoc/config.cpp
+ ${CMAKE_CURRENT_LIST_DIR}/../../src/qdoc/location.cpp
+ ${CMAKE_CURRENT_LIST_DIR}/../../src/qdoc/qdoccommandlineparser.cpp
+ ${CMAKE_CURRENT_LIST_DIR}/../../src/qdoc/utilities.cpp
+ INCLUDE_DIRECTORIES
+ ${CMAKE_CURRENT_LIST_DIR}/../../src/
+)
diff --git a/src/qdoc/qdoc/tests/config/testdata/configs/exampletest.qdocconf b/src/qdoc/qdoc/tests/config/testdata/configs/exampletest.qdocconf
new file mode 100644
index 000000000..a1459f977
--- /dev/null
+++ b/src/qdoc/qdoc/tests/config/testdata/configs/exampletest.qdocconf
@@ -0,0 +1,2 @@
+project = ExampleTest
+exampledirs = ../exampletest/examples
diff --git a/src/qdoc/qdoc/tests/config/testdata/configs/expandvars.qdocconf b/src/qdoc/qdoc/tests/config/testdata/configs/expandvars.qdocconf
new file mode 100644
index 000000000..ace4ab13b
--- /dev/null
+++ b/src/qdoc/qdoc/tests/config/testdata/configs/expandvars.qdocconf
@@ -0,0 +1,13 @@
+project = ExpandVars
+
+{data,data1,data2} = foo
+data1 += bar
+data2 += "bar baz"
+
+expanded1 = $data
+expanded2 = ${data1,,}
+expanded3 = "${data1,} ${data2,}"
+literally = \$data \${data}
+
+listdata = ${QDOC_TSTCONFIG_LIST}
+csvlist = ${listdata,,}
diff --git a/src/qdoc/qdoc/tests/config/testdata/configs/includepaths.qdocconf b/src/qdoc/qdoc/tests/config/testdata/configs/includepaths.qdocconf
new file mode 100644
index 000000000..2d6ff22af
--- /dev/null
+++ b/src/qdoc/qdoc/tests/config/testdata/configs/includepaths.qdocconf
@@ -0,0 +1,2 @@
+project = IncludePaths
+include(../includepaths/includepaths.qdocconf)
diff --git a/src/qdoc/qdoc/tests/config/testdata/configs/includes/test.qdoc b/src/qdoc/qdoc/tests/config/testdata/configs/includes/test.qdoc
new file mode 100644
index 000000000..700f1e819
--- /dev/null
+++ b/src/qdoc/qdoc/tests/config/testdata/configs/includes/test.qdoc
@@ -0,0 +1 @@
+/*! nothing here */
diff --git a/src/qdoc/qdoc/tests/config/testdata/configs/paths.qdocconf b/src/qdoc/qdoc/tests/config/testdata/configs/paths.qdocconf
new file mode 100644
index 000000000..93f7b3586
--- /dev/null
+++ b/src/qdoc/qdoc/tests/config/testdata/configs/paths.qdocconf
@@ -0,0 +1,5 @@
+project = Paths
+include(../paths/paths.qdocconf)
+
+sourcedirs += .
+sourcedirs += includes
diff --git a/src/qdoc/qdoc/tests/config/testdata/configs/vars.qdocconf b/src/qdoc/qdoc/tests/config/testdata/configs/vars.qdocconf
new file mode 100644
index 000000000..1f5da6eb8
--- /dev/null
+++ b/src/qdoc/qdoc/tests/config/testdata/configs/vars.qdocconf
@@ -0,0 +1,17 @@
+project = Variables
+
+untrue = false
+true = "Sir Yes, Sir!"
+
+void =
+int = 1
+int += 1
+
+list = \
+ testing line \
+ "by\n" \
+ "line"
+
+some.thing =
+some.where =
+some.time =
diff --git a/src/qdoc/qdoc/tests/config/testdata/exampletest/examples/test/empty/test.pro b/src/qdoc/qdoc/tests/config/testdata/exampletest/examples/test/empty/test.pro
new file mode 100644
index 000000000..556df42ea
--- /dev/null
+++ b/src/qdoc/qdoc/tests/config/testdata/exampletest/examples/test/empty/test.pro
@@ -0,0 +1 @@
+# nothing
diff --git a/src/qdoc/qdoc/tests/config/testdata/exampletest/examples/test/example1/example1.pro b/src/qdoc/qdoc/tests/config/testdata/exampletest/examples/test/example1/example1.pro
new file mode 100644
index 000000000..556df42ea
--- /dev/null
+++ b/src/qdoc/qdoc/tests/config/testdata/exampletest/examples/test/example1/example1.pro
@@ -0,0 +1 @@
+# nothing
diff --git a/src/qdoc/qdoc/tests/config/testdata/exampletest/examples/test/example2/example2.qmlproject b/src/qdoc/qdoc/tests/config/testdata/exampletest/examples/test/example2/example2.qmlproject
new file mode 100644
index 000000000..556df42ea
--- /dev/null
+++ b/src/qdoc/qdoc/tests/config/testdata/exampletest/examples/test/example2/example2.qmlproject
@@ -0,0 +1 @@
+# nothing
diff --git a/src/qdoc/qdoc/tests/config/testdata/exampletest/examples/test/example3/example3.pyproject b/src/qdoc/qdoc/tests/config/testdata/exampletest/examples/test/example3/example3.pyproject
new file mode 100644
index 000000000..556df42ea
--- /dev/null
+++ b/src/qdoc/qdoc/tests/config/testdata/exampletest/examples/test/example3/example3.pyproject
@@ -0,0 +1 @@
+# nothing
diff --git a/src/qdoc/qdoc/tests/config/testdata/exampletest/examples/test/example4/CMakeLists.txt b/src/qdoc/qdoc/tests/config/testdata/exampletest/examples/test/example4/CMakeLists.txt
new file mode 100644
index 000000000..556df42ea
--- /dev/null
+++ b/src/qdoc/qdoc/tests/config/testdata/exampletest/examples/test/example4/CMakeLists.txt
@@ -0,0 +1 @@
+# nothing
diff --git a/src/qdoc/qdoc/tests/config/testdata/exampletest/examples/test/example4/example4.pro b/src/qdoc/qdoc/tests/config/testdata/exampletest/examples/test/example4/example4.pro
new file mode 100644
index 000000000..556df42ea
--- /dev/null
+++ b/src/qdoc/qdoc/tests/config/testdata/exampletest/examples/test/example4/example4.pro
@@ -0,0 +1 @@
+# nothing
diff --git a/src/qdoc/qdoc/tests/config/testdata/includepaths/include/framework/ignore.h b/src/qdoc/qdoc/tests/config/testdata/includepaths/include/framework/ignore.h
new file mode 100644
index 000000000..b2a4ba591
--- /dev/null
+++ b/src/qdoc/qdoc/tests/config/testdata/includepaths/include/framework/ignore.h
@@ -0,0 +1 @@
+# nothing here
diff --git a/src/qdoc/qdoc/tests/config/testdata/includepaths/include/more/ignore.h b/src/qdoc/qdoc/tests/config/testdata/includepaths/include/more/ignore.h
new file mode 100644
index 000000000..b2a4ba591
--- /dev/null
+++ b/src/qdoc/qdoc/tests/config/testdata/includepaths/include/more/ignore.h
@@ -0,0 +1 @@
+# nothing here
diff --git a/src/qdoc/qdoc/tests/config/testdata/includepaths/include/purpose.h b/src/qdoc/qdoc/tests/config/testdata/includepaths/include/purpose.h
new file mode 100644
index 000000000..0f7af352b
--- /dev/null
+++ b/src/qdoc/qdoc/tests/config/testdata/includepaths/include/purpose.h
@@ -0,0 +1 @@
+#define PURPOSE "Pass butter"
diff --git a/src/qdoc/qdoc/tests/config/testdata/includepaths/include/system/ignore.h b/src/qdoc/qdoc/tests/config/testdata/includepaths/include/system/ignore.h
new file mode 100644
index 000000000..b2a4ba591
--- /dev/null
+++ b/src/qdoc/qdoc/tests/config/testdata/includepaths/include/system/ignore.h
@@ -0,0 +1 @@
+# nothing here
diff --git a/src/qdoc/qdoc/tests/config/testdata/includepaths/includepaths.qdocconf b/src/qdoc/qdoc/tests/config/testdata/includepaths/includepaths.qdocconf
new file mode 100644
index 000000000..6288c4258
--- /dev/null
+++ b/src/qdoc/qdoc/tests/config/testdata/includepaths/includepaths.qdocconf
@@ -0,0 +1,16 @@
+includepaths = -I./include
+
+# without prefix but same path, should be identical
+# (Config should not remove duplicates)
+includepaths += include
+
+# space between prefix and path - incorrect but we allow it
+includepaths += -I include/more
+
+# system paths and framework paths
+includepaths += \
+ -F./include/framework \
+ -isysteminclude/system
+
+# nonexistent paths are to be ignored
+includepaths += invalid
diff --git a/src/qdoc/qdoc/tests/config/testdata/paths/includes/test.qdoc b/src/qdoc/qdoc/tests/config/testdata/paths/includes/test.qdoc
new file mode 100644
index 000000000..700f1e819
--- /dev/null
+++ b/src/qdoc/qdoc/tests/config/testdata/paths/includes/test.qdoc
@@ -0,0 +1 @@
+/*! nothing here */
diff --git a/src/qdoc/qdoc/tests/config/testdata/paths/paths.qdocconf b/src/qdoc/qdoc/tests/config/testdata/paths/paths.qdocconf
new file mode 100644
index 000000000..d360f4c1e
--- /dev/null
+++ b/src/qdoc/qdoc/tests/config/testdata/paths/paths.qdocconf
@@ -0,0 +1,2 @@
+sourcedirs += does/not/exist
+sourcedirs = includes
diff --git a/src/qdoc/qdoc/tests/config/tst_config.cpp b/src/qdoc/qdoc/tests/config/tst_config.cpp
new file mode 100644
index 000000000..0e73130b8
--- /dev/null
+++ b/src/qdoc/qdoc/tests/config/tst_config.cpp
@@ -0,0 +1,180 @@
+// Copyright (C) 2020 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#include "qdoc/config.h"
+
+#include <QtCore/qdir.h>
+#include <QtCore/qfileinfo.h>
+#include <QtCore/qhash.h>
+#include <QtCore/qstringlist.h>
+#include <QtTest/QtTest>
+
+class tst_Config : public QObject
+{
+ Q_OBJECT
+
+private slots:
+ void classMembersInitializeToFalseOrEmpty();
+ void includePathsFromCommandLine();
+ void variables();
+ void paths();
+ void includepaths();
+ void getExampleProjectFile();
+ void expandVars();
+
+private:
+ Config &initConfig(const QStringList &args = QStringList(),
+ const char *qdocconf = nullptr);
+ Config &initConfig(const char *qdocconf)
+ {
+ return initConfig(QStringList(), qdocconf);
+ }
+};
+
+/*
+ Initializes the Config with optional arguments and a .qdocconf file
+ to load, and returns a reference to it.
+*/
+Config &tst_Config::initConfig(const QStringList &args, const char *qdocconf)
+{
+ QStringList fullArgs = { QStringLiteral("./qdoc") };
+ fullArgs << args;
+ Config::instance().init("QDoc Test", fullArgs);
+
+ if (qdocconf) {
+ const auto configFile = QFINDTESTDATA(qdocconf);
+ if (!configFile.isEmpty())
+ Config::instance().load(configFile);
+ }
+
+ return Config::instance();
+}
+
+void tst_Config::classMembersInitializeToFalseOrEmpty()
+{
+ auto &config = initConfig();
+ QCOMPARE(config.showInternal(), false);
+ QCOMPARE(config.singleExec(), false);
+
+ QVERIFY(config.defines().isEmpty());
+ QVERIFY(config.includePaths().isEmpty());
+ QVERIFY(config.dependModules().isEmpty());
+ QVERIFY(config.indexDirs().isEmpty());
+ QVERIFY(config.currentDir().isEmpty());
+ QVERIFY(config.previousCurrentDir().isEmpty());
+}
+
+void tst_Config::includePathsFromCommandLine()
+{
+ const auto mockIncludePath1 = QString("-I" + QDir().absoluteFilePath("/qt5/qtdoc/doc/."));
+ const auto mockIncludePath2 = QString("-I" + QDir().absoluteFilePath("/qt5/qtbase/mkspecs/linux-g++"));
+ const QStringList commandLineArgs = { mockIncludePath1, mockIncludePath2 };
+ auto &config = initConfig(commandLineArgs);
+
+ const QStringList expected = { mockIncludePath1, mockIncludePath2 };
+ const QStringList actual = config.includePaths();
+
+ QCOMPARE(actual, expected);
+}
+
+// Tests different types of variables; string, string list, bool, int,
+// empty and undefined variables, and subvariables.
+void tst_Config::variables()
+{
+ auto &config = initConfig("/testdata/configs/vars.qdocconf");
+
+ const QStringList list = { "testing", "line", "by\n", "line" };
+ QCOMPARE(config.get("list").asStringList(), list);
+ QCOMPARE(config.get("list").asString(), "testing line by\nline");
+ QCOMPARE(config.get("true").asBool(), true);
+ QCOMPARE(config.get("untrue").asBool(), false);
+ QCOMPARE(config.get("int").asInt(), 2);
+ QCOMPARE(config.get("void").asString(), QString());
+ QVERIFY(!config.get("void").asString().isNull());
+ QCOMPARE(config.get("void").asString("undefined"), QString());
+ QCOMPARE(config.get("undefined").asString("undefined"), "undefined");
+ QVERIFY(config.get("undefined").asString().isNull());
+
+ QSet<QString> subVars = { "thing", "where", "time" };
+ QCOMPARE(config.subVars("some"), subVars);
+}
+
+// Tests whether paths or variables are resolved correctly.
+void tst_Config::paths()
+{
+ auto &config = initConfig();
+ const auto docConfig = QFINDTESTDATA("/testdata/configs/paths.qdocconf");
+ if (!docConfig.isEmpty())
+ config.load(docConfig);
+
+ auto rootDir = QFileInfo(docConfig).dir();
+ QVERIFY(rootDir.cdUp());
+
+ const auto paths = config.getCanonicalPathList("sourcedirs");
+ QVERIFY(paths.size() == 3);
+
+ QCOMPARE(paths[0], rootDir.absoluteFilePath("paths/includes"));
+ QCOMPARE(paths[1], rootDir.absoluteFilePath("configs"));
+ QCOMPARE(paths[2], rootDir.absoluteFilePath("configs/includes"));
+}
+
+// Tests whether includepaths are resolved correctly
+void tst_Config::includepaths()
+{
+ auto &config = initConfig();
+ const auto docConfig = QFINDTESTDATA("/testdata/configs/includepaths.qdocconf");
+ if (!docConfig.isEmpty())
+ config.load(docConfig);
+
+ auto rootDir = QFileInfo(docConfig).dir();
+ QVERIFY(rootDir.cdUp());
+
+ const auto paths = config.getCanonicalPathList("includepaths",
+ Config::IncludePaths);
+ QVERIFY(paths.size() == 5);
+
+ QCOMPARE(paths[0], "-I" + rootDir.absoluteFilePath("includepaths/include"));
+ QCOMPARE(paths[0], paths[1]);
+ QCOMPARE(paths[2], "-I" + rootDir.absoluteFilePath("includepaths/include/more"));
+ QCOMPARE(paths[3], "-F" + rootDir.absoluteFilePath("includepaths/include/framework"));
+ QCOMPARE(paths[4], "-isystem" + rootDir.absoluteFilePath("includepaths/include/system"));
+}
+
+void::tst_Config::getExampleProjectFile()
+{
+ auto &config = initConfig();
+ const auto docConfig = QFINDTESTDATA("/testdata/configs/exampletest.qdocconf");
+ if (!docConfig.isEmpty())
+ config.load(docConfig);
+
+ auto rootDir = QFileInfo(docConfig).dir();
+ QVERIFY(rootDir.cd("../exampletest/examples/test"));
+
+ QVERIFY(config.getExampleProjectFile("invalid").isEmpty());
+ QVERIFY(config.getExampleProjectFile("test/empty").isEmpty());
+
+ QCOMPARE(config.getExampleProjectFile("test/example1"),
+ rootDir.absoluteFilePath("example1/example1.pro"));
+ QCOMPARE(config.getExampleProjectFile("test/example2"),
+ rootDir.absoluteFilePath("example2/example2.qmlproject"));
+ QCOMPARE(config.getExampleProjectFile("test/example3"),
+ rootDir.absoluteFilePath("example3/example3.pyproject"));
+ QCOMPARE(config.getExampleProjectFile("test/example4"),
+ rootDir.absoluteFilePath("example4/CMakeLists.txt"));
+}
+
+void::tst_Config::expandVars()
+{
+ qputenv("QDOC_TSTCONFIG_LIST", QByteArray("a b c"));
+ auto &config = initConfig("/testdata/configs/expandvars.qdocconf");
+
+ QCOMPARE(config.get("expanded1").asString(), "foo");
+ QCOMPARE(config.get("expanded2").asString(), "foo,bar");
+ QCOMPARE(config.get("expanded3").asString(), "foobar foobar baz");
+ QCOMPARE(config.get("literally").asString(), "$data ${data}");
+ QCOMPARE(config.get("csvlist").asString(), "a,b,c");
+}
+
+QTEST_APPLESS_MAIN(tst_Config)
+
+#include "tst_config.moc"
diff --git a/src/qdoc/qdoc/tests/generatedoutput/CMakeLists.txt b/src/qdoc/qdoc/tests/generatedoutput/CMakeLists.txt
new file mode 100644
index 000000000..1dea3ae59
--- /dev/null
+++ b/src/qdoc/qdoc/tests/generatedoutput/CMakeLists.txt
@@ -0,0 +1,46 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+# It's mostly manually written.
+
+#####################################################################
+## tst_generatedOutput Test:
+#####################################################################
+
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_generatedOutput LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
+qt_internal_add_test(tst_generatedOutput
+ SOURCES
+ ${CMAKE_CURRENT_LIST_DIR}/tst_generatedoutput.cpp
+)
+
+# Write relevant Qt include path to a file, to be read in by QDoc
+set(config_subfolder "")
+get_cmake_property(is_multi_config GENERATOR_IS_MULTI_CONFIG)
+if(is_multi_config)
+ set(config_subfolder "$<CONFIG>/")
+endif()
+set(includepathsfile "${CMAKE_CURRENT_BINARY_DIR}/${config_subfolder}qdocincludepaths.inc")
+set(framework_path "\n")
+
+find_package(Qt6 COMPONENTS Core REQUIRED)
+if(Qt6Core_FOUND)
+ get_target_property(include_paths Qt6::Core INTERFACE_INCLUDE_DIRECTORIES)
+endif()
+
+while(include_paths)
+ list(POP_BACK include_paths inc_path)
+ if(inc_path MATCHES "(.+)/QtCore\.framework$")
+ string(APPEND framework_path "-F${CMAKE_MATCH_1}")
+ break()
+ endif()
+endwhile()
+
+set (include_paths "$<TARGET_PROPERTY:tst_generatedOutput,INCLUDE_DIRECTORIES>")
+file(GENERATE OUTPUT ${includepathsfile} CONTENT "-I$<JOIN:${include_paths},\n-I>${framework_path}")
+
+add_dependencies(tst_generatedOutput Qt::qdoc)
diff --git a/src/qdoc/qdoc/tests/generatedoutput/expected_output/autolinking.html b/src/qdoc/qdoc/tests/generatedoutput/expected_output/autolinking.html
new file mode 100644
index 000000000..ac04c33cd
--- /dev/null
+++ b/src/qdoc/qdoc/tests/generatedoutput/expected_output/autolinking.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+<!-- classlists.qdoc -->
+ <title>Autolinking | TestCPP</title>
+</head>
+<body>
+<div class="sidebar">
+<div class="toc">
+<h3 id="toc">Contents</h3>
+<ul>
+<li class="level1"><a href="#testqdoc">TestQDoc</a></li>
+<li class="level1"><a href="#someprop">someProp</a></li>
+</ul>
+</div>
+<div class="sidebar-content" id="sidebar-content"></div></div>
+<h1 class="title">Autolinking</h1>
+<!-- $$$autolinking.html-description -->
+<div class="descr" id="details">
+<h2 id="testqdoc">TestQDoc</h2>
+<p>The string <a href="testqdoc.html" translate="no">TestQDoc</a> links to the C++ namespace unless linking explicitly, <a href="autolinking.html#testqdoc">like this</a>, or <a href="testqdoc.html" translate="no">this</a>. Also,</p>
+<p>Autolinks:</p>
+<ul>
+<li><a href="testqdoc-testderived.html" translate="no">TestQDoc::TestDerived</a></li>
+</ul>
+<p>Explicit links:</p>
+<ul>
+<li><a href="testqdoc-testderived.html" translate="no">TestQDoc::TestDerived</a></li>
+<li><a href="obsolete-classes.html#testqdoc">Obsolete Classes#TestQDoc</a></li>
+</ul>
+<h2 id="someprop">someProp</h2>
+</div>
+<!-- @@@autolinking.html -->
+</body>
+</html>
diff --git a/src/qdoc/qdoc/tests/generatedoutput/expected_output/cpptypes.html b/src/qdoc/qdoc/tests/generatedoutput/expected_output/cpptypes.html
new file mode 100644
index 000000000..8d91afd76
--- /dev/null
+++ b/src/qdoc/qdoc/tests/generatedoutput/expected_output/cpptypes.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+<!-- classlists.qdoc -->
+ <title>Test C++ Types | TestCPP</title>
+</head>
+<body>
+<div class="sidebar"><div class="sidebar-content" id="sidebar-content"></div></div>
+<h1 class="title">Test C++ Types</h1>
+<!-- $$$cpptypes-description -->
+<div class="descr" id="details">
+<ul>
+<li translate="no"><a href="testqdoc-test.html">TestQDoc::Test</a></li>
+<li translate="no"><a href="testqdoc-test.html#QDOCTEST_MACRO2">TestQDoc::Test::QDOCTEST_MACRO2</a></li>
+<li translate="no"><a href="testqdoc-test.html#operator-eq">TestQDoc::Test::operator=()</a></li>
+<li translate="no"><a href="testqdoc-test.html#someFunctionDefaultArg">TestQDoc::Test::someFunctionDefaultArg()</a></li>
+</ul>
+</div>
+<!-- @@@cpptypes -->
+<div class="table"><table class="annotated">
+<tr class="odd topAlign"><td class="tblName" translate="no"><p><a href="testqdoc-test.html">TestQDoc::Test</a></p></td><td class="tblDescr"><p>A class in a namespace</p></td></tr>
+</table></div>
+</body>
+</html>
diff --git a/src/qdoc/qdoc/tests/generatedoutput/expected_output/crossmodule/crossmodule/all-namespaces.html b/src/qdoc/qdoc/tests/generatedoutput/expected_output/crossmodule/crossmodule/all-namespaces.html
new file mode 100644
index 000000000..8d23c2aef
--- /dev/null
+++ b/src/qdoc/qdoc/tests/generatedoutput/expected_output/crossmodule/crossmodule/all-namespaces.html
@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+<!-- namespaces.qdoc -->
+ <title>Namespaces | CrossModule</title>
+</head>
+<body>
+<div class="sidebar"><div class="sidebar-content" id="sidebar-content"></div></div>
+<h1 class="title">Namespaces</h1>
+<!-- $$$all-namespaces.html-description -->
+<div class="descr" id="details">
+<div class="table"><table class="annotated">
+<tr class="odd topAlign"><td class="tblName" translate="no"><p><a href="crossmoduleref.html">CrossModuleRef</a></p></td><td class="tblDescr"><p>Namespace that has documented functions in multiple modules</p></td></tr>
+<tr class="even topAlign"><td class="tblName" translate="no"><p><a href="testqdoc.html">TestQDoc</a></p></td><td class="tblDescr"><p>A namespace</p></td></tr>
+</table></div>
+</div>
+<!-- @@@all-namespaces.html -->
+</body>
+</html>
diff --git a/src/qdoc/qdoc/tests/generatedoutput/expected_output/crossmodule/crossmodule/testtype-members.html b/src/qdoc/qdoc/tests/generatedoutput/expected_output/crossmodule/crossmodule/testtype-members.html
new file mode 100644
index 000000000..0cb92b933
--- /dev/null
+++ b/src/qdoc/qdoc/tests/generatedoutput/expected_output/crossmodule/crossmodule/testtype-members.html
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+<!-- testtype.cpp -->
+ <meta name="description" content="A class inheriting another class that lives in an external doc module.">
+ <title>List of All Members for TestType | CrossModule</title>
+</head>
+<body>
+<li>TestType</li>
+<div class="sidebar"><div class="sidebar-content" id="sidebar-content"></div></div>
+<h1 class="title" translate="no">List of All Members for TestType</h1>
+<p>This is the complete list of members for <a href="testtype.html">TestType</a>, including inherited members.</p>
+<div class="table"><table class="propsummary" translate="no">
+<tr><td class="topAlign"><ul>
+<li class="fn" translate="no"><span class="name"><b><a href="testqdoc-testderived.html#DerivedType-typedef" translate="no">DerivedType</a></b></span></li>
+<li class="fn" translate="no"><span class="name"><b><a href="testqdoc-testderived.html#NotTypedef-typedef" translate="no">NotTypedef</a></b></span></li>
+<li class="fn" translate="no"><span class="name"><b><a href="testqdoc-test.html#SomeType-typedef" translate="no">SomeType</a></b></span></li>
+<li class="fn" translate="no"><span class="name"><b><a href="testqdoc-test.html#funcPtr" translate="no">funcPtr</a></b></span>(bool, const char *) : void (*)(bool)</li>
+<li class="fn" translate="no"><span class="name"><b><a href="testqdoc-test.html#inlineFunction" translate="no">inlineFunction</a></b></span>()</li>
+<li class="fn" translate="no"><span class="name"><b><a href="testqdoc-test.html#methodWithEmDashInItsDocs" translate="no">methodWithEmDashInItsDocs</a></b></span>()</li>
+<li class="fn" translate="no"><span class="name"><b><a href="testqdoc-test.html#methodWithEnDashInItsDocs" translate="no">methodWithEnDashInItsDocs</a></b></span>()</li>
+<li class="fn" translate="no"><span class="name"><b><a href="testtype.html#nothing" translate="no">nothing</a></b></span>()</li>
+</ul></td><td class="topAlign"><ul>
+<li class="fn" translate="no"><span class="name"><b><a href="testqdoc-test.html#overload" translate="no">overload</a></b></span>()</li>
+<li class="fn" translate="no"><span class="name"><b><a href="testqdoc-test.html#overload-1" translate="no">overload</a></b></span>(bool)</li>
+<li class="fn" translate="no"><span class="name"><b><a href="testqdoc-test.html#someFunction" translate="no">someFunction</a></b></span>(int, int) : int</li>
+<li class="fn" translate="no"><span class="name"><b><a href="testqdoc-test.html#someFunctionDefaultArg" translate="no">someFunctionDefaultArg</a></b></span>(int, bool) const</li>
+<li class="fn" translate="no"><span class="name"><b><a href="testqdoc-testderived.html#someValue" translate="no">someValue</a></b></span>() : TestQDoc::TestDerived::NotTypedef</li>
+<li class="fn" translate="no"><span class="name"><b><a href="testqdoc-testderived.html#virtualFun" translate="no">virtualFun</a></b></span>()</li>
+<li class="fn" translate="no"><span class="name"><b><a href="testqdoc-test.html#virtualFun" translate="no">virtualFun</a></b></span>()</li>
+<li class="fn" translate="no"><span class="name"><b><a href="testqdoc-test.html#operator-eq" translate="no">operator=</a></b></span>(TestQDoc::Test &amp;&amp;) : TestQDoc::Test &amp;</li>
+</ul>
+</td></tr>
+</table></div>
+</body>
+</html>
diff --git a/src/qdoc/qdoc/tests/generatedoutput/expected_output/crossmodule/crossmodule/testtype.html b/src/qdoc/qdoc/tests/generatedoutput/expected_output/crossmodule/crossmodule/testtype.html
new file mode 100644
index 000000000..ed8bfc96c
--- /dev/null
+++ b/src/qdoc/qdoc/tests/generatedoutput/expected_output/crossmodule/crossmodule/testtype.html
@@ -0,0 +1,67 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+<!-- testtype.cpp -->
+ <meta name="description" content="A class inheriting another class that lives in an external doc module.">
+ <title>TestType Class | CrossModule</title>
+</head>
+<body>
+<li>TestType</li>
+<div class="sidebar">
+<div class="toc">
+<h3 id="toc">Contents</h3>
+<ul>
+<li class="level1"><a href="#public-functions">Public Functions</a></li>
+<li class="level1"><a href="#details">Detailed Description</a></li>
+<li class="level2"><a href="#linking">Linking</a></li>
+<li class="level2"><a href="#generated-lists">Generated Lists</a></li>
+</ul>
+</div>
+<div class="sidebar-content" id="sidebar-content"></div></div>
+<h1 class="title" translate="no">TestType Class</h1>
+<!-- $$$TestType-brief -->
+<p>A class inheriting another class that lives in an external doc module. <a href="#details">More...</a></p>
+<!-- @@@TestType -->
+<div class="table"><table class="alignedsummary" translate="no">
+<tr><td class="memItemLeft rightAlign topAlign"> Header:</td><td class="memItemRight bottomAlign"> <span class="preprocessor">#include &lt;TestType&gt;</span></td></tr>
+<tr><td class="memItemLeft rightAlign topAlign"> Inherits:</td><td class="memItemRight bottomAlign"> <a href="testqdoc-testderived.html" translate="no">TestQDoc::TestDerived</a></td></tr>
+</table></div>
+<ul>
+<li><a href="testtype-members.html">List of all members, including inherited members</a></li>
+</ul>
+<h2 id="public-functions">Public Functions</h2>
+<div class="table"><table class="alignedsummary" translate="no">
+<tr><td class="memItemLeft rightAlign topAlign"> void </td><td class="memItemRight bottomAlign"><b><a href="testtype.html#nothing" translate="no">nothing</a></b>()</td></tr>
+</table></div>
+<!-- $$$TestType-description -->
+<div class="descr">
+<h2 id="details">Detailed Description</h2>
+<h3 id="linking">Linking</h3>
+<p>These links go to the parent class:</p>
+<ul>
+<li><a href="testqdoc-testderived.html" translate="no">TestQDoc::TestDerived</a></li>
+<li><a href="testqdoc-test.html" translate="no">Test</a> class <a href="testqdoc.html#usage" translate="no">Usage</a>.</li>
+<li><a href="testqdoc.html#QDOCTEST_MACRO" translate="no">QDOCTEST_MACRO</a></li>
+<li>DontLinkToMe</li>
+</ul>
+<h3 id="generated-lists">Generated Lists</h3>
+<p>This is an annotated list of entries in a group: <div class="table"><table class="annotated">
+<tr class="odd topAlign"><td class="tblName" translate="no"><p><a href="testqdoc-test.html">TestQDoc::Test</a></p></td><td class="tblDescr"><p>A class in a namespace</p></td></tr>
+<tr class="even topAlign"><td class="tblName" translate="no"><p><a href="testqdoc-test.html#QDOCTEST_MACRO2">TestQDoc::Test::QDOCTEST_MACRO2</a></p></td><td class="tblDescr"><p>A macro with argument x</p></td></tr>
+<tr class="odd topAlign"><td class="tblName" translate="no"><p><a href="testqdoc-test.html#operator-eq">TestQDoc::Test::operator=()</a></p></td></tr>
+<tr class="even topAlign"><td class="tblName" translate="no"><p><a href="testqdoc-test.html#someFunctionDefaultArg">TestQDoc::Test::someFunctionDefaultArg()</a></p></td></tr>
+</table></div>
+</p>
+</div>
+<p><b>See also </b><a href="testqdoc-test.html#someFunction" translate="no">someFunction</a>().</p>
+<!-- @@@TestType -->
+<div class="func">
+<h2>Member Function Documentation</h2>
+<!-- $$$nothing[overload1]$$$nothing -->
+<h3 class="fn" translate="no" id="nothing"><span class="type">void</span> TestType::<span class="name">nothing</span>()</h3>
+<p>Nothing to see here.</p>
+<!-- @@@nothing -->
+</div>
+</body>
+</html>
diff --git a/src/qdoc/qdoc/tests/generatedoutput/expected_output/crossmodule/crossmoduleref-sub-crossmodule.html b/src/qdoc/qdoc/tests/generatedoutput/expected_output/crossmodule/crossmoduleref-sub-crossmodule.html
new file mode 100644
index 000000000..7d40a92d6
--- /dev/null
+++ b/src/qdoc/qdoc/tests/generatedoutput/expected_output/crossmodule/crossmoduleref-sub-crossmodule.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+ <title>CrossModuleRef Namespace | CrossModule</title>
+</head>
+<body>
+<div class="sidebar">
+<div class="toc">
+<h3 id="toc">Contents</h3>
+<ul>
+<li class="level1"><a href="#functions">Functions</a></li>
+</ul>
+</div>
+<div class="sidebar-content" id="sidebar-content"></div></div>
+<h1 class="title" translate="no">CrossModuleRef Namespace</h1>
+<p>The CrossModuleRef namespace includes the following elements from module CrossModule. The full namespace is documented in module TestCPP<a href="crossmoduleref.html" translate="no"> here.</a></p>
+<h2 id="functions">Functions</h2>
+<div class="table"><table class="alignedsummary" translate="no">
+<tr><td class="memItemLeft rightAlign topAlign"> void </td><td class="memItemRight bottomAlign"><b><a href="crossmoduleref-sub-crossmodule.html#documentMeToo" translate="no">documentMeToo</a></b>()</td></tr>
+</table></div>
+<div class="func">
+<h2>Function Documentation</h2>
+<!-- $$$documentMeToo[overload1]$$$documentMeToo -->
+<h3 class="fn" translate="no" id="documentMeToo"><span class="type">void</span> CrossModuleRef::<span class="name">documentMeToo</span>()</h3>
+<p>Function under a namespace that's documented elsewhere.</p>
+<!-- @@@documentMeToo -->
+</div>
+</body>
+</html>
diff --git a/src/qdoc/qdoc/tests/generatedoutput/expected_output/crossmodule/testtype-members.html b/src/qdoc/qdoc/tests/generatedoutput/expected_output/crossmodule/testtype-members.html
new file mode 100644
index 000000000..0cb92b933
--- /dev/null
+++ b/src/qdoc/qdoc/tests/generatedoutput/expected_output/crossmodule/testtype-members.html
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+<!-- testtype.cpp -->
+ <meta name="description" content="A class inheriting another class that lives in an external doc module.">
+ <title>List of All Members for TestType | CrossModule</title>
+</head>
+<body>
+<li>TestType</li>
+<div class="sidebar"><div class="sidebar-content" id="sidebar-content"></div></div>
+<h1 class="title" translate="no">List of All Members for TestType</h1>
+<p>This is the complete list of members for <a href="testtype.html">TestType</a>, including inherited members.</p>
+<div class="table"><table class="propsummary" translate="no">
+<tr><td class="topAlign"><ul>
+<li class="fn" translate="no"><span class="name"><b><a href="testqdoc-testderived.html#DerivedType-typedef" translate="no">DerivedType</a></b></span></li>
+<li class="fn" translate="no"><span class="name"><b><a href="testqdoc-testderived.html#NotTypedef-typedef" translate="no">NotTypedef</a></b></span></li>
+<li class="fn" translate="no"><span class="name"><b><a href="testqdoc-test.html#SomeType-typedef" translate="no">SomeType</a></b></span></li>
+<li class="fn" translate="no"><span class="name"><b><a href="testqdoc-test.html#funcPtr" translate="no">funcPtr</a></b></span>(bool, const char *) : void (*)(bool)</li>
+<li class="fn" translate="no"><span class="name"><b><a href="testqdoc-test.html#inlineFunction" translate="no">inlineFunction</a></b></span>()</li>
+<li class="fn" translate="no"><span class="name"><b><a href="testqdoc-test.html#methodWithEmDashInItsDocs" translate="no">methodWithEmDashInItsDocs</a></b></span>()</li>
+<li class="fn" translate="no"><span class="name"><b><a href="testqdoc-test.html#methodWithEnDashInItsDocs" translate="no">methodWithEnDashInItsDocs</a></b></span>()</li>
+<li class="fn" translate="no"><span class="name"><b><a href="testtype.html#nothing" translate="no">nothing</a></b></span>()</li>
+</ul></td><td class="topAlign"><ul>
+<li class="fn" translate="no"><span class="name"><b><a href="testqdoc-test.html#overload" translate="no">overload</a></b></span>()</li>
+<li class="fn" translate="no"><span class="name"><b><a href="testqdoc-test.html#overload-1" translate="no">overload</a></b></span>(bool)</li>
+<li class="fn" translate="no"><span class="name"><b><a href="testqdoc-test.html#someFunction" translate="no">someFunction</a></b></span>(int, int) : int</li>
+<li class="fn" translate="no"><span class="name"><b><a href="testqdoc-test.html#someFunctionDefaultArg" translate="no">someFunctionDefaultArg</a></b></span>(int, bool) const</li>
+<li class="fn" translate="no"><span class="name"><b><a href="testqdoc-testderived.html#someValue" translate="no">someValue</a></b></span>() : TestQDoc::TestDerived::NotTypedef</li>
+<li class="fn" translate="no"><span class="name"><b><a href="testqdoc-testderived.html#virtualFun" translate="no">virtualFun</a></b></span>()</li>
+<li class="fn" translate="no"><span class="name"><b><a href="testqdoc-test.html#virtualFun" translate="no">virtualFun</a></b></span>()</li>
+<li class="fn" translate="no"><span class="name"><b><a href="testqdoc-test.html#operator-eq" translate="no">operator=</a></b></span>(TestQDoc::Test &amp;&amp;) : TestQDoc::Test &amp;</li>
+</ul>
+</td></tr>
+</table></div>
+</body>
+</html>
diff --git a/src/qdoc/qdoc/tests/generatedoutput/expected_output/crossmodule/testtype.html b/src/qdoc/qdoc/tests/generatedoutput/expected_output/crossmodule/testtype.html
new file mode 100644
index 000000000..ed8bfc96c
--- /dev/null
+++ b/src/qdoc/qdoc/tests/generatedoutput/expected_output/crossmodule/testtype.html
@@ -0,0 +1,67 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+<!-- testtype.cpp -->
+ <meta name="description" content="A class inheriting another class that lives in an external doc module.">
+ <title>TestType Class | CrossModule</title>
+</head>
+<body>
+<li>TestType</li>
+<div class="sidebar">
+<div class="toc">
+<h3 id="toc">Contents</h3>
+<ul>
+<li class="level1"><a href="#public-functions">Public Functions</a></li>
+<li class="level1"><a href="#details">Detailed Description</a></li>
+<li class="level2"><a href="#linking">Linking</a></li>
+<li class="level2"><a href="#generated-lists">Generated Lists</a></li>
+</ul>
+</div>
+<div class="sidebar-content" id="sidebar-content"></div></div>
+<h1 class="title" translate="no">TestType Class</h1>
+<!-- $$$TestType-brief -->
+<p>A class inheriting another class that lives in an external doc module. <a href="#details">More...</a></p>
+<!-- @@@TestType -->
+<div class="table"><table class="alignedsummary" translate="no">
+<tr><td class="memItemLeft rightAlign topAlign"> Header:</td><td class="memItemRight bottomAlign"> <span class="preprocessor">#include &lt;TestType&gt;</span></td></tr>
+<tr><td class="memItemLeft rightAlign topAlign"> Inherits:</td><td class="memItemRight bottomAlign"> <a href="testqdoc-testderived.html" translate="no">TestQDoc::TestDerived</a></td></tr>
+</table></div>
+<ul>
+<li><a href="testtype-members.html">List of all members, including inherited members</a></li>
+</ul>
+<h2 id="public-functions">Public Functions</h2>
+<div class="table"><table class="alignedsummary" translate="no">
+<tr><td class="memItemLeft rightAlign topAlign"> void </td><td class="memItemRight bottomAlign"><b><a href="testtype.html#nothing" translate="no">nothing</a></b>()</td></tr>
+</table></div>
+<!-- $$$TestType-description -->
+<div class="descr">
+<h2 id="details">Detailed Description</h2>
+<h3 id="linking">Linking</h3>
+<p>These links go to the parent class:</p>
+<ul>
+<li><a href="testqdoc-testderived.html" translate="no">TestQDoc::TestDerived</a></li>
+<li><a href="testqdoc-test.html" translate="no">Test</a> class <a href="testqdoc.html#usage" translate="no">Usage</a>.</li>
+<li><a href="testqdoc.html#QDOCTEST_MACRO" translate="no">QDOCTEST_MACRO</a></li>
+<li>DontLinkToMe</li>
+</ul>
+<h3 id="generated-lists">Generated Lists</h3>
+<p>This is an annotated list of entries in a group: <div class="table"><table class="annotated">
+<tr class="odd topAlign"><td class="tblName" translate="no"><p><a href="testqdoc-test.html">TestQDoc::Test</a></p></td><td class="tblDescr"><p>A class in a namespace</p></td></tr>
+<tr class="even topAlign"><td class="tblName" translate="no"><p><a href="testqdoc-test.html#QDOCTEST_MACRO2">TestQDoc::Test::QDOCTEST_MACRO2</a></p></td><td class="tblDescr"><p>A macro with argument x</p></td></tr>
+<tr class="odd topAlign"><td class="tblName" translate="no"><p><a href="testqdoc-test.html#operator-eq">TestQDoc::Test::operator=()</a></p></td></tr>
+<tr class="even topAlign"><td class="tblName" translate="no"><p><a href="testqdoc-test.html#someFunctionDefaultArg">TestQDoc::Test::someFunctionDefaultArg()</a></p></td></tr>
+</table></div>
+</p>
+</div>
+<p><b>See also </b><a href="testqdoc-test.html#someFunction" translate="no">someFunction</a>().</p>
+<!-- @@@TestType -->
+<div class="func">
+<h2>Member Function Documentation</h2>
+<!-- $$$nothing[overload1]$$$nothing -->
+<h3 class="fn" translate="no" id="nothing"><span class="type">void</span> TestType::<span class="name">nothing</span>()</h3>
+<p>Nothing to see here.</p>
+<!-- @@@nothing -->
+</div>
+</body>
+</html>
diff --git a/src/qdoc/qdoc/tests/generatedoutput/expected_output/includefromexampledirs/index.html b/src/qdoc/qdoc/tests/generatedoutput/expected_output/includefromexampledirs/index.html
new file mode 100644
index 000000000..0d984f60d
--- /dev/null
+++ b/src/qdoc/qdoc/tests/generatedoutput/expected_output/includefromexampledirs/index.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+<!-- includefromparent.qdoc -->
+ <title>doc index | Test</title>
+</head>
+<body>
+<h1 class="title">doc index</h1>
+<!-- $$$index.html-description -->
+<div class="descr" id="details">
+<h2 id="c-classes">C++ Classes</h2>
+<div class="table"><table class="annotated">
+<tr class="odd topAlign"><td class="tblName" translate="no"><p><a href="testqdoc-test.html">TestQDoc::Test</a></p></td><td class="tblDescr"><p>A class in a namespace</p></td></tr>
+<tr class="even topAlign"><td class="tblName" translate="no"><p><a href="testqdoc-testderived.html">TestQDoc::TestDerived</a></p></td><td class="tblDescr"><p>A class in a namespace, derived from Test</p></td></tr>
+</table></div>
+<h2 id="qml-types">QML Types</h2>
+<div class="table"><table class="annotated">
+<tr class="odd topAlign"><td class="tblName" translate="no"><p><a href="qml-qdoc-test-abstractparent.html">AbstractParent</a></p></td><td class="tblDescr"><p>Abstract base QML type</p></td></tr>
+<tr class="even topAlign"><td class="tblName" translate="no"><p><a href="qml-qdoc-test-child.html">Child</a></p></td><td class="tblDescr"><p>A Child inheriting its parent</p></td></tr>
+<tr class="odd topAlign"><td class="tblName" translate="no"><p><a href="qml-int.html">int</a></p></td><td class="tblDescr"><p>An integer value type</p></td></tr>
+</table></div>
+<p>Test include file that is part of the sourcedirs.</p>
+</div>
+<!-- @@@index.html -->
+</body>
+</html>
diff --git a/src/qdoc/qdoc/tests/generatedoutput/expected_output/includefromexampledirs/qml-qdoc-test-abstractparent-members.html b/src/qdoc/qdoc/tests/generatedoutput/expected_output/includefromexampledirs/qml-qdoc-test-abstractparent-members.html
new file mode 100644
index 000000000..beb7792f7
--- /dev/null
+++ b/src/qdoc/qdoc/tests/generatedoutput/expected_output/includefromexampledirs/qml-qdoc-test-abstractparent-members.html
@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+<!-- includefromparent.qdoc -->
+ <meta name="description" content="Abstract base QML type.">
+ <title>List of All Members for AbstractParent | Test</title>
+</head>
+<body>
+<li><a href="qdoc-test-qmlmodule.html" translate="no">Types</a></li>
+<li>AbstractParent</li>
+<div class="sidebar"><div class="sidebar-content" id="sidebar-content"></div></div>
+<h1 class="title" translate="no">List of All Members for AbstractParent</h1>
+<p>This is the complete list of members for <a href="qml-qdoc-test-abstractparent.html">AbstractParent</a>, including inherited members.</p>
+<ul>
+<li class="fn" translate="no"><b><a href="qml-qdoc-test-abstractparent.html#children-prop" translate="no">children</a></b> : list&lt;Child&gt; [default]</li>
+<li class="fn" translate="no">void <b><a href="qml-qdoc-test-abstractparent.html#rear-method" translate="no">rear</a></b>(Child <i>child</i>)</li>
+</ul>
+</body>
+</html>
diff --git a/src/qdoc/qdoc/tests/generatedoutput/expected_output/includefromexampledirs/qml-qdoc-test-abstractparent.html b/src/qdoc/qdoc/tests/generatedoutput/expected_output/includefromexampledirs/qml-qdoc-test-abstractparent.html
new file mode 100644
index 000000000..6d6d47838
--- /dev/null
+++ b/src/qdoc/qdoc/tests/generatedoutput/expected_output/includefromexampledirs/qml-qdoc-test-abstractparent.html
@@ -0,0 +1,68 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+<!-- includefromparent.qdoc -->
+ <meta name="description" content="Abstract base QML type.">
+ <title>AbstractParent QML Type | Test</title>
+</head>
+<body>
+<li><a href="qdoc-test-qmlmodule.html" translate="no">Types</a></li>
+<li>AbstractParent</li>
+<div class="sidebar">
+<div class="toc">
+<h3 id="toc">Contents</h3>
+<ul>
+<li class="level1"><a href="#properties">Properties</a></li>
+<li class="level1"><a href="#methods">Methods</a></li>
+<li class="level1"><a href="#details">Detailed Description</a></li>
+</ul>
+</div>
+<div class="sidebar-content" id="sidebar-content"></div></div>
+<h1 class="title" translate="no">AbstractParent QML Type</h1>
+<!-- $$$AbstractParent-brief -->
+<p>Abstract base QML type. <a href="#details">More...</a></p>
+<!-- @@@AbstractParent -->
+<div class="table"><table class="alignedsummary" translate="no">
+<tr><td class="memItemLeft rightAlign topAlign"> Import Statement:</td><td class="memItemRight bottomAlign"> import QDoc.Test 1.1</td></tr><tr><td class="memItemLeft rightAlign topAlign"> Since:</td><td class="memItemRight bottomAlign"> Qt 1.1</td></tr><tr><td class="memItemLeft rightAlign topAlign"> Inherited By:</td><td class="memItemRight bottomAlign"> <p><a href="qml-qdoc-test-child.html" translate="no">Child</a></p>
+</td></tr></table></div><ul>
+<li><a href="qml-qdoc-test-abstractparent-members.html">List of all members, including inherited members</a></li>
+</ul>
+<h2 id="properties">Properties</h2>
+<ul>
+<li class="fn" translate="no"><b><a href="qml-qdoc-test-abstractparent.html#children-prop" translate="no">children</a></b> : list&lt;Child&gt;</li>
+</ul>
+<h2 id="methods">Methods</h2>
+<ul>
+<li class="fn" translate="no">void <b><a href="qml-qdoc-test-abstractparent.html#rear-method" translate="no">rear</a></b>(Child <i>child</i>)</li>
+</ul>
+<!-- $$$AbstractParent-description -->
+<h2 id="details">Detailed Description</h2>
+<p>Test include file that is part of the sourcedirs.</p>
+<!-- @@@AbstractParent -->
+<h2>Property Documentation</h2>
+<!-- $$$children -->
+<div class="qmlitem"><div class="qmlproto" translate="no">
+<div class="table"><table class="qmlname">
+<tr valign="top" class="odd" id="children-prop">
+<td class="tblQmlPropNode"><p>
+<span class="name">children</span> : <span class="type">list</span>&lt;<span class="type"><a href="qml-qdoc-test-child.html" translate="no">Child</a></span>&gt; <code class="details extra" translate="no">[default]</code></p></td></tr>
+</table></div></div>
+<div class="qmldoc"><p>Children of the type.</p>
+<p>Test include file that is part of the sourcedirs.</p>
+</div></div><!-- @@@children -->
+<br/>
+<h2>Method Documentation</h2>
+<!-- $$$rear[overload1]$$$rearChild -->
+<div class="qmlitem"><div class="qmlproto" translate="no">
+<div class="table"><table class="qmlname">
+<tr valign="top" class="odd" id="rear-method">
+<td class="tblQmlFuncNode"><p>
+<span class="type">void</span> <span class="name">rear</span>(<span class="type"><a href="qml-qdoc-test-child.html" translate="no">Child</a></span> <i>child</i>)</p></td></tr>
+</table></div></div>
+<div class="qmldoc"><p>Do some abstract parenting on <i translate="no">child</i>.</p>
+<p>Test include file that is part of the sourcedirs.</p>
+</div></div><!-- @@@rear -->
+<br/>
+</body>
+</html>
diff --git a/src/qdoc/qdoc/tests/generatedoutput/expected_output/index-linking.html b/src/qdoc/qdoc/tests/generatedoutput/expected_output/index-linking.html
new file mode 100644
index 000000000..694a3f061
--- /dev/null
+++ b/src/qdoc/qdoc/tests/generatedoutput/expected_output/index-linking.html
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+<!-- linking.qdoc -->
+ <title>Linking | IndexLinking</title>
+</head>
+<body>
+<div class="sidebar">
+<div class="toc">
+<h3 id="toc">Contents</h3>
+<ul>
+<li class="level1"><a href="#qml-properties">QML properties</a></li>
+<li class="level1"><a href="#auto-linking-to-types-in-code-snippets">Auto-linking to types in code snippets</a></li>
+</ul>
+</div>
+<div class="sidebar-content" id="sidebar-content"></div></div>
+<h1 class="title">Linking</h1>
+<!-- $$$index-linking.html-description -->
+<div class="descr" id="details">
+<h2 id="qml-properties">QML properties</h2>
+<ol class="A" type="A"><li>Property group: <a href="https://doc.qt.io/QmlPropertyGroups/qml-qdoc-test-parent.html#group-prop" translate="no">Parent::group</a>.</li>
+<li>Property in a group: <a href="https://doc.qt.io/QmlPropertyGroups/qml-qdoc-test-parent.html#group.c-prop" translate="no">QDoc.Test::Parent::group.c</a>.</li>
+</ol>
+<h2 id="auto-linking-to-types-in-code-snippets">Auto-linking to types in code snippets</h2>
+<pre class="qml" translate="no">import QtQuick
+
+<span class="type"><a href="https://doc.qt.io/QmlPropertyGroups/qml-uicomponents-progressbar.html" translate="no">ProgressBar</a></span> {
+ <span class="comment">// Linking to int value type</span>
+ property <span class="type"><a href="https://doc.qt.io/QmlPropertyGroups/qml-int.html" translate="no">int</a></span> <span class="name">progress</span>: <span class="number">0</span>
+}</pre>
+<pre class="cpp" translate="no"><span class="comment">// 'int' should not link anywhere in C++ code</span>
+<span class="type">int</span> main() { <span class="type">int</span> x{<span class="number">0</span>}; <span class="keyword">return</span> x; }</pre>
+</div>
+<!-- @@@index-linking.html -->
+</body>
+</html>
diff --git a/src/qdoc/qdoc/tests/generatedoutput/expected_output/noautolist-docbook/qdoc-test-qmlmodule.xml b/src/qdoc/qdoc/tests/generatedoutput/expected_output/noautolist-docbook/qdoc-test-qmlmodule.xml
new file mode 100644
index 000000000..73a5f9653
--- /dev/null
+++ b/src/qdoc/qdoc/tests/generatedoutput/expected_output/noautolist-docbook/qdoc-test-qmlmodule.xml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<db:article xmlns:db="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" version="5.2" xml:lang="en">
+<db:info>
+<db:title>QDoc.Test QML Module</db:title>
+<db:productname>Test</db:productname>
+<db:titleabbrev>A test project for QDoc build artifacts</db:titleabbrev>
+<db:abstract>
+<db:para>QML Types for the Test module.</db:para>
+<db:para><db:emphasis role="bold">This module is under development and is subject to change.</db:emphasis></db:para>
+<db:para>This module was introduced in Qt 1.1.</db:para>
+</db:abstract>
+</db:info>
+<db:para><db:emphasis role="bold">This module is under development and is subject to change.</db:emphasis></db:para>
+<db:para>This module was introduced in Qt 1.1.</db:para>
+<db:anchor xml:id="details"/>
+</db:article>
diff --git a/src/qdoc/qdoc/tests/generatedoutput/expected_output/noautolist-docbook/test-componentset-example.xml b/src/qdoc/qdoc/tests/generatedoutput/expected_output/noautolist-docbook/test-componentset-example.xml
new file mode 100644
index 000000000..609b7cec9
--- /dev/null
+++ b/src/qdoc/qdoc/tests/generatedoutput/expected_output/noautolist-docbook/test-componentset-example.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<db:article xmlns:db="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" version="5.2" xml:lang="en">
+<db:info>
+<db:title>QML Documentation Example</db:title>
+<db:productname>Test</db:productname>
+<db:titleabbrev>A test project for QDoc build artifacts</db:titleabbrev>
+<db:abstract>
+<db:para>Example for documenting QML types.</db:para>
+</db:abstract>
+</db:info>
+<db:para>This example demonstrates one of the ways to document QML types. It also generates a warning about a missing example image, on purpose.</db:para>
+<db:para>In particular, there are sample types that are documented with QDoc commands comments. There are documentation comments for the QML types and their public interfaces. The types are grouped into a module, the <db:link xlink:href="uicomponents-qmlmodule.xml">UI Components</db:link> module.</db:para>
+<db:para>The uicomponents.qdoc file generates the overview page for the <db:link xlink:href="uicomponents-qmlmodule.xml">UI Components</db:link> module page.</db:para>
+<db:para>The generated documentation is available in the <db:link xlink:href="uicomponents-qmlmodule.xml">UI Components</db:link> module.</db:para>
+<db:section xml:id="qml-class">
+<db:title>QML Class</db:title>
+<db:para>The QML types use the \qmltype to document the type. In addition, they have the \inmodule command in order for QDoc to associate them to the <db:code>UIComponents</db:code> module.</db:para>
+<db:para>QDoc uses the \brief command to place a basic description when listing the types.</db:para>
+</db:section>
+<db:section xml:id="properties-signals-handlers-and-methods">
+<db:title>Properties, Signals, Handlers, and Methods</db:title>
+<db:para>The types have their properties, signals, handlers, and methods defined in their respective QML files. QDoc associates the properties and methods to the types, therefore, you only need to place the documentation above the property, method, or signal.</db:para>
+<db:para>To document the type of a <db:emphasis>property alias</db:emphasis>, you must use the \qmlproperty command to specify the data type.</db:para>
+<db:programlisting language="cpp">\qmlproperty int anAliasedProperty
+An aliased property of type int.
+</db:programlisting>
+<db:section xml:id="internal-documentation">
+<db:title>Internal Documentation</db:title>
+<db:para>You may declare that a documentation is for internal use by placing the \internal command after the beginning QDoc comment <db:code>/*</db:code>. QDoc will prevent the internal documentation from appearing in the public API.</db:para>
+<db:para>If you wish to omit certain parts of the documentation, you may use the \omit and \endomit command.</db:para>
+</db:section>
+</db:section>
+<db:section xml:id="qml-types-with-c-implementation">
+<db:title>QML Types with C++ Implementation</db:title>
+<db:para>This example only demonstrates the documentation for types in QML files, but the regular QML commands may be placed inside C++ classes to define the public API of the QML type.</db:para>
+</db:section>
+</db:article>
diff --git a/src/qdoc/qdoc/tests/generatedoutput/expected_output/noautolist-docbook/testcpp-module.xml b/src/qdoc/qdoc/tests/generatedoutput/expected_output/noautolist-docbook/testcpp-module.xml
new file mode 100644
index 000000000..3217c0d94
--- /dev/null
+++ b/src/qdoc/qdoc/tests/generatedoutput/expected_output/noautolist-docbook/testcpp-module.xml
@@ -0,0 +1,49 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<db:article xmlns:db="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" version="5.2" xml:lang="en">
+<db:info>
+<db:title>QDoc Test C++ Classes</db:title>
+<db:productname>Test</db:productname>
+<db:titleabbrev>A test project for QDoc build artifacts</db:titleabbrev>
+<db:abstract>
+<db:para>A test module page.</db:para>
+<db:para>This module was introduced in Qt 2.0.</db:para>
+</db:abstract>
+</db:info>
+<db:para>A test module page.</db:para>
+<db:para>This module was introduced in Qt 2.0.</db:para>
+<db:section xml:id="details">
+<db:title>Detailed Description</db:title>
+<db:note>
+<db:para>This is just a test. /* Look, Ma! {I'm made of arguments!} */</db:para>
+</db:note>
+<db:section xml:id="linking-to-function-like-things">
+<db:title>Linking to function-like things</db:title>
+<db:itemizedlist>
+<db:listitem>
+<db:para><db:link xlink:href="testqdoc-test.xml#someFunctionDefaultArg">someFunctionDefaultArg</db:link>()</db:para>
+</db:listitem>
+<db:listitem>
+<db:para><db:link xlink:href="testqdoc-test.xml#QDOCTEST_MACRO2">QDOCTEST_MACRO2</db:link>()</db:para>
+</db:listitem>
+<db:listitem>
+<db:para><db:link xlink:href="testqdoc-test.xml#QDOCTEST_MACRO2">QDOCTEST_MACRO2</db:link>(int &amp;x)</db:para>
+</db:listitem>
+<db:listitem>
+<db:para><db:link xlink:href="testcpp-module.xml#section">section()</db:link></db:para>
+</db:listitem>
+<db:listitem>
+<db:para><db:link xlink:href="testcpp-module.xml#section">section() is a section title</db:link></db:para>
+</db:listitem>
+<db:listitem>
+<db:para><db:link xlink:href="testqdoc-test.xml#Test">open( parenthesis</db:link></db:para>
+</db:listitem>
+<db:listitem>
+<db:para><db:link xlink:href="https://en.cppreference.com/w/cpp/utility/move">C++11 added std::move(T&amp;&amp; t)</db:link></db:para>
+</db:listitem>
+</db:itemizedlist>
+<db:section xml:id="section">
+<db:title>section()</db:title>
+</db:section>
+</db:section>
+</db:section>
+</db:article>
diff --git a/src/qdoc/qdoc/tests/generatedoutput/expected_output/noautolist/qdoc-test-qmlmodule.html b/src/qdoc/qdoc/tests/generatedoutput/expected_output/noautolist/qdoc-test-qmlmodule.html
new file mode 100644
index 000000000..23c56acef
--- /dev/null
+++ b/src/qdoc/qdoc/tests/generatedoutput/expected_output/noautolist/qdoc-test-qmlmodule.html
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+<!-- type.cpp -->
+ <meta name="description" content="QML Types for the Test module.">
+ <title>QDoc.Test QML Module | Test</title>
+</head>
+<body>
+<div class="sidebar"><div class="sidebar-content" id="sidebar-content"></div></div>
+<h1 class="title" translate="no">QDoc.Test QML Module</h1>
+<p><b>This module is under development and is subject to change.</b></p>
+<p>This module was introduced in Qt 1.1.</p>
+<!-- $$$QDoc.Test-description -->
+<div class="descr" id="details">
+</div>
+<!-- @@@QDoc.Test -->
+</body>
+</html>
diff --git a/src/qdoc/qdoc/tests/generatedoutput/expected_output/noautolist/test-componentset-example.html b/src/qdoc/qdoc/tests/generatedoutput/expected_output/noautolist/test-componentset-example.html
new file mode 100644
index 000000000..a6d63699b
--- /dev/null
+++ b/src/qdoc/qdoc/tests/generatedoutput/expected_output/noautolist/test-componentset-example.html
@@ -0,0 +1,47 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+<!-- examples.qdoc -->
+ <meta name="description" content="Example for documenting QML types.">
+ <title>QML Documentation Example | Test</title>
+</head>
+<body>
+<div class="sidebar">
+<div class="toc">
+<h3 id="toc">Contents</h3>
+<ul>
+<li class="level1"><a href="#qml-class">QML Class</a></li>
+<li class="level1"><a href="#properties-signals-handlers-and-methods">Properties, Signals, Handlers, and Methods</a></li>
+<li class="level2"><a href="#internal-documentation">Internal Documentation</a></li>
+<li class="level1"><a href="#qml-types-with-c-implementation">QML Types with C++ Implementation</a></li>
+</ul>
+</div>
+<div class="sidebar-content" id="sidebar-content"></div></div>
+<h1 class="title">QML Documentation Example</h1>
+<!-- $$$componentset-brief -->
+<p>Example for documenting QML types.</p>
+<!-- @@@componentset -->
+<!-- $$$componentset-description -->
+<div class="descr" id="details">
+<p>This example demonstrates one of the ways to document QML types. It also generates a warning about a missing example image, on purpose.</p>
+<p>In particular, there are sample types that are documented with QDoc commands comments. There are documentation comments for the QML types and their public interfaces. The types are grouped into a module, the <a href="uicomponents-qmlmodule.html" translate="no">UI Components</a> module.</p>
+<p>The uicomponents.qdoc file generates the overview page for the <a href="uicomponents-qmlmodule.html" translate="no">UI Components</a> module page.</p>
+<p>The generated documentation is available in the <a href="uicomponents-qmlmodule.html" translate="no">UI Components</a> module.</p>
+<h4 id="qml-class">QML Class</h4>
+<p>The QML types use the \qmltype to document the type. In addition, they have the \inmodule command in order for QDoc to associate them to the <code translate="no">UIComponents</code> module.</p>
+<p>QDoc uses the \brief command to place a basic description when listing the types.</p>
+<h4 id="properties-signals-handlers-and-methods">Properties, Signals, Handlers, and Methods</h4>
+<p>The types have their properties, signals, handlers, and methods defined in their respective QML files. QDoc associates the properties and methods to the types, therefore, you only need to place the documentation above the property, method, or signal.</p>
+<p>To document the type of a <i>property alias</i>, you must use the \qmlproperty command to specify the data type.</p>
+<pre class="cpp" translate="no">\qmlproperty <span class="type">int</span> anAliasedProperty
+An aliased property of type <span class="type">int</span><span class="operator">.</span></pre>
+<h5 id="internal-documentation">Internal Documentation</h5>
+<p>You may declare that a documentation is for internal use by placing the \internal command after the beginning QDoc comment <code translate="no">/*</code>. QDoc will prevent the internal documentation from appearing in the public API.</p>
+<p>If you wish to omit certain parts of the documentation, you may use the \omit and \endomit command.</p>
+<h4 id="qml-types-with-c-implementation">QML Types with C++ Implementation</h4>
+<p>This example only demonstrates the documentation for types in QML files, but the regular QML commands may be placed inside C++ classes to define the public API of the QML type.</p>
+</div>
+<!-- @@@componentset -->
+</body>
+</html>
diff --git a/src/qdoc/qdoc/tests/generatedoutput/expected_output/noautolist/testcpp-module.html b/src/qdoc/qdoc/tests/generatedoutput/expected_output/noautolist/testcpp-module.html
new file mode 100644
index 000000000..6b58461da
--- /dev/null
+++ b/src/qdoc/qdoc/tests/generatedoutput/expected_output/noautolist/testcpp-module.html
@@ -0,0 +1,45 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+<!-- testcpp.cpp -->
+ <meta name="description" content="A test module page.">
+ <title>QDoc Test C++ Classes | Test</title>
+</head>
+<body>
+<div class="sidebar">
+<div class="toc">
+<h3 id="toc">Contents</h3>
+<ul>
+<li class="level1"><a href="#details">Detailed Description</a></li>
+<li class="level2"><a href="#linking-to-function-like-things">Linking to function-like things</a></li>
+<li class="level3"><a href="#section">section()</a></li>
+</ul>
+</div>
+<div class="sidebar-content" id="sidebar-content"></div></div>
+<h1 class="title" translate="no">QDoc Test C++ Classes</h1>
+<!-- $$$TestCPP-brief -->
+<p>A test module page. <a href="#details">More...</a></p>
+<!-- @@@TestCPP -->
+<p>This module was introduced in Qt 2.0.</p>
+<!-- $$$TestCPP-description -->
+<div class="descr">
+<h2 id="details">Detailed Description</h2>
+<div class="admonition note">
+<p><b>Note: </b>This is just a test. /* Look, Ma! {I'm made of arguments!} */</p>
+</div>
+<h3 id="linking-to-function-like-things">Linking to function-like things</h3>
+<ul>
+<li><a href="testqdoc-test.html#someFunctionDefaultArg" translate="no">someFunctionDefaultArg</a>()</li>
+<li><a href="testqdoc-test.html#QDOCTEST_MACRO2" translate="no">QDOCTEST_MACRO2</a>()</li>
+<li><a href="testqdoc-test.html#QDOCTEST_MACRO2" translate="no">QDOCTEST_MACRO2</a>(int &amp;x)</li>
+<li><a href="testcpp-module.html#section" translate="no">section()</a></li>
+<li><a href="testcpp-module.html#section" translate="no">section() is a section title</a></li>
+<li><a href="testqdoc-test.html#Test" translate="no">open( parenthesis</a></li>
+<li><a href="https://en.cppreference.com/w/cpp/utility/move">C++11 added std::move(T&amp;&amp; t)</a></li>
+</ul>
+<h4 id="section">section()</h4>
+</div>
+<!-- @@@TestCPP -->
+</body>
+</html>
diff --git a/src/qdoc/qdoc/tests/generatedoutput/expected_output/obsolete-classes.html b/src/qdoc/qdoc/tests/generatedoutput/expected_output/obsolete-classes.html
new file mode 100644
index 000000000..01198c82d
--- /dev/null
+++ b/src/qdoc/qdoc/tests/generatedoutput/expected_output/obsolete-classes.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+<!-- classlists.qdoc -->
+ <title>Obsolete Classes | TestCPP</title>
+</head>
+<body>
+<div class="sidebar">
+<div class="toc">
+<h3 id="toc">Contents</h3>
+<ul>
+<li class="level1"><a href="#classes-with-obsolete-members">Classes with obsolete members</a></li>
+<li class="level2"><a href="#testqdoc">TestQDoc</a></li>
+</ul>
+</div>
+<div class="sidebar-content" id="sidebar-content"></div></div>
+<h1 class="title">Obsolete Classes</h1>
+<!-- $$$obsolete-classes.html-description -->
+<div class="descr" id="details">
+<h2 id="classes-with-obsolete-members">Classes with obsolete members</h2>
+<div class="flowListDiv" translate="no">
+<dl class="flowList odd"><dt class="alphaChar"><b>T</b></dt>
+<dd><a href="testqdoc-test-obsolete.html">Test</a> (<a href="testqdoc.html">TestQDoc</a>)</dd>
+<dd><a href="testqdoc-testderived-obsolete.html">TestDerived</a> (<a href="testqdoc.html">TestQDoc</a>)</dd>
+</dl>
+</div>
+<h3 id="testqdoc">TestQDoc</h3>
+</div>
+<!-- @@@obsolete-classes.html -->
+</body>
+</html>
diff --git a/src/qdoc/qdoc/tests/generatedoutput/expected_output/qml-linkmodule-grandchild-members.html b/src/qdoc/qdoc/tests/generatedoutput/expected_output/qml-linkmodule-grandchild-members.html
new file mode 100644
index 000000000..7bec0458b
--- /dev/null
+++ b/src/qdoc/qdoc/tests/generatedoutput/expected_output/qml-linkmodule-grandchild-members.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+<!-- linking.qdoc -->
+ <title>List of All Members for GrandChild | IndexLinking</title>
+</head>
+<body>
+<li><a href="linkmodule-qmlmodule.html" translate="no">LinkModule</a></li>
+<li>GrandChild</li>
+<div class="sidebar"><div class="sidebar-content" id="sidebar-content"></div></div>
+<h1 class="title" translate="no">List of All Members for GrandChild</h1>
+<p>This is the complete list of members for <a href="qml-linkmodule-grandchild.html">GrandChild</a>, including inherited members.</p>
+<p>The following members are inherited from <a href="https://doc.qt.io/QmlPropertyGroups/qml-qdoc-test-anotherchild.html">AnotherChild</a>.</p>
+<ul>
+<li class="fn" translate="no"><b><a href="https://doc.qt.io/QmlPropertyGroups/qml-qdoc-test-anotherchild.html#name-prop" translate="no">name</a></b> : string</li>
+</ul>
+<p>The following members are inherited from <a href="https://doc.qt.io/QmlPropertyGroups/qml-qdoc-test-parent.html">Parent</a>.</p>
+<ul>
+<li class="fn" translate="no"><b><a href="https://doc.qt.io/QmlPropertyGroups/qml-qdoc-test-parent.html#group-prop" translate="no">group</a></b> : </li>
+<li class="fn" translate="no"><b><a href="https://doc.qt.io/QmlPropertyGroups/qml-qdoc-test-parent.html#group.a-prop" translate="no">group.a</a></b> : int</li>
+<li class="fn" translate="no"><b><a href="https://doc.qt.io/QmlPropertyGroups/qml-qdoc-test-parent.html#group.b-prop" translate="no">group.b</a></b> : int</li>
+<li class="fn" translate="no"><b><a href="https://doc.qt.io/QmlPropertyGroups/qml-qdoc-test-parent.html#group.c-prop" translate="no">group.c</a></b> : int <code class="summary extra" translate="no">(since 2.0)</code></li>
+</ul>
+</body>
+</html>
diff --git a/src/qdoc/qdoc/tests/generatedoutput/expected_output/qmlpropertygroups-docbook/qml-qdoc-test-parent.xml b/src/qdoc/qdoc/tests/generatedoutput/expected_output/qmlpropertygroups-docbook/qml-qdoc-test-parent.xml
new file mode 100644
index 000000000..e223892b1
--- /dev/null
+++ b/src/qdoc/qdoc/tests/generatedoutput/expected_output/qmlpropertygroups-docbook/qml-qdoc-test-parent.xml
@@ -0,0 +1,45 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<db:article xmlns:db="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" version="5.2" xml:lang="en">
+<db:info>
+<db:title>Parent QML Type</db:title>
+<db:productname>QmlPropertyGroups</db:productname>
+<db:titleabbrev>A test project for QDoc build artifacts</db:titleabbrev>
+<db:abstract>
+<db:para>Base QML type.</db:para>
+<db:para>This type was introduced in Qt 1.1.</db:para>
+</db:abstract>
+</db:info>
+<db:variablelist>
+<db:varlistentry>
+<db:term>Import Statement</db:term>
+<db:listitem>
+<db:para>import QDoc.Test 1.1</db:para>
+</db:listitem>
+</db:varlistentry>
+<db:varlistentry>
+<db:term>Since:</db:term>
+<db:listitem>
+<db:para>Qt 1.1</db:para>
+</db:listitem>
+</db:varlistentry>
+<db:varlistentry>
+<db:term>Inherited By:</db:term>
+<db:listitem>
+<db:para><db:link xlink:href="qml-qdoc-test-anotherchild.xml" xlink:role="">AnotherChild</db:link></db:para>
+</db:listitem>
+</db:varlistentry>
+</db:variablelist>
+<db:section xml:id="details">
+<db:title>Detailed Description</db:title>
+</db:section>
+<db:section xml:id="property-documentation">
+<db:title>Property Documentation</db:title>
+<db:section xml:id="group-prop">
+<db:title>group group</db:title>
+<db:bridgehead renderas="sect2" xml:id="group.a-prop">group.a : int</db:bridgehead>
+<db:bridgehead renderas="sect2" xml:id="group.b-prop">[read-only] group.b : int</db:bridgehead>
+<db:bridgehead renderas="sect2" xml:id="group.c-prop">[since 2.0] group.c : int</db:bridgehead>
+<db:para>Property group.</db:para>
+</db:section>
+</db:section>
+</db:article>
diff --git a/src/qdoc/qdoc/tests/generatedoutput/expected_output/qmlpropertygroups/qml-qdoc-test-anotherchild-members.html b/src/qdoc/qdoc/tests/generatedoutput/expected_output/qmlpropertygroups/qml-qdoc-test-anotherchild-members.html
new file mode 100644
index 000000000..a52f5e194
--- /dev/null
+++ b/src/qdoc/qdoc/tests/generatedoutput/expected_output/qmlpropertygroups/qml-qdoc-test-anotherchild-members.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+<!-- parent.qdoc -->
+ <meta name="description" content="Just another child inheriting a parent.">
+ <title>List of All Members for AnotherChild | QmlPropertyGroups</title>
+</head>
+<body>
+<li><a href="qdoc-test-qmlmodule.html" translate="no">Types</a></li>
+<li>AnotherChild</li>
+<div class="sidebar"><div class="sidebar-content" id="sidebar-content"></div></div>
+<h1 class="title" translate="no">List of All Members for AnotherChild</h1>
+<p>This is the complete list of members for <a href="qml-qdoc-test-anotherchild.html">AnotherChild</a>, including inherited members.</p>
+<ul>
+<li class="fn" translate="no"><b><a href="qml-qdoc-test-anotherchild.html#name-prop" translate="no">name</a></b> : string</li>
+</ul>
+<p>The following members are inherited from <a href="qml-qdoc-test-parent.html">Parent</a>.</p>
+<ul>
+<li class="fn" translate="no"><b><a href="qml-qdoc-test-parent.html#group-prop" translate="no">group</a></b><ul>
+<li class="fn" translate="no"><b><a href="qml-qdoc-test-parent.html#group.a-prop" translate="no">group.a</a></b> : int</li>
+<li class="fn" translate="no"><b><a href="qml-qdoc-test-parent.html#group.b-prop" translate="no">group.b</a></b> : int</li>
+<li class="fn" translate="no"><b><a href="qml-qdoc-test-parent.html#group.c-prop" translate="no">group.c</a></b> : int <code class="summary extra" translate="no">(since 2.0)</code></li>
+</ul>
+</li>
+</ul>
+</body>
+</html>
diff --git a/src/qdoc/qdoc/tests/generatedoutput/expected_output/qmlpropertygroups/qml-qdoc-test-parent.html b/src/qdoc/qdoc/tests/generatedoutput/expected_output/qmlpropertygroups/qml-qdoc-test-parent.html
new file mode 100644
index 000000000..200dd572a
--- /dev/null
+++ b/src/qdoc/qdoc/tests/generatedoutput/expected_output/qmlpropertygroups/qml-qdoc-test-parent.html
@@ -0,0 +1,61 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+<!-- parent.qdoc -->
+ <meta name="description" content="Base QML type.">
+ <title>Parent QML Type | QmlPropertyGroups</title>
+</head>
+<body>
+<li><a href="qdoc-test-qmlmodule.html" translate="no">Types</a></li>
+<li>Parent</li>
+<div class="sidebar">
+<div class="toc">
+<h3 id="toc">Contents</h3>
+<ul>
+<li class="level1"><a href="#properties">Properties</a></li>
+<li class="level1"><a href="#details">Detailed Description</a></li>
+</ul>
+</div>
+<div class="sidebar-content" id="sidebar-content"></div></div>
+<h1 class="title" translate="no">Parent QML Type</h1>
+<!-- $$$Parent-brief -->
+<p>Base QML type. <a href="#details">More...</a></p>
+<!-- @@@Parent -->
+<div class="table"><table class="alignedsummary" translate="no">
+<tr><td class="memItemLeft rightAlign topAlign"> Import Statement:</td><td class="memItemRight bottomAlign"> import QDoc.Test 1.1</td></tr><tr><td class="memItemLeft rightAlign topAlign"> Since:</td><td class="memItemRight bottomAlign"> Qt 1.1</td></tr><tr><td class="memItemLeft rightAlign topAlign"> Inherited By:</td><td class="memItemRight bottomAlign"> <p><a href="qml-qdoc-test-anotherchild.html" translate="no">AnotherChild</a></p>
+</td></tr></table></div><ul>
+<li><a href="qml-qdoc-test-parent-members.html">List of all members, including inherited members</a></li>
+</ul>
+<h2 id="properties">Properties</h2>
+<ul>
+<li class="fn" translate="no"><b><a href="qml-qdoc-test-parent.html#group-prop" translate="no">group</a></b><ul>
+<li class="fn" translate="no"><b><a href="qml-qdoc-test-parent.html#group.a-prop" translate="no">group.a</a></b> : int</li>
+<li class="fn" translate="no"><b><a href="qml-qdoc-test-parent.html#group.b-prop" translate="no">group.b</a></b> : int</li>
+<li class="fn" translate="no"><b><a href="qml-qdoc-test-parent.html#group.c-prop" translate="no">group.c</a></b> : int <code class="summary extra" translate="no">(since 2.0)</code></li>
+</ul>
+</li>
+</ul>
+<!-- $$$Parent-description -->
+<h2 id="details">Detailed Description</h2>
+<!-- @@@Parent -->
+<h2>Property Documentation</h2>
+<!-- $$$group -->
+<div class="qmlitem"><div class="qmlproto" translate="no">
+<div class="table"><table class="qmlname">
+<tr valign="top" class="even" id="group-prop"><th class="centerAlign"><p><b>group group</b></p></th></tr>
+<tr valign="top" class="odd" id="group.a-prop">
+<td class="tblQmlPropNode"><p>
+<span class="name">group.a</span> : <span class="type"><a href="qml-int.html" translate="no">int</a></span></p></td></tr>
+<tr valign="top" class="odd" id="group.b-prop">
+<td class="tblQmlPropNode"><p>
+<span class="name">group.b</span> : <span class="type"><a href="qml-int.html" translate="no">int</a></span> <code class="details extra" translate="no">[read-only]</code></p></td></tr>
+<tr valign="top" class="odd" id="group.c-prop">
+<td class="tblQmlPropNode"><p>
+<span class="name">group.c</span> : <span class="type"><a href="qml-int.html" translate="no">int</a></span> <code class="details extra" translate="no">[since 2.0]</code></p></td></tr>
+</table></div></div>
+<div class="qmldoc"><p>Property group.</p>
+</div></div><!-- @@@group -->
+<br/>
+</body>
+</html>
diff --git a/src/qdoc/qdoc/tests/generatedoutput/expected_output/testcpp-module.html b/src/qdoc/qdoc/tests/generatedoutput/expected_output/testcpp-module.html
new file mode 100644
index 000000000..83ab5b175
--- /dev/null
+++ b/src/qdoc/qdoc/tests/generatedoutput/expected_output/testcpp-module.html
@@ -0,0 +1,57 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+<!-- testcpp.cpp -->
+ <meta name="description" content="A test module page.">
+ <title>QDoc Test C++ Classes | TestCPP</title>
+</head>
+<body>
+<div class="sidebar">
+<div class="toc">
+<h3 id="toc">Contents</h3>
+<ul>
+<li class="level1"><a href="#namespaces">Namespaces</a></li>
+<li class="level1"><a href="#classes">Classes</a></li>
+<li class="level1"><a href="#details">Detailed Description</a></li>
+<li class="level2"><a href="#linking-to-function-like-things">Linking to function-like things</a></li>
+<li class="level3"><a href="#section">section()</a></li>
+</ul>
+</div>
+<div class="sidebar-content" id="sidebar-content"></div></div>
+<h1 class="title" translate="no">QDoc Test C++ Classes</h1>
+<!-- $$$TestCPP-brief -->
+<p>A test module page. <a href="#details">More...</a></p>
+<!-- @@@TestCPP -->
+<p>This module was introduced in Qt 2.0.</p>
+<h2 id="namespaces">Namespaces</h2>
+<div class="table"><table class="annotated">
+<tr class="odd topAlign"><td class="tblName" translate="no"><p><a href="crossmoduleref.html">CrossModuleRef</a></p></td><td class="tblDescr"><p>Namespace that has documented functions in multiple modules</p></td></tr>
+<tr class="even topAlign"><td class="tblName" translate="no"><p><a href="testqdoc.html">TestQDoc</a></p></td><td class="tblDescr"><p>A namespace</p></td></tr>
+</table></div>
+<h2 id="classes">Classes</h2>
+<div class="table"><table class="annotated">
+<tr class="odd topAlign"><td class="tblName" translate="no"><p><a href="testqdoc-test.html">TestQDoc::Test</a></p></td><td class="tblDescr"><p>A class in a namespace</p></td></tr>
+<tr class="even topAlign"><td class="tblName" translate="no"><p><a href="testqdoc-testderived.html">TestQDoc::TestDerived</a></p></td><td class="tblDescr"><p>A class in a namespace, derived from Test</p></td></tr>
+</table></div>
+<!-- $$$TestCPP-description -->
+<div class="descr">
+<h2 id="details">Detailed Description</h2>
+<div class="admonition note">
+<p><b>Note: </b>This is just a test. /* Look, Ma! {I'm made of arguments!} */</p>
+</div>
+<h3 id="linking-to-function-like-things">Linking to function-like things</h3>
+<ul>
+<li><a href="testqdoc-test.html#someFunctionDefaultArg" translate="no">someFunctionDefaultArg</a>()</li>
+<li><a href="testqdoc-test.html#QDOCTEST_MACRO2" translate="no">QDOCTEST_MACRO2</a>()</li>
+<li><a href="testqdoc-test.html#QDOCTEST_MACRO2" translate="no">QDOCTEST_MACRO2</a>(int &amp;x)</li>
+<li><a href="testcpp-module.html#section" translate="no">section()</a></li>
+<li><a href="testcpp-module.html#section" translate="no">section() is a section title</a></li>
+<li><a href="testqdoc-test.html#Test" translate="no">open( parenthesis</a></li>
+<li><a href="https://en.cppreference.com/w/cpp/utility/move">C++11 added std::move(T&amp;&amp; t)</a></li>
+</ul>
+<h4 id="section">section()</h4>
+</div>
+<!-- @@@TestCPP -->
+</body>
+</html>
diff --git a/src/qdoc/qdoc/tests/generatedoutput/expected_output/testcpp.index b/src/qdoc/qdoc/tests/generatedoutput/expected_output/testcpp.index
new file mode 100644
index 000000000..9dcb763a8
--- /dev/null
+++ b/src/qdoc/qdoc/tests/generatedoutput/expected_output/testcpp.index
@@ -0,0 +1,79 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE QDOCINDEX>
+<INDEX url="" title="TestCPP Reference Documentation" version="" project="TestCPP">
+ <namespace name="" status="active" access="public" module="testcpp">
+ <function name="QDOCTEST_MACRO" href="testqdoc.html#QDOCTEST_MACRO" status="active" access="public" documented="true" related="0" meta="macrowithoutparams" signature="QDOCTEST_MACRO"/>
+ <function name="QDOCTEST_MACRO2" href="testqdoc-test.html#QDOCTEST_MACRO2" status="active" access="public" documented="true" related="1" since="Test 1.1" meta="macrowithparams" brief="A macro with argument x" signature="QDOCTEST_MACRO2(int &amp;x)" groups="testgroup">
+ <parameter type="int &amp;" name="x" default=""/>
+ </function>
+ <page name="autolinking.html" href="autolinking.html" status="active" location="classlists.qdoc" documented="true" subtype="page" title="Autolinking" fulltitle="Autolinking" subtitle="">
+ <contents name="testqdoc" title="TestQDoc" level="1"/>
+ <contents name="someprop" title="someProp" level="1"/>
+ </page>
+ <namespace name="CrossModuleRef" href="crossmoduleref.html" status="active" access="public" location="testcpp.h" since="3.0" documented="true" module="TestCPP" brief="Namespace that has documented functions in multiple modules">
+ <function name="documentMe" fullname="CrossModuleRef::documentMe" href="crossmoduleref.html#documentMe" status="active" access="public" location="testcpp.h" documented="true" meta="plain" type="void" signature="void documentMe()"/>
+ </namespace>
+ <class name="DontLinkToMe" href="dontlinktome.html" status="ignored" access="public" location="testcpp.h" since="2.0" documented="true" module="TestCPP" brief="Class that does not generate documentation"/>
+ <page name="obsolete-classes.html" href="obsolete-classes.html" status="active" location="classlists.qdoc" documented="true" subtype="page" title="Obsolete Classes" fulltitle="Obsolete Classes" subtitle="">
+ <contents name="classes-with-obsolete-members" title="Classes with obsolete members" level="1"/>
+ <contents name="testqdoc" title="TestQDoc" level="2"/>
+ </page>
+ <namespace name="TestQDoc" href="testqdoc.html" status="active" access="public" location="testcpp.h" since="2.0" documented="true" module="TestCPP" brief="A namespace">
+ <contents name="usage" title="Usage" level="1"/>
+ <function name="QDOCTEST_MACRO" href="testqdoc.html#QDOCTEST_MACRO" status="active" access="public" documented="true" related="0" meta="macrowithoutparams" signature="QDOCTEST_MACRO"/>
+ <class threadsafety="reentrant" name="Test" fullname="TestQDoc::Test" href="testqdoc-test.html" status="active" access="public" location="testcpp.h" since="2.0" documented="true" groups="cpptypes,testgroup" module="TestCPP" brief="A class in a namespace">
+ <function name="QDOCTEST_MACRO2" href="testqdoc-test.html#QDOCTEST_MACRO2" status="active" access="public" documented="true" related="1" since="Test 1.1" meta="macrowithparams" brief="A macro with argument x" signature="QDOCTEST_MACRO2(int &amp;x)" groups="testgroup">
+ <parameter type="int &amp;" name="x" default=""/>
+ </function>
+ <function name="Test" fullname="TestQDoc::Test::Test" href="testqdoc-test.html#Test" status="active" access="public" documented="true" meta="constructor" noexcept="true" signature="Test()"/>
+ <function name="anotherObsoleteMember" fullname="TestQDoc::Test::anotherObsoleteMember" href="testqdoc-test-obsolete.html#anotherObsoleteMember" status="deprecated" access="public" location="testcpp.h" documented="true" meta="plain" type="void" signature="void anotherObsoleteMember()"/>
+ <function name="deprecatedMember" fullname="TestQDoc::Test::deprecatedMember" href="testqdoc-test-obsolete.html#deprecatedMember" status="deprecated" access="public" location="testcpp.h" documented="true" meta="plain" type="void" signature="void deprecatedMember()"/>
+ <function name="funcPtr" fullname="TestQDoc::Test::funcPtr" href="testqdoc-test.html#funcPtr" status="active" access="public" location="testcpp.h" documented="true" meta="plain" type="void (*)(bool)" signature="void (*)(bool) funcPtr(bool b, const char *s)">
+ <parameter type="bool" name="b" default=""/>
+ <parameter type="const char *" name="s" default=""/>
+ </function>
+ <function name="inlineFunction" fullname="TestQDoc::Test::inlineFunction" href="testqdoc-test.html#inlineFunction" status="active" access="public" location="testcpp.h" documented="true" meta="plain" type="void" brief="An inline function, documented using the \fn QDoc command" signature="void inlineFunction()"/>
+ <function name="methodWithEmDashInItsDocs" fullname="TestQDoc::Test::methodWithEmDashInItsDocs" href="testqdoc-test.html#methodWithEmDashInItsDocs" status="active" access="public" location="testcpp.h" documented="true" meta="plain" type="void" signature="void methodWithEmDashInItsDocs()"/>
+ <function name="methodWithEnDashInItsDocs" fullname="TestQDoc::Test::methodWithEnDashInItsDocs" href="testqdoc-test.html#methodWithEnDashInItsDocs" status="active" access="public" location="testcpp.h" documented="true" meta="plain" type="void" signature="void methodWithEnDashInItsDocs()"/>
+ <function name="obsoleteMember" fullname="TestQDoc::Test::obsoleteMember" href="testqdoc-test-obsolete.html#obsoleteMember" status="deprecated" access="public" location="testcpp.h" documented="true" meta="plain" type="void" signature="void obsoleteMember()"/>
+ <function name="operator++" fullname="TestQDoc::Test::operator++" href="testqdoc-test-obsolete.html#operator-2b-2b" status="deprecated" access="public" location="testcpp.h" documented="true" meta="plain" type="TestQDoc::Test &amp;" signature="TestQDoc::Test &amp; operator++()"/>
+ <function name="operator--" fullname="TestQDoc::Test::operator--" href="testqdoc-test-obsolete.html#operator--" status="deprecated" access="public" location="testcpp.h" documented="true" meta="plain" type="TestQDoc::Test &amp;" signature="TestQDoc::Test &amp; operator--()"/>
+ <function name="operator=" fullname="TestQDoc::Test::operator=" href="testqdoc-test.html#operator-eq" status="active" access="public" documented="true" meta="move-assign" noexcept="true" type="TestQDoc::Test &amp;" signature="TestQDoc::Test &amp; operator=(TestQDoc::Test &amp;&amp;other)" groups="testgroup">
+ <parameter type="TestQDoc::Test &amp;&amp;" name="other" default=""/>
+ </function>
+ <function name="operator==" href="testqdoc-test.html#operator-eq-eq" status="active" access="public" location="testcpp.h" documented="true" related="2" meta="plain" type="bool" signature="bool operator==(const TestQDoc::Test &amp;lhs, const TestQDoc::Test &amp;rhs)">
+ <parameter type="const TestQDoc::Test &amp;" name="lhs" default=""/>
+ <parameter type="const TestQDoc::Test &amp;" name="rhs" default=""/>
+ </function>
+ <function name="overload" fullname="TestQDoc::Test::overload" href="testqdoc-test.html#overload" status="active" access="protected" location="testcpp.h" documented="true" meta="plain" type="void" signature="void overload()"/>
+ <function name="overload" fullname="TestQDoc::Test::overload" href="testqdoc-test.html#overload-1" status="active" access="protected" location="testcpp.h" documented="true" since="Test 1.2" meta="plain" overload="true" overload-number="1" type="void" signature="void overload(bool b)">
+ <parameter type="bool" name="b" default=""/>
+ </function>
+ <function name="someFunction" fullname="TestQDoc::Test::someFunction" href="testqdoc-test.html#someFunction" status="active" access="public" location="testcpp.h" documented="true" meta="plain" type="int" signature="int someFunction(int, int v)">
+ <parameter type="int" name="" default=""/>
+ <parameter type="int" name="v" default="0"/>
+ </function>
+ <function name="someFunctionDefaultArg" fullname="TestQDoc::Test::someFunctionDefaultArg" href="testqdoc-test.html#someFunctionDefaultArg" threadsafety="non-reentrant" status="active" access="public" location="testcpp.h" documented="true" meta="plain" const="true" type="void" signature="void someFunctionDefaultArg(int i, bool b) const" groups="testgroup">
+ <parameter type="int" name="i" default=""/>
+ <parameter type="bool" name="b" default="false"/>
+ </function>
+ <function name="virtualFun" fullname="TestQDoc::Test::virtualFun" href="testqdoc-test.html#virtualFun" status="active" access="public" location="testcpp.h" documented="true" meta="plain" virtual="virtual" type="void" signature="void virtualFun()"/>
+ <typedef name="SomeType" fullname="TestQDoc::Test::SomeType" href="testqdoc-test.html#SomeType-typedef" status="active" access="public" location="testcpp.h" documented="true"/>
+ </class>
+ <class name="TestDerived" fullname="TestQDoc::TestDerived" href="testqdoc-testderived.html" status="active" access="public" location="testcpp.h" since="2.0" documented="true" bases="TestQDoc::Test" module="TestCPP" brief="A class in a namespace, derived from Test">
+ <function name="someValue" fullname="TestQDoc::TestDerived::someValue" href="testqdoc-testderived.html#someValue" status="active" access="public" location="testcpp.h" documented="true" meta="plain" type="TestQDoc::TestDerived::NotTypedef" signature="TestQDoc::TestDerived::NotTypedef someValue()"/>
+ <function name="staticObsoleteMember" fullname="TestQDoc::TestDerived::staticObsoleteMember" href="testqdoc-testderived-obsolete.html#staticObsoleteMember" status="deprecated" access="public" location="testcpp.h" documented="true" meta="plain" static="true" type="void" signature="void staticObsoleteMember()"/>
+ <function name="virtualFun" fullname="TestQDoc::TestDerived::virtualFun" href="testqdoc-testderived.html#virtualFun" status="active" access="public" location="testcpp.h" documented="true" meta="plain" virtual="virtual" override="true" type="void" signature="void virtualFun() override"/>
+ <typedef name="DerivedType" fullname="TestQDoc::TestDerived::DerivedType" href="testqdoc-testderived.html#DerivedType-typedef" status="active" access="public" location="testcpp.h" documented="true" aliasedtype="Test::SomeType"/>
+ <typedef name="NotTypedef" fullname="TestQDoc::TestDerived::NotTypedef" href="testqdoc-testderived.html#NotTypedef-typedef" status="active" access="public" location="testcpp.h" documented="true" aliasedtype="int"/>
+ </class>
+ </namespace>
+ <page name="https://doc.qt.io/qt/17-qdoc-commands-thread.html#reentrant-command" href="https://doc.qt.io/qt/17-qdoc-commands-thread.html#reentrant-command" status="active" location="classlists.qdoc" documented="true" subtype="externalpage" title="reentrant" fulltitle="reentrant" subtitle=""/>
+ <group name="cpptypes" href="cpptypes.html" status="active" location="classlists.qdoc" documented="true" seen="true" title="Test C++ Types"/>
+ <group name="testgroup" href="testgroup.html" status="internal" seen="false" title=""/>
+ <module name="TestCPP" href="testcpp-module.html" status="active" since="2.0" documented="true" seen="true" title="QDoc Test C++ Classes" brief="A test module page">
+ <contents name="linking-to-function-like-things" title="Linking to function-like things" level="1"/>
+ <contents name="section" title="section()" level="2"/>
+ </module>
+ </namespace>
+</INDEX>
diff --git a/src/qdoc/qdoc/tests/generatedoutput/expected_output/testcpp/crossmoduleref.html b/src/qdoc/qdoc/tests/generatedoutput/expected_output/testcpp/crossmoduleref.html
new file mode 100644
index 000000000..2d073e237
--- /dev/null
+++ b/src/qdoc/qdoc/tests/generatedoutput/expected_output/testcpp/crossmoduleref.html
@@ -0,0 +1,48 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+<!-- testcpp.cpp -->
+ <meta name="description" content="Namespace that has documented functions in multiple modules.">
+ <title>CrossModuleRef Namespace | TestCPP</title>
+</head>
+<body>
+<div class="sidebar">
+<div class="toc">
+<h3 id="toc">Contents</h3>
+<ul>
+<li class="level1"><a href="#functions">Functions</a></li>
+<li class="level1"><a href="#details">Detailed Description</a></li>
+</ul>
+</div>
+<div class="sidebar-content" id="sidebar-content"></div></div>
+<h1 class="title" translate="no">CrossModuleRef Namespace</h1>
+<!-- $$$CrossModuleRef-brief -->
+<p>Namespace that has documented functions in multiple modules. <a href="#details">More...</a></p>
+<!-- @@@CrossModuleRef -->
+<div class="table"><table class="alignedsummary" translate="no">
+<tr><td class="memItemLeft rightAlign topAlign"> Header:</td><td class="memItemRight bottomAlign"> <span class="preprocessor">#include &lt;CrossModuleRef&gt;</span></td></tr>
+<tr><td class="memItemLeft rightAlign topAlign"> CMake:</td><td class="memItemRight bottomAlign"> find_package(Qt6 REQUIRED COMPONENTS QDocTest) <br/>
+target_link_libraries(mytarget PRIVATE Qt6::QDocTest)</td></tr>
+<tr><td class="memItemLeft rightAlign topAlign"> qmake:</td><td class="memItemRight bottomAlign"> QT += testcpp</td></tr>
+<tr><td class="memItemLeft rightAlign topAlign"> Since:</td><td class="memItemRight bottomAlign"> Qt 3.0</td></tr>
+</table></div>
+<h2 id="functions">Functions</h2>
+<div class="table"><table class="alignedsummary" translate="no">
+<tr><td class="memItemLeft rightAlign topAlign"> void </td><td class="memItemRight bottomAlign"><b><a href="crossmoduleref.html#documentMe" translate="no">documentMe</a></b>()</td></tr>
+<tr><td class="memItemLeft rightAlign topAlign"> void </td><td class="memItemRight bottomAlign"><b><a href="crossmoduleref-sub-crossmodule.html#documentMeToo" translate="no">documentMeToo</a></b>()</td></tr>
+</table></div>
+<!-- $$$CrossModuleRef-description -->
+<div class="descr">
+<h2 id="details">Detailed Description</h2>
+</div>
+<!-- @@@CrossModuleRef -->
+<div class="func">
+<h2>Function Documentation</h2>
+<!-- $$$documentMe[overload1]$$$documentMe -->
+<h3 class="fn" translate="no" id="documentMe"><span class="type">void</span> CrossModuleRef::<span class="name">documentMe</span>()</h3>
+<p>Document me!</p>
+<!-- @@@documentMe -->
+</div>
+</body>
+</html>
diff --git a/src/qdoc/qdoc/tests/generatedoutput/expected_output/testcpp/testcpp-module.html b/src/qdoc/qdoc/tests/generatedoutput/expected_output/testcpp/testcpp-module.html
new file mode 100644
index 000000000..83ab5b175
--- /dev/null
+++ b/src/qdoc/qdoc/tests/generatedoutput/expected_output/testcpp/testcpp-module.html
@@ -0,0 +1,57 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+<!-- testcpp.cpp -->
+ <meta name="description" content="A test module page.">
+ <title>QDoc Test C++ Classes | TestCPP</title>
+</head>
+<body>
+<div class="sidebar">
+<div class="toc">
+<h3 id="toc">Contents</h3>
+<ul>
+<li class="level1"><a href="#namespaces">Namespaces</a></li>
+<li class="level1"><a href="#classes">Classes</a></li>
+<li class="level1"><a href="#details">Detailed Description</a></li>
+<li class="level2"><a href="#linking-to-function-like-things">Linking to function-like things</a></li>
+<li class="level3"><a href="#section">section()</a></li>
+</ul>
+</div>
+<div class="sidebar-content" id="sidebar-content"></div></div>
+<h1 class="title" translate="no">QDoc Test C++ Classes</h1>
+<!-- $$$TestCPP-brief -->
+<p>A test module page. <a href="#details">More...</a></p>
+<!-- @@@TestCPP -->
+<p>This module was introduced in Qt 2.0.</p>
+<h2 id="namespaces">Namespaces</h2>
+<div class="table"><table class="annotated">
+<tr class="odd topAlign"><td class="tblName" translate="no"><p><a href="crossmoduleref.html">CrossModuleRef</a></p></td><td class="tblDescr"><p>Namespace that has documented functions in multiple modules</p></td></tr>
+<tr class="even topAlign"><td class="tblName" translate="no"><p><a href="testqdoc.html">TestQDoc</a></p></td><td class="tblDescr"><p>A namespace</p></td></tr>
+</table></div>
+<h2 id="classes">Classes</h2>
+<div class="table"><table class="annotated">
+<tr class="odd topAlign"><td class="tblName" translate="no"><p><a href="testqdoc-test.html">TestQDoc::Test</a></p></td><td class="tblDescr"><p>A class in a namespace</p></td></tr>
+<tr class="even topAlign"><td class="tblName" translate="no"><p><a href="testqdoc-testderived.html">TestQDoc::TestDerived</a></p></td><td class="tblDescr"><p>A class in a namespace, derived from Test</p></td></tr>
+</table></div>
+<!-- $$$TestCPP-description -->
+<div class="descr">
+<h2 id="details">Detailed Description</h2>
+<div class="admonition note">
+<p><b>Note: </b>This is just a test. /* Look, Ma! {I'm made of arguments!} */</p>
+</div>
+<h3 id="linking-to-function-like-things">Linking to function-like things</h3>
+<ul>
+<li><a href="testqdoc-test.html#someFunctionDefaultArg" translate="no">someFunctionDefaultArg</a>()</li>
+<li><a href="testqdoc-test.html#QDOCTEST_MACRO2" translate="no">QDOCTEST_MACRO2</a>()</li>
+<li><a href="testqdoc-test.html#QDOCTEST_MACRO2" translate="no">QDOCTEST_MACRO2</a>(int &amp;x)</li>
+<li><a href="testcpp-module.html#section" translate="no">section()</a></li>
+<li><a href="testcpp-module.html#section" translate="no">section() is a section title</a></li>
+<li><a href="testqdoc-test.html#Test" translate="no">open( parenthesis</a></li>
+<li><a href="https://en.cppreference.com/w/cpp/utility/move">C++11 added std::move(T&amp;&amp; t)</a></li>
+</ul>
+<h4 id="section">section()</h4>
+</div>
+<!-- @@@TestCPP -->
+</body>
+</html>
diff --git a/src/qdoc/qdoc/tests/generatedoutput/expected_output/testcpp/testqdoc-test-members.html b/src/qdoc/qdoc/tests/generatedoutput/expected_output/testcpp/testqdoc-test-members.html
new file mode 100644
index 000000000..6749f2332
--- /dev/null
+++ b/src/qdoc/qdoc/tests/generatedoutput/expected_output/testcpp/testqdoc-test-members.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+<!-- testcpp.cpp -->
+ <meta name="description" content="A class in a namespace.">
+ <title>List of All Members for Test | TestCPP</title>
+</head>
+<body>
+<li><a href="testcpp-module.html" translate="no">C++ Classes</a></li>
+<li>Test</li>
+<div class="sidebar"><div class="sidebar-content" id="sidebar-content"></div></div>
+<h1 class="title" translate="no">List of All Members for Test</h1>
+<p>This is the complete list of members for <a href="testqdoc-test.html">TestQDoc::Test</a>, including inherited members.</p>
+<ul>
+<li class="fn" translate="no"><span class="name"><b><a href="testqdoc-test.html#SomeType-typedef" translate="no">SomeType</a></b></span></li>
+<li class="fn" translate="no"><span class="name"><b><a href="testqdoc-test.html#Test" translate="no">Test</a></b></span>()</li>
+<li class="fn" translate="no"><span class="name"><b><a href="testqdoc-test.html#funcPtr" translate="no">funcPtr</a></b></span>(bool, const char *) : void (*)(bool)</li>
+<li class="fn" translate="no"><span class="name"><b><a href="testqdoc-test.html#inlineFunction" translate="no">inlineFunction</a></b></span>()</li>
+<li class="fn" translate="no"><span class="name"><b><a href="testqdoc-test.html#methodWithEmDashInItsDocs" translate="no">methodWithEmDashInItsDocs</a></b></span>()</li>
+<li class="fn" translate="no"><span class="name"><b><a href="testqdoc-test.html#methodWithEnDashInItsDocs" translate="no">methodWithEnDashInItsDocs</a></b></span>()</li>
+<li class="fn" translate="no"><span class="name"><b><a href="testqdoc-test.html#overload" translate="no">overload</a></b></span>()</li>
+<li class="fn" translate="no"><span class="name"><b><a href="testqdoc-test.html#overload-1" translate="no">overload</a></b></span>(bool)</li>
+<li class="fn" translate="no"><span class="name"><b><a href="testqdoc-test.html#someFunction" translate="no">someFunction</a></b></span>(int, int) : int</li>
+<li class="fn" translate="no"><span class="name"><b><a href="testqdoc-test.html#someFunctionDefaultArg" translate="no">someFunctionDefaultArg</a></b></span>(int, bool) const</li>
+<li class="fn" translate="no"><span class="name"><b><a href="testqdoc-test.html#virtualFun" translate="no">virtualFun</a></b></span>()</li>
+<li class="fn" translate="no"><span class="name"><b><a href="testqdoc-test.html#operator-eq" translate="no">operator=</a></b></span>(TestQDoc::Test &amp;&amp;) : TestQDoc::Test &amp;</li>
+</ul>
+</body>
+</html>
diff --git a/src/qdoc/qdoc/tests/generatedoutput/expected_output/testcpp/testqdoc-test.html b/src/qdoc/qdoc/tests/generatedoutput/expected_output/testcpp/testqdoc-test.html
new file mode 100644
index 000000000..028339088
--- /dev/null
+++ b/src/qdoc/qdoc/tests/generatedoutput/expected_output/testcpp/testqdoc-test.html
@@ -0,0 +1,162 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+<!-- testcpp.cpp -->
+ <meta name="description" content="A class in a namespace.">
+ <title>Test Class | TestCPP</title>
+</head>
+<body>
+<li><a href="testcpp-module.html" translate="no">C++ Classes</a></li>
+<li>Test</li>
+<div class="sidebar">
+<div class="toc">
+<h3 id="toc">Contents</h3>
+<ul>
+<li class="level1"><a href="#public-types">Public Types</a></li>
+<li class="level1"><a href="#public-functions">Public Functions</a></li>
+<li class="level1"><a href="#protected-functions">Protected Functions</a></li>
+<li class="level1"><a href="#related-non-members">Related Non-Members</a></li>
+<li class="level1"><a href="#macros">Macros</a></li>
+<li class="level1"><a href="#details">Detailed Description</a></li>
+</ul>
+</div>
+<div class="sidebar-content" id="sidebar-content"></div></div>
+<h1 class="title" translate="no">Test Class</h1>
+<span class="small-subtitle" translate="no">class <a href="testqdoc.html" translate="no">TestQDoc</a>::Test</span>
+<!-- $$$Test-brief -->
+<p>A class in a namespace. <a href="#details">More...</a></p>
+<!-- @@@Test -->
+<div class="table"><table class="alignedsummary" translate="no">
+<tr><td class="memItemLeft rightAlign topAlign"> Header:</td><td class="memItemRight bottomAlign"> <span class="preprocessor">#include &lt;Test&gt;</span></td></tr>
+<tr><td class="memItemLeft rightAlign topAlign"> CMake:</td><td class="memItemRight bottomAlign"> find_package(Qt6 REQUIRED COMPONENTS QDocTest) <br/>
+target_link_libraries(mytarget PRIVATE Qt6::QDocTest)</td></tr>
+<tr><td class="memItemLeft rightAlign topAlign"> qmake:</td><td class="memItemRight bottomAlign"> QT += testcpp</td></tr>
+<tr><td class="memItemLeft rightAlign topAlign"> Since:</td><td class="memItemRight bottomAlign"> Qt 2.0</td></tr>
+<tr><td class="memItemLeft rightAlign topAlign"> Inherited By:</td><td class="memItemRight bottomAlign"> <p><a href="testqdoc-testderived.html" translate="no">TestQDoc::TestDerived</a></p>
+</td></tr>
+</table></div>
+<ul>
+<li><a href="testqdoc-test-members.html">List of all members, including inherited members</a></li>
+<li><a href="testqdoc-test-obsolete.html">Deprecated members</a></li>
+<li>Test is part of <a href="cpptypes.html">Test C++ Types</a>.</li>
+</ul>
+<p><b>Note:</b> All functions in this class are <a href="https://doc.qt.io/qt/17-qdoc-commands-thread.html#reentrant-command">reentrant</a> with the following exceptions:</p>
+<ul>
+<li><a href="testqdoc-test.html#someFunctionDefaultArg" translate="no">someFunctionDefaultArg</a>(int i, bool b) const</li>
+</ul>
+<h2 id="public-types">Public Types</h2>
+<div class="table"><table class="alignedsummary" translate="no">
+<tr><td class="memItemLeft rightAlign topAlign"> </td><td class="memItemRight bottomAlign"><b><a href="testqdoc-test.html#SomeType-typedef" translate="no">SomeType</a></b></td></tr>
+</table></div>
+<h2 id="public-functions">Public Functions</h2>
+<div class="table"><table class="alignedsummary" translate="no">
+<tr><td class="memItemLeft rightAlign topAlign"> </td><td class="memItemRight bottomAlign"><b><a href="testqdoc-test.html#Test" translate="no">Test</a></b>()</td></tr>
+<tr><td class="memItemLeft rightAlign topAlign"> void (*)(bool) </td><td class="memItemRight bottomAlign"><b><a href="testqdoc-test.html#funcPtr" translate="no">funcPtr</a></b>(bool <i>b</i>, const char *<i>s</i>)</td></tr>
+<tr><td class="memItemLeft rightAlign topAlign"> void </td><td class="memItemRight bottomAlign"><b><a href="testqdoc-test.html#inlineFunction" translate="no">inlineFunction</a></b>()</td></tr>
+<tr><td class="memItemLeft rightAlign topAlign"> void </td><td class="memItemRight bottomAlign"><b><a href="testqdoc-test.html#methodWithEmDashInItsDocs" translate="no">methodWithEmDashInItsDocs</a></b>()</td></tr>
+<tr><td class="memItemLeft rightAlign topAlign"> void </td><td class="memItemRight bottomAlign"><b><a href="testqdoc-test.html#methodWithEnDashInItsDocs" translate="no">methodWithEnDashInItsDocs</a></b>()</td></tr>
+<tr><td class="memItemLeft rightAlign topAlign"> int </td><td class="memItemRight bottomAlign"><b><a href="testqdoc-test.html#someFunction" translate="no">someFunction</a></b>(int, int <i>v</i> = 0)</td></tr>
+<tr><td class="memItemLeft rightAlign topAlign"> void </td><td class="memItemRight bottomAlign"><b><a href="testqdoc-test.html#someFunctionDefaultArg" translate="no">someFunctionDefaultArg</a></b>(int <i>i</i>, bool <i>b</i> = false) const</td></tr>
+<tr><td class="memItemLeft rightAlign topAlign"> virtual void </td><td class="memItemRight bottomAlign"><b><a href="testqdoc-test.html#virtualFun" translate="no">virtualFun</a></b>()</td></tr>
+<tr><td class="memItemLeft rightAlign topAlign"> TestQDoc::Test &amp;</td><td class="memItemRight bottomAlign"><b><a href="testqdoc-test.html#operator-eq" translate="no">operator=</a></b>(TestQDoc::Test &amp;&amp;<i>other</i>)</td></tr>
+</table></div>
+<h2 id="protected-functions">Protected Functions</h2>
+<div class="table"><table class="alignedsummary" translate="no">
+<tr><td class="memItemLeft rightAlign topAlign"> void </td><td class="memItemRight bottomAlign"><b><a href="testqdoc-test.html#overload" translate="no">overload</a></b>()</td></tr>
+<tr><td class="memItemLeft rightAlign topAlign"> <code class="summary extra" translate="no">(since Test 1.2)</code> void </td><td class="memItemRight bottomAlign"><b><a href="testqdoc-test.html#overload-1" translate="no">overload</a></b>(bool <i>b</i>)</td></tr>
+</table></div>
+<h2 id="related-non-members">Related Non-Members</h2>
+<div class="table"><table class="alignedsummary" translate="no">
+<tr><td class="memItemLeft rightAlign topAlign"> bool </td><td class="memItemRight bottomAlign"><b><a href="testqdoc-test.html#operator-eq-eq" translate="no">operator==</a></b>(const TestQDoc::Test &amp;<i>lhs</i>, const TestQDoc::Test &amp;<i>rhs</i>)</td></tr>
+</table></div>
+<h2 id="macros">Macros</h2>
+<div class="table"><table class="alignedsummary" translate="no">
+<tr><td class="memItemLeft rightAlign topAlign"> <code class="summary extra" translate="no">(since Test 1.1)</code> </td><td class="memItemRight bottomAlign"><b><a href="testqdoc-test.html#QDOCTEST_MACRO2" translate="no">QDOCTEST_MACRO2</a></b>(int &amp;<i>x</i>)</td></tr>
+</table></div>
+<!-- $$$Test-description -->
+<div class="descr">
+<h2 id="details">Detailed Description</h2>
+</div>
+<!-- @@@Test -->
+<div class="types">
+<h2>Member Type Documentation</h2>
+<!-- $$$SomeType -->
+<h3 class="fn" translate="no" id="SomeType-typedef">Test::<span class="name">SomeType</span></h3>
+<p>A typedef.</p>
+<!-- @@@SomeType -->
+</div>
+<div class="func">
+<h2>Member Function Documentation</h2>
+<!-- $$$ -->
+<div class="fngroup">
+<h3 class="fn fngroupitem" translate="no" id="overload"><code class="details extra" translate="no">[protected]</code> <span class="type">void</span> Test::<span class="name">overload</span>()</h3><h3 class="fn fngroupitem" translate="no" id="overload-1"><code class="details extra" translate="no">[protected, since Test 1.2]</code> <span class="type">void</span> Test::<span class="name">overload</span>(<span class="type">bool</span> <i>b</i>)</h3></div>
+<p>Overloads that share a documentation comment, optionally taking a parameter <i translate="no">b</i>.</p>
+<!-- @@@ -->
+<!-- $$$Test[overload1]$$$Test -->
+<h3 class="fn" translate="no" id="Test"><code class="details extra" translate="no">[noexcept default]</code> Test::<span class="name">Test</span>()</h3>
+<p>Default constructor.</p>
+<!-- @@@Test -->
+<!-- $$$funcPtr[overload1]$$$funcPtrboolconstchar* -->
+<h3 class="fn" translate="no" id="funcPtr"><span class="type">void</span> (*)(<span class="type">bool</span>) Test::<span class="name">funcPtr</span>(<span class="type">bool</span> <i>b</i>, const <span class="type">char</span> *<i>s</i>)</h3>
+<p>Returns a pointer to a function that takes a boolean. Uses <i translate="no">b</i> and <i translate="no">s</i>.</p>
+<!-- @@@funcPtr -->
+<!-- $$$inlineFunction[overload1]$$$inlineFunction -->
+<h3 class="fn" translate="no" id="inlineFunction"><span class="type">void</span> Test::<span class="name">inlineFunction</span>()</h3>
+<p>An inline function, documented using the \fn QDoc command.</p>
+<!-- @@@inlineFunction -->
+<!-- $$$methodWithEmDashInItsDocs[overload1]$$$methodWithEmDashInItsDocs -->
+<h3 class="fn" translate="no" id="methodWithEmDashInItsDocs"><span class="type">void</span> Test::<span class="name">methodWithEmDashInItsDocs</span>()</h3>
+<p>This method has em dashes in its documentation&mdash;as you'll find represented by <code translate="no">---</code> in the sources&mdash;here and there. The important bit to note is that when passed e.g. to the \c command, the three hyphens are processed as input to the command and not replaced by an em dash.</p>
+<p>-----------------------------------------------------------------------</p>
+<p>People can still add a bunch of dashes, though, without QDoc replacing them all with a series of em dashes.</p>
+<p>&mdash;You can also start a new paragraph with an em dash, if you want to.</p>
+<p><b>See also </b><a href="testqdoc-test.html#methodWithEnDashInItsDocs" translate="no">methodWithEnDashInItsDocs</a>.</p>
+<!-- @@@methodWithEmDashInItsDocs -->
+<!-- $$$methodWithEnDashInItsDocs[overload1]$$$methodWithEnDashInItsDocs -->
+<h3 class="fn" translate="no" id="methodWithEnDashInItsDocs"><span class="type">void</span> Test::<span class="name">methodWithEnDashInItsDocs</span>()</h3>
+<p>This method has en dashes in its documentation &ndash; as you'll find represented by <code translate="no">--</code> in the sources &ndash; here and there. The important bit to note is that when passed e.g. to the \c command, the two hyphens are processed as input to the command and not replaced by an en dash. This also applies to code blocks, where otherwise, the decrement operator would get completely borked:</p>
+<pre class="cpp" translate="no"><span class="keyword">for</span> (<span class="type">int</span> i <span class="operator">=</span> <span class="number">42</span>; i <span class="operator">&gt;</span> <span class="number">0</span>; <span class="operator">-</span><span class="operator">-</span>i)
+ <span class="comment">// Do something cool during countdown.</span></pre>
+<p>...as it would be silly if this would output &ndash;i instead of <code translate="no">--i</code>.</p>
+<p>-----------------------------------------------------------------------</p>
+<p>It still allows people to add a bunch of dashes, though, without replacing them all with a series of en dashes. Of course, they might want to use the \hr command instead, like this:</p>
+<hr />
+<p>&ndash; You can also start a new paragraph with an en dash, if you want to.</p>
+<p><b>See also </b>methodWithEnDashInItsDocs.</p>
+<!-- @@@methodWithEnDashInItsDocs -->
+<!-- $$$someFunction[overload1]$$$someFunctionintint -->
+<h3 class="fn" translate="no" id="someFunction"><span class="type">int</span> Test::<span class="name">someFunction</span>(<span class="type">int</span>, <span class="type">int</span> <i>v</i> = 0)</h3>
+<p>Function that takes a parameter <i translate="no">v</i>. Also returns the value of <i translate="no">v</i>.</p>
+<!-- @@@someFunction -->
+<!-- $$$someFunctionDefaultArg[overload1]$$$someFunctionDefaultArgintbool -->
+<h3 class="fn" translate="no" id="someFunctionDefaultArg"><span class="type">void</span> Test::<span class="name">someFunctionDefaultArg</span>(<span class="type">int</span> <i>i</i>, <span class="type">bool</span> <i>b</i> = false) const</h3>
+<p>Function that takes a parameter <i translate="no">i</i> and <i translate="no">b</i>.</p>
+<p><b>Warning:</b> This function is not <a href="https://doc.qt.io/qt/17-qdoc-commands-thread.html#reentrant-command">reentrant</a>.</p>
+<!-- @@@someFunctionDefaultArg -->
+<!-- $$$virtualFun[overload1]$$$virtualFun -->
+<h3 class="fn" translate="no" id="virtualFun"><code class="details extra" translate="no">[virtual]</code> <span class="type">void</span> Test::<span class="name">virtualFun</span>()</h3>
+<p>Function that must be reimplemented.</p>
+<!-- @@@virtualFun -->
+<!-- $$$operator=[overload1]$$$operator=TestQDoc::Test&& -->
+<h3 class="fn" translate="no" id="operator-eq"><code class="details extra" translate="no">[noexcept default]</code> <span class="type"><a href="testqdoc-test.html" translate="no">TestQDoc::Test</a></span> &amp;Test::<span class="name">operator=</span>(<span class="type"><a href="testqdoc-test.html" translate="no">TestQDoc::Test</a></span> &amp;&amp;<i>other</i>)</h3>
+<p>Move-assigns <i translate="no">other</i>.</p>
+<!-- @@@operator= -->
+</div>
+<div class="relnonmem">
+<h2>Related Non-Members</h2>
+<!-- $$$operator==[overload1]$$$operator==constTestQDoc::Test&constTestQDoc::Test& -->
+<h3 class="fn" translate="no" id="operator-eq-eq"><span class="type">bool</span> <span class="name">operator==</span>(const <span class="type"><a href="testqdoc-test.html" translate="no">TestQDoc::Test</a></span> &amp;<i>lhs</i>, const <span class="type"><a href="testqdoc-test.html" translate="no">TestQDoc::Test</a></span> &amp;<i>rhs</i>)</h3>
+<p>Returns true if <i translate="no">lhs</i> and <i translate="no">rhs</i> are equal.</p>
+<!-- @@@operator== -->
+</div>
+<div class="macros">
+<h2>Macro Documentation</h2>
+<!-- $$$QDOCTEST_MACRO2[overload1]$$$QDOCTEST_MACRO2int& -->
+<h3 class="fn" translate="no" id="QDOCTEST_MACRO2"><code class="details extra" translate="no">[since Test 1.1]</code> <span class="name">QDOCTEST_MACRO2</span>(<span class="type">int</span> &amp;<i>x</i>)</h3>
+<p>A macro with argument <i translate="no">x</i>.</p>
+<p>This macro was introduced in Test 1.1.</p>
+<!-- @@@QDOCTEST_MACRO2 -->
+</div>
+</body>
+</html>
diff --git a/src/qdoc/qdoc/tests/generatedoutput/expected_output/testcpp/testqdoc.html b/src/qdoc/qdoc/tests/generatedoutput/expected_output/testcpp/testqdoc.html
new file mode 100644
index 000000000..425c7b396
--- /dev/null
+++ b/src/qdoc/qdoc/tests/generatedoutput/expected_output/testcpp/testqdoc.html
@@ -0,0 +1,64 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+<!-- testcpp.cpp -->
+ <meta name="description" content="A namespace.">
+ <title>TestQDoc Namespace | TestCPP</title>
+</head>
+<body>
+<div class="sidebar">
+<div class="toc">
+<h3 id="toc">Contents</h3>
+<ul>
+<li class="level1"><a href="#classes">Classes</a></li>
+<li class="level1"><a href="#macros">Macros</a></li>
+<li class="level1"><a href="#details">Detailed Description</a></li>
+<li class="level2"><a href="#usage">Usage</a></li>
+</ul>
+</div>
+<div class="sidebar-content" id="sidebar-content"></div></div>
+<h1 class="title" translate="no">TestQDoc Namespace</h1>
+<!-- $$$TestQDoc-brief -->
+<p>A namespace. <a href="#details">More...</a></p>
+<!-- @@@TestQDoc -->
+<div class="table"><table class="alignedsummary" translate="no">
+<tr><td class="memItemLeft rightAlign topAlign"> Header:</td><td class="memItemRight bottomAlign"> <span class="preprocessor">#include &lt;TestCPP&gt;</span></td></tr>
+<tr><td class="memItemLeft rightAlign topAlign"> CMake:</td><td class="memItemRight bottomAlign"> find_package(Qt6 REQUIRED COMPONENTS QDocTest) <br/>
+target_link_libraries(mytarget PRIVATE Qt6::QDocTest)</td></tr>
+<tr><td class="memItemLeft rightAlign topAlign"> qmake:</td><td class="memItemRight bottomAlign"> QT += testcpp</td></tr>
+<tr><td class="memItemLeft rightAlign topAlign"> Since:</td><td class="memItemRight bottomAlign"> Qt 2.0</td></tr>
+</table></div>
+<h2 id="classes">Classes</h2>
+<div class="table"><table class="alignedsummary" translate="no">
+<tr><td class="memItemLeft rightAlign topAlign"> <code class="summary extra" translate="no">(since 2.0)</code> class </td><td class="memItemRight bottomAlign"><b><a href="testqdoc-test.html" translate="no">Test</a></b></td></tr>
+<tr><td class="memItemLeft rightAlign topAlign"> <code class="summary extra" translate="no">(since 2.0)</code> class </td><td class="memItemRight bottomAlign"><b><a href="testqdoc-testderived.html" translate="no">TestDerived</a></b></td></tr>
+</table></div>
+<h2 id="macros">Macros</h2>
+<div class="table"><table class="alignedsummary" translate="no">
+<tr><td class="memItemLeft rightAlign topAlign"> </td><td class="memItemRight bottomAlign"><b><a href="testqdoc.html#QDOCTEST_MACRO" translate="no">QDOCTEST_MACRO</a></b></td></tr>
+</table></div>
+<!-- $$$TestQDoc-description -->
+<div class="descr">
+<h2 id="details">Detailed Description</h2>
+<h3 id="usage">Usage</h3>
+<p>This namespace is for testing QDoc output.</p>
+</div>
+<!-- @@@TestQDoc -->
+<div class="classes">
+<h2>Classes</h2>
+<h3> class <a href="testqdoc-test.html">Test</a></h3><!-- $$$Test-brief -->
+<p>A class in a namespace. <a href="testqdoc-test.html#details">More...</a></p>
+<!-- @@@Test -->
+<h3> class <a href="testqdoc-testderived.html">TestDerived</a></h3><!-- $$$TestDerived-brief -->
+<p>A class in a namespace, derived from <a href="testqdoc-test.html" translate="no">Test</a>. <a href="testqdoc-testderived.html#details">More...</a></p>
+<!-- @@@TestDerived -->
+</div>
+<div class="macros">
+<h2>Macro Documentation</h2>
+<!-- $$$QDOCTEST_MACRO[overload1]$$$QDOCTEST_MACRO -->
+<h3 class="fn" translate="no" id="QDOCTEST_MACRO"><span class="name">QDOCTEST_MACRO</span></h3>
+<!-- @@@QDOCTEST_MACRO -->
+</div>
+</body>
+</html>
diff --git a/src/qdoc/qdoc/tests/generatedoutput/expected_output/testqdoc-test-members.html b/src/qdoc/qdoc/tests/generatedoutput/expected_output/testqdoc-test-members.html
new file mode 100644
index 000000000..6749f2332
--- /dev/null
+++ b/src/qdoc/qdoc/tests/generatedoutput/expected_output/testqdoc-test-members.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+<!-- testcpp.cpp -->
+ <meta name="description" content="A class in a namespace.">
+ <title>List of All Members for Test | TestCPP</title>
+</head>
+<body>
+<li><a href="testcpp-module.html" translate="no">C++ Classes</a></li>
+<li>Test</li>
+<div class="sidebar"><div class="sidebar-content" id="sidebar-content"></div></div>
+<h1 class="title" translate="no">List of All Members for Test</h1>
+<p>This is the complete list of members for <a href="testqdoc-test.html">TestQDoc::Test</a>, including inherited members.</p>
+<ul>
+<li class="fn" translate="no"><span class="name"><b><a href="testqdoc-test.html#SomeType-typedef" translate="no">SomeType</a></b></span></li>
+<li class="fn" translate="no"><span class="name"><b><a href="testqdoc-test.html#Test" translate="no">Test</a></b></span>()</li>
+<li class="fn" translate="no"><span class="name"><b><a href="testqdoc-test.html#funcPtr" translate="no">funcPtr</a></b></span>(bool, const char *) : void (*)(bool)</li>
+<li class="fn" translate="no"><span class="name"><b><a href="testqdoc-test.html#inlineFunction" translate="no">inlineFunction</a></b></span>()</li>
+<li class="fn" translate="no"><span class="name"><b><a href="testqdoc-test.html#methodWithEmDashInItsDocs" translate="no">methodWithEmDashInItsDocs</a></b></span>()</li>
+<li class="fn" translate="no"><span class="name"><b><a href="testqdoc-test.html#methodWithEnDashInItsDocs" translate="no">methodWithEnDashInItsDocs</a></b></span>()</li>
+<li class="fn" translate="no"><span class="name"><b><a href="testqdoc-test.html#overload" translate="no">overload</a></b></span>()</li>
+<li class="fn" translate="no"><span class="name"><b><a href="testqdoc-test.html#overload-1" translate="no">overload</a></b></span>(bool)</li>
+<li class="fn" translate="no"><span class="name"><b><a href="testqdoc-test.html#someFunction" translate="no">someFunction</a></b></span>(int, int) : int</li>
+<li class="fn" translate="no"><span class="name"><b><a href="testqdoc-test.html#someFunctionDefaultArg" translate="no">someFunctionDefaultArg</a></b></span>(int, bool) const</li>
+<li class="fn" translate="no"><span class="name"><b><a href="testqdoc-test.html#virtualFun" translate="no">virtualFun</a></b></span>()</li>
+<li class="fn" translate="no"><span class="name"><b><a href="testqdoc-test.html#operator-eq" translate="no">operator=</a></b></span>(TestQDoc::Test &amp;&amp;) : TestQDoc::Test &amp;</li>
+</ul>
+</body>
+</html>
diff --git a/src/qdoc/qdoc/tests/generatedoutput/expected_output/testqdoc-test-obsolete.html b/src/qdoc/qdoc/tests/generatedoutput/expected_output/testqdoc-test-obsolete.html
new file mode 100644
index 000000000..2b5192da1
--- /dev/null
+++ b/src/qdoc/qdoc/tests/generatedoutput/expected_output/testqdoc-test-obsolete.html
@@ -0,0 +1,45 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+<!-- testcpp.cpp -->
+ <meta name="description" content="A class in a namespace.">
+ <title>Obsolete Members for Test | TestCPP</title>
+</head>
+<body>
+<li><a href="testcpp-module.html" translate="no">C++ Classes</a></li>
+<li>Test</li>
+<div class="sidebar"><div class="sidebar-content" id="sidebar-content"></div></div>
+<h1 class="title" translate="no">Obsolete Members for Test</h1>
+<p><b>The following members of class <a href="testqdoc-test.html" translate="no">Test</a> are deprecated.</b> They are provided to keep old source code working. We strongly advise against using them in new code.</p>
+<h2>Public Functions</h2>
+<div class="table"><table class="alignedsummary" translate="no">
+<tr><td class="memItemLeft topAlign rightAlign"> <code class="summary extra" translate="no">(deprecated)</code> void </td><td class="memItemRight bottomAlign"><b><a href="testqdoc-test-obsolete.html#anotherObsoleteMember" translate="no">anotherObsoleteMember</a></b>()</td></tr>
+<tr><td class="memItemLeft topAlign rightAlign"> <code class="summary extra" translate="no">(deprecated in 6.0)</code> void </td><td class="memItemRight bottomAlign"><b><a href="testqdoc-test-obsolete.html#deprecatedMember" translate="no">deprecatedMember</a></b>()</td></tr>
+<tr><td class="memItemLeft topAlign rightAlign"> <code class="summary extra" translate="no">(deprecated)</code> void </td><td class="memItemRight bottomAlign"><b><a href="testqdoc-test-obsolete.html#obsoleteMember" translate="no">obsoleteMember</a></b>()</td></tr>
+<tr><td class="memItemLeft topAlign rightAlign"> <code class="summary extra" translate="no">(deprecated)</code> TestQDoc::Test &amp;</td><td class="memItemRight bottomAlign"><b><a href="testqdoc-test-obsolete.html#operator-2b-2b" translate="no">operator++</a></b>()</td></tr>
+<tr><td class="memItemLeft topAlign rightAlign"> <code class="summary extra" translate="no">(deprecated)</code> TestQDoc::Test &amp;</td><td class="memItemRight bottomAlign"><b><a href="testqdoc-test-obsolete.html#operator--" translate="no">operator--</a></b>()</td></tr>
+</table></div>
+<h2>Member Function Documentation</h2>
+<!-- $$$ -->
+<div class="fngroup">
+<h3 class="fn fngroupitem" translate="no" id="operator-2b-2b"><code class="details extra" translate="no">[deprecated]</code> <span class="type"><a href="testqdoc-test.html" translate="no">TestQDoc::Test</a></span> &amp;Test::<span class="name">operator++</span>()</h3><h3 class="fn fngroupitem" translate="no" id="operator--"><code class="details extra" translate="no">[deprecated]</code> <span class="type"><a href="testqdoc-test.html" translate="no">TestQDoc::Test</a></span> &amp;Test::<span class="name">operator--</span>()</h3></div>
+<p>This function is deprecated. We strongly advise against using it in new code.</p>
+<!-- @@@ -->
+<!-- $$$anotherObsoleteMember[overload1]$$$anotherObsoleteMember -->
+<h3 class="fn" translate="no" id="anotherObsoleteMember"><code class="details extra" translate="no">[deprecated]</code> <span class="type">void</span> Test::<span class="name">anotherObsoleteMember</span>()</h3>
+<p>This function is deprecated. We strongly advise against using it in new code.</p>
+<p>Use <a href="testqdoc-test-obsolete.html#obsoleteMember" translate="no">obsoleteMember</a>() instead.</p>
+<!-- @@@anotherObsoleteMember -->
+<!-- $$$deprecatedMember[overload1]$$$deprecatedMember -->
+<h3 class="fn" translate="no" id="deprecatedMember"><code class="details extra" translate="no">[deprecated in 6.0]</code> <span class="type">void</span> Test::<span class="name">deprecatedMember</span>()</h3>
+<p>This function is deprecated since 6.0. We strongly advise against using it in new code.</p>
+<p>Use <a href="testqdoc-test.html#someFunction" translate="no">someFunction</a>() instead.</p>
+<!-- @@@deprecatedMember -->
+<!-- $$$obsoleteMember[overload1]$$$obsoleteMember -->
+<h3 class="fn" translate="no" id="obsoleteMember"><code class="details extra" translate="no">[deprecated]</code> <span class="type">void</span> Test::<span class="name">obsoleteMember</span>()</h3>
+<p>This function is deprecated. We strongly advise against using it in new code.</p>
+<p>Use <a href="testqdoc-test.html#someFunction" translate="no">someFunction</a>() instead.</p>
+<!-- @@@obsoleteMember -->
+</body>
+</html>
diff --git a/src/qdoc/qdoc/tests/generatedoutput/expected_output/testqdoc-test.html b/src/qdoc/qdoc/tests/generatedoutput/expected_output/testqdoc-test.html
new file mode 100644
index 000000000..028339088
--- /dev/null
+++ b/src/qdoc/qdoc/tests/generatedoutput/expected_output/testqdoc-test.html
@@ -0,0 +1,162 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+<!-- testcpp.cpp -->
+ <meta name="description" content="A class in a namespace.">
+ <title>Test Class | TestCPP</title>
+</head>
+<body>
+<li><a href="testcpp-module.html" translate="no">C++ Classes</a></li>
+<li>Test</li>
+<div class="sidebar">
+<div class="toc">
+<h3 id="toc">Contents</h3>
+<ul>
+<li class="level1"><a href="#public-types">Public Types</a></li>
+<li class="level1"><a href="#public-functions">Public Functions</a></li>
+<li class="level1"><a href="#protected-functions">Protected Functions</a></li>
+<li class="level1"><a href="#related-non-members">Related Non-Members</a></li>
+<li class="level1"><a href="#macros">Macros</a></li>
+<li class="level1"><a href="#details">Detailed Description</a></li>
+</ul>
+</div>
+<div class="sidebar-content" id="sidebar-content"></div></div>
+<h1 class="title" translate="no">Test Class</h1>
+<span class="small-subtitle" translate="no">class <a href="testqdoc.html" translate="no">TestQDoc</a>::Test</span>
+<!-- $$$Test-brief -->
+<p>A class in a namespace. <a href="#details">More...</a></p>
+<!-- @@@Test -->
+<div class="table"><table class="alignedsummary" translate="no">
+<tr><td class="memItemLeft rightAlign topAlign"> Header:</td><td class="memItemRight bottomAlign"> <span class="preprocessor">#include &lt;Test&gt;</span></td></tr>
+<tr><td class="memItemLeft rightAlign topAlign"> CMake:</td><td class="memItemRight bottomAlign"> find_package(Qt6 REQUIRED COMPONENTS QDocTest) <br/>
+target_link_libraries(mytarget PRIVATE Qt6::QDocTest)</td></tr>
+<tr><td class="memItemLeft rightAlign topAlign"> qmake:</td><td class="memItemRight bottomAlign"> QT += testcpp</td></tr>
+<tr><td class="memItemLeft rightAlign topAlign"> Since:</td><td class="memItemRight bottomAlign"> Qt 2.0</td></tr>
+<tr><td class="memItemLeft rightAlign topAlign"> Inherited By:</td><td class="memItemRight bottomAlign"> <p><a href="testqdoc-testderived.html" translate="no">TestQDoc::TestDerived</a></p>
+</td></tr>
+</table></div>
+<ul>
+<li><a href="testqdoc-test-members.html">List of all members, including inherited members</a></li>
+<li><a href="testqdoc-test-obsolete.html">Deprecated members</a></li>
+<li>Test is part of <a href="cpptypes.html">Test C++ Types</a>.</li>
+</ul>
+<p><b>Note:</b> All functions in this class are <a href="https://doc.qt.io/qt/17-qdoc-commands-thread.html#reentrant-command">reentrant</a> with the following exceptions:</p>
+<ul>
+<li><a href="testqdoc-test.html#someFunctionDefaultArg" translate="no">someFunctionDefaultArg</a>(int i, bool b) const</li>
+</ul>
+<h2 id="public-types">Public Types</h2>
+<div class="table"><table class="alignedsummary" translate="no">
+<tr><td class="memItemLeft rightAlign topAlign"> </td><td class="memItemRight bottomAlign"><b><a href="testqdoc-test.html#SomeType-typedef" translate="no">SomeType</a></b></td></tr>
+</table></div>
+<h2 id="public-functions">Public Functions</h2>
+<div class="table"><table class="alignedsummary" translate="no">
+<tr><td class="memItemLeft rightAlign topAlign"> </td><td class="memItemRight bottomAlign"><b><a href="testqdoc-test.html#Test" translate="no">Test</a></b>()</td></tr>
+<tr><td class="memItemLeft rightAlign topAlign"> void (*)(bool) </td><td class="memItemRight bottomAlign"><b><a href="testqdoc-test.html#funcPtr" translate="no">funcPtr</a></b>(bool <i>b</i>, const char *<i>s</i>)</td></tr>
+<tr><td class="memItemLeft rightAlign topAlign"> void </td><td class="memItemRight bottomAlign"><b><a href="testqdoc-test.html#inlineFunction" translate="no">inlineFunction</a></b>()</td></tr>
+<tr><td class="memItemLeft rightAlign topAlign"> void </td><td class="memItemRight bottomAlign"><b><a href="testqdoc-test.html#methodWithEmDashInItsDocs" translate="no">methodWithEmDashInItsDocs</a></b>()</td></tr>
+<tr><td class="memItemLeft rightAlign topAlign"> void </td><td class="memItemRight bottomAlign"><b><a href="testqdoc-test.html#methodWithEnDashInItsDocs" translate="no">methodWithEnDashInItsDocs</a></b>()</td></tr>
+<tr><td class="memItemLeft rightAlign topAlign"> int </td><td class="memItemRight bottomAlign"><b><a href="testqdoc-test.html#someFunction" translate="no">someFunction</a></b>(int, int <i>v</i> = 0)</td></tr>
+<tr><td class="memItemLeft rightAlign topAlign"> void </td><td class="memItemRight bottomAlign"><b><a href="testqdoc-test.html#someFunctionDefaultArg" translate="no">someFunctionDefaultArg</a></b>(int <i>i</i>, bool <i>b</i> = false) const</td></tr>
+<tr><td class="memItemLeft rightAlign topAlign"> virtual void </td><td class="memItemRight bottomAlign"><b><a href="testqdoc-test.html#virtualFun" translate="no">virtualFun</a></b>()</td></tr>
+<tr><td class="memItemLeft rightAlign topAlign"> TestQDoc::Test &amp;</td><td class="memItemRight bottomAlign"><b><a href="testqdoc-test.html#operator-eq" translate="no">operator=</a></b>(TestQDoc::Test &amp;&amp;<i>other</i>)</td></tr>
+</table></div>
+<h2 id="protected-functions">Protected Functions</h2>
+<div class="table"><table class="alignedsummary" translate="no">
+<tr><td class="memItemLeft rightAlign topAlign"> void </td><td class="memItemRight bottomAlign"><b><a href="testqdoc-test.html#overload" translate="no">overload</a></b>()</td></tr>
+<tr><td class="memItemLeft rightAlign topAlign"> <code class="summary extra" translate="no">(since Test 1.2)</code> void </td><td class="memItemRight bottomAlign"><b><a href="testqdoc-test.html#overload-1" translate="no">overload</a></b>(bool <i>b</i>)</td></tr>
+</table></div>
+<h2 id="related-non-members">Related Non-Members</h2>
+<div class="table"><table class="alignedsummary" translate="no">
+<tr><td class="memItemLeft rightAlign topAlign"> bool </td><td class="memItemRight bottomAlign"><b><a href="testqdoc-test.html#operator-eq-eq" translate="no">operator==</a></b>(const TestQDoc::Test &amp;<i>lhs</i>, const TestQDoc::Test &amp;<i>rhs</i>)</td></tr>
+</table></div>
+<h2 id="macros">Macros</h2>
+<div class="table"><table class="alignedsummary" translate="no">
+<tr><td class="memItemLeft rightAlign topAlign"> <code class="summary extra" translate="no">(since Test 1.1)</code> </td><td class="memItemRight bottomAlign"><b><a href="testqdoc-test.html#QDOCTEST_MACRO2" translate="no">QDOCTEST_MACRO2</a></b>(int &amp;<i>x</i>)</td></tr>
+</table></div>
+<!-- $$$Test-description -->
+<div class="descr">
+<h2 id="details">Detailed Description</h2>
+</div>
+<!-- @@@Test -->
+<div class="types">
+<h2>Member Type Documentation</h2>
+<!-- $$$SomeType -->
+<h3 class="fn" translate="no" id="SomeType-typedef">Test::<span class="name">SomeType</span></h3>
+<p>A typedef.</p>
+<!-- @@@SomeType -->
+</div>
+<div class="func">
+<h2>Member Function Documentation</h2>
+<!-- $$$ -->
+<div class="fngroup">
+<h3 class="fn fngroupitem" translate="no" id="overload"><code class="details extra" translate="no">[protected]</code> <span class="type">void</span> Test::<span class="name">overload</span>()</h3><h3 class="fn fngroupitem" translate="no" id="overload-1"><code class="details extra" translate="no">[protected, since Test 1.2]</code> <span class="type">void</span> Test::<span class="name">overload</span>(<span class="type">bool</span> <i>b</i>)</h3></div>
+<p>Overloads that share a documentation comment, optionally taking a parameter <i translate="no">b</i>.</p>
+<!-- @@@ -->
+<!-- $$$Test[overload1]$$$Test -->
+<h3 class="fn" translate="no" id="Test"><code class="details extra" translate="no">[noexcept default]</code> Test::<span class="name">Test</span>()</h3>
+<p>Default constructor.</p>
+<!-- @@@Test -->
+<!-- $$$funcPtr[overload1]$$$funcPtrboolconstchar* -->
+<h3 class="fn" translate="no" id="funcPtr"><span class="type">void</span> (*)(<span class="type">bool</span>) Test::<span class="name">funcPtr</span>(<span class="type">bool</span> <i>b</i>, const <span class="type">char</span> *<i>s</i>)</h3>
+<p>Returns a pointer to a function that takes a boolean. Uses <i translate="no">b</i> and <i translate="no">s</i>.</p>
+<!-- @@@funcPtr -->
+<!-- $$$inlineFunction[overload1]$$$inlineFunction -->
+<h3 class="fn" translate="no" id="inlineFunction"><span class="type">void</span> Test::<span class="name">inlineFunction</span>()</h3>
+<p>An inline function, documented using the \fn QDoc command.</p>
+<!-- @@@inlineFunction -->
+<!-- $$$methodWithEmDashInItsDocs[overload1]$$$methodWithEmDashInItsDocs -->
+<h3 class="fn" translate="no" id="methodWithEmDashInItsDocs"><span class="type">void</span> Test::<span class="name">methodWithEmDashInItsDocs</span>()</h3>
+<p>This method has em dashes in its documentation&mdash;as you'll find represented by <code translate="no">---</code> in the sources&mdash;here and there. The important bit to note is that when passed e.g. to the \c command, the three hyphens are processed as input to the command and not replaced by an em dash.</p>
+<p>-----------------------------------------------------------------------</p>
+<p>People can still add a bunch of dashes, though, without QDoc replacing them all with a series of em dashes.</p>
+<p>&mdash;You can also start a new paragraph with an em dash, if you want to.</p>
+<p><b>See also </b><a href="testqdoc-test.html#methodWithEnDashInItsDocs" translate="no">methodWithEnDashInItsDocs</a>.</p>
+<!-- @@@methodWithEmDashInItsDocs -->
+<!-- $$$methodWithEnDashInItsDocs[overload1]$$$methodWithEnDashInItsDocs -->
+<h3 class="fn" translate="no" id="methodWithEnDashInItsDocs"><span class="type">void</span> Test::<span class="name">methodWithEnDashInItsDocs</span>()</h3>
+<p>This method has en dashes in its documentation &ndash; as you'll find represented by <code translate="no">--</code> in the sources &ndash; here and there. The important bit to note is that when passed e.g. to the \c command, the two hyphens are processed as input to the command and not replaced by an en dash. This also applies to code blocks, where otherwise, the decrement operator would get completely borked:</p>
+<pre class="cpp" translate="no"><span class="keyword">for</span> (<span class="type">int</span> i <span class="operator">=</span> <span class="number">42</span>; i <span class="operator">&gt;</span> <span class="number">0</span>; <span class="operator">-</span><span class="operator">-</span>i)
+ <span class="comment">// Do something cool during countdown.</span></pre>
+<p>...as it would be silly if this would output &ndash;i instead of <code translate="no">--i</code>.</p>
+<p>-----------------------------------------------------------------------</p>
+<p>It still allows people to add a bunch of dashes, though, without replacing them all with a series of en dashes. Of course, they might want to use the \hr command instead, like this:</p>
+<hr />
+<p>&ndash; You can also start a new paragraph with an en dash, if you want to.</p>
+<p><b>See also </b>methodWithEnDashInItsDocs.</p>
+<!-- @@@methodWithEnDashInItsDocs -->
+<!-- $$$someFunction[overload1]$$$someFunctionintint -->
+<h3 class="fn" translate="no" id="someFunction"><span class="type">int</span> Test::<span class="name">someFunction</span>(<span class="type">int</span>, <span class="type">int</span> <i>v</i> = 0)</h3>
+<p>Function that takes a parameter <i translate="no">v</i>. Also returns the value of <i translate="no">v</i>.</p>
+<!-- @@@someFunction -->
+<!-- $$$someFunctionDefaultArg[overload1]$$$someFunctionDefaultArgintbool -->
+<h3 class="fn" translate="no" id="someFunctionDefaultArg"><span class="type">void</span> Test::<span class="name">someFunctionDefaultArg</span>(<span class="type">int</span> <i>i</i>, <span class="type">bool</span> <i>b</i> = false) const</h3>
+<p>Function that takes a parameter <i translate="no">i</i> and <i translate="no">b</i>.</p>
+<p><b>Warning:</b> This function is not <a href="https://doc.qt.io/qt/17-qdoc-commands-thread.html#reentrant-command">reentrant</a>.</p>
+<!-- @@@someFunctionDefaultArg -->
+<!-- $$$virtualFun[overload1]$$$virtualFun -->
+<h3 class="fn" translate="no" id="virtualFun"><code class="details extra" translate="no">[virtual]</code> <span class="type">void</span> Test::<span class="name">virtualFun</span>()</h3>
+<p>Function that must be reimplemented.</p>
+<!-- @@@virtualFun -->
+<!-- $$$operator=[overload1]$$$operator=TestQDoc::Test&& -->
+<h3 class="fn" translate="no" id="operator-eq"><code class="details extra" translate="no">[noexcept default]</code> <span class="type"><a href="testqdoc-test.html" translate="no">TestQDoc::Test</a></span> &amp;Test::<span class="name">operator=</span>(<span class="type"><a href="testqdoc-test.html" translate="no">TestQDoc::Test</a></span> &amp;&amp;<i>other</i>)</h3>
+<p>Move-assigns <i translate="no">other</i>.</p>
+<!-- @@@operator= -->
+</div>
+<div class="relnonmem">
+<h2>Related Non-Members</h2>
+<!-- $$$operator==[overload1]$$$operator==constTestQDoc::Test&constTestQDoc::Test& -->
+<h3 class="fn" translate="no" id="operator-eq-eq"><span class="type">bool</span> <span class="name">operator==</span>(const <span class="type"><a href="testqdoc-test.html" translate="no">TestQDoc::Test</a></span> &amp;<i>lhs</i>, const <span class="type"><a href="testqdoc-test.html" translate="no">TestQDoc::Test</a></span> &amp;<i>rhs</i>)</h3>
+<p>Returns true if <i translate="no">lhs</i> and <i translate="no">rhs</i> are equal.</p>
+<!-- @@@operator== -->
+</div>
+<div class="macros">
+<h2>Macro Documentation</h2>
+<!-- $$$QDOCTEST_MACRO2[overload1]$$$QDOCTEST_MACRO2int& -->
+<h3 class="fn" translate="no" id="QDOCTEST_MACRO2"><code class="details extra" translate="no">[since Test 1.1]</code> <span class="name">QDOCTEST_MACRO2</span>(<span class="type">int</span> &amp;<i>x</i>)</h3>
+<p>A macro with argument <i translate="no">x</i>.</p>
+<p>This macro was introduced in Test 1.1.</p>
+<!-- @@@QDOCTEST_MACRO2 -->
+</div>
+</body>
+</html>
diff --git a/src/qdoc/qdoc/tests/generatedoutput/expected_output/testqdoc-testderived-members.html b/src/qdoc/qdoc/tests/generatedoutput/expected_output/testqdoc-testderived-members.html
new file mode 100644
index 000000000..df89becd0
--- /dev/null
+++ b/src/qdoc/qdoc/tests/generatedoutput/expected_output/testqdoc-testderived-members.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+<!-- testcpp.cpp -->
+ <meta name="description" content="A class in a namespace, derived from Test.">
+ <title>List of All Members for TestDerived | TestCPP</title>
+</head>
+<body>
+<li><a href="testcpp-module.html" translate="no">C++ Classes</a></li>
+<li>TestDerived</li>
+<div class="sidebar"><div class="sidebar-content" id="sidebar-content"></div></div>
+<h1 class="title" translate="no">List of All Members for TestDerived</h1>
+<p>This is the complete list of members for <a href="testqdoc-testderived.html">TestQDoc::TestDerived</a>, including inherited members.</p>
+<ul>
+<li class="fn" translate="no"><span class="name"><b><a href="testqdoc-testderived.html#DerivedType-typedef" translate="no">DerivedType</a></b></span></li>
+<li class="fn" translate="no"><span class="name"><b><a href="testqdoc-testderived.html#NotTypedef-typedef" translate="no">NotTypedef</a></b></span></li>
+<li class="fn" translate="no"><span class="name"><b><a href="testqdoc-test.html#SomeType-typedef" translate="no">SomeType</a></b></span></li>
+<li class="fn" translate="no"><span class="name"><b><a href="testqdoc-test.html#funcPtr" translate="no">funcPtr</a></b></span>(bool, const char *) : void (*)(bool)</li>
+<li class="fn" translate="no"><span class="name"><b><a href="testqdoc-test.html#inlineFunction" translate="no">inlineFunction</a></b></span>()</li>
+<li class="fn" translate="no"><span class="name"><b><a href="testqdoc-test.html#methodWithEmDashInItsDocs" translate="no">methodWithEmDashInItsDocs</a></b></span>()</li>
+<li class="fn" translate="no"><span class="name"><b><a href="testqdoc-test.html#methodWithEnDashInItsDocs" translate="no">methodWithEnDashInItsDocs</a></b></span>()</li>
+<li class="fn" translate="no"><span class="name"><b><a href="testqdoc-test.html#overload" translate="no">overload</a></b></span>()</li>
+<li class="fn" translate="no"><span class="name"><b><a href="testqdoc-test.html#overload-1" translate="no">overload</a></b></span>(bool)</li>
+<li class="fn" translate="no"><span class="name"><b><a href="testqdoc-test.html#someFunction" translate="no">someFunction</a></b></span>(int, int) : int</li>
+<li class="fn" translate="no"><span class="name"><b><a href="testqdoc-test.html#someFunctionDefaultArg" translate="no">someFunctionDefaultArg</a></b></span>(int, bool) const</li>
+<li class="fn" translate="no"><span class="name"><b><a href="testqdoc-testderived.html#someValue" translate="no">someValue</a></b></span>() : TestQDoc::TestDerived::NotTypedef</li>
+<li class="fn" translate="no"><span class="name"><b><a href="testqdoc-testderived.html#virtualFun" translate="no">virtualFun</a></b></span>()</li>
+<li class="fn" translate="no"><span class="name"><b><a href="testqdoc-test.html#virtualFun" translate="no">virtualFun</a></b></span>()</li>
+<li class="fn" translate="no"><span class="name"><b><a href="testqdoc-test.html#operator-eq" translate="no">operator=</a></b></span>(TestQDoc::Test &amp;&amp;) : TestQDoc::Test &amp;</li>
+</ul>
+</body>
+</html>
diff --git a/src/qdoc/qdoc/tests/generatedoutput/expected_output/testqdoc-testderived-obsolete.html b/src/qdoc/qdoc/tests/generatedoutput/expected_output/testqdoc-testderived-obsolete.html
new file mode 100644
index 000000000..42b536e09
--- /dev/null
+++ b/src/qdoc/qdoc/tests/generatedoutput/expected_output/testqdoc-testderived-obsolete.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+<!-- testcpp.cpp -->
+ <meta name="description" content="A class in a namespace, derived from Test.">
+ <title>Obsolete Members for TestDerived | TestCPP</title>
+</head>
+<body>
+<li><a href="testcpp-module.html" translate="no">C++ Classes</a></li>
+<li>TestDerived</li>
+<div class="sidebar"><div class="sidebar-content" id="sidebar-content"></div></div>
+<h1 class="title" translate="no">Obsolete Members for TestDerived</h1>
+<p><b>The following members of class <a href="testqdoc-testderived.html" translate="no">TestDerived</a> are deprecated.</b> They are provided to keep old source code working. We strongly advise against using them in new code.</p>
+<h2>Static Public Members</h2>
+<div class="table"><table class="alignedsummary" translate="no">
+<tr><td class="memItemLeft topAlign rightAlign"> <code class="summary extra" translate="no">(deprecated)</code> void </td><td class="memItemRight bottomAlign"><b><a href="testqdoc-testderived-obsolete.html#staticObsoleteMember" translate="no">staticObsoleteMember</a></b>()</td></tr>
+</table></div>
+<h2>Member Function Documentation</h2>
+<!-- $$$staticObsoleteMember[overload1]$$$staticObsoleteMember -->
+<h3 class="fn" translate="no" id="staticObsoleteMember"><code class="details extra" translate="no">[static, deprecated]</code> <span class="type">void</span> TestDerived::<span class="name">staticObsoleteMember</span>()</h3>
+<p>This function is deprecated. We strongly advise against using it in new code.</p>
+<p>Static obsolete method.</p>
+<!-- @@@staticObsoleteMember -->
+</body>
+</html>
diff --git a/src/qdoc/qdoc/tests/generatedoutput/expected_output/testqdoc-testderived.html b/src/qdoc/qdoc/tests/generatedoutput/expected_output/testqdoc-testderived.html
new file mode 100644
index 000000000..04f789abb
--- /dev/null
+++ b/src/qdoc/qdoc/tests/generatedoutput/expected_output/testqdoc-testderived.html
@@ -0,0 +1,81 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+<!-- testcpp.cpp -->
+ <meta name="description" content="A class in a namespace, derived from Test.">
+ <title>TestDerived Class | TestCPP</title>
+</head>
+<body>
+<li><a href="testcpp-module.html" translate="no">C++ Classes</a></li>
+<li>TestDerived</li>
+<div class="sidebar">
+<div class="toc">
+<h3 id="toc">Contents</h3>
+<ul>
+<li class="level1"><a href="#public-types">Public Types</a></li>
+<li class="level1"><a href="#public-functions">Public Functions</a></li>
+<li class="level1"><a href="#reimplemented-public-functions">Reimplemented Public Functions</a></li>
+<li class="level1"><a href="#details">Detailed Description</a></li>
+</ul>
+</div>
+<div class="sidebar-content" id="sidebar-content"></div></div>
+<h1 class="title" translate="no">TestDerived Class</h1>
+<span class="small-subtitle" translate="no">class <a href="testqdoc.html" translate="no">TestQDoc</a>::TestDerived</span>
+<!-- $$$TestDerived-brief -->
+<p>A class in a namespace, derived from <a href="testqdoc-test.html" translate="no">Test</a>. <a href="#details">More...</a></p>
+<!-- @@@TestDerived -->
+<div class="table"><table class="alignedsummary" translate="no">
+<tr><td class="memItemLeft rightAlign topAlign"> Header:</td><td class="memItemRight bottomAlign"> <span class="preprocessor">#include &lt;TestDerived&gt;</span></td></tr>
+<tr><td class="memItemLeft rightAlign topAlign"> CMake:</td><td class="memItemRight bottomAlign"> find_package(Qt6 REQUIRED COMPONENTS QDocTest) <br/>
+target_link_libraries(mytarget PRIVATE Qt6::QDocTest)</td></tr>
+<tr><td class="memItemLeft rightAlign topAlign"> qmake:</td><td class="memItemRight bottomAlign"> QT += testcpp</td></tr>
+<tr><td class="memItemLeft rightAlign topAlign"> Since:</td><td class="memItemRight bottomAlign"> Qt 2.0</td></tr>
+<tr><td class="memItemLeft rightAlign topAlign"> Inherits:</td><td class="memItemRight bottomAlign"> <a href="testqdoc-test.html" translate="no">TestQDoc::Test</a></td></tr>
+</table></div>
+<ul>
+<li><a href="testqdoc-testderived-members.html">List of all members, including inherited members</a></li>
+<li><a href="testqdoc-testderived-obsolete.html">Deprecated members</a></li>
+</ul>
+<h2 id="public-types">Public Types</h2>
+<div class="table"><table class="alignedsummary" translate="no">
+<tr><td class="memItemLeft rightAlign topAlign"> </td><td class="memItemRight bottomAlign"><b><a href="testqdoc-testderived.html#DerivedType-typedef" translate="no">DerivedType</a></b></td></tr>
+<tr><td class="memItemLeft rightAlign topAlign"> </td><td class="memItemRight bottomAlign"><b><a href="testqdoc-testderived.html#NotTypedef-typedef" translate="no">NotTypedef</a></b></td></tr>
+</table></div>
+<h2 id="public-functions">Public Functions</h2>
+<div class="table"><table class="alignedsummary" translate="no">
+<tr><td class="memItemLeft rightAlign topAlign"> TestQDoc::TestDerived::NotTypedef </td><td class="memItemRight bottomAlign"><b><a href="testqdoc-testderived.html#someValue" translate="no">someValue</a></b>()</td></tr>
+</table></div>
+<h2 id="reimplemented-public-functions">Reimplemented Public Functions</h2>
+<div class="table"><table class="alignedsummary" translate="no">
+<tr><td class="memItemLeft rightAlign topAlign"> virtual void </td><td class="memItemRight bottomAlign"><b><a href="testqdoc-testderived.html#virtualFun" translate="no">virtualFun</a></b>() override</td></tr>
+</table></div>
+<!-- $$$TestDerived-description -->
+<div class="descr">
+<h2 id="details">Detailed Description</h2>
+</div>
+<!-- @@@TestDerived -->
+<div class="types">
+<h2>Member Type Documentation</h2>
+<!-- $$$DerivedType -->
+<h3 class="fn" translate="no" id="DerivedType-typedef"><code class="details extra" translate="no">[alias]</code> TestDerived::<span class="name">DerivedType</span></h3>
+<p>An aliased typedef.</p>
+<!-- @@@DerivedType -->
+<!-- $$$NotTypedef -->
+<h3 class="fn" translate="no" id="NotTypedef-typedef"><code class="details extra" translate="no">[alias]</code> TestDerived::<span class="name">NotTypedef</span></h3>
+<p>I'm an alias, not a typedef.</p>
+<!-- @@@NotTypedef -->
+</div>
+<div class="func">
+<h2>Member Function Documentation</h2>
+<!-- $$$someValue[overload1]$$$someValue -->
+<h3 class="fn" translate="no" id="someValue"><span class="type"><a href="testqdoc-testderived.html#NotTypedef-typedef" translate="no">TestQDoc::TestDerived::NotTypedef</a></span> TestDerived::<span class="name">someValue</span>()</h3>
+<p>Returns a value using an aliases type.</p>
+<!-- @@@someValue -->
+<!-- $$$virtualFun[overload1]$$$virtualFun -->
+<h3 class="fn" translate="no" id="virtualFun"><code class="details extra" translate="no">[override virtual]</code> <span class="type">void</span> TestDerived::<span class="name">virtualFun</span>()</h3>
+<p>Reimplements: <a href="testqdoc-test.html#virtualFun" translate="no">Test::virtualFun</a>().</p>
+<!-- @@@virtualFun -->
+</div>
+</body>
+</html>
diff --git a/src/qdoc/qdoc/tests/generatedoutput/expected_output/testqdoc.html b/src/qdoc/qdoc/tests/generatedoutput/expected_output/testqdoc.html
new file mode 100644
index 000000000..425c7b396
--- /dev/null
+++ b/src/qdoc/qdoc/tests/generatedoutput/expected_output/testqdoc.html
@@ -0,0 +1,64 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+<!-- testcpp.cpp -->
+ <meta name="description" content="A namespace.">
+ <title>TestQDoc Namespace | TestCPP</title>
+</head>
+<body>
+<div class="sidebar">
+<div class="toc">
+<h3 id="toc">Contents</h3>
+<ul>
+<li class="level1"><a href="#classes">Classes</a></li>
+<li class="level1"><a href="#macros">Macros</a></li>
+<li class="level1"><a href="#details">Detailed Description</a></li>
+<li class="level2"><a href="#usage">Usage</a></li>
+</ul>
+</div>
+<div class="sidebar-content" id="sidebar-content"></div></div>
+<h1 class="title" translate="no">TestQDoc Namespace</h1>
+<!-- $$$TestQDoc-brief -->
+<p>A namespace. <a href="#details">More...</a></p>
+<!-- @@@TestQDoc -->
+<div class="table"><table class="alignedsummary" translate="no">
+<tr><td class="memItemLeft rightAlign topAlign"> Header:</td><td class="memItemRight bottomAlign"> <span class="preprocessor">#include &lt;TestCPP&gt;</span></td></tr>
+<tr><td class="memItemLeft rightAlign topAlign"> CMake:</td><td class="memItemRight bottomAlign"> find_package(Qt6 REQUIRED COMPONENTS QDocTest) <br/>
+target_link_libraries(mytarget PRIVATE Qt6::QDocTest)</td></tr>
+<tr><td class="memItemLeft rightAlign topAlign"> qmake:</td><td class="memItemRight bottomAlign"> QT += testcpp</td></tr>
+<tr><td class="memItemLeft rightAlign topAlign"> Since:</td><td class="memItemRight bottomAlign"> Qt 2.0</td></tr>
+</table></div>
+<h2 id="classes">Classes</h2>
+<div class="table"><table class="alignedsummary" translate="no">
+<tr><td class="memItemLeft rightAlign topAlign"> <code class="summary extra" translate="no">(since 2.0)</code> class </td><td class="memItemRight bottomAlign"><b><a href="testqdoc-test.html" translate="no">Test</a></b></td></tr>
+<tr><td class="memItemLeft rightAlign topAlign"> <code class="summary extra" translate="no">(since 2.0)</code> class </td><td class="memItemRight bottomAlign"><b><a href="testqdoc-testderived.html" translate="no">TestDerived</a></b></td></tr>
+</table></div>
+<h2 id="macros">Macros</h2>
+<div class="table"><table class="alignedsummary" translate="no">
+<tr><td class="memItemLeft rightAlign topAlign"> </td><td class="memItemRight bottomAlign"><b><a href="testqdoc.html#QDOCTEST_MACRO" translate="no">QDOCTEST_MACRO</a></b></td></tr>
+</table></div>
+<!-- $$$TestQDoc-description -->
+<div class="descr">
+<h2 id="details">Detailed Description</h2>
+<h3 id="usage">Usage</h3>
+<p>This namespace is for testing QDoc output.</p>
+</div>
+<!-- @@@TestQDoc -->
+<div class="classes">
+<h2>Classes</h2>
+<h3> class <a href="testqdoc-test.html">Test</a></h3><!-- $$$Test-brief -->
+<p>A class in a namespace. <a href="testqdoc-test.html#details">More...</a></p>
+<!-- @@@Test -->
+<h3> class <a href="testqdoc-testderived.html">TestDerived</a></h3><!-- $$$TestDerived-brief -->
+<p>A class in a namespace, derived from <a href="testqdoc-test.html" translate="no">Test</a>. <a href="testqdoc-testderived.html#details">More...</a></p>
+<!-- @@@TestDerived -->
+</div>
+<div class="macros">
+<h2>Macro Documentation</h2>
+<!-- $$$QDOCTEST_MACRO[overload1]$$$QDOCTEST_MACRO -->
+<h3 class="fn" translate="no" id="QDOCTEST_MACRO"><span class="name">QDOCTEST_MACRO</span></h3>
+<!-- @@@QDOCTEST_MACRO -->
+</div>
+</body>
+</html>
diff --git a/src/qdoc/qdoc/tests/generatedoutput/testdata/configs/noautolist.qdocconf b/src/qdoc/qdoc/tests/generatedoutput/testdata/configs/noautolist.qdocconf
new file mode 100644
index 000000000..ad3b402ea
--- /dev/null
+++ b/src/qdoc/qdoc/tests/generatedoutput/testdata/configs/noautolist.qdocconf
@@ -0,0 +1,64 @@
+sources.fileextensions = "*.qml *.cpp *.qdoc"
+headers.fileextensions = "*.h"
+
+# images
+imagedirs = ../images
+
+# zero warning policy
+warninglimit = 0
+warninglimit.enabled = true
+
+# don't write host system-specific paths to index files
+locationinfo = false
+
+includepaths += -I../testcpp
+
+headers = ../testcpp/testcpp.h
+sources = ../testcpp/testcpp.cpp \
+ ../testcpp/classlists.qdoc
+
+macro.CMDFN = \\\\fn
+macro.nothing = \\dontdocument ()
+macro.testnoautolist = \\if defined(test_noautolist)\n\\noautolist\n\\endif
+
+navigation.cppclassespage = "QDoc Test C++ Classes"
+
+moduleheader = TestCPP
+
+project = Test
+description = "A test project for QDoc build artifacts"
+outputdir = ./html
+
+exampledirs = ../qml
+
+headerdirs += ../
+sourcedirs += ../qml
+
+# Exclude source files from other tests' subdirs
+excludedirs = ../bug80259
+
+examples.fileextensions = "*.qml *.cpp"
+
+macro.begincomment = "\\c{/*}"
+macro.QDocTestVer = "1.1"
+
+navigation.qmltypespage = "QDoc.Test QML Module"
+navigation.qmltypestitle = "Types"
+warninglimit += 1
+
+manifestmeta.examplecategories = "Application Examples" \
+ "Desktop" \
+ "Mobile" \
+ "Embedded"
+defines += test_noautolist
+
+outputformats = HTML DocBook
+
+HTML.nosubdirs = true
+DocBook.nosubdirs = true
+HTML.outputsubdir = noautolist
+DocBook.outputsubdir = noautolist-docbook
+
+# TODO: DocBook generator has trouble handling shared comment nodes
+# - allow two warnings related to these
+warninglimit = 2
diff --git a/src/qdoc/qdoc/tests/generatedoutput/testdata/configs/testcpp.qdocconf b/src/qdoc/qdoc/tests/generatedoutput/testdata/configs/testcpp.qdocconf
new file mode 100644
index 000000000..1d4ce57a6
--- /dev/null
+++ b/src/qdoc/qdoc/tests/generatedoutput/testdata/configs/testcpp.qdocconf
@@ -0,0 +1,31 @@
+sources.fileextensions = "*.qml *.cpp *.qdoc"
+headers.fileextensions = "*.h"
+
+# images
+imagedirs = ../images
+
+# zero warning policy
+warninglimit = 0
+warninglimit.enabled = true
+
+# don't write host system-specific paths to index files
+locationinfo = false
+
+# By default, use -outputdir directly, no subdir.
+# outputsubdir '.' matches the root of expected_output/
+HTML.nosubdirs = true
+HTML.outputsubdir = .
+
+project = TestCPP
+includepaths += -I../testcpp
+
+headers = ../testcpp/testcpp.h
+sources = ../testcpp/testcpp.cpp \
+ ../testcpp/classlists.qdoc
+exampledirs = ../testcpp/snippets
+
+macro.CMDFN = \\\\fn
+macro.nothing = \\dontdocument ()
+macro.testnoautolist = \\if defined(test_noautolist)\n\\noautolist\n\\endif
+
+navigation.cppclassespage = "QDoc Test C++ Classes"
diff --git a/src/qdoc/qdoc/tests/generatedoutput/testdata/configs/testcpp_singleexec.qdocconf b/src/qdoc/qdoc/tests/generatedoutput/testdata/configs/testcpp_singleexec.qdocconf
new file mode 100644
index 000000000..e9b72b75d
--- /dev/null
+++ b/src/qdoc/qdoc/tests/generatedoutput/testdata/configs/testcpp_singleexec.qdocconf
@@ -0,0 +1,32 @@
+sources.fileextensions = "*.qml *.cpp *.qdoc"
+headers.fileextensions = "*.h"
+
+# images
+imagedirs = ../images
+
+# zero warning policy
+warninglimit = 0
+warninglimit.enabled = true
+
+# don't write host system-specific paths to index files
+locationinfo = false
+
+# By default, use -outputdir directly, no subdir.
+# outputsubdir '.' matches the root of expected_output/
+HTML.nosubdirs = true
+HTML.outputsubdir = .
+
+project = TestCPP
+includepaths += -I../testcpp
+
+headers = ../testcpp/testcpp.h
+sources = ../testcpp/testcpp.cpp \
+ ../testcpp/classlists.qdoc
+exampledirs = ../testcpp/snippets
+
+macro.CMDFN = \\\\fn
+macro.nothing = \\dontdocument ()
+macro.testnoautolist = \\if defined(test_noautolist)\n\\noautolist\n\\endif
+
+navigation.cppclassespage = "QDoc Test C++ Classes"
+
diff --git a/src/qdoc/qdoc/tests/generatedoutput/testdata/crossmodule/CrossModule b/src/qdoc/qdoc/tests/generatedoutput/testdata/crossmodule/CrossModule
new file mode 100644
index 000000000..50bea93ab
--- /dev/null
+++ b/src/qdoc/qdoc/tests/generatedoutput/testdata/crossmodule/CrossModule
@@ -0,0 +1,2 @@
+#include "../testcpp/TestCPP"
+#include "testtype.h"
diff --git a/src/qdoc/qdoc/tests/generatedoutput/testdata/crossmodule/crossmodule.qdocconf b/src/qdoc/qdoc/tests/generatedoutput/testdata/crossmodule/crossmodule.qdocconf
new file mode 100644
index 000000000..404981136
--- /dev/null
+++ b/src/qdoc/qdoc/tests/generatedoutput/testdata/crossmodule/crossmodule.qdocconf
@@ -0,0 +1,26 @@
+project = CrossModule
+includepaths += -I.
+
+depends = testcpp
+
+headers = testtype.h
+sources = testtype.cpp
+
+HTML.nosubdirs = true
+HTML.outputsubdir = crossmodule
+
+defines += test_crossmodule
+
+sources.fileextensions = "*.qml *.cpp *.qdoc"
+headers.fileextensions = "*.h"
+
+# images
+imagedirs = ../images
+
+# zero warning policy
+warninglimit = 0
+warninglimit.enabled = true
+
+# don't write host system-specific paths to index files
+locationinfo = false
+
diff --git a/src/qdoc/qdoc/tests/generatedoutput/testdata/crossmodule/crossmodule_singleexec.qdocconf b/src/qdoc/qdoc/tests/generatedoutput/testdata/crossmodule/crossmodule_singleexec.qdocconf
new file mode 100644
index 000000000..1040bb5ad
--- /dev/null
+++ b/src/qdoc/qdoc/tests/generatedoutput/testdata/crossmodule/crossmodule_singleexec.qdocconf
@@ -0,0 +1,6 @@
+include(crossmodule.qdocconf)
+
+sources += namespaces.qdoc
+
+HTML.nosubdirs = true
+HTML.outputsubdir = crossmodule
diff --git a/src/qdoc/qdoc/tests/generatedoutput/testdata/crossmodule/namespaces.qdoc b/src/qdoc/qdoc/tests/generatedoutput/testdata/crossmodule/namespaces.qdoc
new file mode 100644
index 000000000..3732008ac
--- /dev/null
+++ b/src/qdoc/qdoc/tests/generatedoutput/testdata/crossmodule/namespaces.qdoc
@@ -0,0 +1,9 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+/*!
+ \page all-namespaces.html
+ \title Namespaces
+
+ \generatelist namespaces
+*/
diff --git a/src/qdoc/qdoc/tests/generatedoutput/testdata/crossmodule/testtype.cpp b/src/qdoc/qdoc/tests/generatedoutput/testdata/crossmodule/testtype.cpp
new file mode 100644
index 000000000..d7557da73
--- /dev/null
+++ b/src/qdoc/qdoc/tests/generatedoutput/testdata/crossmodule/testtype.cpp
@@ -0,0 +1,45 @@
+// Copyright (C) 2020 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+#include "testtype.h"
+
+/*!
+ \module CrossModule
+*/
+
+/*!
+ Function under a namespace that's documented elsewhere.
+*/
+void CrossModuleRef::documentMeToo()
+{
+}
+
+/*!
+ \class TestType
+ \inmodule CrossModule
+ \brief A class inheriting another class that lives in an external doc
+ module.
+
+ \section1 Linking
+
+ These links go to the parent class:
+ \list
+ \li \l {TestQDoc::TestDerived}
+ \li \l {TestQDoc::}{Test} class \l Usage.
+ \li QDOCTEST_MACRO
+ \li DontLinkToMe
+ \endlist
+
+ \section1 Generated Lists
+
+ This is an annotated list of entries in a group:
+ \annotatedlist testgroup
+
+ \sa {TestQDoc::Test::}{someFunction()}
+*/
+
+/*!
+ Nothing to see here.
+*/
+void TestType::nothing()
+{
+}
diff --git a/src/qdoc/qdoc/tests/generatedoutput/testdata/crossmodule/testtype.h b/src/qdoc/qdoc/tests/generatedoutput/testdata/crossmodule/testtype.h
new file mode 100644
index 000000000..b6b0d446f
--- /dev/null
+++ b/src/qdoc/qdoc/tests/generatedoutput/testdata/crossmodule/testtype.h
@@ -0,0 +1,16 @@
+// Copyright (C) 2020 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+#pragma once
+
+#include "../testcpp/testcpp.h"
+
+namespace CrossModuleRef {
+ void documentMeToo();
+}
+
+class TestType : public TestQDoc::TestDerived
+{
+public:
+ TestType() {}
+ void nothing() {}
+};
diff --git a/src/qdoc/qdoc/tests/generatedoutput/testdata/dontdocument/TestCPP b/src/qdoc/qdoc/tests/generatedoutput/testdata/dontdocument/TestCPP
new file mode 100644
index 000000000..7291e6d8f
--- /dev/null
+++ b/src/qdoc/qdoc/tests/generatedoutput/testdata/dontdocument/TestCPP
@@ -0,0 +1,2 @@
+#include "../TestCPP"
+#include "dont.h"
diff --git a/src/qdoc/qdoc/tests/generatedoutput/testdata/dontdocument/dontdocument.qdocconf b/src/qdoc/qdoc/tests/generatedoutput/testdata/dontdocument/dontdocument.qdocconf
new file mode 100644
index 000000000..605c68b88
--- /dev/null
+++ b/src/qdoc/qdoc/tests/generatedoutput/testdata/dontdocument/dontdocument.qdocconf
@@ -0,0 +1,46 @@
+sources.fileextensions = "*.qml *.cpp *.qdoc"
+headers.fileextensions = "*.h"
+
+# images
+imagedirs = ../images
+
+# zero warning policy
+warninglimit = 0
+warninglimit.enabled = true
+
+# don't write host system-specific paths to index files
+locationinfo = false
+
+project = TestCPP
+includepaths += -I../testcpp
+
+headers = ../testcpp/testcpp.h
+sources = ../testcpp/testcpp.cpp \
+ ../testcpp/classlists.qdoc
+exampledirs = ../testcpp/snippets
+
+macro.CMDFN = \\\\fn
+macro.nothing = \\dontdocument ()
+macro.testnoautolist = \\if defined(test_noautolist)\n\\noautolist\n\\endif
+
+navigation.cppclassespage = "QDoc Test C++ Classes"
+
+headers += dont.h
+sources += dont.cpp test.qdoc
+
+qhp.projects = DontDocument
+
+qhp.DontDocument.file = dontdocument.qhp
+qhp.DontDocument.namespace = org.qt-project.dontdocument.001
+qhp.DontDocument.virtualFolder = test
+qhp.DontDocument.indexTitle = QDoc Test C++ Classes
+qhp.DontDocument.indexRoot =
+
+qhp.DontDocument.subprojects = classes
+qhp.DontDocument.subprojects.classes.title = Classes
+qhp.DontDocument.subprojects.classes.indexTitle = QDoc Test C++ Classes
+qhp.DontDocument.subprojects.classes.selectors = class
+qhp.DontDocument.subprojects.classes.sortPages = true
+
+HTML.nosubdirs = true
+HTML.outputsubdir = dontdocument
diff --git a/src/qdoc/qdoc/tests/generatedoutput/testdata/examples/demos/demo/demo.cpp b/src/qdoc/qdoc/tests/generatedoutput/testdata/examples/demos/demo/demo.cpp
new file mode 100644
index 000000000..a5e983503
--- /dev/null
+++ b/src/qdoc/qdoc/tests/generatedoutput/testdata/examples/demos/demo/demo.cpp
@@ -0,0 +1,11 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+bool isOverThousand(int n)
+{
+//! [integer literal with separator]
+ if (n > 1'000)
+ return true;
+//! [integer literal with separator]
+ return false;
+}
diff --git a/src/qdoc/qdoc/tests/generatedoutput/testdata/examples/demos/demo/demo.pro b/src/qdoc/qdoc/tests/generatedoutput/testdata/examples/demos/demo/demo.pro
new file mode 100644
index 000000000..dbe8ff3c0
--- /dev/null
+++ b/src/qdoc/qdoc/tests/generatedoutput/testdata/examples/demos/demo/demo.pro
@@ -0,0 +1,2 @@
+TEMPLATE = aux
+message("Nothing to see here.")
diff --git a/src/qdoc/qdoc/tests/generatedoutput/testdata/examples/demos/demo/doc/src/demo.qdoc b/src/qdoc/qdoc/tests/generatedoutput/testdata/examples/demos/demo/doc/src/demo.qdoc
new file mode 100644
index 000000000..d06e34ca2
--- /dev/null
+++ b/src/qdoc/qdoc/tests/generatedoutput/testdata/examples/demos/demo/doc/src/demo.qdoc
@@ -0,0 +1,11 @@
+// Copyright (C) 2020 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+/*!
+ \example demos/demo
+ \title Demo
+ \image leonardo-da-vinci.png
+ //! Icon made by Smashicons from www.flaticon.com
+
+ \snippet demos/demo/demo.cpp integer literal with separator
+*/
diff --git a/src/qdoc/qdoc/tests/generatedoutput/testdata/examples/demos/demo/dontxclude/CMakeLists.txt b/src/qdoc/qdoc/tests/generatedoutput/testdata/examples/demos/demo/dontxclude/CMakeLists.txt
new file mode 100644
index 000000000..d29157aad
--- /dev/null
+++ b/src/qdoc/qdoc/tests/generatedoutput/testdata/examples/demos/demo/dontxclude/CMakeLists.txt
@@ -0,0 +1,2 @@
+cmake_minimum_required(VERSION 3.16)
+project (DONTXCLUDEDIRS_QDOCTEST)
diff --git a/src/qdoc/qdoc/tests/generatedoutput/testdata/examples/demos/demo/excludes/CMakeLists.txt b/src/qdoc/qdoc/tests/generatedoutput/testdata/examples/demos/demo/excludes/CMakeLists.txt
new file mode 100644
index 000000000..09b447642
--- /dev/null
+++ b/src/qdoc/qdoc/tests/generatedoutput/testdata/examples/demos/demo/excludes/CMakeLists.txt
@@ -0,0 +1,2 @@
+cmake_minimum_required(VERSION 3.16)
+project (EXCLUDEDIR_QDOCTEST)
diff --git a/src/qdoc/qdoc/tests/generatedoutput/testdata/examples/demos/hidden/doc/src/hidden.qdoc b/src/qdoc/qdoc/tests/generatedoutput/testdata/examples/demos/hidden/doc/src/hidden.qdoc
new file mode 100644
index 000000000..9001e3d96
--- /dev/null
+++ b/src/qdoc/qdoc/tests/generatedoutput/testdata/examples/demos/hidden/doc/src/hidden.qdoc
@@ -0,0 +1,11 @@
+// Copyright (C) 2020 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+/*!
+ \example demos/hidden
+ \title Hidden Demo
+ \meta tag broken
+ \brief Tagged 'broken', does not appear in examples-manifest.xml.
+
+ Also missing an image, but that's OK as it's broken anyway.
+*/
diff --git a/src/qdoc/qdoc/tests/generatedoutput/testdata/examples/demos/hidden/hidden.pro b/src/qdoc/qdoc/tests/generatedoutput/testdata/examples/demos/hidden/hidden.pro
new file mode 100644
index 000000000..dbe8ff3c0
--- /dev/null
+++ b/src/qdoc/qdoc/tests/generatedoutput/testdata/examples/demos/hidden/hidden.pro
@@ -0,0 +1,2 @@
+TEMPLATE = aux
+message("Nothing to see here.")
diff --git a/src/qdoc/qdoc/tests/generatedoutput/testdata/images/01.png b/src/qdoc/qdoc/tests/generatedoutput/testdata/images/01.png
new file mode 100644
index 000000000..d73ab969b
--- /dev/null
+++ b/src/qdoc/qdoc/tests/generatedoutput/testdata/images/01.png
Binary files differ
diff --git a/src/qdoc/qdoc/tests/generatedoutput/testdata/images/leonardo-da-vinci.png b/src/qdoc/qdoc/tests/generatedoutput/testdata/images/leonardo-da-vinci.png
new file mode 100644
index 000000000..854acb4ca
--- /dev/null
+++ b/src/qdoc/qdoc/tests/generatedoutput/testdata/images/leonardo-da-vinci.png
Binary files differ
diff --git a/src/qdoc/qdoc/tests/generatedoutput/testdata/includefromexampledirs/excludes/anotherindex.qdoc b/src/qdoc/qdoc/tests/generatedoutput/testdata/includefromexampledirs/excludes/anotherindex.qdoc
new file mode 100644
index 000000000..f8436416f
--- /dev/null
+++ b/src/qdoc/qdoc/tests/generatedoutput/testdata/includefromexampledirs/excludes/anotherindex.qdoc
@@ -0,0 +1,14 @@
+// Copyright (C) 2020 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+/*!
+//! exampledirs-include
+ \page index.html
+ \title doc index
+
+ \section1 C++ Classes
+ \generatelist {classesbymodule TestCPP}
+ \section1 QML Types
+ \annotatedlist qmltypes
+//! exampledirs-include
+*/
diff --git a/src/qdoc/qdoc/tests/generatedoutput/testdata/includefromexampledirs/excludes/parentinclude.qdoc b/src/qdoc/qdoc/tests/generatedoutput/testdata/includefromexampledirs/excludes/parentinclude.qdoc
new file mode 100644
index 000000000..4409fba8f
--- /dev/null
+++ b/src/qdoc/qdoc/tests/generatedoutput/testdata/includefromexampledirs/excludes/parentinclude.qdoc
@@ -0,0 +1,28 @@
+// Copyright (C) 2020 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+/*!
+//! abstract-type
+ \qmltype AbstractParent
+ \inqmlmodule QDoc.Test
+ \ingroup qmltypes
+ \qmlabstract
+ \brief Abstract base QML type.
+//! abstract-type
+*/
+
+/*!
+ //! --- Observe the indented snippet tag:
+ //! children-qmlproperty
+ \qmlproperty list<Child> AbstractParent::children
+ \qmldefault
+ \brief Children of the type.
+ //! children-qmlproperty
+*/
+
+/*!
+//! rear-qmlmethod
+ \qmlmethod void AbstractParent::rear(Child child)
+ \brief Do some abstract parenting on \a child.
+//! rear-qmlmethod
+*/
diff --git a/src/qdoc/qdoc/tests/generatedoutput/testdata/includefromexampledirs/includefromexampledirs.qdocconf b/src/qdoc/qdoc/tests/generatedoutput/testdata/includefromexampledirs/includefromexampledirs.qdocconf
new file mode 100644
index 000000000..abbfabb87
--- /dev/null
+++ b/src/qdoc/qdoc/tests/generatedoutput/testdata/includefromexampledirs/includefromexampledirs.qdocconf
@@ -0,0 +1,64 @@
+sources.fileextensions = "*.qml *.cpp *.qdoc"
+headers.fileextensions = "*.h"
+
+# images
+imagedirs = ../images
+
+# zero warning policy
+warninglimit = 0
+warninglimit.enabled = true
+
+# don't write host system-specific paths to index files
+locationinfo = false
+
+includepaths += -I../testcpp
+
+headers = ../testcpp/testcpp.h
+sources = ../testcpp/testcpp.cpp \
+ ../testcpp/classlists.qdoc
+
+macro.CMDFN = \\\\fn
+macro.nothing = \\dontdocument ()
+macro.testnoautolist = \\if defined(test_noautolist)\n\\noautolist\n\\endif
+
+navigation.cppclassespage = "QDoc Test C++ Classes"
+
+moduleheader = TestCPP
+
+project = Test
+description = "A test project for QDoc build artifacts"
+outputdir = ./html
+
+exampledirs = ../qml
+
+headerdirs += ../
+sourcedirs += ../qml
+
+# Exclude source files from other tests' subdirs
+excludedirs = ../bug80259
+
+examples.fileextensions = "*.qml *.cpp"
+
+macro.begincomment = "\\c{/*}"
+macro.QDocTestVer = "1.1"
+
+navigation.qmltypespage = "QDoc.Test QML Module"
+navigation.qmltypestitle = "Types"
+warninglimit += 1
+
+manifestmeta.examplecategories = "Application Examples" \
+ "Desktop" \
+ "Mobile" \
+ "Embedded"
+
+sourcedirs += src
+
+excludedirs += excludes \
+ ../qml/componentset
+
+excludefiles += ../qml/parent.qdoc
+
+exampledirs += excludes
+
+HTML.nosubdirs = true
+HTML.outputsubdir = includefromexampledirs
diff --git a/src/qdoc/qdoc/tests/generatedoutput/testdata/includefromexampledirs/src/includefromparent.qdoc b/src/qdoc/qdoc/tests/generatedoutput/testdata/includefromexampledirs/src/includefromparent.qdoc
new file mode 100644
index 000000000..a7ac5454b
--- /dev/null
+++ b/src/qdoc/qdoc/tests/generatedoutput/testdata/includefromexampledirs/src/includefromparent.qdoc
@@ -0,0 +1,41 @@
+// Copyright (C) 2020 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+/*!
+\include anotherindex.qdoc exampledirs-include
+
+\include parent.qdocinc
+*/
+
+/*!
+\include parentinclude.qdoc abstract-type
+
+\include parent.qdocinc
+*/
+
+/*!
+\include parentinclude.qdoc children-qmlproperty
+
+\include parent.qdocinc
+*/
+
+/*!
+\include parentinclude.qdoc rear-qmlmethod
+
+\include parent.qdocinc
+*/
+
+/*!
+ \qmltype Child
+ \inqmlmodule QDoc.Test
+ \ingroup qmltypes
+ \inherits AbstractParent
+ \brief A Child inheriting its parent.
+*/
+
+/*!
+ \qmlvaluetype int
+ \inqmlmodule QDoc.Test
+ \ingroup qmltypes
+ \brief An integer value type.
+*/
diff --git a/src/qdoc/qdoc/tests/generatedoutput/testdata/includefromexampledirs/src/parent.qdocinc b/src/qdoc/qdoc/tests/generatedoutput/testdata/includefromexampledirs/src/parent.qdocinc
new file mode 100644
index 000000000..307c39dbd
--- /dev/null
+++ b/src/qdoc/qdoc/tests/generatedoutput/testdata/includefromexampledirs/src/parent.qdocinc
@@ -0,0 +1 @@
+Test include file that is part of the sourcedirs.
diff --git a/src/qdoc/qdoc/tests/generatedoutput/testdata/indexlinking/indexlinking.qdocconf b/src/qdoc/qdoc/tests/generatedoutput/testdata/indexlinking/indexlinking.qdocconf
new file mode 100644
index 000000000..f2c1967de
--- /dev/null
+++ b/src/qdoc/qdoc/tests/generatedoutput/testdata/indexlinking/indexlinking.qdocconf
@@ -0,0 +1,29 @@
+# test linking to entities loaded from index
+project = IndexLinking
+
+# Use a URL different from what the dependency below uses,
+# to avoid linking with relative paths. See QTBUG-107762.
+url = indexlinking
+
+depends = \
+ qmlpropertygroups
+
+sources = linking.qdoc
+
+sources.fileextensions = "*.qml *.cpp *.qdoc"
+headers.fileextensions = "*.h"
+
+# images
+imagedirs = ../images
+
+# zero warning policy
+warninglimit = 0
+warninglimit.enabled = true
+
+# don't write host system-specific paths to index files
+locationinfo = false
+
+# By default, use -outputdir directly, no subdir.
+# outputsubdir '.' matches the root of expected_output/
+HTML.nosubdirs = true
+HTML.outputsubdir = .
diff --git a/src/qdoc/qdoc/tests/generatedoutput/testdata/indexlinking/linking.qdoc b/src/qdoc/qdoc/tests/generatedoutput/testdata/indexlinking/linking.qdoc
new file mode 100644
index 000000000..2206ec712
--- /dev/null
+++ b/src/qdoc/qdoc/tests/generatedoutput/testdata/indexlinking/linking.qdoc
@@ -0,0 +1,41 @@
+// Copyright (C) 2020 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+/*!
+ \page index-linking.html
+ \title Linking
+
+ \section1 QML properties
+ \list A
+ \li Property group: \l [QML] {Parent::group}.
+ \li Property in a group: \l [QmlPropertyGroups]
+ QDoc.Test::Parent::group.c.
+ \endlist
+
+ \section1 Auto-linking to types in code snippets
+
+ \qml
+ import QtQuick
+
+ ProgressBar {
+ // Linking to int value type
+ property int progress: 0
+ }
+ \endqml
+
+ \code
+ // 'int' should not link anywhere in C++ code
+ int main() { int x{0}; return x; }
+ \endcode
+*/
+
+/*!
+ \title LinkModule QML Types
+ \qmlmodule LinkModule 1.0
+*/
+
+/*!
+ \qmltype GrandChild
+ \inqmlmodule LinkModule
+ \inherits AnotherChild
+*/
diff --git a/src/qdoc/qdoc/tests/generatedoutput/testdata/qml/DocTest.qml b/src/qdoc/qdoc/tests/generatedoutput/testdata/qml/DocTest.qml
new file mode 100644
index 000000000..0bf95132d
--- /dev/null
+++ b/src/qdoc/qdoc/tests/generatedoutput/testdata/qml/DocTest.qml
@@ -0,0 +1,86 @@
+// Copyright (C) 2019 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick 2.0
+
+/*!
+ \qmltype DocTest
+ \inherits Test
+ \inqmlmodule QDoc.Test
+ \brief Represents a doc test case.
+ \since QDoc.Test 0.9
+
+ \section1 Introduction
+
+ A documentation test case, itself documented inline in \DocTest.qml.
+*/
+Item {
+ id: testCase
+
+ /*!
+ \qmlsignal QDocTest::completed
+ */
+ signal completed
+
+ /*!
+ \qmlsignal DocTest::test(var bar)
+ Signal with parameter \a bar.
+ */
+ signal foo(var bar)
+
+ /*!
+ Signals that something is \a really happening.
+ */
+ signal itsHappening(bool really)
+
+ /*!
+ \qmlproperty string DocTest::name
+
+ Name of the test.
+ \qml
+ DocTest {
+ name: "test"
+ // ...
+ }
+ \endqml
+ */
+ required property string name
+
+ /*!
+ Whether the test is active.
+ \default true
+
+ \sa name
+ */
+ property bool active: true
+
+ /*! \internal */
+ property int doctest_internal: -1
+
+ /*!
+ \qmlmethod DocTest::fail(message = "oops")
+ \since QDoc.Test 1.0
+
+ Fails the current test case, with the optional \a message.
+ */
+ function fail(msg) {
+ if (msg === undefined)
+ msg = "oops";
+ }
+
+ /*! \internal */
+ function doctest_fail(msg) {
+ if (msg === undefined)
+ msg = "";
+ }
+
+ /*!
+ \brief Fails the current test case, hard.
+ \list
+ \li Prints out \a msg.
+ \li Accepts a random \a option.
+ \endlist
+ */
+ function fail_hard(msg = "facepalm", option = 123) {
+ }
+}
diff --git a/src/qdoc/qdoc/tests/generatedoutput/testdata/qml/cmaketest/CMakeLists.txt b/src/qdoc/qdoc/tests/generatedoutput/testdata/qml/cmaketest/CMakeLists.txt
new file mode 100644
index 000000000..89eafa300
--- /dev/null
+++ b/src/qdoc/qdoc/tests/generatedoutput/testdata/qml/cmaketest/CMakeLists.txt
@@ -0,0 +1,2 @@
+cmake_minimum_required(VERSION 3.16)
+project (QDOCTEST)
diff --git a/src/qdoc/qdoc/tests/generatedoutput/testdata/qml/cmaketest/doc/src/cmaketest.qdoc b/src/qdoc/qdoc/tests/generatedoutput/testdata/qml/cmaketest/doc/src/cmaketest.qdoc
new file mode 100644
index 000000000..bb7b63263
--- /dev/null
+++ b/src/qdoc/qdoc/tests/generatedoutput/testdata/qml/cmaketest/doc/src/cmaketest.qdoc
@@ -0,0 +1,9 @@
+// Copyright (C) 2020 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+/*!
+ \example cmaketest
+ \title CMake Example Project
+ \image leonardo-da-vinci.png
+ //! Icon made by Smashicons from www.flaticon.com
+*/
diff --git a/src/qdoc/qdoc/tests/generatedoutput/testdata/qml/cmaketest/main.cpp b/src/qdoc/qdoc/tests/generatedoutput/testdata/qml/cmaketest/main.cpp
new file mode 100644
index 000000000..68d71eb71
--- /dev/null
+++ b/src/qdoc/qdoc/tests/generatedoutput/testdata/qml/cmaketest/main.cpp
@@ -0,0 +1 @@
+void main(){}
diff --git a/src/qdoc/qdoc/tests/generatedoutput/testdata/qml/componentset/ProgressBar.qml b/src/qdoc/qdoc/tests/generatedoutput/testdata/qml/componentset/ProgressBar.qml
new file mode 100644
index 000000000..39e48da54
--- /dev/null
+++ b/src/qdoc/qdoc/tests/generatedoutput/testdata/qml/componentset/ProgressBar.qml
@@ -0,0 +1,98 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick 1.0
+
+/*!
+ \qmltype ProgressBar
+ \inqmlmodule UIComponents
+ \brief A component that shows the progress of an event.
+
+ A ProgressBar shows the linear progress of an event as its \l value.
+ The range is specified using the \l {minimum} and the \l{maximum} values.
+
+ The ProgressBar component is part of the \l {UI Components} module.
+
+ This documentation is part of the \l{componentset}{UIComponents} example.
+*/
+Item {
+ id: progressbar
+
+ /*!
+ The minimum value of the ProgressBar range.
+ The \l value must not be less than this value.
+ */
+ property int minimum: 0
+
+ /*!
+ The maximum value of the ProgressBar range.
+ The \l value must not be more than this value.
+ */
+ property int maximum: 100
+
+ /*!
+ The value of the progress.
+ */
+ property int value: 0
+
+ /*!
+ \qmlproperty color ProgressBar::color
+ The color of the ProgressBar's gradient. Must bind to a color type.
+
+ \omit
+ The "\qmlproperty <type> <property name>" is needed because
+ property alias need to have their types manually entered.
+
+ QDoc will not publish the documentation within omit and endomit.
+ \endomit
+
+ \sa secondColor
+ */
+ property alias color: gradient1.color
+
+ /*!
+ \qmlproperty color ProgressBar::secondColor
+ The second color of the ProgressBar's gradient.
+ Must bind to a color type.
+
+ \omit
+ The "\qmlproperty <type> <property name>" is needed because
+ property alias need to have their types manually entered.
+
+ QDoc will not publish the documentation within omit and endomit.
+ \endomit
+
+ \sa color
+ */
+ property alias secondColor: gradient2.color
+
+ width: 250; height: 23
+ clip: true
+
+ Rectangle {
+ id: highlight
+
+ /*!
+ An internal documentation comment. The widthDest property is not
+ a public API and therefore will not be exposed.
+ */
+ property int widthDest: ((progressbar.width * (value - minimum)) / (maximum - minimum) - 6)
+
+ width: highlight.widthDest
+ Behavior on width { SmoothedAnimation { velocity: 1200 } }
+
+ anchors { left: parent.left; top: parent.top; bottom: parent.bottom; margins: 3 }
+ radius: 1
+ gradient: Gradient {
+ GradientStop { id: gradient1; position: 0.0 }
+ GradientStop { id: gradient2; position: 1.0 }
+ }
+
+ }
+ Text {
+ anchors { right: highlight.right; rightMargin: 6; verticalCenter: parent.verticalCenter }
+ color: "white"
+ font.bold: true
+ text: Math.floor((value - minimum) / (maximum - minimum) * 100) + '%'
+ }
+}
diff --git a/src/qdoc/qdoc/tests/generatedoutput/testdata/qml/componentset/Switch.qml b/src/qdoc/qdoc/tests/generatedoutput/testdata/qml/componentset/Switch.qml
new file mode 100644
index 000000000..2b39bb820
--- /dev/null
+++ b/src/qdoc/qdoc/tests/generatedoutput/testdata/qml/componentset/Switch.qml
@@ -0,0 +1,105 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick 1.0
+
+/*!
+ \qmltype ToggleSwitch
+ \inqmlmodule UIComponents
+ \brief A component that can be turned on or off.
+
+ A toggle switch has two states: an \c on and an \c off state. The \c off
+ state is when the \l on property is set to \c false.
+
+ The ToggleSwitch component is part of the \l {UI Components} module.
+
+ This documentation is part of the \l{componentset}{UIComponents} example.
+
+*/
+Item {
+ id: toggleswitch
+ width: background.width; height: background.height
+
+ /*!
+ Indicates the state of the switch. If \c false, then the switch is in
+ the \c off state.
+
+ \omit
+ The \qmlproperty <type> <propertyname> is not necessary as QDoc
+ will associate this property to the ToggleSwitch
+
+ QDoc will not publish the documentation within omit and endomit.
+ \endomit
+ */
+ property bool on: false
+
+
+ /*!
+ A method to toggle the switch. If the switch is \c on, the toggling it
+ will turn it \c off. Toggling a switch in the \c off position will
+ turn it \c on.
+ */
+ function toggle() {
+ if (toggleswitch.state == "on")
+ toggleswitch.state = "off";
+ else
+ toggleswitch.state = "on";
+ }
+
+
+ /*!
+ \internal
+
+ An internal function to synchronize the switch's internals. This
+ function is not for public access. The \internal command will
+ prevent QDoc from publishing this comment in the public API.
+ */
+ function releaseSwitch() {
+ if (knob.x == 1) {
+ if (toggleswitch.state == "off") return;
+ }
+ if (knob.x == 78) {
+ if (toggleswitch.state == "on") return;
+ }
+ toggle();
+ }
+
+ Rectangle {
+ id: background
+ width: 130; height: 48
+ radius: 48
+ color: "lightsteelblue"
+ MouseArea { anchors.fill: parent; onClicked: toggle() }
+ }
+
+ Rectangle {
+ id: knob
+ width: 48; height: 48
+ radius: width
+ color: "lightblue"
+
+ MouseArea {
+ anchors.fill: parent
+ drag.target: knob; drag.axis: Drag.XAxis; drag.minimumX: 1; drag.maximumX: 78
+ onClicked: toggle()
+ onReleased: releaseSwitch()
+ }
+ }
+
+ states: [
+ State {
+ name: "on"
+ PropertyChanges { target: knob; x: 78 }
+ PropertyChanges { target: toggleswitch; on: true }
+ },
+ State {
+ name: "off"
+ PropertyChanges { target: knob; x: 1 }
+ PropertyChanges { target: toggleswitch; on: false }
+ }
+ ]
+
+ transitions: Transition {
+ NumberAnimation { properties: "x"; easing.type: Easing.InOutQuad; duration: 200 }
+ }
+}
diff --git a/src/qdoc/qdoc/tests/generatedoutput/testdata/qml/componentset/TabWidget.qml b/src/qdoc/qdoc/tests/generatedoutput/testdata/qml/componentset/TabWidget.qml
new file mode 100644
index 000000000..1816d31c0
--- /dev/null
+++ b/src/qdoc/qdoc/tests/generatedoutput/testdata/qml/componentset/TabWidget.qml
@@ -0,0 +1,146 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick 1.0
+
+/*!
+ \qmltype TabWidget
+ \inqmlmodule UIComponents
+ \brief A widget that places its children as tabs.
+
+ A TabWidget places its children as tabs in a view. Selecting
+ a tab involves selecting the tab at the top.
+
+ The TabWidget component is part of the \l {UI Components} module.
+
+ This documentation is part of the \l{componentset}{UIComponents} example.
+
+ \section1 Adding Tabs
+
+ To add a tab, declare the tab as a child of the TabWidget.
+
+ \code
+ TabWidget {
+ id: tabwidget
+
+ Rectangle {
+ id: tab1
+ color: "red"
+ //... omitted
+ }
+ Rectangle {
+ id: tab2
+ color: "blue"
+ //... omitted
+ }
+
+ }
+ \endcode
+
+*/
+Item {
+ id: tabWidget
+
+ /*!
+ \internal
+
+ Setting the default property to stack.children means any child items
+ of the TabWidget are actually added to the 'stack' item's children.
+
+ See the \l{"Property Binding in QML"}
+ documentation for details on default properties.
+
+ This is an implementation detail, not meant for public knowledge. Putting
+ the \internal command at the beginning will cause QDoc to not publish this
+ documentation in the public API page.
+
+ Normally, a property alias needs to have a
+ "\qmlproperty <type> <propertyname>" to assign the alias a type.
+
+ */
+ default property alias content: stack.children
+
+
+ /*!
+ The currently active tab in the TabWidget.
+ */
+ property int current: 0
+
+ /*!
+ A sample \c{read-only} property.
+ A contrived property to demonstrate QDoc's ability to detect
+ read-only properties.
+
+ The signature is:
+ \code
+ readonly property int sampleReadOnlyProperty: 0
+ \endcode
+
+ Note that the property must be initialized to a value.
+
+ */
+ readonly property int sampleReadOnlyProperty: 0
+
+ /*!
+ \internal
+
+ This handler is an implementation
+ detail. The \c{\internal} command will prevent QDoc from publishing this
+ documentation on the public API.
+ */
+ onCurrentChanged: setOpacities()
+ Component.onCompleted: setOpacities()
+
+ /*!
+ \internal
+
+ An internal function to set the opacity.
+ The \internal command will prevent QDoc from publishing this
+ documentation on the public API.
+ */
+ function setOpacities() {
+ for (var i = 0; i < stack.children.length; ++i) {
+ stack.children[i].opacity = (i == current ? 1 : 0)
+ }
+ }
+
+ Row {
+ id: header
+
+ Repeater {
+ model: stack.children.length
+ delegate: Rectangle {
+ width: tabWidget.width / stack.children.length; height: 36
+
+ Rectangle {
+ width: parent.width; height: 1
+ anchors { bottom: parent.bottom; bottomMargin: 1 }
+ color: "#acb2c2"
+ }
+ BorderImage {
+ anchors { fill: parent; leftMargin: 2; topMargin: 5; rightMargin: 1 }
+ border { left: 7; right: 7 }
+ source: "tab.png"
+ visible: tabWidget.current == index
+ }
+ Text {
+ horizontalAlignment: Qt.AlignHCenter; verticalAlignment: Qt.AlignVCenter
+ anchors.fill: parent
+ text: stack.children[index].title
+ elide: Text.ElideRight
+ font.bold: tabWidget.current == index
+ }
+ MouseArea {
+ anchors.fill: parent
+ onClicked: tabWidget.current = index
+ }
+ }
+ }
+ }
+
+ Item {
+ id: stack
+ width: tabWidget.width
+ anchors.top: header.bottom; anchors.bottom: tabWidget.bottom
+ }
+}
diff --git a/src/qdoc/qdoc/tests/generatedoutput/testdata/qml/componentset/componentset.pro b/src/qdoc/qdoc/tests/generatedoutput/testdata/qml/componentset/componentset.pro
new file mode 100644
index 000000000..5b44737c2
--- /dev/null
+++ b/src/qdoc/qdoc/tests/generatedoutput/testdata/qml/componentset/componentset.pro
@@ -0,0 +1,5 @@
+SOURCES = componentset.pro \
+ ProgressBar.qml \
+ Switch.qml \
+ TabWidget.qml \
+ uicomponents.qdoc
diff --git a/src/qdoc/qdoc/tests/generatedoutput/testdata/qml/componentset/componentset.qml b/src/qdoc/qdoc/tests/generatedoutput/testdata/qml/componentset/componentset.qml
new file mode 100644
index 000000000..4f75ece75
--- /dev/null
+++ b/src/qdoc/qdoc/tests/generatedoutput/testdata/qml/componentset/componentset.qml
@@ -0,0 +1,7 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick 2.0
+
+Item {
+}
diff --git a/src/qdoc/qdoc/tests/generatedoutput/testdata/qml/componentset/examples.qdoc b/src/qdoc/qdoc/tests/generatedoutput/testdata/qml/componentset/examples.qdoc
new file mode 100644
index 000000000..898f706c4
--- /dev/null
+++ b/src/qdoc/qdoc/tests/generatedoutput/testdata/qml/componentset/examples.qdoc
@@ -0,0 +1,82 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+/*!
+ \example componentset
+ \title QML Documentation Example
+ \brief Example for documenting QML types.
+
+ \testnoautolist
+
+ \meta tags {test,sample}
+ \meta tag {application}
+ \meta category {Application Example}
+ \meta installpath tutorials
+
+ This example demonstrates one of the ways to document QML types. It also
+ generates a warning about a missing example image, on purpose.
+
+ In particular, there are sample types that are documented with QDoc
+ commands comments. There are documentation comments for the QML types
+ and their public interfaces. The types are grouped into a module, the
+ \l{UI Components} module.
+
+ The uicomponents.qdoc file generates
+ the overview page for the \l{UI Components} module page.
+
+ The generated documentation is available in the \l{UI Components} module.
+
+ \section1 QML Class
+
+ The QML types use the \\qmltype to document the
+ type. In addition, they have the \\inmodule
+ command in order for QDoc to associate them to the \c UIComponents module.
+
+ QDoc uses the \\brief command to place a basic
+ description when listing the types.
+
+ \section1 Properties, Signals, Handlers, and Methods
+
+ The types have their properties, signals, handlers, and methods
+ defined in their respective QML files. QDoc associates the properties and
+ methods to the types, therefore, you only need to place the
+ documentation above the property, method, or signal.
+
+ To document the type of a \e {property alias}, you must use the
+ \\qmlproperty command to specify the data type.
+
+ \code
+ \qmlproperty int anAliasedProperty
+ An aliased property of type int.
+ \endcode
+
+ \section2 Internal Documentation
+
+ You may declare that a documentation is for internal use by placing the
+ \\internal command after the beginning QDoc comment
+ \begincomment. QDoc will prevent the internal documentation from appearing
+ in the public API.
+
+ If you wish to omit certain parts of the documentation, you may use the
+ \\omit and \\endomit command.
+
+ \section1 QML Types with C++ Implementation
+
+ This example only demonstrates the documentation for types in QML
+ files, but the regular QML commands may be placed
+ inside C++ classes to define the public API of the QML type.
+
+*/
+
+
+/*!
+ \qmlmodule UIComponents 1.0
+ \title UI Components
+ \brief Basic set of UI components.
+
+ This is a listing of a list of UI components implemented by QML types. These
+ files are available for general import and they are based on the
+ Qt Quick Code Samples.
+
+ This module is part of the \l{componentset}{UIComponents} example.
+*/
diff --git a/src/qdoc/qdoc/tests/generatedoutput/testdata/qml/componentset/uicomponents.qdoc.sample b/src/qdoc/qdoc/tests/generatedoutput/testdata/qml/componentset/uicomponents.qdoc.sample
new file mode 100644
index 000000000..9818adc06
--- /dev/null
+++ b/src/qdoc/qdoc/tests/generatedoutput/testdata/qml/componentset/uicomponents.qdoc.sample
@@ -0,0 +1,14 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+/*!
+ \qmlmodule UIComponents 1.0
+ \title UI Components
+ \brief Basic set of UI components
+
+ This is a listing of a list of UI components implemented by QML types. These
+ files are available for general import and they are based off the \l{Qt
+ Quick Code Samples}.
+
+ This module is part of the \l{componentset}{UIComponents} example.
+*/
diff --git a/src/qdoc/qdoc/tests/generatedoutput/testdata/qml/doctest/DocTest.qml b/src/qdoc/qdoc/tests/generatedoutput/testdata/qml/doctest/DocTest.qml
new file mode 100644
index 000000000..ea2312c4c
--- /dev/null
+++ b/src/qdoc/qdoc/tests/generatedoutput/testdata/qml/doctest/DocTest.qml
@@ -0,0 +1,11 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+
+/*!
+ //! omit the \qmltype command, file name (base) is the type name.
+ \inqmlmodule Test.NoVer
+ \brief Shadows the type name in QDoc.Test module.
+*/
+Item {}
diff --git a/src/qdoc/qdoc/tests/generatedoutput/testdata/qml/modules.qdoc b/src/qdoc/qdoc/tests/generatedoutput/testdata/qml/modules.qdoc
new file mode 100644
index 000000000..c3f4379f9
--- /dev/null
+++ b/src/qdoc/qdoc/tests/generatedoutput/testdata/qml/modules.qdoc
@@ -0,0 +1,19 @@
+// Copyright (C) 2020 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+/*!
+ \page qmlmodules.html
+ \title QML Modules
+
+ \generatelist qml-modules
+
+ \section1 QML types
+
+ \generatelist qmltypesbymodule QDoc.Test
+
+ \section1 QML value types
+
+ \generatelist qmlvaluetypes
+
+ \generatelist qmlvaluetypesbymodule QDoc.Test
+*/
diff --git a/src/qdoc/qdoc/tests/generatedoutput/testdata/qml/parent.qdoc b/src/qdoc/qdoc/tests/generatedoutput/testdata/qml/parent.qdoc
new file mode 100644
index 000000000..a2e41eadd
--- /dev/null
+++ b/src/qdoc/qdoc/tests/generatedoutput/testdata/qml/parent.qdoc
@@ -0,0 +1,87 @@
+// Copyright (C) 2019 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+/*!
+ \qmltype AbstractParent
+ \inqmlmodule QDoc.Test
+ \qmlabstract
+ \brief Abstract base QML type.
+*/
+
+/*!
+ \qmlproperty list<Child> AbstractParent::children
+ \qmldefault
+ \brief Children of the type.
+*/
+
+/*!
+ \qmlmethod void AbstractParent::rear(Child child, var method = Strict)
+ \brief Do some abstract parenting on \a child using a specific \a method.
+*/
+
+/*!
+ \qmlproperty string AbstractParent::name
+ \brief Name of this parent.
+*/
+
+/*!
+ \qmlmethod void AbstractParent::name(Child child, name)
+ \brief Name a \a child using \a name.
+
+*/
+
+/*!
+ \qmlmethod void AbstractParent::name()
+ \brief Name all children with random names.
+*/
+
+/*!
+ \qmltype Child
+ \inqmlmodule QDoc.Test
+ \inherits AbstractParent
+ \brief A Child inheriting its parent.
+*/
+
+/*!
+ //! override from abstract base
+ \qmlproperty string Child::name
+ \brief Name of this child.
+*/
+
+/*!
+ //! override from abstract base
+ \qmlmethod void Child::name(Child child, name)
+ \brief Name a \a child of this child using \a name.
+*/
+
+/*!
+ \qmlvaluetype int
+ \inqmlmodule QDoc.Test
+
+ \brief An integer value type.
+*/
+
+/*!
+ \qmlmethod int int::abs()
+ Returns the absolute value of this integer.
+*/
+
+/*!
+ \qmltype InternParent
+ \inqmlmodule QDoc.Test
+ \internal
+ \qmlabstract
+ \brief Internal abstract base QML type.
+*/
+
+/*!
+ \qmlproperty int InternParent::prop
+ \brief Propagated to inheriting type docs.
+*/
+
+/*!
+ \qmltype YetAnotherChild
+ \inherits InternParent
+ \inqmlmodule QDoc.Test
+ \brief A type inheriting from internal abstract parent.
+*/
diff --git a/src/qdoc/qdoc/tests/generatedoutput/testdata/qml/type.cpp b/src/qdoc/qdoc/tests/generatedoutput/testdata/qml/type.cpp
new file mode 100644
index 000000000..f3b8b9f0d
--- /dev/null
+++ b/src/qdoc/qdoc/tests/generatedoutput/testdata/qml/type.cpp
@@ -0,0 +1,127 @@
+// Copyright (C) 2019 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#include "testcpp.h"
+
+/*!
+ \qmlmodule QDoc.Test \QDocTestVer
+ \title QDoc.Test QML Module
+ \brief QML Types for the Test module.
+ \since 1.1
+ \preliminary
+
+ \testnoautolist
+*/
+
+/*!
+ \qmlmodule Test.Empty 1.0
+ \title No QML Types Here
+ \brief A QML module with no member types.
+*/
+
+/*!
+ \qmlmodule Test.NoVer
+ \title Versionless QML Module
+ \brief QML Types for the Test module without version.
+ \since 1.1
+ \modulestate Tech Preview
+*/
+
+/*!
+ \qmltype Type
+ \instantiates TestQDoc::Test
+ \inqmlmodule QDoc.Test
+ \brief A QML type documented in a .cpp file.
+ \meta status { <Work In Progress> }
+*/
+
+/*!
+ \qmltype TypeNoVersion
+ \instantiates TestQDoc::TestDerived
+ \inqmlmodule Test.NoVer
+ \brief Another QML type documented in a .cpp file.
+*/
+
+/*!
+ \qmltype OldType
+ \inqmlmodule QDoc.Test
+ \brief Deprecated old type.
+ \deprecated [1.0]
+*/
+
+/*!
+ \qmlproperty int Type::id
+ \readonly
+ \brief A read-only property.
+*/
+
+/*!
+ \qmlproperty string QDoc.Test::Type::name
+ \required
+ \brief Name of the Test.
+*/
+
+/*!
+ \qmlattachedproperty enumeration Type::type
+ \default Type.NoType
+
+ \value Type.NoType
+ Nothing
+ \value Type.SomeType
+ Something
+*/
+
+/*!
+ \qmlproperty int Type::group.first
+ \qmlproperty int Type::group.second
+ \qmlproperty int Type::group.third
+
+ \brief A property group.
+*/
+
+/*!
+ \qmlsignal Type::group.created
+
+ This signal is prefixed with \e group.
+*/
+
+/*!
+ \qmlproperty int Type::fourth
+ \qmlproperty int Type::fifth
+
+ \brief A group of properties sharing a documentation comment.
+*/
+
+/*!
+ \qmlmethod Type Type::copy(a)
+
+ Returns another Type based on \a a.
+*/
+
+/*!
+ \qmlmethod Type::enable()
+ \qmlmethod Type::disable()
+
+ Enables or disables this type.
+*/
+
+/*!
+ \qmlsignal Type::completed(int status)
+
+ This signal is emitted when the operation completed with \a status.
+*/
+
+/*!
+ \qmlattachedsignal Type::configured()
+
+ This attached signal is emitted when the type was configured.
+*/
+
+/*!
+ \qmlmethod Type::deprecatedMethod()
+
+ \deprecated [6.2] This method has no replacement.
+
+ This is a method that should include information about being deprecated
+ and that it has been so since 6.2 in its docs.
+*/
diff --git a/src/qdoc/qdoc/tests/generatedoutput/testdata/qmlpropertygroups/parent.qdoc b/src/qdoc/qdoc/tests/generatedoutput/testdata/qmlpropertygroups/parent.qdoc
new file mode 100644
index 000000000..baabde91d
--- /dev/null
+++ b/src/qdoc/qdoc/tests/generatedoutput/testdata/qmlpropertygroups/parent.qdoc
@@ -0,0 +1,37 @@
+// Copyright (C) 2020 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+/*!
+ \qmltype Parent
+ \inqmlmodule QDoc.Test
+ \brief Base QML type.
+*/
+
+/*!
+ \qmlproperty int Parent::group.c
+ \qmlproperty int Parent::group.a
+ \qmlproperty int Parent::group.b
+ \brief Property group.
+*/
+
+/*!
+ \qmlproperty int Parent::group.b
+ \readonly
+*/
+
+/*!
+ \qmlproperty int Parent::group.c
+ \since 2.0
+*/
+
+/*!
+ \qmltype AnotherChild
+ \inqmlmodule QDoc.Test
+ \inherits Parent
+ \brief Just another child inheriting a parent.
+*/
+
+/*!
+ \qmlproperty string AnotherChild::name
+ \brief Name of this child.
+*/
diff --git a/src/qdoc/qdoc/tests/generatedoutput/testdata/qmlpropertygroups/qmlpropertygroups.qdocconf b/src/qdoc/qdoc/tests/generatedoutput/testdata/qmlpropertygroups/qmlpropertygroups.qdocconf
new file mode 100644
index 000000000..b2e569e43
--- /dev/null
+++ b/src/qdoc/qdoc/tests/generatedoutput/testdata/qmlpropertygroups/qmlpropertygroups.qdocconf
@@ -0,0 +1,63 @@
+sources.fileextensions = "*.qml *.cpp *.qdoc"
+headers.fileextensions = "*.h"
+
+# images
+imagedirs = ../images
+
+# zero warning policy
+warninglimit = 0
+warninglimit.enabled = true
+
+# don't write host system-specific paths to index files
+locationinfo = false
+
+includepaths += -I../testcpp
+
+headers = ../testcpp/testcpp.h
+sources = ../testcpp/testcpp.cpp \
+ ../testcpp/classlists.qdoc
+
+macro.CMDFN = \\\\fn
+macro.nothing = \\dontdocument ()
+macro.testnoautolist = \\if defined(test_noautolist)\n\\noautolist\n\\endif
+
+navigation.cppclassespage = "QDoc Test C++ Classes"
+
+moduleheader = TestCPP
+
+project = Test
+description = "A test project for QDoc build artifacts"
+outputdir = ./html
+
+exampledirs = ../qml
+
+headerdirs += ../
+sourcedirs += ../qml
+
+# Exclude source files from other tests' subdirs
+excludedirs = ../bug80259
+
+examples.fileextensions = "*.qml *.cpp"
+
+macro.begincomment = "\\c{/*}"
+macro.QDocTestVer = "1.1"
+
+navigation.qmltypespage = "QDoc.Test QML Module"
+navigation.qmltypestitle = "Types"
+warninglimit += 1
+
+manifestmeta.examplecategories = "Application Examples" \
+ "Desktop" \
+ "Mobile" \
+ "Embedded"
+
+project = QmlPropertyGroups
+url = https://doc.qt.io/${project}
+
+sourcedirs += .
+
+outputformats = HTML DocBook
+
+{HTML.nosubdirs,DocBook.nosubdirs} = true
+HTML.outputsubdir = qmlpropertygroups
+DocBook.outputsubdir = qmlpropertygroups-docbook
diff --git a/src/qdoc/qdoc/tests/generatedoutput/testdata/singleexec/singleexec.qdocconf b/src/qdoc/qdoc/tests/generatedoutput/testdata/singleexec/singleexec.qdocconf
new file mode 100644
index 000000000..7a3354329
--- /dev/null
+++ b/src/qdoc/qdoc/tests/generatedoutput/testdata/singleexec/singleexec.qdocconf
@@ -0,0 +1,4 @@
+../configs/testcpp_singleexec.qdocconf
+
+../crossmodule/crossmodule_singleexec.qdocconf
+
diff --git a/src/qdoc/qdoc/tests/generatedoutput/testdata/testcpp/TestCPP b/src/qdoc/qdoc/tests/generatedoutput/testdata/testcpp/TestCPP
new file mode 100644
index 000000000..4ed786108
--- /dev/null
+++ b/src/qdoc/qdoc/tests/generatedoutput/testdata/testcpp/TestCPP
@@ -0,0 +1,5 @@
+#include "testcpp.h"
+
+#ifdef test_template
+# include "testtemplate.h"
+#endif
diff --git a/src/qdoc/qdoc/tests/generatedoutput/testdata/testcpp/classlists.qdoc b/src/qdoc/qdoc/tests/generatedoutput/testdata/testcpp/classlists.qdoc
new file mode 100644
index 000000000..84b543cb2
--- /dev/null
+++ b/src/qdoc/qdoc/tests/generatedoutput/testdata/testcpp/classlists.qdoc
@@ -0,0 +1,51 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+/*!
+ \page obsolete-classes.html
+ \title Obsolete Classes
+
+ \section1 Classes with obsolete members
+ \generatelist obsoletecppmembers
+
+ \section2 TestQDoc
+*/
+
+/*!
+ \page autolinking.html
+ \title Autolinking
+
+ //! a section title that qualifies for autolinking
+ \section1 TestQDoc
+
+ The string TestQDoc links to the C++ namespace unless linking explicitly,
+ \l {#TestQDoc}{like this}, or \l {TestQDoc}{this}. Also,
+
+ Autolinks:
+
+ \list
+ \li TestQDoc::TestDerived
+ \endlist
+
+ Explicit links:
+
+ \list
+ \li \l [CPP] {TestQDoc::TestDerived}
+ \li \l {Obsolete Classes#TestQDoc}
+ \endlist
+
+ //! a section title shadowing a known property name
+ \section1 someProp
+*/
+
+/*!
+ \group cpptypes
+ \title Test C++ Types
+
+ \generatelist testgroup
+*/
+
+/*!
+ \externalpage https://doc.qt.io/qt/17-qdoc-commands-thread.html#reentrant-command
+ \title reentrant
+*/
diff --git a/src/qdoc/qdoc/tests/generatedoutput/testdata/testcpp/properties.qdoc b/src/qdoc/qdoc/tests/generatedoutput/testdata/testcpp/properties.qdoc
new file mode 100644
index 000000000..2de2f93d1
--- /dev/null
+++ b/src/qdoc/qdoc/tests/generatedoutput/testdata/testcpp/properties.qdoc
@@ -0,0 +1,55 @@
+// Copyright (C) 2020 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+/*!
+ \property TestQDoc::TestDerived::bindableProp
+ Some property.
+
+ \sa someProp
+*/
+
+/*!
+ \property TestQDoc::TestDerived::someProp
+ Another property.
+*/
+
+/*!
+ \property TestQDoc::TestDerived::name
+ \brief a name.
+*/
+
+/*!
+ \property TestQDoc::TestDerived::intProp
+ An integer property.
+*/
+
+/*!
+ \property TestQDoc::TestDerived::boolProp
+ A boolean property.
+*/
+
+/*!
+ \fn TestQDoc::TestDerived::invokeMe() const
+ \brief Something invokable.
+*/
+
+/*!
+ \qmlmodule TheModule
+*/
+
+/*!
+ \qmltype TheType
+ \instantiates TestQDoc::TestDerived
+ \inqmlmodule TheModule
+*/
+
+/*!
+ \qmlproperty string TheType::name
+ \brief Read-only status of this property is resolved from Q_PROPERTY.
+*/
+
+/*!
+ //! avoid link warnings for auto-generated links to QProperty
+ \externalpage https://wiki.qt.io/QProperty
+ \title QProperty
+*/
diff --git a/src/qdoc/qdoc/tests/generatedoutput/testdata/testcpp/snippets/snippet_testcpp.cpp b/src/qdoc/qdoc/tests/generatedoutput/testdata/testcpp/snippets/snippet_testcpp.cpp
new file mode 100644
index 000000000..1660fbc2b
--- /dev/null
+++ b/src/qdoc/qdoc/tests/generatedoutput/testdata/testcpp/snippets/snippet_testcpp.cpp
@@ -0,0 +1,3 @@
+//! [random tag]
+You're not supposed to see this.
+//! [random tag]
diff --git a/src/qdoc/qdoc/tests/generatedoutput/testdata/testcpp/testcpp.cpp b/src/qdoc/qdoc/tests/generatedoutput/testdata/testcpp/testcpp.cpp
new file mode 100644
index 000000000..033db4791
--- /dev/null
+++ b/src/qdoc/qdoc/tests/generatedoutput/testdata/testcpp/testcpp.cpp
@@ -0,0 +1,418 @@
+// Copyright (C) 2019 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+#include "testcpp.h"
+
+namespace TestQDoc {
+
+/*
+//! [random tag]
+\note This is just a test.
+//! [random tag]
+
+//! [args]
+\1\2 \3 \2\1
+//! [args]
+*/
+
+/*!
+ \module TestCPP
+ \qtvariable testcpp
+ \qtcmakepackage QDocTest
+ \title QDoc Test C++ Classes
+ \brief A test module page.
+ \since 2.0
+
+ \testnoautolist
+
+ \include testcpp.cpp random tag
+ \include testcpp.cpp {args} {/} {*} {Look, Ma! {I'm made of arguments!}}
+
+\if defined(test_nestedmacro)
+ \versionnote {module} {\ver 5.15.0}
+ \ver 1.0.0
+\endif
+
+ \section1 Linking to function-like things
+
+ \list
+ \li \l {TestQDoc::Test::someFunctionDefaultArg}
+ {someFunctionDefaultArg()}
+ \li QDOCTEST_MACRO2()
+ \li \l {TestQDoc::Test::}{QDOCTEST_MACRO2(int &x)}
+ \li \l {section()}
+ \li \l {section()} {section() is a section title}
+ \li \l {TestQDoc::Test::Test()} {open( parenthesis}
+ \li \l {https://en.cppreference.com/w/cpp/utility/move}
+ {C++11 added std::move(T&& t)}
+ \endlist
+
+ \section2 section()
+*/
+
+/*!
+ \namespace TestQDoc
+ \inheaderfile TestCPP
+ \inmodule TestCPP
+ \brief A namespace.
+
+ \section1 Usage
+ This namespace is for testing QDoc output.
+*/
+
+/*!
+ \class TestQDoc::Test
+ \inmodule TestCPP
+ \brief A class in a namespace.
+
+\if defined(test_ignoresince)
+ //! omitted by ignoresince
+ \since 1.1
+\endif
+ \ingroup testgroup
+ \ingroup cpptypes
+ \reentrant
+*/
+
+/*!
+ \fn TestQDoc::Test::Test()
+
+ Default constructor.
+*/
+
+/*!
+ \fn Test &Test::operator=(Test &&other)
+ \ingroup testgroup
+
+ Move-assigns \a other.
+*/
+
+/*!
+ \class TestQDoc::TestDerived
+ \inmodule TestCPP
+ \brief A class in a namespace, derived from \l [CPP] Test.
+*/
+
+/*!
+ \macro QDOCTEST_MACRO
+ \relates TestQDoc
+\if defined(test_ignoresince)
+ //! omitted by ignoresince.Test
+ \since Test 0.9
+\endif
+*/
+
+/*!
+ \macro QDOCTEST_MACRO2(int &x)
+ \relates TestQDoc::Test
+ \since Test 1.1
+ \brief A macro with argument \a x.
+ \ingroup testgroup
+*/
+
+/*!
+\if defined(test_properties)
+ \property TestQDoc::Test::id
+\else
+ \nothing
+\endif
+*/
+
+/*!
+ \deprecated [6.0] Use someFunction() instead.
+*/
+void Test::deprecatedMember()
+{
+ return;
+}
+
+/*!
+ \obsolete
+
+ Use someFunction() instead.
+*/
+void Test::obsoleteMember()
+{
+ return;
+}
+
+/*!
+ \obsolete Use obsoleteMember() instead.
+*/
+void Test::anotherObsoleteMember()
+{
+ return;
+}
+
+/*!
+ \nonreentrant
+ Function that takes a parameter \a i and \a b.
+\if defined(test_ignoresince)
+ \since 2.0
+\endif
+ \ingroup testgroup
+*/
+void Test::someFunctionDefaultArg(int i, bool b = false) const
+{
+ return;
+}
+
+/*!
+ \fn void Test::func(bool)
+ \internal
+*/
+
+/*!
+ \fn [funcPtr] void (*funcPtr(bool b, const char *s))(bool)
+
+ Returns a pointer to a function that takes a boolean. Uses \a b and \a s.
+*/
+
+/*!
+ \fn [op-inc] Test::operator++()
+ \fn [op-dec] Test::operator--()
+ \deprecated
+*/
+
+/*!
+ This method has en dashes in its documentation -- as you'll find
+ represented by \c{--} in the sources -- here and there. The important bit
+ to note is that when passed e.g. to the \\c command, the two hyphens are
+ processed as input to the command and not replaced by an en dash. This also
+ applies to code blocks, where otherwise, the decrement operator would get
+ completely borked:
+
+ \code
+ for (int i = 42; i > 0; --i)
+ // Do something cool during countdown.
+ \endcode
+
+ ...as it would be silly if this would output --i instead of \c {--i}.
+
+ -----------------------------------------------------------------------
+
+ It still allows people to add a bunch of dashes, though, without replacing
+ them all with a series of en dashes. Of course, they might want to use the
+ \\hr command instead, like this:
+ \hr
+
+ -- You can also start a new paragraph with an en dash, if you want to.
+
+ //! Self-referencing \sa-command for tests.
+ \sa methodWithEnDashInItsDocs
+*/
+void Test::methodWithEnDashInItsDocs()
+{
+ // Nothing to see here.
+}
+
+/*!
+ This method has em dashes in its documentation---as you'll find
+ represented by \c{---} in the sources---here and there. The important bit
+ to note is that when passed e.g. to the \\c command, the three hyphens are
+ processed as input to the command and not replaced by an em dash.
+
+ -----------------------------------------------------------------------
+
+ People can still add a bunch of dashes, though, without QDoc replacing
+ them all with a series of em dashes.
+
+ ---You can also start a new paragraph with an em dash, if you want to.
+
+ \sa methodWithEnDashInItsDocs
+
+*/
+void Test::methodWithEmDashInItsDocs()
+{
+ // Woah! Look at that!
+}
+
+// Documented below with an \fn command. Unnecessary but we support it, and it's used.
+int Test::someFunction(int, int v)
+{
+ return v;
+}
+
+/*!
+ \fn void TestQDoc::Test::inlineFunction()
+
+ \brief An inline function, documented using the \CMDFN QDoc command.
+*/
+
+/*!
+ \fn int Test::someFunction(int, int v = 0)
+
+ Function that takes a parameter \a v.
+ Also returns the value of \a v.
+\if defined(test_ignoresince)
+ \since Test 1.0
+\endif
+*/
+
+/*!
+ Function that must be reimplemented.
+*/
+void Test::virtualFun()
+{
+ return;
+}
+
+/*!
+ \fn bool Test::operator==(const Test &lhs, const Test &rhs)
+
+ Returns true if \a lhs and \a rhs are equal.
+*/
+
+/*!
+ \typedef TestQDoc::Test::SomeType
+ \brief A typedef.
+*/
+
+/*!
+ \reimp
+*/
+void TestDerived::virtualFun()
+{
+ return;
+}
+
+/*!
+ \fn TestQDoc::Test::overload()
+ \fn Test::overload(bool b)
+ //! The second overload should match even without the fully qualified path
+
+ Overloads that share a documentation comment, optionally taking
+ a parameter \a b.
+*/
+
+/*!
+ \fn Test::overload(bool b)
+ \since Test 1.2
+*/
+
+/*!
+ \typealias TestQDoc::TestDerived::DerivedType
+ An aliased typedef.
+*/
+
+/*!
+ \typedef TestQDoc::TestDerived::NotTypedef
+ I'm an alias, not a typedef.
+*/
+
+/*!
+ \obsolete
+
+ Static obsolete method.
+*/
+void TestDerived::staticObsoleteMember()
+{
+ return;
+}
+
+/*!
+\if defined(test_properties)
+ \fn void TestDerived::emitSomething()
+ Emitted when things happen.
+\else
+ \nothing
+\endif
+*/
+
+/*!
+\if defined(test_properties)
+ \reimp
+\else
+ \nothing
+\endif
+*/
+int TestDerived::id()
+{
+ return 1;
+}
+
+/*!
+ Returns a value using an aliases type.
+*/
+TestDerived::NotTypedef TestDerived::someValue()
+{
+ return 0;
+}
+
+/*!
+\if defined(test_template)
+ \fn template <typename T1, typename T2> void TestQDoc::Test::funcTemplate(T1 a, T2 b)
+ \brief Function template with two parameters, \a a and \a b.
+\else
+ \nothing
+\endif
+*/
+
+/*!
+\if defined(test_template)
+ \struct TestQDoc::Test::Struct
+ \inmodule TestCPP
+ \brief Templated struct.
+\else
+ \nothing
+\endif
+*/
+
+/*!
+\if defined(test_template)
+ \typealias TestQDoc::Test::Specialized
+\else
+ \nothing
+\endif
+*/
+
+/*!
+\if defined(test_template)
+ \class TestQDoc::Vec
+ \inmodule TestCPP
+ \brief Type alias that has its own reference.
+\else
+ \nothing
+\endif
+*/
+
+/*!
+\if defined(test_template)
+ \macro Q_INVOKABLE
+ \relates TestQDoc::Test
+
+ This is a mock Q_INVOKABLE for the purpose of ensuring QDoc autolink to it
+ as expected.
+\else
+ \nothing
+\endif
+*/
+
+} // namespace TestQDoc
+
+
+/*!
+ \namespace CrossModuleRef
+ \inmodule TestCPP
+ \brief Namespace that has documented functions in multiple modules.
+ \since 3.0
+*/
+namespace CrossModuleRef {
+
+/*!
+ Document me!
+*/
+void documentMe()
+{
+}
+
+} // namespace CrossModuleRef
+
+/*!
+ \class DontLinkToMe
+ \inmodule TestCPP
+ \brief Class that does not generate documentation.
+*/
+
+/*!
+ \dontdocument (DontLinkToMe)
+*/
diff --git a/src/qdoc/qdoc/tests/generatedoutput/testdata/testcpp/testcpp.h b/src/qdoc/qdoc/tests/generatedoutput/testdata/testcpp/testcpp.h
new file mode 100644
index 000000000..a5b2d221f
--- /dev/null
+++ b/src/qdoc/qdoc/tests/generatedoutput/testdata/testcpp/testcpp.h
@@ -0,0 +1,137 @@
+// Copyright (C) 2019 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+#pragma once
+
+#ifdef test_properties
+#include <QtCore/qmetaobject.h>
+#include <QtCore/qproperty.h>
+#include <QtCore/qstring.h>
+#endif
+
+#define QDOCTEST_MACRO test
+#define QDOCTEST_MACRO2(x) (x) < 0 ? 0 : (x))
+
+namespace TestQDoc {
+
+class Test {
+#ifdef test_properties
+ Q_OBJECT
+ Q_PROPERTY(int id READ id)
+#endif
+public:
+
+#ifdef test_template
+template<typename D, typename T> struct Struct {};
+template<typename T>
+using Specialized = Struct<int, T>;
+#endif
+
+#ifdef test_template
+# define Q_INVOKABLE void foo() {};
+#endif
+
+#ifdef test_scopedenum
+ enum ClassicEnum { Yee, Haw, Howdy, Partner };
+
+ enum class ScopedEnum : unsigned char {
+ This = 0x01,
+ That = 0x02,
+ All = This | That,
+ OmittedValue = 99,
+ UselessValue,
+ VeryLastValue
+ };
+#endif
+ typedef struct {
+ int data;
+ } SomeType;
+ int someFunction(int, int v = 0);
+ void someFunctionDefaultArg(int i, bool b) const;
+ void obsoleteMember();
+ void anotherObsoleteMember();
+ void deprecatedMember();
+ void methodWithEnDashInItsDocs();
+ void methodWithEmDashInItsDocs();
+ void func(bool) {};
+ //! [funcPtr]
+ void (*funcPtr(bool b, const char *s))(bool) {
+ return func;
+ }
+ //! [op-inc]
+ Test &operator++() { return *this; }
+ //! [op-dec]
+ Test &operator--() { return *this; }
+
+ void anotherFunc() {};
+ inline void inlineFunction() {};
+ virtual void virtualFun();
+
+ friend bool operator==(const Test &lhs, const Test &rhs) { return false; }
+
+protected:
+ void overload() {}
+ void overload(bool b) { if (!b) return; }
+#ifdef test_template
+ template <typename T1, typename T2> void funcTemplate(T1 a, T2 b) {
+ a = b;
+ }
+#endif
+#ifdef test_properties
+ virtual int id() { return 0; }
+#endif
+};
+
+class TestDerived : public Test {
+#ifdef test_properties
+ Q_OBJECT
+
+ Q_PROPERTY(QString bindableProp READ bindableProp WRITE setBindableProp NOTIFY bindablePropChanged BINDABLE bindableProp)
+ Q_PROPERTY(QString someProp READ someProp BINDABLE somBindableProp)
+ Q_PROPERTY(int *intProp READ getInt STORED false CONSTANT FINAL)
+ Q_PROPERTY(const QString *name READ name)
+ QDOC_PROPERTY(bool boolProp READ boolProp WRITE setBoolProp NOTIFY boolPropChanged RESET resetBoolProp REVISION 1)
+#endif
+
+public:
+ using DerivedType = Test::SomeType;
+ using NotTypedef = int;
+ void virtualFun() override;
+ static void staticObsoleteMember();
+ NotTypedef someValue();
+#ifdef test_properties
+ QBindable<QString> bindableProp();
+ QBindable<QString> someBindableProp();
+ const QString &someProp();
+ int *getInt();
+ bool boolProp();
+ const QString *name() const;
+
+ Q_INVOKABLE void invokeMe() const {}
+ int id() override;
+
+Q_SIGNALS:
+ void emitSomething(QPrivateSignal);
+ void bindablePropChanged();
+ Q_REVISION(1) void boolPropChanged();
+
+public Q_SLOTS:
+ void setBindableProp(const QString &s);
+ void setBoolProp(bool b);
+ void resetBoolProp();
+#endif
+};
+
+#ifdef test_template
+template <typename T>
+struct BaseVec {};
+template <typename T>
+using Vec = BaseVec<T>;
+#endif
+
+} // namespace TestQDoc
+
+namespace CrossModuleRef {
+ void documentMe();
+}
+
+class DontLinkToMe {};
diff --git a/src/qdoc/qdoc/tests/generatedoutput/tst_generatedoutput.cpp b/src/qdoc/qdoc/tests/generatedoutput/tst_generatedoutput.cpp
new file mode 100644
index 000000000..07ded08ad
--- /dev/null
+++ b/src/qdoc/qdoc/tests/generatedoutput/tst_generatedoutput.cpp
@@ -0,0 +1,283 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+#include <QProcess>
+#include <QTemporaryDir>
+#include <QDirIterator>
+#include <QtTest>
+
+class tst_generatedOutput : public QObject
+{
+ Q_OBJECT
+
+public:
+ void setRegenerate() { m_regen = true; }
+
+private slots:
+ void initTestCase();
+ void init();
+
+ // HTML generator
+ void htmlFromCpp();
+
+ // Output format independent tests
+ void inheritedQmlPropertyGroups();
+ void crossModuleLinking();
+ void indexLinking();
+ void includeFromExampleDirs();
+ void singleExec();
+ void preparePhase();
+ void generatePhase();
+ void noAutoList();
+
+private:
+ QScopedPointer<QTemporaryDir> m_outputDir;
+ QString m_qdoc;
+ QDir m_expectedDir;
+ QString m_extraParams;
+ bool m_regen = false;
+
+ void runQDocProcess(const QStringList &arguments);
+ void compareLineByLine(const QStringList &expectedFiles);
+ void testAndCompare(const char *input, const char *outNames, const char *extraParams = nullptr);
+ void copyIndexFiles();
+};
+
+void tst_generatedOutput::initTestCase()
+{
+ // Build the path to the QDoc binary the same way moc tests do for moc.
+ const auto binpath = QLibraryInfo::path(QLibraryInfo::BinariesPath);
+ const auto extension = QSysInfo::productType() == "windows" ? ".exe" : "";
+ m_qdoc = binpath + QLatin1String("/qdoc") + extension;
+ m_expectedDir.setPath(QFINDTESTDATA("expected_output"));
+
+ // Resolve the path to the file containing extra parameters
+ m_extraParams = QFileInfo(QTest::currentAppName()).dir().filePath("qdocincludepaths.inc");
+ if (!QFileInfo::exists(m_extraParams)) {
+ qWarning().nospace() << QStringLiteral("Cannot locate")
+ << qUtf8Printable(m_extraParams);
+ m_extraParams.clear();
+ } else {
+ m_extraParams.insert(0, '@');
+ }
+}
+
+void tst_generatedOutput::init()
+{
+ m_outputDir.reset(new QTemporaryDir());
+ if (!m_outputDir->isValid()) {
+ const QString errorMessage =
+ "Couldn't create temporary directory: " + m_outputDir->errorString();
+ QFAIL(qPrintable(errorMessage));
+ }
+}
+
+void tst_generatedOutput::runQDocProcess(const QStringList &arguments)
+{
+ QProcess qdocProcess;
+ qdocProcess.setProgram(m_qdoc);
+ qdocProcess.setArguments(arguments);
+
+ auto failQDoc = [&](QProcess::ProcessError) {
+ QFAIL(qPrintable(QStringLiteral("Running qdoc failed with exit code %1: %2")
+ .arg(qdocProcess.exitCode()).arg(qdocProcess.errorString())));
+ };
+ QObject::connect(&qdocProcess, &QProcess::errorOccurred, this, failQDoc);
+
+ qdocProcess.start();
+ qdocProcess.waitForFinished();
+ if (qdocProcess.exitCode() == 0)
+ return;
+
+ QString errors = qdocProcess.readAllStandardError();
+ if (!errors.isEmpty())
+ qInfo().nospace() << "Received errors:\n" << qUtf8Printable(errors);
+ if (!QTest::currentTestFailed())
+ failQDoc(QProcess::UnknownError);
+}
+
+void tst_generatedOutput::compareLineByLine(const QStringList &expectedFiles)
+{
+ for (const auto &file : expectedFiles) {
+ QString expected(m_expectedDir.filePath(file));
+ QString actual(m_outputDir->filePath(file));
+
+ QFile expectedFile(expected);
+ if (!expectedFile.open(QIODevice::ReadOnly))
+ QFAIL(qPrintable(QString("Cannot open expected data file: %1").arg(expected)));
+ QTextStream expectedIn(&expectedFile);
+
+ QFile actualFile(actual);
+ if (!actualFile.open(QIODevice::ReadOnly))
+ QFAIL(qPrintable(QString("Cannot open actual data file: %1").arg(actual)));
+ QTextStream actualIn(&actualFile);
+
+ const QLatin1String delim(": ");
+ int lineNumber = 0;
+ while (!expectedIn.atEnd() && !actualIn.atEnd()) {
+ lineNumber++;
+ QString prefix = file + delim + QString::number(lineNumber) + delim;
+ QString expectedLine = prefix + expectedIn.readLine();
+ QString actualLine = prefix + actualIn.readLine();
+ QCOMPARE(actualLine, expectedLine);
+ }
+ }
+}
+
+void tst_generatedOutput::testAndCompare(const char *input, const char *outNames,
+ const char *extraParams)
+{
+ QStringList args{ "-outputdir", m_outputDir->path() + "/", QFINDTESTDATA(input) };
+ if (extraParams)
+ args << QString(QLatin1String(extraParams)).split(QChar(' '));
+
+ runQDocProcess(args);
+
+ if (QTest::currentTestFailed())
+ return;
+
+ QStringList expectedOuts(QString(QLatin1String(outNames)).split(QChar(' ')));
+ if (m_regen) {
+ QVERIFY(m_expectedDir.mkpath(m_expectedDir.path()));
+ for (const auto &file : std::as_const(expectedOuts)) {
+ QFileInfo fileInfo(m_expectedDir.filePath(file));
+ fileInfo.dir().remove(fileInfo.fileName()); // Allowed to fail
+ QVERIFY(m_expectedDir.mkpath(fileInfo.dir().path()));
+ QVERIFY2(QFile::copy(m_outputDir->filePath(file), fileInfo.filePath()),
+ qPrintable(QStringLiteral("Failed to copy '%1'").arg(file)));
+ }
+ QSKIP("Regenerated expected output only.");
+ }
+
+ compareLineByLine(expectedOuts);
+}
+
+// Copy <project>.index to <project>/<project>.index in the outputdir
+void tst_generatedOutput::copyIndexFiles()
+{
+ QDirIterator it(m_outputDir->path(), QStringList("*.index"), QDir::Files, QDirIterator::Subdirectories);
+ while (it.hasNext()) {
+ QFileInfo fileInfo(it.next());
+ QDir indexDir(m_outputDir->path());
+ QVERIFY(indexDir.mkpath(fileInfo.baseName()));
+ QVERIFY(indexDir.cd(fileInfo.baseName()));
+ if (!indexDir.exists(fileInfo.fileName()))
+ QVERIFY(QFile::copy(fileInfo.filePath(), indexDir.filePath(fileInfo.fileName())));
+ }
+}
+
+void tst_generatedOutput::htmlFromCpp()
+{
+ testAndCompare("testdata/configs/testcpp.qdocconf",
+ "testcpp-module.html "
+ "testqdoc-test.html "
+ "testqdoc-test-members.html "
+ "testqdoc-test-obsolete.html "
+ "testqdoc-testderived.html "
+ "testqdoc-testderived-members.html "
+ "testqdoc-testderived-obsolete.html "
+ "obsolete-classes.html "
+ "autolinking.html "
+ "cpptypes.html "
+ "testqdoc.html");
+}
+
+void tst_generatedOutput::inheritedQmlPropertyGroups()
+{
+ testAndCompare("testdata/qmlpropertygroups/qmlpropertygroups.qdocconf",
+ "qmlpropertygroups/qml-qdoc-test-anotherchild-members.html "
+ "qmlpropertygroups/qml-qdoc-test-parent.html "
+ "qmlpropertygroups-docbook/qml-qdoc-test-parent.xml");
+}
+
+void tst_generatedOutput::indexLinking()
+{
+ {
+ QScopedValueRollback<bool> skipRegen(m_regen, false);
+ inheritedQmlPropertyGroups();
+ }
+ copyIndexFiles();
+ QString indexDir = QLatin1String("-indexdir ") + m_outputDir->path();
+ testAndCompare("testdata/indexlinking/indexlinking.qdocconf",
+ "index-linking.html "
+ "qml-linkmodule-grandchild-members.html",
+ indexDir.toLatin1().data());
+}
+
+void tst_generatedOutput::crossModuleLinking()
+{
+ {
+ QScopedValueRollback<bool> skipRegen(m_regen, false);
+ htmlFromCpp();
+ }
+ copyIndexFiles();
+ QString indexDir = QLatin1String("-indexdir ") + m_outputDir->path();
+ testAndCompare("testdata/crossmodule/crossmodule.qdocconf",
+ "crossmodule/testtype.html "
+ "crossmodule/testtype-members.html "
+ "crossmodule/crossmoduleref-sub-crossmodule.html",
+ indexDir.toLatin1().data());
+}
+
+void tst_generatedOutput::includeFromExampleDirs()
+{
+ testAndCompare("testdata/includefromexampledirs/includefromexampledirs.qdocconf",
+ "includefromexampledirs/index.html "
+ "includefromexampledirs/qml-qdoc-test-abstractparent.html "
+ "includefromexampledirs/qml-qdoc-test-abstractparent-members.html");
+}
+
+void tst_generatedOutput::singleExec()
+{
+ // Build both testcpp and crossmodule projects in single-exec mode
+ testAndCompare("testdata/singleexec/singleexec.qdocconf",
+ "testcpp/testcpp-module.html "
+ "testcpp/testqdoc-test.html "
+ "testcpp/testqdoc-test-members.html "
+ "testcpp/testqdoc.html "
+ "testcpp/crossmoduleref.html "
+ "crossmodule/crossmodule/all-namespaces.html "
+ "crossmodule/crossmodule/testtype.html "
+ "crossmodule/crossmodule/testtype-members.html",
+ "-single-exec");
+}
+
+void tst_generatedOutput::preparePhase()
+{
+ testAndCompare("testdata/configs/testcpp.qdocconf",
+ "testcpp.index",
+ "-prepare");
+}
+
+void tst_generatedOutput::generatePhase()
+{
+ testAndCompare("testdata/configs/testcpp.qdocconf",
+ "testcpp-module.html "
+ "testqdoc-test.html "
+ "testqdoc-test-members.html "
+ "testqdoc.html",
+ "-generate");
+}
+
+void tst_generatedOutput::noAutoList()
+{
+ testAndCompare("testdata/configs/noautolist.qdocconf",
+ "noautolist/testcpp-module.html "
+ "noautolist/test-componentset-example.html "
+ "noautolist/qdoc-test-qmlmodule.html "
+ "noautolist-docbook/testcpp-module.xml "
+ "noautolist-docbook/test-componentset-example.xml "
+ "noautolist-docbook/qdoc-test-qmlmodule.xml");
+}
+
+int main(int argc, char *argv[])
+{
+ tst_generatedOutput tc;
+ // Re-populate expected data and skip tests if option -regenerate is set
+ if (argc == 2 && QByteArray(argv[1]) == "-regenerate") {
+ tc.setRegenerate();
+ --argc;
+ }
+ return QTest::qExec(&tc, argc, argv);
+}
+
+#include "tst_generatedoutput.moc"
diff --git a/src/qdoc/qdoc/tests/qdoc/CMakeLists.txt b/src/qdoc/qdoc/tests/qdoc/CMakeLists.txt
new file mode 100644
index 000000000..03c177772
--- /dev/null
+++ b/src/qdoc/qdoc/tests/qdoc/CMakeLists.txt
@@ -0,0 +1,28 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_QDoc LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
+qt_internal_add_test(tst_QDoc
+ SOURCES
+ ${CMAKE_CURRENT_LIST_DIR}/main.cpp
+
+ ${CMAKE_CURRENT_LIST_DIR}/boundaries/filesystem/catch_filepath.cpp
+ ${CMAKE_CURRENT_LIST_DIR}/boundaries/filesystem/catch_directorypath.cpp
+ ${CMAKE_CURRENT_LIST_DIR}/filesystem/catch_fileresolver.cpp
+
+ ${CMAKE_CURRENT_LIST_DIR}/../../src/qdoc/boundaries/filesystem/filepath.cpp
+ ${CMAKE_CURRENT_LIST_DIR}/../../src/qdoc/boundaries/filesystem/directorypath.cpp
+ ${CMAKE_CURRENT_LIST_DIR}/../../src/qdoc/boundaries/filesystem/resolvedfile.cpp
+ ${CMAKE_CURRENT_LIST_DIR}/../../src/qdoc/filesystem/fileresolver.cpp
+ INCLUDE_DIRECTORIES
+ ${CMAKE_CURRENT_LIST_DIR}/../../src/
+ LIBRARIES
+ Qt::QDocCatchPrivate
+ Qt::QDocCatchConversionsPrivate
+ Qt::QDocCatchGeneratorsPrivate
+)
diff --git a/src/qdoc/qdoc/tests/qdoc/boundaries/filesystem/catch_directorypath.cpp b/src/qdoc/qdoc/tests/qdoc/boundaries/filesystem/catch_directorypath.cpp
new file mode 100644
index 000000000..d590c1c4f
--- /dev/null
+++ b/src/qdoc/qdoc/tests/qdoc/boundaries/filesystem/catch_directorypath.cpp
@@ -0,0 +1,195 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#include <catch_conversions/qdoc_catch_conversions.h>
+
+#include <catch/catch.hpp>
+
+#include <qdoc/boundaries/filesystem/directorypath.h>
+
+#include <catch_generators/generators/path_generator.h>
+
+#include <QFileInfo>
+#include <QTemporaryDir>
+#include <QDir>
+#include <QIODeviceBase>
+#include <QRegularExpression>
+
+SCENARIO("Obtaining a DirectoryPath", "[DirectoryPath][Boundaries][Validation][Canonicalization][Path]") {
+
+ GIVEN("Any string representing a path that does not represent an existing element on the filesystem") {
+ QString path = GENERATE(take(100, filter([](auto path){ return !QFileInfo{path}.exists(); }, qdoc::catch_generators::native_path())));
+ CAPTURE(path);
+
+ WHEN("A DirectoryPath instance is requested from that string") {
+ auto maybe_directory_path{DirectoryPath::refine(path)};
+
+ THEN("A DirectoryPath instance is not obtained") {
+ REQUIRE(!maybe_directory_path);
+ }
+ }
+ }
+
+ GIVEN("Any string representing a path to a file") {
+ QString relative_path = GENERATE(take(100, qdoc::catch_generators::native_relative_file_path()));
+ CAPTURE(relative_path);
+
+ QTemporaryDir working_directory{};
+ REQUIRE(working_directory.isValid());
+
+ QString path_to_file = working_directory.path() + "/" + relative_path;
+
+ AND_GIVEN("That the path represents an existing file on the filesystem") {
+ REQUIRE(QDir{working_directory.path()}.mkpath(QFileInfo{relative_path}.path()));
+ REQUIRE(QFile{path_to_file}.open(QIODeviceBase::ReadWrite | QIODevice::NewOnly));
+
+ WHEN("A DirectoryPath instance is requested from that string") {
+ auto maybe_directory_path{DirectoryPath::refine(path_to_file)};
+
+ THEN("A DirectoryPath instance is not obtained") {
+ REQUIRE(!maybe_directory_path);
+ }
+ }
+ }
+ }
+
+#if defined(Q_OS_LINUX) || defined(Q_OS_MACOS)
+
+ GIVEN("Any string representing a path to a directory") {
+ // REMARK: [relative-component-permissions]
+ // For tests where we change the permissions of the path, we
+ // want to avoid relative components in a final position.
+ // Relative components are actual objects on the filesystem in
+ // *nix systems.
+ // What this means is that to perform some operations on them,
+ // such as changing permissions, we need the correct
+ // permission in their containing or parent directory.
+ // When we change permissions for those files, the permissions
+ // for their containing or parent directory is actually
+ // changed.
+ // Depending on the way in which the permissions where
+ // changed, it may then be impossible to change them back, as
+ // the containing or parent directory might not provide the
+ // necessary permission to read or change the nodes that it
+ // contains.
+ // For tests in particular, this means that we are not able to
+ // ensure that the correct permissions will be available for
+ // the cleanup of the temporary directories that we need for
+ // testing.
+ // To avoid this situation, we filter out those paths that end
+ // in a relative component.
+ QString relative_path = GENERATE(take(100,
+ filter(
+ [](QString path){
+ QString last_component = path.split(QRegularExpression{R"(\/+)"}, Qt::SkipEmptyParts).last();
+ return (last_component != ".") && (last_component != "..");
+ },
+ qdoc::catch_generators::native_relative_file_path()
+ )
+ ));
+ CAPTURE(relative_path);
+
+ QTemporaryDir working_directory{};
+ REQUIRE(working_directory.isValid());
+
+ QString path_to_directory = working_directory.path() + "/" + relative_path;
+
+ AND_GIVEN("That the path represents an existing directory on the filesystem") {
+ REQUIRE(QDir{working_directory.path()}.mkpath(relative_path));
+
+ AND_GIVEN("That the directory represented by the path is not readable") {
+ REQUIRE(QFile::setPermissions(path_to_directory, QFileDevice::WriteOwner |
+ QFileDevice::ExeOwner |
+ QFileDevice::WriteGroup |
+ QFileDevice::ExeGroup |
+ QFileDevice::WriteOther |
+ QFileDevice::ExeOther));
+
+ CAPTURE(QFileInfo{path_to_directory}.isReadable());
+
+ WHEN("A DirectoryPath instance is requested from that string") {
+ auto maybe_directory_path{DirectoryPath::refine(path_to_directory)};
+
+ THEN("A DirectoryPath instance is not obtained") {
+ // REMARK: [temporary_directory_cleanup]
+ CHECK(!maybe_directory_path);
+ REQUIRE(QFile::setPermissions(path_to_directory, QFileDevice::WriteUser | QFileDevice::ReadUser | QFileDevice::ExeUser));
+ }
+ }
+ }
+
+ AND_GIVEN("That the directory represented by the path is not executable") {
+ REQUIRE(QFile::setPermissions(path_to_directory, QFileDevice::WriteOwner |
+ QFileDevice::ReadOwner |
+ QFileDevice::WriteGroup |
+ QFileDevice::ReadGroup |
+ QFileDevice::WriteOther |
+ QFileDevice::ReadOther));
+
+ WHEN("A DirectoryPath instance is requested from that string") {
+ auto maybe_directory_path{DirectoryPath::refine(path_to_directory)};
+
+ THEN("A DirectoryPath instance is not obtained") {
+ // REMARK: [temporary_directory_cleanup]
+ CHECK(!maybe_directory_path);
+ REQUIRE(QFile::setPermissions(path_to_directory, QFileDevice::WriteUser | QFileDevice::ReadUser | QFileDevice::ExeUser));
+ }
+ }
+ }
+
+ AND_GIVEN("That the directory represented by the path is readable and executable") {
+ REQUIRE(QFile::setPermissions(path_to_directory, QFileDevice::ReadOwner |
+ QFileDevice::ExeOwner |
+ QFileDevice::ReadGroup |
+ QFileDevice::ExeGroup |
+ QFileDevice::ReadOther |
+ QFileDevice::ExeOther));
+
+ WHEN("A DirectoryPath instance is requested from that string") {
+ auto maybe_directory_path{DirectoryPath::refine(path_to_directory)};
+
+ THEN("A DirectoryPath instance is obtained") {
+ // REMARK: [temporary_directory_cleanup]
+ // We restore all permission to
+ // ensure that the temporary directory can be
+ // automatically cleaned up.
+ CHECK(maybe_directory_path);
+ REQUIRE(QFile::setPermissions(path_to_directory, QFileDevice::WriteUser | QFileDevice::ReadUser | QFileDevice::ExeUser));
+ }
+ }
+ }
+ }
+ }
+
+#endif
+
+}
+
+SCENARIO("Inspecting the contents of a DirectoryPath", "[DirectoryPath][Boundaries][Canonicalization][Path][Contents]") {
+ GIVEN("Any string representing a path from which a DirectoryPath instance can be obtained") {
+ QString relative_path = GENERATE(take(100, qdoc::catch_generators::native_relative_directory_path()));
+ CAPTURE(relative_path);
+
+ QTemporaryDir working_directory{};
+ REQUIRE(working_directory.isValid());
+
+ QString path_to_directory = QFileInfo{working_directory.path() + "/" + relative_path}.filePath();
+
+ REQUIRE(QDir{working_directory.path()}.mkpath(relative_path));
+
+ AND_GIVEN("A DirectoryPath instance obtained from that path") {
+ auto maybe_directory_path{DirectoryPath::refine(path_to_directory)};
+ REQUIRE(maybe_directory_path);
+
+ auto directory_path{*maybe_directory_path};
+
+ WHEN("The path that the DirectoryPath contains is inspected") {
+ auto path{directory_path.value()};
+
+ THEN("That path is the same as the canonicazlied version of the path that the DirectoryPath was built from") {
+ REQUIRE(path == QFileInfo(path_to_directory).canonicalFilePath());
+ }
+ }
+ }
+ }
+}
diff --git a/src/qdoc/qdoc/tests/qdoc/boundaries/filesystem/catch_filepath.cpp b/src/qdoc/qdoc/tests/qdoc/boundaries/filesystem/catch_filepath.cpp
new file mode 100644
index 000000000..44d59017b
--- /dev/null
+++ b/src/qdoc/qdoc/tests/qdoc/boundaries/filesystem/catch_filepath.cpp
@@ -0,0 +1,136 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#include <catch_conversions/qdoc_catch_conversions.h>
+
+#include <catch/catch.hpp>
+
+#include <qdoc/boundaries/filesystem/filepath.h>
+
+#include <catch_generators/generators/path_generator.h>
+
+#include <QFileInfo>
+#include <QTemporaryDir>
+#include <QDir>
+#include <QIODeviceBase>
+
+SCENARIO("Obtaining a FilePath", "[FilePath][Boundaries][Validation][Canonicalization][Path]") {
+
+ GIVEN("Any string representing a path that does not represent an existing element on the filesystem") {
+ QString path = GENERATE(take(100, filter([](auto path){ return !QFileInfo{path}.exists(); }, qdoc::catch_generators::native_path())));
+ CAPTURE(path);
+
+ WHEN("A FilePath instance is requested from that string") {
+ auto maybe_filepath{FilePath::refine(path)};
+
+ THEN("A FilePath instance is not obtained") {
+ REQUIRE(!maybe_filepath);
+ }
+ }
+ }
+
+ GIVEN("Any string representing a path to a directory") {
+ QString relative_path = GENERATE(take(100, qdoc::catch_generators::native_relative_directory_path()));
+ CAPTURE(relative_path);
+
+ QTemporaryDir working_directory{};
+ REQUIRE(working_directory.isValid());
+
+ QString path_to_directory = working_directory.path() + "/" + relative_path;
+
+ AND_GIVEN("That the path represents an existing directory on the filesystem") {
+ REQUIRE(QDir{working_directory.path()}.mkpath(relative_path));
+
+ WHEN("A FilePath instance is requested from that string") {
+ auto maybe_filepath{FilePath::refine(path_to_directory)};
+
+ THEN("A FilePath instance is not obtained") {
+ REQUIRE(!maybe_filepath);
+ }
+ }
+ }
+ }
+
+#if defined(Q_OS_LINUX) || defined(Q_OS_MACOS)
+
+ GIVEN("Any string representing a path to a file") {
+ QString relative_path = GENERATE(take(100, qdoc::catch_generators::native_relative_file_path()));
+ CAPTURE(relative_path);
+
+ QTemporaryDir working_directory{};
+ REQUIRE(working_directory.isValid());
+
+ QString path_to_file = working_directory.path() + "/" + relative_path;
+
+ AND_GIVEN("That the path represents an existing file on the filesystem") {
+ REQUIRE(QDir{working_directory.path()}.mkpath(QFileInfo{relative_path}.path()));
+ REQUIRE(QFile{path_to_file}.open(QIODeviceBase::ReadWrite | QIODeviceBase::NewOnly));
+
+ AND_GIVEN("That the file represented by the path is not readable") {
+ REQUIRE(QFile::setPermissions(path_to_file, QFileDevice::WriteOwner |
+ QFileDevice::ExeOwner |
+ QFileDevice::WriteGroup |
+ QFileDevice::ExeGroup |
+ QFileDevice::WriteOther |
+ QFileDevice::ExeOther));
+
+ WHEN("A FilePath instance is requested from that string") {
+ auto maybe_filepath{FilePath::refine(path_to_file)};
+
+ THEN("A FilePath instance is not obtained") {
+ // REMARK: [temporary_directory_cleanup]
+ CHECK(!maybe_filepath);
+ REQUIRE(QFile::setPermissions(path_to_file, QFileDevice::WriteUser | QFileDevice::ReadUser | QFileDevice::ExeUser));
+ }
+ }
+ }
+
+ AND_GIVEN("That the file represented by the path is readable") {
+ REQUIRE(QFile::setPermissions(path_to_file, QFileDevice::ReadOwner | QFileDevice::ReadGroup | QFileDevice::ReadOther));
+
+ WHEN("A FilePath instance is requested from that string") {
+ auto maybe_filepath{FilePath::refine(path_to_file)};
+
+ THEN("A FilePath instance is obtained") {
+ // REMARK: [temporary_directory_cleanup]
+ CHECK(maybe_filepath);
+ REQUIRE(QFile::setPermissions(path_to_file, QFileDevice::WriteUser | QFileDevice::ReadUser | QFileDevice::ExeUser));
+ }
+ }
+ }
+ }
+ }
+
+#endif
+
+}
+
+SCENARIO("Inspecting the contents of a FilePath", "[FilePath][Boundaries][Canonicalization][Path][Contents]") {
+ GIVEN("Any string representing a path from which a FilePath instance can be obtained") {
+ QString relative_path = GENERATE(take(100, qdoc::catch_generators::native_relative_file_path()));
+ CAPTURE(relative_path);
+
+ QTemporaryDir working_directory{};
+ REQUIRE(working_directory.isValid());
+
+ QString path_to_file = QFileInfo{working_directory.path() + "/" + relative_path}.filePath();
+
+ REQUIRE(QDir{working_directory.path()}.mkpath(QFileInfo{relative_path}.path()));
+ REQUIRE(QFile{path_to_file}.open(QIODeviceBase::ReadWrite | QIODeviceBase::NewOnly));
+
+ AND_GIVEN("A FilePath instance obtained from that path") {
+ auto maybe_filepath{FilePath::refine(path_to_file)};
+ REQUIRE(maybe_filepath);
+
+ auto filepath{*maybe_filepath};
+
+ WHEN("The path that the FilePath contains is inspected") {
+ auto path{filepath.value()};
+
+ THEN("That path is the same as the canonicazlied version of the path that the FilePath was built from") {
+ REQUIRE(path == QFileInfo(path_to_file).canonicalFilePath());
+ }
+ }
+ }
+ }
+}
diff --git a/src/qdoc/qdoc/tests/qdoc/filesystem/catch_fileresolver.cpp b/src/qdoc/qdoc/tests/qdoc/filesystem/catch_fileresolver.cpp
new file mode 100644
index 000000000..f9b5af66b
--- /dev/null
+++ b/src/qdoc/qdoc/tests/qdoc/filesystem/catch_fileresolver.cpp
@@ -0,0 +1,307 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#include <catch_conversions/qdoc_catch_conversions.h>
+
+#include <catch/catch.hpp>
+
+#include <qdoc/filesystem/fileresolver.h>
+
+#include <catch_generators/generators/path_generator.h>
+
+#include <vector>
+#include <algorithm>
+#include <random>
+
+#include <QTemporaryDir>
+#include <QFileInfo>
+#include <QDir>
+#include <QIODeviceBase>
+
+SCENARIO("Inspecting the directories that will be used for searching", "[ResolvingFiles][Directory][Path][Canonicalization][Contents]") {
+ GIVEN("Some collection of paths representing existing directories on the filesystem") {
+ std::size_t directories_amount = GENERATE(take(10, random(2, 10)));
+
+ std::vector<QTemporaryDir> working_directories(directories_amount);
+
+ std::vector<DirectoryPath> directories;
+ directories.reserve(directories_amount);
+
+ std::transform(
+ working_directories.begin(), working_directories.end(),
+ std::back_inserter(directories),
+ [](auto& dir){ return *DirectoryPath::refine(dir.path()); }
+ );
+
+ AND_GIVEN("That the collection of those paths is ordered and contains no duplicates") {
+ std::sort(directories.begin(), directories.end());
+ directories.erase(std::unique(directories.begin(), directories.end()), directories.end());
+
+ WHEN("A mean of searching for files is obtained from that collection") {
+ FileResolver file_resolver{std::vector(directories)};
+
+ THEN("The collection of directories that will be used for searching is equivalent to the one from which a mean of searching for files was obtained") {
+ REQUIRE(file_resolver.get_search_directories() == directories);
+ }
+ }
+ }
+
+ AND_GIVEN("That the collection of those paths is potentially unordered but contains no duplicates") {
+ std::sort(directories.begin(), directories.end());
+ directories.erase(std::unique(directories.begin(), directories.end()), directories.end());
+
+ std::shuffle(directories.begin(), directories.end(), std::mt19937{std::random_device{}()});
+
+ WHEN("A mean of searching for files is obtained from that collection") {
+ FileResolver file_resolver{std::vector(directories)};
+
+ THEN("The collection of directories that will be used for searching is equivalent to the one from which a mean of searching for files was obtained if it was sorted") {
+ std::sort(directories.begin(), directories.end());
+
+ REQUIRE(file_resolver.get_search_directories() == directories);
+ }
+ }
+ }
+
+ AND_GIVEN("That the collection of those paths is ordered but contains duplicates") {
+ directories.reserve(directories.size());
+
+ std::transform(
+ working_directories.begin(), working_directories.end(),
+ std::back_inserter(directories),
+ [](auto& dir){ return *DirectoryPath::refine(dir.path()); }
+ );
+
+ std::sort(directories.begin(), directories.end());
+
+ WHEN("A mean of searching for files is obtained from that collection") {
+ FileResolver file_resolver{std::vector(directories)};
+
+ THEN("The collection of directories that will be used for searching is equivalent to the one from which a mean of searching for files was obtained if it contained no duplicates") {
+ directories.erase(std::unique(directories.begin(), directories.end()), directories.end());
+
+ REQUIRE(file_resolver.get_search_directories() == directories);
+ }
+ }
+ }
+
+ AND_GIVEN("That the collection of those paths is potentially unordered and contains duplicates") {
+ directories.reserve(directories.size());
+
+ std::transform(
+ working_directories.begin(), working_directories.end(),
+ std::back_inserter(directories),
+ [](auto& dir){ return *DirectoryPath::refine(dir.path()); }
+ );
+
+ std::shuffle(directories.begin(), directories.end(), std::mt19937{std::random_device{}()});
+
+ WHEN("A mean of searching for files is obtained from that collection") {
+ FileResolver file_resolver{std::vector(directories)};
+
+ THEN("The collection of directories that will be used for searching is equivalent to the one from which a mean of searching for files was obtained if it was sorted and it contained no duplicates") {
+ std::sort(directories.begin(), directories.end());
+ directories.erase(std::unique(directories.begin(), directories.end()), directories.end());
+
+ REQUIRE(file_resolver.get_search_directories() == directories);
+ }
+ }
+ }
+ }
+}
+
+SCENARIO("Finding a file based on some root search directories", "[ResolvingFiles][File][Path][Validation]") {
+ // TODO: Rewrite those tests under a single setup. Be careful of
+ // how this is done as Catch will rerun sections only under
+ // specific condition such that we may incur in collisions for the
+ // path if done incorrectly.
+ GIVEN("Some directory on the filesystem") {
+ QTemporaryDir working_directory{};
+ REQUIRE(working_directory.isValid());
+
+ DirectoryPath directory{*DirectoryPath::refine(working_directory.path())};
+
+ AND_GIVEN("A mean of searching for files based on that directory") {
+ FileResolver file_resolver{std::vector{directory}};
+
+ AND_GIVEN("A relative path that does not represent an element on the filesystem that is reachable from that directory") {
+ QString relative_path = GENERATE(filter([](auto& path){ return path != "." && path != ".."; }, take(100, qdoc::catch_generators::native_relative_path())));
+ CAPTURE(relative_path);
+
+ REQUIRE(!QFileInfo{working_directory.path() + '/' + relative_path}.exists());
+
+ WHEN("The relative path is used as a query to resolve a file") {
+ auto maybe_resolved_file{file_resolver.resolve(relative_path)};
+
+ THEN("The query cannot be resolved") {
+ REQUIRE(!maybe_resolved_file);
+ }
+ }
+ }
+ }
+ }
+
+ GIVEN("Some directory on the filesystem") {
+ QTemporaryDir working_directory{};
+ REQUIRE(working_directory.isValid());
+
+ DirectoryPath directory{*DirectoryPath::refine(working_directory.path())};
+
+ AND_GIVEN("A mean of searching for files based on that directory") {
+ FileResolver file_resolver{std::vector{directory}};
+
+ AND_GIVEN("A relative path that represents an existing directory on the filesystem that is reachable from that directory") {
+ QString relative_path = GENERATE(take(100, qdoc::catch_generators::native_relative_directory_path()));
+ CAPTURE(relative_path);
+
+ REQUIRE(QDir{working_directory.path()}.mkpath(relative_path));
+
+ WHEN("The relative path is used as a query to resolve a file") {
+ auto maybe_resolved_file{file_resolver.resolve(relative_path)};
+
+ THEN("The query cannot be resolved") {
+ REQUIRE(!maybe_resolved_file);
+ }
+ }
+ }
+ }
+ }
+
+
+ GIVEN("Some directory on the filesystem") {
+ QTemporaryDir working_directory{};
+ REQUIRE(working_directory.isValid());
+
+ DirectoryPath directory{*DirectoryPath::refine(working_directory.path())};
+
+ AND_GIVEN("A mean of searching for files based on that directory") {
+ FileResolver file_resolver{std::vector{directory}};
+
+ AND_GIVEN("A relative path that represents an existing file on the filesystem that is reachable from that directory") {
+ QString relative_path = GENERATE(take(100, qdoc::catch_generators::native_relative_file_path()));
+ CAPTURE(relative_path);
+
+ REQUIRE(QDir{working_directory.path()}.mkpath(QFileInfo{relative_path}.path()));
+ REQUIRE(QFile{working_directory.path() + "/" + relative_path}.open(QIODeviceBase::ReadWrite | QIODeviceBase::NewOnly));
+
+ WHEN("The relative path is used as a query to resolve a file") {
+ auto maybe_resolved_file{file_resolver.resolve(relative_path)};
+
+ THEN("The query can be resolved") {
+ REQUIRE(maybe_resolved_file);
+ }
+ }
+ }
+ }
+ }
+
+ GIVEN("Some directories on the filesystem") {
+ std::size_t directories_amount = GENERATE(take(10, random(2, 10)));
+
+ std::vector<QTemporaryDir> working_directories(directories_amount);
+ REQUIRE(std::all_of(working_directories.cbegin(), working_directories.cend(), [](auto& dir){ return dir.isValid(); }));
+
+ std::vector<DirectoryPath> directories;
+ directories.reserve(directories_amount);
+
+ std::transform(
+ working_directories.begin(), working_directories.end(),
+ std::back_inserter(directories),
+ [](auto& dir){ return *DirectoryPath::refine(dir.path()); }
+ );
+
+ AND_GIVEN("A relative path that represents an existing file on the filesystem that is reachable from exactly one of those directories") {
+ QString relative_path = GENERATE(take(10, qdoc::catch_generators::native_relative_file_path()));
+ CAPTURE(relative_path);
+
+ std::size_t containing_directory_index = GENERATE_COPY(take(1, random(std::size_t{0}, directories_amount - 1)));
+ CAPTURE(containing_directory_index);
+ CAPTURE(working_directories[containing_directory_index].path());
+
+ REQUIRE(QDir{working_directories[containing_directory_index].path()}.mkpath(QFileInfo{relative_path}.path()));
+ REQUIRE(QFile{working_directories[containing_directory_index].path() + "/" + relative_path}.open(QIODeviceBase::ReadWrite | QIODeviceBase::NewOnly));
+
+ AND_GIVEN("A mean of searching for files based on all of those directories") {
+ FileResolver file_resolver{std::move(directories)};
+
+ WHEN("The relative path is used as a query to resolve a file") {
+ auto maybe_resolved_file{file_resolver.resolve(relative_path)};
+
+ THEN("The query can be resolved") {
+ REQUIRE(maybe_resolved_file);
+ }
+ }
+ }
+ }
+ }
+}
+
+SCENARIO("Inspecting the content of a file that was resolved", "[ResolvingFiles][File][Path][Validation][Contents]") {
+ GIVEN("A mean of resolving files based on some directory") {
+ QTemporaryDir working_directory{};
+ REQUIRE(working_directory.isValid());
+
+ DirectoryPath directory{*DirectoryPath::refine(working_directory.path())};
+
+ FileResolver file_resolver{std::vector{directory}};
+
+ AND_GIVEN("A relative path that represents an existing file on the filesystem that is reachable from that directory") {
+ QString relative_path = GENERATE(take(100, qdoc::catch_generators::native_relative_file_path()));
+ CAPTURE(relative_path);
+
+ REQUIRE(QDir{working_directory.path()}.mkpath(QFileInfo{relative_path}.path()));
+ REQUIRE(QFile{working_directory.path() + "/" + relative_path}.open(QIODeviceBase::ReadWrite | QIODeviceBase::NewOnly));
+
+ WHEN("A file is resolved using that path as a query") {
+ auto resolved_file{*file_resolver.resolve(relative_path)};
+
+ THEN("The resolved file contains the query that was used to resolve it") {
+ REQUIRE(resolved_file.get_query() == relative_path);
+ }
+
+ THEN("The resolved file contains a canonicalized path pointing to the resolved file") {
+ REQUIRE(resolved_file.get_path() == QFileInfo{working_directory.path() + "/" + relative_path}.canonicalFilePath());
+ }
+ }
+ }
+ }
+}
+
+TEST_CASE(
+ "When a query can be resolved in more than one search directory, it is resolved in the greatest lower bound of the set of directories",
+ "[ResolvingFiles][File][Path][Validation][SpecialCase]"
+) {
+ std::size_t directories_amount = GENERATE(take(10, random(2, 10)));
+
+ QString relative_path = GENERATE(take(10, qdoc::catch_generators::native_relative_file_path()));
+ CAPTURE(relative_path);
+
+ std::vector<QTemporaryDir> working_directories(directories_amount);
+ REQUIRE(std::all_of(working_directories.cbegin(), working_directories.cend(), [](auto& dir){ return dir.isValid(); }));
+
+ for (const auto& directory : working_directories) {
+ CAPTURE(directory.path());
+ REQUIRE(QDir{directory.path()}.mkpath(QFileInfo{relative_path}.path()));
+ REQUIRE(QFile{directory.path() + "/" + relative_path}.open(QIODeviceBase::ReadWrite | QIODeviceBase::NewOnly));
+ }
+
+ std::vector<DirectoryPath> directories;
+ directories.reserve(directories_amount);
+
+ std::transform(
+ working_directories.begin(), working_directories.end(),
+ std::back_inserter(directories),
+ [](auto& dir){ return *DirectoryPath::refine(dir.path()); }
+ );
+
+ FileResolver file_resolver{std::move(directories)};
+ auto resolved_file{*file_resolver.resolve(relative_path)};
+
+ auto& greatest_lower_bound{*std::min_element(file_resolver.get_search_directories().cbegin(), file_resolver.get_search_directories().cend())};
+
+ REQUIRE(
+ resolved_file.get_path()
+ ==
+ QFileInfo{greatest_lower_bound.value() + "/" + relative_path}.canonicalFilePath()
+ );
+}
diff --git a/src/qdoc/qdoc/tests/qdoc/main.cpp b/src/qdoc/qdoc/tests/qdoc/main.cpp
new file mode 100644
index 000000000..5f85451f6
--- /dev/null
+++ b/src/qdoc/qdoc/tests/qdoc/main.cpp
@@ -0,0 +1,12 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#define CATCH_CONFIG_RUNNER
+#include <catch/catch.hpp>
+
+// A custom main was provided to avoid linking errors when using minGW
+// that were appearing in CI.
+// See https://github.com/catchorg/Catch2/issues/1287
+int main(int argc, char* argv[]) {
+ return Catch::Session().run(argc, argv);
+}
diff --git a/src/qdoc/qdoc/tests/qdoccommandlineparser/CMakeLists.txt b/src/qdoc/qdoc/tests/qdoccommandlineparser/CMakeLists.txt
new file mode 100644
index 000000000..4adb376d9
--- /dev/null
+++ b/src/qdoc/qdoc/tests/qdoccommandlineparser/CMakeLists.txt
@@ -0,0 +1,41 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+
+#####################################################################
+## tst_qdoccommandlineparser Test:
+#####################################################################
+
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qdoccommandlineparser LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
+qt_internal_add_test(tst_qdoccommandlineparser
+ WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
+ SOURCES
+ ${CMAKE_CURRENT_LIST_DIR}/tst_qdoccommandlineparser.cpp
+
+ ${CMAKE_CURRENT_LIST_DIR}/../../src/qdoc/qdoccommandlineparser.cpp
+ ${CMAKE_CURRENT_LIST_DIR}/../../src/qdoc/utilities.cpp
+ INCLUDE_DIRECTORIES
+ ${CMAKE_CURRENT_LIST_DIR}/../../src/
+)
+
+# Resources:
+set_source_files_properties("${CMAKE_CURRENT_LIST_DIR}/tst_arguments.txt"
+ PROPERTIES QT_RESOURCE_ALIAS "tst_arguments.txt"
+)
+
+set(tst_qdoccommandlineparser_resource_files
+ "${CMAKE_CURRENT_LIST_DIR}/tst_arguments.txt"
+)
+
+qt_internal_add_resource(tst_qdoccommandlineparser "tst_qdoccommandlineparser"
+ PREFIX
+ "/"
+ FILES
+ ${tst_qdoccommandlineparser_resource_files}
+)
+
diff --git a/src/qdoc/qdoc/tests/qdoccommandlineparser/tst_arguments.txt b/src/qdoc/qdoc/tests/qdoccommandlineparser/tst_arguments.txt
new file mode 100644
index 000000000..5797de394
--- /dev/null
+++ b/src/qdoc/qdoc/tests/qdoccommandlineparser/tst_arguments.txt
@@ -0,0 +1,22 @@
+-outputdir
+/src/qt5/qtbase/doc/qtgamepad
+-installdir
+/src/qt5/qtbase/doc
+/src/qt5/qtgamepad/src/gamepad/doc/qtgamepad.qdocconf
+-prepare
+-indexdir
+/src/qt5/qtbase/doc
+-no-link-errors
+-I.
+-I/src/qt5/qtbase/include
+-I/src/qt5/qtbase/include/QtGamepad
+-I/src/qt5/qtbase/include/QtGamepad/5.14.0
+-I/src/qt5/qtbase/include/QtGamepad/5.14.0/QtGamepad
+-I/src/qt5/qtbase/include/QtCore/5.14.0
+-I/src/qt5/qtbase/include/QtCore/5.14.0/QtCore
+-I/src/qt5/qtbase/include/QtGui
+-I/src/qt5/qtbase/include/QtCore
+-I.moc
+-isystem
+/usr/include/libdrm
+-I/src/qt5/qtbase/mkspecs/linux-g++
diff --git a/src/qdoc/qdoc/tests/qdoccommandlineparser/tst_qdoccommandlineparser.cpp b/src/qdoc/qdoc/tests/qdoccommandlineparser/tst_qdoccommandlineparser.cpp
new file mode 100644
index 000000000..e78de0425
--- /dev/null
+++ b/src/qdoc/qdoc/tests/qdoccommandlineparser/tst_qdoccommandlineparser.cpp
@@ -0,0 +1,161 @@
+// Copyright (C) 2020 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#include "qdoc/qdoccommandlineparser.h"
+
+#include <QtCore/qstringlist.h>
+#include <QtTest/QtTest>
+
+class tst_QDocCommandLineParser : public QObject
+{
+ Q_OBJECT
+
+private slots:
+ void defaultConstructor();
+ void process();
+ void argumentsFromCommandLineAndFile();
+};
+
+void tst_QDocCommandLineParser::defaultConstructor()
+{
+ QDocCommandLineParser parser;
+
+ QVERIFY2(parser.applicationDescription() == QStringLiteral("Qt documentation generator"),
+ "The application description is incorrect.");
+}
+
+void tst_QDocCommandLineParser::process()
+{
+ const QStringList arguments =
+ QStringLiteral("/src/qt5/qtbase/bin/qdoc "
+ "-outputdir "
+ "/src/qt5/qtbase/doc/qtgamepad "
+ "-installdir "
+ "/src/qt5/qtbase/doc "
+ "/src/qt5/qtgamepad/src/gamepad/doc/qtgamepad.qdocconf "
+ "-prepare "
+ "-indexdir "
+ "/src/qt5/qtbase/doc "
+ "-no-link-errors "
+ "-I. "
+ "-I/src/qt5/qtbase/include "
+ "-I/src/qt5/qtbase/include/QtGamepad "
+ "-I/src/qt5/qtbase/include/QtGamepad/5.14.0 "
+ "-I/src/qt5/qtbase/include/QtGamepad/5.14.0/QtGamepad "
+ "-I/src/qt5/qtbase/include/QtCore/5.14.0 "
+ "-I/src/qt5/qtbase/include/QtCore/5.14.0/QtCore "
+ "-I/src/qt5/qtbase/include/QtGui "
+ "-I/src/qt5/qtbase/include/QtCore "
+ "-I.moc "
+ "-isystem "
+ "/usr/include/libdrm "
+ "-I/src/qt5/qtbase/mkspecs/linux-g++"
+ ).split(QString(" "));
+ const QStringList expectedIncludePaths =
+ QStringLiteral(". "
+ "/src/qt5/qtbase/include "
+ "/src/qt5/qtbase/include/QtGamepad "
+ "/src/qt5/qtbase/include/QtGamepad/5.14.0 "
+ "/src/qt5/qtbase/include/QtGamepad/5.14.0/QtGamepad "
+ "/src/qt5/qtbase/include/QtCore/5.14.0 "
+ "/src/qt5/qtbase/include/QtCore/5.14.0/QtCore "
+ "/src/qt5/qtbase/include/QtGui "
+ "/src/qt5/qtbase/include/QtCore "
+ ".moc "
+ "/src/qt5/qtbase/mkspecs/linux-g++"
+ ).split(QString(" "));
+ const QStringList expectedSystemIncludePath(QStringLiteral("/usr/include/libdrm"));
+
+ QDocCommandLineParser parser;
+ parser.process(arguments);
+
+ QVERIFY(parser.isSet(parser.outputDirOption));
+ QCOMPARE(parser.value(parser.outputDirOption), QStringLiteral("/src/qt5/qtbase/doc/qtgamepad"));
+ QVERIFY(parser.isSet(parser.installDirOption));
+ QCOMPARE(parser.value(parser.installDirOption), QStringLiteral("/src/qt5/qtbase/doc"));
+ QVERIFY(parser.isSet(parser.prepareOption));
+ QVERIFY(parser.isSet(parser.indexDirOption));
+ QCOMPARE(parser.value(parser.indexDirOption), QStringLiteral("/src/qt5/qtbase/doc"));
+ QVERIFY(parser.isSet(parser.noLinkErrorsOption));
+ QVERIFY(parser.isSet(parser.includePathOption));
+ QCOMPARE(parser.values(parser.includePathOption), expectedIncludePaths);
+ QVERIFY(parser.isSet(parser.includePathSystemOption));
+ QCOMPARE(parser.values(parser.includePathSystemOption), expectedSystemIncludePath);
+
+ QVERIFY(!parser.isSet(parser.timestampsOption));
+ QVERIFY(!parser.isSet(parser.dependsOption));
+ QVERIFY(!parser.isSet(parser.highlightingOption));
+ QVERIFY(!parser.isSet(parser.showInternalOption));
+ QVERIFY(!parser.isSet(parser.redirectDocumentationToDevNullOption));
+ QVERIFY(!parser.isSet(parser.noExamplesOption));
+ QVERIFY(!parser.isSet(parser.autoLinkErrorsOption));
+ QVERIFY(!parser.isSet(parser.debugOption));
+ QVERIFY(!parser.isSet(parser.generateOption));
+ QVERIFY(!parser.isSet(parser.logProgressOption));
+ QVERIFY(!parser.isSet(parser.singleExecOption));
+ QVERIFY(!parser.isSet(parser.frameworkOption));
+
+ const QStringList expectedPositionalArgument = {
+ QStringLiteral("/src/qt5/qtgamepad/src/gamepad/doc/qtgamepad.qdocconf")
+ };
+ QCOMPARE(parser.positionalArguments(), expectedPositionalArgument);
+}
+
+void tst_QDocCommandLineParser::argumentsFromCommandLineAndFile()
+{
+ const QString atFilePath("@" + QFINDTESTDATA("tst_arguments.txt"));
+ const QStringList arguments { "/src/qt5/qtbase/bin/qdoc", atFilePath };
+
+ QDocCommandLineParser parser;
+ parser.process(arguments);
+
+ const QStringList expectedIncludePaths =
+ QStringLiteral(". "
+ "/src/qt5/qtbase/include "
+ "/src/qt5/qtbase/include/QtGamepad "
+ "/src/qt5/qtbase/include/QtGamepad/5.14.0 "
+ "/src/qt5/qtbase/include/QtGamepad/5.14.0/QtGamepad "
+ "/src/qt5/qtbase/include/QtCore/5.14.0 "
+ "/src/qt5/qtbase/include/QtCore/5.14.0/QtCore "
+ "/src/qt5/qtbase/include/QtGui "
+ "/src/qt5/qtbase/include/QtCore "
+ ".moc "
+ "/src/qt5/qtbase/mkspecs/linux-g++"
+ ).split(QString(" "));
+ const QStringList expectedSystemIncludePath(QStringLiteral("/usr/include/libdrm"));
+ const QStringList expectedPositionalArgument = {
+ QStringLiteral("/src/qt5/qtgamepad/src/gamepad/doc/qtgamepad.qdocconf")
+ };
+
+ QVERIFY(parser.isSet(parser.outputDirOption));
+ QCOMPARE(parser.value(parser.outputDirOption), QStringLiteral("/src/qt5/qtbase/doc/qtgamepad"));
+ QVERIFY(parser.isSet(parser.installDirOption));
+ QCOMPARE(parser.value(parser.installDirOption), QStringLiteral("/src/qt5/qtbase/doc"));
+ QVERIFY(parser.isSet(parser.prepareOption));
+ QVERIFY(parser.isSet(parser.indexDirOption));
+ QCOMPARE(parser.value(parser.indexDirOption), QStringLiteral("/src/qt5/qtbase/doc"));
+ QVERIFY(parser.isSet(parser.noLinkErrorsOption));
+ QVERIFY(parser.isSet(parser.includePathOption));
+ QCOMPARE(parser.values(parser.includePathOption), expectedIncludePaths);
+ QVERIFY(parser.isSet(parser.includePathSystemOption));
+ QCOMPARE(parser.values(parser.includePathSystemOption), expectedSystemIncludePath);
+
+ QVERIFY(!parser.isSet(parser.timestampsOption));
+ QVERIFY(!parser.isSet(parser.dependsOption));
+ QVERIFY(!parser.isSet(parser.highlightingOption));
+ QVERIFY(!parser.isSet(parser.showInternalOption));
+ QVERIFY(!parser.isSet(parser.redirectDocumentationToDevNullOption));
+ QVERIFY(!parser.isSet(parser.noExamplesOption));
+ QVERIFY(!parser.isSet(parser.autoLinkErrorsOption));
+ QVERIFY(!parser.isSet(parser.debugOption));
+ QVERIFY(!parser.isSet(parser.generateOption));
+ QVERIFY(!parser.isSet(parser.logProgressOption));
+ QVERIFY(!parser.isSet(parser.singleExecOption));
+ QVERIFY(!parser.isSet(parser.frameworkOption));
+
+ QCOMPARE(parser.positionalArguments(), expectedPositionalArgument);
+}
+
+QTEST_APPLESS_MAIN(tst_QDocCommandLineParser)
+
+#include "tst_qdoccommandlineparser.moc"
diff --git a/src/qdoc/qdoc/tests/utilities/CMakeLists.txt b/src/qdoc/qdoc/tests/utilities/CMakeLists.txt
new file mode 100644
index 000000000..dd2883d32
--- /dev/null
+++ b/src/qdoc/qdoc/tests/utilities/CMakeLists.txt
@@ -0,0 +1,21 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+#####################################################################
+## tst_utilities Test:
+#####################################################################
+
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_utilities LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
+qt_internal_add_test(tst_utilities
+ SOURCES
+ ${CMAKE_CURRENT_LIST_DIR}/tst_utilities.cpp
+
+ ${CMAKE_CURRENT_LIST_DIR}/../../src/qdoc/utilities.cpp
+ INCLUDE_DIRECTORIES
+ ${CMAKE_CURRENT_LIST_DIR}/../../src/
+)
diff --git a/src/qdoc/qdoc/tests/utilities/tst_utilities.cpp b/src/qdoc/qdoc/tests/utilities/tst_utilities.cpp
new file mode 100644
index 000000000..160e4d15c
--- /dev/null
+++ b/src/qdoc/qdoc/tests/utilities/tst_utilities.cpp
@@ -0,0 +1,134 @@
+// Copyright (C) 2020 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#include "qdoc/utilities.h"
+
+#include <QtTest/QtTest>
+
+class tst_Utilities : public QObject
+{
+ Q_OBJECT
+
+private slots:
+ void loggingCategoryName();
+ void loggingCategoryDefaults();
+ void startDebugging();
+ void stopDebugging();
+ void debugging();
+ void callSeparatorForOneWord();
+ void callSeparatorForMoreThanOneWord();
+ void callCommaForOneWord();
+ void callCommaForTwoWords();
+ void callCommaForThreeWords();
+};
+
+void tst_Utilities::loggingCategoryName()
+{
+ const QString expected = "qt.qdoc";
+ QCOMPARE(lcQdoc().categoryName(), expected);
+}
+
+void tst_Utilities::loggingCategoryDefaults()
+{
+ QVERIFY(lcQdoc().isCriticalEnabled());
+ QVERIFY(lcQdoc().isWarningEnabled());
+ QVERIFY(!lcQdoc().isDebugEnabled());
+ QVERIFY(lcQdoc().isInfoEnabled());
+}
+
+void tst_Utilities::startDebugging()
+{
+ QVERIFY(!lcQdoc().isDebugEnabled());
+ Utilities::startDebugging("test");
+ QVERIFY(lcQdoc().isDebugEnabled());
+}
+
+void tst_Utilities::stopDebugging()
+{
+ Utilities::startDebugging("test");
+ QVERIFY(lcQdoc().isDebugEnabled());
+ Utilities::stopDebugging("test");
+ QVERIFY(!lcQdoc().isDebugEnabled());
+}
+
+void tst_Utilities::debugging()
+{
+ QVERIFY(!lcQdoc().isDebugEnabled());
+ QVERIFY(!Utilities::debugging());
+ Utilities::startDebugging("test");
+ QVERIFY(lcQdoc().isDebugEnabled());
+ QVERIFY(Utilities::debugging());
+}
+
+void tst_Utilities::callSeparatorForOneWord()
+{
+ const QStringList listOfWords { "one" };
+ const QString expected = QStringLiteral("one.");
+
+ int index = 0;
+ QString result;
+ for (const auto &word : listOfWords) {
+ result.append(word);
+ result.append(Utilities::separator(index++, listOfWords.size()));
+ }
+ QCOMPARE(result, expected);
+}
+
+void tst_Utilities::callSeparatorForMoreThanOneWord()
+{
+ const QStringList listOfWords { "one", "two" };
+ const QString expected = QStringLiteral("one and two.");
+
+ int index = 0;
+ QString result;
+ for (const auto &word : listOfWords) {
+ result.append(word);
+ result.append(Utilities::separator(index++, listOfWords.size()));
+ }
+ QCOMPARE(result, expected);
+}
+
+void tst_Utilities::callCommaForOneWord()
+{
+ const QStringList listOfWords { "one" };
+ const QString expected = QStringLiteral("one");
+
+ int index = 0;
+ QString result;
+ for (const auto &word : listOfWords) {
+ result.append(word);
+ result.append(Utilities::comma(index++, listOfWords.size()));
+ }
+ QCOMPARE(result, expected);
+}
+void tst_Utilities::callCommaForTwoWords()
+{
+ const QStringList listOfWords { "one", "two" };
+ const QString expected = QStringLiteral("one and two");
+
+ int index = 0;
+ QString result;
+ for (const auto &word : listOfWords) {
+ result.append(word);
+ result.append(Utilities::comma(index++, listOfWords.size()));
+ }
+ QCOMPARE(result, expected);
+}
+
+void tst_Utilities::callCommaForThreeWords()
+{
+ const QStringList listOfWords { "one", "two", "three" };
+ const QString expected = QStringLiteral("one, two, and three");
+
+ int index = 0;
+ QString result;
+ for (const auto &word : listOfWords) {
+ result.append(word);
+ result.append(Utilities::comma(index++, listOfWords.size()));
+ }
+ QCOMPARE(result, expected);
+}
+
+QTEST_APPLESS_MAIN(tst_Utilities)
+
+#include "tst_utilities.moc"
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/CMakeLists.txt b/src/qdoc/qdoc/tests/validateqdocoutputfiles/CMakeLists.txt
new file mode 100644
index 000000000..2d7416f82
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/CMakeLists.txt
@@ -0,0 +1,44 @@
+# Copyright (C) 2024 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+#####################################################################
+## tst_validateQdocOutputFiles Test:
+#####################################################################
+
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_validateQdocOutputFiles LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
+qt_internal_add_test(tst_validateQdocOutputFiles
+ SOURCES
+ ${CMAKE_CURRENT_LIST_DIR}/tst_validateqdocoutputfiles.cpp
+)
+
+# Write relevant Qt include path to a file, to be read in by QDoc
+set(config_subfolder "")
+get_cmake_property(is_multi_config GENERATOR_IS_MULTI_CONFIG)
+if(is_multi_config)
+ set(config_subfolder "$<CONFIG>/")
+endif()
+set(includepathsfile "${CMAKE_CURRENT_BINARY_DIR}/${config_subfolder}qdocincludepaths.inc")
+set(framework_path "\n")
+
+find_package(Qt6 COMPONENTS Core REQUIRED)
+if(Qt6Core_FOUND)
+ get_target_property(include_paths Qt6::Core INTERFACE_INCLUDE_DIRECTORIES)
+endif()
+
+while(include_paths)
+ list(POP_BACK include_paths inc_path)
+ if(inc_path MATCHES "(.+)/QtCore\.framework$")
+ string(APPEND framework_path "-F${CMAKE_MATCH_1}")
+ break()
+ endif()
+endwhile()
+
+set (include_paths "$<TARGET_PROPERTY:tst_validateQdocOutputFiles,INCLUDE_DIRECTORIES>")
+file(GENERATE OUTPUT ${includepathsfile} CONTENT "-I$<JOIN:${include_paths},\n-I>${framework_path}")
+target_compile_definitions(tst_validateQdocOutputFiles PRIVATE DOCINCPATH="${includepathsfile}")
+add_dependencies(tst_validateQdocOutputFiles Qt::qdoc)
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/README.md b/src/qdoc/qdoc/tests/validateqdocoutputfiles/README.md
new file mode 100644
index 000000000..728cbee62
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/README.md
@@ -0,0 +1,82 @@
+<!--
+ Copyright (C) 2024 The Qt Company Ltd.
+ SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+-->
+
+# QDoc output test
+This is a test that validates the files QDoc generates. It is not a test for
+QDoc itself. The test calls the qdoc executable for each project in the
+`testdata` directory (see [Test project structure](# Test project structure),
+below), and then compares the output to that in the `expected` directory for
+the test project. Each test is identified in the test output by the name of the
+test project directory *and* the name of the .qdocconf-file.
+
+The test creates a new temporary directory for each test project. The
+comparison is done by running `git diff` on the content in this temporary
+directory (the output directory) and the contents in the test's `expected`
+directory. A non-zero exit code fails the test for that project, and the test
+includes the diff in its output if this happens.
+
+## How to add a new test
+1. Create a new directory in the `testdata` directory. The name of the new
+ directory should be descriptive of the test project.
+2. Create a `.qdocconf` file in the new directory. See
+ [The .qdocconf file](# The .qdocconf file) below for further details.
+3. Add the necessary files (.qdoc, .h/.cpp, .qml, etc) so that the project
+ can be built in a meaningful manner.
+4. Run QDoc on the project.
+5. Verify that the output looks correct.
+6. Copy the output into `[testdata/[new test directory]/expected`.
+7. Run the test executable and verify that the test output includes a **PASS**
+ line for the new test-case.
+8. Push your change upstream.
+
+## Update the expected content for all tests
+If you make a change to QDoc that causes significant changes in output, you may
+need to update the expected output for many, or even all, tests. If you set the
+environment variable `QDOC_REGENERATE_TESTDATA=1` before running the test, all
+current test data will be removed, and QDoc will be run on each project to
+generate new output. Note, however, that the comparison per project is skipped
+in this scenario.
+
+## Test project structure
+The `testdata` directory is where the test looks for projects to test. Each
+project has its own directory, a `qdocconf` file, and an `expected`
+directory that contains the expected file output from QDoc.
+
+The project directory must contain a .qdocconf whose name matches that of the
+project directory, otherwise the project won't be picked up by the test.
+
+### The .qdocconf file
+The `.qdocconf` file contains the configuration necessary to build the
+QDoc project in the directory. Observe the following:
+- **Important!** Make sure to set `locationinfo = false` to avoid test failures
+ due to differences in the location information in the generated output.
+- Test projects should be warning free, so set `warninglimit.enabled = true`.
+- The name of the .qdocconf-file must be identical as that of the directory
+ that contains it. This means that for a project **foo**, it should go into
+ `testdata/foo` and the configuration file should be
+ `testdata/foo/foo.qdocconf`.
+- Any other `.qdocconf` file will not be picked up explicitly by the test.
+ This means that for a test project **foo** that resides in `testdata/foo`,
+ `testdata/foo/foo.qdocconf` will be treated as a test-case, while
+ `testdata/foo/bar.qdocconf` will not. However, `foo.qdocconf` may contain
+ `include(bar.qdocconf)`, and this will work as expected.
+- The `warninglimit` should be set to the number of warnings expected
+ from QDoc for that specific project, if any are to be expected at all.
+- You probably want to configure all output formats, otherwise only the HTML
+ output will be generated. The test will compare all output formats that are
+ generated to the expected output, so remember to specify the output directory
+ for each format. By convention, the output directories are named after the
+ format, for example `html`, `docbook`, `webxml`, etc.
+- Place the sources for your new test in a subdirectory of the test project
+ directory. By convention, the sources are placed in a directory named `src`.
+
+### The `expected` directory
+The `expected` directory contains the expected output from QDoc
+for the project. If QDoc generates an empty directory (for example, it
+always creates the `images/` directory for a project, whether
+the project contains any images or not), that directory isn't tracked
+in the `expected` directory, as **git** doesn't track directories, only
+files.
+
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/bug80259/bug80259.qdocconf b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/bug80259/bug80259.qdocconf
new file mode 100644
index 000000000..2b7a1e5f4
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/bug80259/bug80259.qdocconf
@@ -0,0 +1,31 @@
+project = TestModule
+
+moduleheader = TestModule.h
+
+headerdirs = ./src/inc
+sourcedirs = ./src
+includepaths += ./src/inc/testmodule
+
+sources.fileextensions = "*.cpp *.qdoc"
+headers.fileextensions = "*.h"
+
+outputdir = doc
+outputformats = HTML WebXML
+
+# images
+imagedirs = ./src/images
+
+# zero warning policy
+warninglimit = 0
+warninglimit.enabled = true
+
+# don't write host system-specific paths to index files
+locationinfo = false
+
+# By default, use -outputdir directly, no subdir.
+# outputsubdir '.' matches the root of expected_output/
+HTML.nosubdirs = true
+HTML.outputsubdir = html
+
+WebXML.nosubdirs = true
+WebXML.outputsubdir = webxml
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/bug80259/expected/html/first-members.html b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/bug80259/expected/html/first-members.html
new file mode 100644
index 000000000..db22018c5
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/bug80259/expected/html/first-members.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+<!-- main.cpp -->
+ <title>List of All Members for First | TestModule</title>
+</head>
+<body>
+<li>First</li>
+<div class="sidebar"><div class="sidebar-content" id="sidebar-content"></div></div>
+<h1 class="title" translate="no">List of All Members for First</h1>
+<p>This is the complete list of members for <a href="first.html">First</a>, including inherited members.</p>
+<ul>
+<li class="fn" translate="no">class <span class="name"><b><a href="first-nested.html" translate="no">Nested</a></b></span></li>
+</ul>
+</body>
+</html>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/bug80259/expected/html/first-nested.html b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/bug80259/expected/html/first-nested.html
new file mode 100644
index 000000000..9a8266e7a
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/bug80259/expected/html/first-nested.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+<!-- main.cpp -->
+ <title>Nested Class | TestModule</title>
+</head>
+<body>
+<li>Nested</li>
+<div class="sidebar">
+<div class="toc">
+<h3 id="toc">Contents</h3>
+<ul>
+<li class="level1"><a href="#details">Detailed Description</a></li>
+</ul>
+</div>
+<div class="sidebar-content" id="sidebar-content"></div></div>
+<h1 class="title" translate="no">Nested Class</h1>
+<span class="small-subtitle" translate="no">class <a href="first.html" translate="no">First</a>::Nested</span>
+<!-- $$$Nested-description -->
+<div class="descr">
+<h2 id="details">Detailed Description</h2>
+<p>This is a nested class</p>
+</div>
+<!-- @@@Nested -->
+</body>
+</html>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/bug80259/expected/html/first.html b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/bug80259/expected/html/first.html
new file mode 100644
index 000000000..74033f353
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/bug80259/expected/html/first.html
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+<!-- main.cpp -->
+ <title>First Struct | TestModule</title>
+</head>
+<body>
+<li>First</li>
+<div class="sidebar">
+<div class="toc">
+<h3 id="toc">Contents</h3>
+<ul>
+<li class="level1"><a href="#public-types">Public Types</a></li>
+<li class="level1"><a href="#details">Detailed Description</a></li>
+</ul>
+</div>
+<div class="sidebar-content" id="sidebar-content"></div></div>
+<h1 class="title" translate="no">First Struct</h1>
+<div class="table"><table class="alignedsummary" translate="no">
+<tr><td class="memItemLeft rightAlign topAlign"> Header:</td><td class="memItemRight bottomAlign"> <span class="preprocessor">#include &lt;First&gt;</span></td></tr>
+</table></div>
+<ul>
+<li><a href="first-members.html">List of all members, including inherited members</a></li>
+</ul>
+<h2 id="public-types">Public Types</h2>
+<div class="table"><table class="alignedsummary" translate="no">
+<tr><td class="memItemLeft rightAlign topAlign"> class </td><td class="memItemRight bottomAlign"><b><a href="first-nested.html" translate="no">Nested</a></b></td></tr>
+</table></div>
+<!-- $$$First-description -->
+<div class="descr">
+<h2 id="details">Detailed Description</h2>
+<p>This is a first class</p>
+</div>
+<!-- @@@First -->
+</body>
+</html>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/bug80259/expected/html/index.html b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/bug80259/expected/html/index.html
new file mode 100644
index 000000000..f934cfdca
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/bug80259/expected/html/index.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+<!-- index.qdoc -->
+ <title>doc index | TestModule</title>
+</head>
+<body>
+<h1 class="title">doc index</h1>
+<!-- $$$index.html-description -->
+<div class="descr" id="details">
+<div class="table"><table class="annotated">
+<tr class="odd topAlign"><td class="tblName" translate="no"><p><a href="first.html">First</a></p></td></tr>
+<tr class="even topAlign"><td class="tblName" translate="no"><p><a href="first-nested.html">First::Nested</a></p></td></tr>
+<tr class="odd topAlign"><td class="tblName" translate="no"><p><a href="second.html">Second</a></p></td></tr>
+<tr class="even topAlign"><td class="tblName" translate="no"><p><a href="third.html">Third</a></p></td></tr>
+</table></div>
+</div>
+<!-- @@@index.html -->
+</body>
+</html>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/bug80259/expected/html/second.html b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/bug80259/expected/html/second.html
new file mode 100644
index 000000000..93f492cec
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/bug80259/expected/html/second.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+<!-- main.cpp -->
+ <title>Second Class | TestModule</title>
+</head>
+<body>
+<li>Second</li>
+<div class="sidebar">
+<div class="toc">
+<h3 id="toc">Contents</h3>
+<ul>
+<li class="level1"><a href="#details">Detailed Description</a></li>
+</ul>
+</div>
+<div class="sidebar-content" id="sidebar-content"></div></div>
+<h1 class="title" translate="no">Second Class</h1>
+<div class="table"><table class="alignedsummary" translate="no">
+<tr><td class="memItemLeft rightAlign topAlign"> Header:</td><td class="memItemRight bottomAlign"> <span class="preprocessor">#include &lt;Second&gt;</span></td></tr>
+</table></div>
+<!-- $$$Second-description -->
+<div class="descr">
+<h2 id="details">Detailed Description</h2>
+<p>This is a second class</p>
+</div>
+<!-- @@@Second -->
+</body>
+</html>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/bug80259/expected/html/testmodule.index b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/bug80259/expected/html/testmodule.index
new file mode 100644
index 000000000..6e8bf310f
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/bug80259/expected/html/testmodule.index
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE QDOCINDEX>
+<INDEX url="" title="TestModule Reference Documentation" version="" project="TestModule">
+ <namespace name="" status="active" access="public" module="testmodule">
+ <struct name="First" href="first.html" status="active" access="public" location="bbb.h" documented="true" module="TestModule">
+ <class name="Nested" fullname="First::Nested" href="first-nested.html" status="active" access="public" location="bbb.h" documented="true" module="TestModule"/>
+ </struct>
+ <class name="Second" href="second.html" status="active" access="public" location="ccc.h" documented="true" module="TestModule"/>
+ <class name="Third" href="third.html" status="active" access="public" location="aaa.h" documented="true" module="TestModule"/>
+ <page name="index.html" href="index.html" status="active" location="index.qdoc" documented="true" subtype="page" title="doc index" fulltitle="doc index" subtitle=""/>
+ <module name="TestModule" href="testmodule-module.html" status="internal" seen="false" title=""/>
+ </namespace>
+</INDEX>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/bug80259/expected/html/third.html b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/bug80259/expected/html/third.html
new file mode 100644
index 000000000..cc8f68404
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/bug80259/expected/html/third.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+<!-- main.cpp -->
+ <title>Third Class | TestModule</title>
+</head>
+<body>
+<li>Third</li>
+<div class="sidebar">
+<div class="toc">
+<h3 id="toc">Contents</h3>
+<ul>
+<li class="level1"><a href="#details">Detailed Description</a></li>
+</ul>
+</div>
+<div class="sidebar-content" id="sidebar-content"></div></div>
+<h1 class="title" translate="no">Third Class</h1>
+<div class="table"><table class="alignedsummary" translate="no">
+<tr><td class="memItemLeft rightAlign topAlign"> Header:</td><td class="memItemRight bottomAlign"> <span class="preprocessor">#include &lt;Third&gt;</span></td></tr>
+</table></div>
+<!-- $$$Third-description -->
+<div class="descr">
+<h2 id="details">Detailed Description</h2>
+<p>This is a third class</p>
+</div>
+<!-- @@@Third -->
+</body>
+</html>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/bug80259/expected/webxml/first-nested.webxml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/bug80259/expected/webxml/first-nested.webxml
new file mode 100644
index 000000000..842570c13
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/bug80259/expected/webxml/first-nested.webxml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<WebXML>
+ <document>
+ <class name="Nested" fullname="First::Nested" href="first-nested.html" status="active" access="public" location="bbb.h" documented="true" module="TestModule">
+ <description>
+ <para>This is a nested class</para>
+ </description>
+ </class>
+ </document>
+</WebXML>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/bug80259/expected/webxml/first.webxml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/bug80259/expected/webxml/first.webxml
new file mode 100644
index 000000000..526bbbe73
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/bug80259/expected/webxml/first.webxml
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<WebXML>
+ <document>
+ <struct name="First" href="first.html" status="active" access="public" location="bbb.h" documented="true" module="TestModule">
+ <description>
+ <para>This is a first class</para>
+ </description>
+ <class name="Nested" fullname="First::Nested" href="first-nested.html" status="active" access="public" location="bbb.h" documented="true" module="TestModule">
+ <description>
+ <para>This is a nested class</para>
+ </description>
+ </class>
+ </struct>
+ </document>
+</WebXML>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/bug80259/expected/webxml/index.webxml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/bug80259/expected/webxml/index.webxml
new file mode 100644
index 000000000..659d98f22
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/bug80259/expected/webxml/index.webxml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<WebXML>
+ <document>
+ <page name="index.html" href="index.html" status="active" location="index.qdoc" documented="true" subtype="page" title="doc index" fulltitle="doc index" subtitle="">
+ <description>
+ <generatedlist contents="classesbymodule TestModule"/>
+ </description>
+ </page>
+ </document>
+</WebXML>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/bug80259/expected/webxml/second.webxml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/bug80259/expected/webxml/second.webxml
new file mode 100644
index 000000000..9ecd3c2fe
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/bug80259/expected/webxml/second.webxml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<WebXML>
+ <document>
+ <class name="Second" href="second.html" status="active" access="public" location="ccc.h" documented="true" module="TestModule">
+ <description>
+ <para>This is a second class</para>
+ </description>
+ </class>
+ </document>
+</WebXML>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/bug80259/expected/webxml/testmodule.index b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/bug80259/expected/webxml/testmodule.index
new file mode 100644
index 000000000..6e8bf310f
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/bug80259/expected/webxml/testmodule.index
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE QDOCINDEX>
+<INDEX url="" title="TestModule Reference Documentation" version="" project="TestModule">
+ <namespace name="" status="active" access="public" module="testmodule">
+ <struct name="First" href="first.html" status="active" access="public" location="bbb.h" documented="true" module="TestModule">
+ <class name="Nested" fullname="First::Nested" href="first-nested.html" status="active" access="public" location="bbb.h" documented="true" module="TestModule"/>
+ </struct>
+ <class name="Second" href="second.html" status="active" access="public" location="ccc.h" documented="true" module="TestModule"/>
+ <class name="Third" href="third.html" status="active" access="public" location="aaa.h" documented="true" module="TestModule"/>
+ <page name="index.html" href="index.html" status="active" location="index.qdoc" documented="true" subtype="page" title="doc index" fulltitle="doc index" subtitle=""/>
+ <module name="TestModule" href="testmodule-module.html" status="internal" seen="false" title=""/>
+ </namespace>
+</INDEX>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/bug80259/expected/webxml/third.webxml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/bug80259/expected/webxml/third.webxml
new file mode 100644
index 000000000..529ffd896
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/bug80259/expected/webxml/third.webxml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<WebXML>
+ <document>
+ <class name="Third" href="third.html" status="active" access="public" location="aaa.h" documented="true" module="TestModule">
+ <description>
+ <para>This is a third class</para>
+ </description>
+ </class>
+ </document>
+</WebXML>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/bug80259/src/inc/testmodule/TestModule.h b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/bug80259/src/inc/testmodule/TestModule.h
new file mode 100644
index 000000000..90adda3e2
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/bug80259/src/inc/testmodule/TestModule.h
@@ -0,0 +1,6 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "aaa.h"
+#include "bbb.h"
+#include "ccc.h"
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/bug80259/src/inc/testmodule/aaa.h b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/bug80259/src/inc/testmodule/aaa.h
new file mode 100644
index 000000000..5051d60a9
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/bug80259/src/inc/testmodule/aaa.h
@@ -0,0 +1,7 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#pragma once
+class Third
+{
+};
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/bug80259/src/inc/testmodule/bbb.h b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/bug80259/src/inc/testmodule/bbb.h
new file mode 100644
index 000000000..c17223c2d
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/bug80259/src/inc/testmodule/bbb.h
@@ -0,0 +1,8 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#pragma once
+struct First
+{
+ class Nested {};
+};
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/bug80259/src/inc/testmodule/ccc.h b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/bug80259/src/inc/testmodule/ccc.h
new file mode 100644
index 000000000..8fa00e3e8
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/bug80259/src/inc/testmodule/ccc.h
@@ -0,0 +1,7 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#pragma once
+class Second
+{
+};
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/bug80259/src/main.cpp b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/bug80259/src/main.cpp
new file mode 100644
index 000000000..bc92fa921
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/bug80259/src/main.cpp
@@ -0,0 +1,29 @@
+// Copyright (C) 2019 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+/*!
+\class First
+\inmodule TestModule
+
+This is a first class
+*/
+
+/*!
+\class First::Nested
+\inmodule TestModule
+
+This is a nested class
+*/
+
+/*!
+\class Second
+\inmodule TestModule
+
+This is a second class
+*/
+
+/*!
+\class Third
+\inmodule TestModule
+
+This is a third class
+*/
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/bug80259/src/qdoc/index.qdoc b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/bug80259/src/qdoc/index.qdoc
new file mode 100644
index 000000000..ae270f378
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/bug80259/src/qdoc/index.qdoc
@@ -0,0 +1,10 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+/*!
+ \page index.html
+ \title doc index
+
+ \generatelist {classesbymodule TestModule}
+
+*/
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/cmakedocumentation/cmakedocumentation.qdocconf b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/cmakedocumentation/cmakedocumentation.qdocconf
new file mode 100644
index 000000000..c62f4938e
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/cmakedocumentation/cmakedocumentation.qdocconf
@@ -0,0 +1,29 @@
+project = cmakedocumentation
+
+headerdirs = ./src
+sourcedirs = ./src
+exampledirs = ./src
+
+outputformats = WebXML HTML DocBook
+WebXML.quotinginformation = true
+WebXML.nosubdirs = true
+WebXML.outputsubdir = webxml
+
+HTML.nosubdirs = true
+HTML.outputsubdir = html
+
+DocBook.nosubdirs = true
+DocBook.outputsubdir = docbook
+
+sources.fileextensions = "*.qml *.cpp *.qdoc"
+headers.fileextensions = "*.h"
+
+# images
+imagedirs = images
+
+# zero warning policy
+warninglimit = 0
+warninglimit.enabled = true
+
+# don't write host system-specific paths to index files
+locationinfo = false
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/cmakedocumentation/expected/docbook/car.xml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/cmakedocumentation/expected/docbook/car.xml
new file mode 100644
index 000000000..ee439cdd0
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/cmakedocumentation/expected/docbook/car.xml
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<db:article xmlns:db="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" version="5.2" xml:lang="en">
+<db:info>
+<db:title>Car Class</db:title>
+<db:productname>cmakedocumentation</db:productname>
+<db:titleabbrev>cmakedocumentation Reference Documentation</db:titleabbrev>
+<db:abstract>
+<db:para>Represents a model of a car.</db:para>
+<db:para>This class was introduced in Qt 6.6.6.</db:para>
+</db:abstract>
+</db:info>
+<db:variablelist>
+<db:varlistentry>
+<db:term>Header</db:term>
+<db:listitem>
+<db:para>Car</db:para>
+</db:listitem>
+</db:varlistentry>
+<db:varlistentry>
+<db:term>Since</db:term>
+<db:listitem>
+<db:para>Qt 6.6.6</db:para>
+</db:listitem>
+</db:varlistentry>
+<db:varlistentry>
+<db:term>CMake</db:term>
+<db:listitem>
+<db:para>find_package(Qt6 REQUIRED COMPONENTS TestCar)</db:para>
+<db:para>target_link_libraries(mytarget PRIVATE Qt6::TestCar)</db:para>
+</db:listitem>
+</db:varlistentry>
+<db:varlistentry>
+<db:term>qmake</db:term>
+<db:listitem>
+<db:para>QT += testcar</db:para>
+</db:listitem>
+</db:varlistentry>
+</db:variablelist>
+<db:section xml:id="details">
+<db:title>Detailed Description</db:title>
+</db:section>
+</db:article>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/cmakedocumentation/expected/docbook/engine.xml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/cmakedocumentation/expected/docbook/engine.xml
new file mode 100644
index 000000000..06fcbd4bd
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/cmakedocumentation/expected/docbook/engine.xml
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<db:article xmlns:db="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" version="5.2" xml:lang="en">
+<db:info>
+<db:title>Engine Class</db:title>
+<db:productname>cmakedocumentation</db:productname>
+<db:titleabbrev>cmakedocumentation Reference Documentation</db:titleabbrev>
+<db:abstract>
+<db:para>Represents a model of an engine.</db:para>
+<db:para>This class was introduced in Qt 6.6.6.</db:para>
+</db:abstract>
+</db:info>
+<db:variablelist>
+<db:varlistentry>
+<db:term>Header</db:term>
+<db:listitem>
+<db:para>Engine</db:para>
+</db:listitem>
+</db:varlistentry>
+<db:varlistentry>
+<db:term>Since</db:term>
+<db:listitem>
+<db:para>Qt 6.6.6</db:para>
+</db:listitem>
+</db:varlistentry>
+<db:varlistentry>
+<db:term>CMake</db:term>
+<db:listitem>
+<db:para>find_package(Qt6 REQUIRED COMPONENTS TestCar)</db:para>
+<db:para>target_link_libraries(mytarget PRIVATE Qt6::TestCarPrivate)</db:para>
+</db:listitem>
+</db:varlistentry>
+<db:varlistentry>
+<db:term>qmake</db:term>
+<db:listitem>
+<db:para>QT += testcar-private</db:para>
+</db:listitem>
+</db:varlistentry>
+</db:variablelist>
+<db:section xml:id="details">
+<db:title>Detailed Description</db:title>
+</db:section>
+</db:article>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/cmakedocumentation/expected/docbook/testcar-module.xml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/cmakedocumentation/expected/docbook/testcar-module.xml
new file mode 100644
index 000000000..03f17d47f
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/cmakedocumentation/expected/docbook/testcar-module.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<db:article xmlns:db="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" version="5.2" xml:lang="en">
+<db:info>
+<db:title></db:title>
+<db:productname>cmakedocumentation</db:productname>
+<db:titleabbrev>cmakedocumentation Reference Documentation</db:titleabbrev>
+<db:abstract>
+<db:para>This module was introduced in Qt 6.6.6.</db:para>
+</db:abstract>
+</db:info>
+<db:para>This module was introduced in Qt 6.6.6.</db:para>
+<db:section xml:id="classes">
+<db:title>Classes</db:title>
+<db:variablelist role="classes">
+<db:varlistentry>
+<db:term><db:link xlink:href="car.xml" xlink:role="class">Car</db:link></db:term>
+<db:listitem>
+<db:para>Represents a model of a car.</db:para>
+</db:listitem>
+</db:varlistentry>
+</db:variablelist>
+</db:section>
+</db:article>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/cmakedocumentation/expected/docbook/testcarprivate-module.xml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/cmakedocumentation/expected/docbook/testcarprivate-module.xml
new file mode 100644
index 000000000..f89a2679b
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/cmakedocumentation/expected/docbook/testcarprivate-module.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<db:article xmlns:db="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" version="5.2" xml:lang="en">
+<db:info>
+<db:title></db:title>
+<db:productname>cmakedocumentation</db:productname>
+<db:titleabbrev>cmakedocumentation Reference Documentation</db:titleabbrev>
+<db:abstract>
+<db:para>This module was introduced in Qt 6.6.6.</db:para>
+</db:abstract>
+</db:info>
+<db:para>This module was introduced in Qt 6.6.6.</db:para>
+<db:section xml:id="classes">
+<db:title>Classes</db:title>
+<db:variablelist role="classes">
+<db:varlistentry>
+<db:term><db:link xlink:href="engine.xml" xlink:role="class">Engine</db:link></db:term>
+<db:listitem>
+<db:para>Represents a model of an engine.</db:para>
+</db:listitem>
+</db:varlistentry>
+</db:variablelist>
+</db:section>
+</db:article>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/cmakedocumentation/expected/html/car.html b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/cmakedocumentation/expected/html/car.html
new file mode 100644
index 000000000..c7ab54dee
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/cmakedocumentation/expected/html/car.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+<!-- car.cpp -->
+ <meta name="description" content="Represents a model of a car.">
+ <title>Car Class | cmakedocumentation</title>
+</head>
+<body>
+<li>Car</li>
+<div class="sidebar">
+<div class="toc">
+<h3 id="toc">Contents</h3>
+<ul>
+<li class="level1"><a href="#details">Detailed Description</a></li>
+</ul>
+</div>
+<div class="sidebar-content" id="sidebar-content"></div></div>
+<h1 class="title" translate="no">Car Class</h1>
+<!-- $$$Car-brief -->
+<p>Represents a model of a car. <a href="#details">More...</a></p>
+<!-- @@@Car -->
+<div class="table"><table class="alignedsummary" translate="no">
+<tr><td class="memItemLeft rightAlign topAlign"> Header:</td><td class="memItemRight bottomAlign"> <span class="preprocessor">#include &lt;Car&gt;</span></td></tr>
+<tr><td class="memItemLeft rightAlign topAlign"> CMake:</td><td class="memItemRight bottomAlign"> find_package(Qt6 REQUIRED COMPONENTS TestCar) <br/>
+target_link_libraries(mytarget PRIVATE Qt6::TestCar)</td></tr>
+<tr><td class="memItemLeft rightAlign topAlign"> qmake:</td><td class="memItemRight bottomAlign"> QT += testcar</td></tr>
+<tr><td class="memItemLeft rightAlign topAlign"> Since:</td><td class="memItemRight bottomAlign"> Qt 6.6.6</td></tr>
+</table></div>
+<!-- $$$Car-description -->
+<div class="descr">
+<h2 id="details">Detailed Description</h2>
+</div>
+<!-- @@@Car -->
+</body>
+</html>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/cmakedocumentation/expected/html/cmakedocumentation.index b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/cmakedocumentation/expected/html/cmakedocumentation.index
new file mode 100644
index 000000000..87ef31650
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/cmakedocumentation/expected/html/cmakedocumentation.index
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE QDOCINDEX>
+<INDEX url="" title="cmakedocumentation Reference Documentation" version="" project="cmakedocumentation">
+ <namespace name="" status="active" access="public" module="cmakedocumentation">
+ <class name="Car" href="car.html" status="active" access="public" location="car.h" since="6.6.6" documented="true" module="TestCar" brief="Represents a model of a car">
+ <function name="drive" fullname="Car::drive" href="car.html#drive" status="active" access="private" location="car.h" documented="true" meta="plain" type="void" brief="Drives the car" signature="void drive()"/>
+ </class>
+ <class name="Engine" href="engine.html" status="active" access="public" location="car.h" since="6.6.6" documented="true" module="TestCarPrivate" brief="Represents a model of an engine">
+ <function name="start" fullname="Engine::start" href="engine.html#start" status="active" access="private" location="car.h" documented="true" meta="plain" type="void" brief="Starts the engine" signature="void start()"/>
+ <function name="stop" fullname="Engine::stop" href="engine.html#stop" status="active" access="private" location="car.h" documented="true" meta="plain" type="void" brief="Stops the engine" signature="void stop()"/>
+ </class>
+ <module name="TestCar" href="testcar-module.html" status="active" since="6.6.6" documented="true" seen="true" title=""/>
+ <module name="TestCarPrivate" href="testcarprivate-module.html" status="active" since="6.6.6" documented="true" seen="true" title=""/>
+ </namespace>
+</INDEX>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/cmakedocumentation/expected/html/engine.html b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/cmakedocumentation/expected/html/engine.html
new file mode 100644
index 000000000..36a2a97da
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/cmakedocumentation/expected/html/engine.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+<!-- car.cpp -->
+ <meta name="description" content="Represents a model of an engine.">
+ <title>Engine Class | cmakedocumentation</title>
+</head>
+<body>
+<li>Engine</li>
+<div class="sidebar">
+<div class="toc">
+<h3 id="toc">Contents</h3>
+<ul>
+<li class="level1"><a href="#details">Detailed Description</a></li>
+</ul>
+</div>
+<div class="sidebar-content" id="sidebar-content"></div></div>
+<h1 class="title" translate="no">Engine Class</h1>
+<!-- $$$Engine-brief -->
+<p>Represents a model of an engine. <a href="#details">More...</a></p>
+<!-- @@@Engine -->
+<div class="table"><table class="alignedsummary" translate="no">
+<tr><td class="memItemLeft rightAlign topAlign"> Header:</td><td class="memItemRight bottomAlign"> <span class="preprocessor">#include &lt;Engine&gt;</span></td></tr>
+<tr><td class="memItemLeft rightAlign topAlign"> CMake:</td><td class="memItemRight bottomAlign"> find_package(Qt6 REQUIRED COMPONENTS TestCar) <br/>
+target_link_libraries(mytarget PRIVATE Qt6::TestCarPrivate)</td></tr>
+<tr><td class="memItemLeft rightAlign topAlign"> qmake:</td><td class="memItemRight bottomAlign"> QT += testcar-private</td></tr>
+<tr><td class="memItemLeft rightAlign topAlign"> Since:</td><td class="memItemRight bottomAlign"> Qt 6.6.6</td></tr>
+</table></div>
+<!-- $$$Engine-description -->
+<div class="descr">
+<h2 id="details">Detailed Description</h2>
+</div>
+<!-- @@@Engine -->
+</body>
+</html>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/cmakedocumentation/expected/html/testcar-module.html b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/cmakedocumentation/expected/html/testcar-module.html
new file mode 100644
index 000000000..5b579cc8a
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/cmakedocumentation/expected/html/testcar-module.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+<!-- car.cpp -->
+ <title>cmakedocumentation</title>
+</head>
+<body>
+<div class="sidebar">
+<div class="toc">
+<h3 id="toc">Contents</h3>
+<ul>
+<li class="level1"><a href="#namespaces">Namespaces</a></li>
+<li class="level1"><a href="#classes">Classes</a></li>
+<li class="level1"><a href="#details">Detailed Description</a></li>
+</ul>
+</div>
+<div class="sidebar-content" id="sidebar-content"></div></div>
+<p>This module was introduced in Qt 6.6.6.</p>
+<h2 id="classes">Classes</h2>
+<div class="table"><table class="annotated">
+<tr class="odd topAlign"><td class="tblName" translate="no"><p><a href="car.html">Car</a></p></td><td class="tblDescr"><p>Represents a model of a car</p></td></tr>
+</table></div>
+<!-- $$$TestCar-description -->
+<div class="descr" id="details">
+</div>
+<!-- @@@TestCar -->
+</body>
+</html>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/cmakedocumentation/expected/html/testcarprivate-module.html b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/cmakedocumentation/expected/html/testcarprivate-module.html
new file mode 100644
index 000000000..762fe3193
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/cmakedocumentation/expected/html/testcarprivate-module.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+<!-- car.cpp -->
+ <title>cmakedocumentation</title>
+</head>
+<body>
+<div class="sidebar">
+<div class="toc">
+<h3 id="toc">Contents</h3>
+<ul>
+<li class="level1"><a href="#namespaces">Namespaces</a></li>
+<li class="level1"><a href="#classes">Classes</a></li>
+<li class="level1"><a href="#details">Detailed Description</a></li>
+</ul>
+</div>
+<div class="sidebar-content" id="sidebar-content"></div></div>
+<p>This module was introduced in Qt 6.6.6.</p>
+<h2 id="classes">Classes</h2>
+<div class="table"><table class="annotated">
+<tr class="odd topAlign"><td class="tblName" translate="no"><p><a href="engine.html">Engine</a></p></td><td class="tblDescr"><p>Represents a model of an engine</p></td></tr>
+</table></div>
+<!-- $$$TestCarPrivate-description -->
+<div class="descr" id="details">
+</div>
+<!-- @@@TestCarPrivate -->
+</body>
+</html>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/cmakedocumentation/expected/webxml/car.webxml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/cmakedocumentation/expected/webxml/car.webxml
new file mode 100644
index 000000000..76725762e
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/cmakedocumentation/expected/webxml/car.webxml
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<WebXML>
+ <document>
+ <class name="Car" href="car.html" status="active" access="public" location="car.h" since="6.6.6" documented="true" module="TestCar" brief="Represents a model of a car">
+ <description>
+ <brief>Represents a model of a car.</brief>
+ </description>
+ <function name="drive" fullname="Car::drive" href="car.html#drive" status="active" access="private" location="car.h" documented="true" meta="plain" type="void" brief="Drives the car" signature="void drive()">
+ <description>
+ <brief>Drives the car.</brief>
+ </description>
+ </function>
+ </class>
+ </document>
+</WebXML>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/cmakedocumentation/expected/webxml/cmakedocumentation.index b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/cmakedocumentation/expected/webxml/cmakedocumentation.index
new file mode 100644
index 000000000..87ef31650
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/cmakedocumentation/expected/webxml/cmakedocumentation.index
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE QDOCINDEX>
+<INDEX url="" title="cmakedocumentation Reference Documentation" version="" project="cmakedocumentation">
+ <namespace name="" status="active" access="public" module="cmakedocumentation">
+ <class name="Car" href="car.html" status="active" access="public" location="car.h" since="6.6.6" documented="true" module="TestCar" brief="Represents a model of a car">
+ <function name="drive" fullname="Car::drive" href="car.html#drive" status="active" access="private" location="car.h" documented="true" meta="plain" type="void" brief="Drives the car" signature="void drive()"/>
+ </class>
+ <class name="Engine" href="engine.html" status="active" access="public" location="car.h" since="6.6.6" documented="true" module="TestCarPrivate" brief="Represents a model of an engine">
+ <function name="start" fullname="Engine::start" href="engine.html#start" status="active" access="private" location="car.h" documented="true" meta="plain" type="void" brief="Starts the engine" signature="void start()"/>
+ <function name="stop" fullname="Engine::stop" href="engine.html#stop" status="active" access="private" location="car.h" documented="true" meta="plain" type="void" brief="Stops the engine" signature="void stop()"/>
+ </class>
+ <module name="TestCar" href="testcar-module.html" status="active" since="6.6.6" documented="true" seen="true" title=""/>
+ <module name="TestCarPrivate" href="testcarprivate-module.html" status="active" since="6.6.6" documented="true" seen="true" title=""/>
+ </namespace>
+</INDEX>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/cmakedocumentation/expected/webxml/engine.webxml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/cmakedocumentation/expected/webxml/engine.webxml
new file mode 100644
index 000000000..4eee94b89
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/cmakedocumentation/expected/webxml/engine.webxml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<WebXML>
+ <document>
+ <class name="Engine" href="engine.html" status="active" access="public" location="car.h" since="6.6.6" documented="true" module="TestCarPrivate" brief="Represents a model of an engine">
+ <description>
+ <brief>Represents a model of an engine.</brief>
+ </description>
+ <function name="start" fullname="Engine::start" href="engine.html#start" status="active" access="private" location="car.h" documented="true" meta="plain" type="void" brief="Starts the engine" signature="void start()">
+ <description>
+ <brief>Starts the engine.</brief>
+ </description>
+ </function>
+ <function name="stop" fullname="Engine::stop" href="engine.html#stop" status="active" access="private" location="car.h" documented="true" meta="plain" type="void" brief="Stops the engine" signature="void stop()">
+ <description>
+ <brief>Stops the engine.</brief>
+ </description>
+ </function>
+ </class>
+ </document>
+</WebXML>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/cmakedocumentation/expected/webxml/testcar-module.webxml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/cmakedocumentation/expected/webxml/testcar-module.webxml
new file mode 100644
index 000000000..5d24b3077
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/cmakedocumentation/expected/webxml/testcar-module.webxml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<WebXML>
+ <document/>
+</WebXML>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/cmakedocumentation/expected/webxml/testcarprivate-module.webxml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/cmakedocumentation/expected/webxml/testcarprivate-module.webxml
new file mode 100644
index 000000000..5d24b3077
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/cmakedocumentation/expected/webxml/testcarprivate-module.webxml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<WebXML>
+ <document/>
+</WebXML>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/cmakedocumentation/src/car.cpp b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/cmakedocumentation/src/car.cpp
new file mode 100644
index 000000000..78823d7e3
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/cmakedocumentation/src/car.cpp
@@ -0,0 +1,46 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+
+#include "car.h"
+
+/*!
+ \module TestCar
+ \since 6.6.6
+ \qtvariable testcar
+ \qtcmakepackage TestCar
+*/
+
+/*!
+ \module TestCarPrivate
+ \since 6.6.6
+ \qtvariable testcar-private
+ \qtcmakepackage TestCar
+ \qtcmaketargetitem TestCarPrivate
+*/
+
+/*!
+ \class Car
+ \inmodule TestCar
+ \brief Represents a model of a car.
+*/
+
+/*!
+ \class Engine
+ \inmodule TestCarPrivate
+ \brief Represents a model of an engine.
+*/
+
+/*!
+ \fn void Car::drive()
+ \brief Drives the car.
+*/
+
+/*!
+ \fn void Engine::start()
+ \brief Starts the engine.
+*/
+
+/*!
+ \fn void Engine::stop()
+ \brief Stops the engine.
+*/
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/cmakedocumentation/src/car.h b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/cmakedocumentation/src/car.h
new file mode 100644
index 000000000..c0203b07c
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/cmakedocumentation/src/car.h
@@ -0,0 +1,17 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+
+#ifndef CAR_H
+#define CAR_H
+
+class Car {
+ void drive() {};
+};
+
+class Engine {
+ void start() {};
+ void stop() {};
+};
+
+
+#endif // CAR_H
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/compiler_generated_member_functions/compiler_generated_member_functions.qdocconf b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/compiler_generated_member_functions/compiler_generated_member_functions.qdocconf
new file mode 100644
index 000000000..3a369e673
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/compiler_generated_member_functions/compiler_generated_member_functions.qdocconf
@@ -0,0 +1,23 @@
+project = Compiler generated member functions
+
+locationinfo = false
+
+headers.fileextensions = "*.h *.hpp"
+sources.fileextensions = "*.cpp *.qml *.qdoc"
+
+headerdirs = ./src
+sourcedirs = ./src
+
+warninglimit = 0
+warninglimit.enabled = true
+
+outputformats = WebXML HTML DocBook
+WebXML.quotinginformation = true
+WebXML.nosubdirs = true
+WebXML.outputsubdir = webxml
+
+HTML.nosubdirs = true
+HTML.outputsubdir = html
+
+DocBook.nosubdirs = true
+DocBook.outputsubdir = docbook
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/compiler_generated_member_functions/expected/docbook/qdoctests-compilergeneratedmemberfunctions.xml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/compiler_generated_member_functions/expected/docbook/qdoctests-compilergeneratedmemberfunctions.xml
new file mode 100644
index 000000000..43bf5cdca
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/compiler_generated_member_functions/expected/docbook/qdoctests-compilergeneratedmemberfunctions.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<db:article xmlns:db="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" version="5.2" xml:lang="en">
+<db:info>
+<db:title>CompilerGeneratedMemberFunctions Class</db:title>
+<db:subtitle>QDocTests::CompilerGeneratedMemberFunctions</db:subtitle>
+<db:productname>Compiler generated member functions</db:productname>
+<db:titleabbrev>Compiler generated member functions Reference Documentation</db:titleabbrev>
+<db:abstract>
+<db:para>A test class for compiler-generated member functions.</db:para>
+</db:abstract>
+</db:info>
+<db:variablelist>
+<db:varlistentry>
+<db:term>Header</db:term>
+<db:listitem>
+<db:para>CompilerGeneratedMemberFunctions</db:para>
+</db:listitem>
+</db:varlistentry>
+</db:variablelist>
+<db:section xml:id="details">
+<db:title>Detailed Description</db:title>
+<db:para>This class is used to test the documentation of compiler-generated member functions.</db:para>
+</db:section>
+<db:section xml:id="member-function-documentation">
+<db:title>Member Function Documentation</db:title>
+<db:section xml:id="CompilerGeneratedMemberFunctions">
+<db:title>[constexpr noexcept default] CompilerGeneratedMemberFunctions::CompilerGeneratedMemberFunctions()</db:title>
+<db:para>Constructs a <db:link xlink:href="qdoctests-compilergeneratedmemberfunctions.xml">CompilerGeneratedMemberFunctions</db:link> object.</db:para>
+</db:section>
+<db:section xml:id="CompilerGeneratedMemberFunctions-1">
+<db:title>[constexpr noexcept default] CompilerGeneratedMemberFunctions::CompilerGeneratedMemberFunctions(const QDocTests::CompilerGeneratedMemberFunctions &amp;<db:emphasis>other</db:emphasis>)</db:title>
+<db:para>Copy-constructs a <db:link xlink:href="qdoctests-compilergeneratedmemberfunctions.xml">CompilerGeneratedMemberFunctions</db:link> object from <db:code role="parameter">other</db:code>.</db:para>
+</db:section>
+<db:section xml:id="CompilerGeneratedMemberFunctions-2">
+<db:title>[constexpr noexcept default] CompilerGeneratedMemberFunctions::CompilerGeneratedMemberFunctions(const QDocTests::CompilerGeneratedMemberFunctions &amp;&amp;<db:emphasis>other</db:emphasis>)</db:title>
+<db:para>Move-constructs a <db:link xlink:href="qdoctests-compilergeneratedmemberfunctions.xml">CompilerGeneratedMemberFunctions</db:link> object from <db:code role="parameter">other</db:code>.</db:para>
+</db:section>
+</db:section>
+</db:article>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/compiler_generated_member_functions/expected/docbook/qdoctests-module.xml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/compiler_generated_member_functions/expected/docbook/qdoctests-module.xml
new file mode 100644
index 000000000..91f92c1c5
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/compiler_generated_member_functions/expected/docbook/qdoctests-module.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<db:article xmlns:db="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" version="5.2" xml:lang="en">
+<db:info>
+<db:title></db:title>
+<db:productname>Compiler generated member functions</db:productname>
+<db:titleabbrev>Compiler generated member functions Reference Documentation</db:titleabbrev>
+<db:abstract>
+<db:para>The <db:link xlink:href="qdoctests-module.xml">QDocTests</db:link> module contains test classes for QDoc.</db:para>
+</db:abstract>
+</db:info>
+<db:para>The <db:link xlink:href="qdoctests-module.xml">QDocTests</db:link> module contains test classes for QDoc.</db:para>
+<db:section xml:id="namespaces">
+<db:title>Namespaces</db:title>
+<db:variablelist role="namespaces">
+<db:varlistentry>
+<db:term><db:link xlink:href="qdoctests.xml" xlink:role="namespace">QDocTests</db:link></db:term>
+<db:listitem>
+<db:para>The QDocTests namespace contains test classes for QDoc.</db:para>
+</db:listitem>
+</db:varlistentry>
+</db:variablelist>
+</db:section>
+<db:section xml:id="classes">
+<db:title>Classes</db:title>
+<db:variablelist role="classes">
+<db:varlistentry>
+<db:term><db:link xlink:href="qdoctests-compilergeneratedmemberfunctions.xml" xlink:role="class">QDocTests::CompilerGeneratedMemberFunctions</db:link></db:term>
+<db:listitem>
+<db:para>A test class for compiler-generated member functions.</db:para>
+</db:listitem>
+</db:varlistentry>
+</db:variablelist>
+</db:section>
+<db:section xml:id="details">
+<db:title>Detailed Description</db:title>
+<db:para>The <db:link xlink:href="qdoctests-module.xml">QDocTests</db:link> module contains test classes for QDoc.</db:para>
+</db:section>
+</db:article>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/compiler_generated_member_functions/expected/docbook/qdoctests.xml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/compiler_generated_member_functions/expected/docbook/qdoctests.xml
new file mode 100644
index 000000000..04ad76f26
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/compiler_generated_member_functions/expected/docbook/qdoctests.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<db:article xmlns:db="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" version="5.2" xml:lang="en">
+<db:info>
+<db:title>QDocTests Namespace</db:title>
+<db:productname>Compiler generated member functions</db:productname>
+<db:titleabbrev>Compiler generated member functions Reference Documentation</db:titleabbrev>
+<db:abstract>
+<db:para>The <db:link xlink:href="qdoctests-module.xml">QDocTests</db:link> namespace contains test classes for QDoc.</db:para>
+</db:abstract>
+</db:info>
+<db:variablelist>
+<db:varlistentry>
+<db:term>Header</db:term>
+<db:listitem>
+<db:para>QDocTests</db:para>
+</db:listitem>
+</db:varlistentry>
+</db:variablelist>
+<db:section xml:id="details">
+<db:title>Detailed Description</db:title>
+<db:para>The <db:link xlink:href="qdoctests-module.xml">QDocTests</db:link> namespace contains test classes for QDoc.</db:para>
+</db:section>
+<db:section xml:id="classes">
+<db:title>Classes</db:title>
+<db:section>
+<db:title>class <db:link xlink:href="qdoctests-compilergeneratedmemberfunctions.xml" xlink:role="class">CompilerGeneratedMemberFunctions</db:link></db:title>
+<db:para>A test class for compiler-generated member functions.</db:para>
+</db:section>
+</db:section>
+</db:article>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/compiler_generated_member_functions/expected/html/compiler-generated-member-functions.index b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/compiler_generated_member_functions/expected/html/compiler-generated-member-functions.index
new file mode 100644
index 000000000..30a694078
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/compiler_generated_member_functions/expected/html/compiler-generated-member-functions.index
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE QDOCINDEX>
+<INDEX url="" title="Compiler generated member functions Reference Documentation" version="" project="Compiler generated member functions">
+ <namespace name="" status="active" access="public" module="compiler generated member functions">
+ <namespace name="QDocTests" href="qdoctests.html" status="active" access="public" location="compilergeneratedmemberfunctions.h" documented="true" module="QDocTests" brief="Contains test classes for QDoc">
+ <class name="CompilerGeneratedMemberFunctions" fullname="QDocTests::CompilerGeneratedMemberFunctions" href="qdoctests-compilergeneratedmemberfunctions.html" status="active" access="public" location="compilergeneratedmemberfunctions.h" documented="true" module="QDocTests" brief="A test class for compiler-generated member functions">
+ <function name="CompilerGeneratedMemberFunctions" fullname="QDocTests::CompilerGeneratedMemberFunctions::CompilerGeneratedMemberFunctions" href="qdoctests-compilergeneratedmemberfunctions.html#CompilerGeneratedMemberFunctions" status="active" access="public" documented="true" meta="constructor" constexpr="true" noexcept="true" signature="CompilerGeneratedMemberFunctions()"/>
+ <function name="CompilerGeneratedMemberFunctions" fullname="QDocTests::CompilerGeneratedMemberFunctions::CompilerGeneratedMemberFunctions" href="qdoctests-compilergeneratedmemberfunctions.html#CompilerGeneratedMemberFunctions-1" status="active" access="public" documented="true" meta="copy-constructor" constexpr="true" noexcept="true" overload="true" overload-number="1" signature="CompilerGeneratedMemberFunctions(const QDocTests::CompilerGeneratedMemberFunctions &amp;other)">
+ <parameter type="const QDocTests::CompilerGeneratedMemberFunctions &amp;" name="other" default=""/>
+ </function>
+ <function name="CompilerGeneratedMemberFunctions" fullname="QDocTests::CompilerGeneratedMemberFunctions::CompilerGeneratedMemberFunctions" href="qdoctests-compilergeneratedmemberfunctions.html#CompilerGeneratedMemberFunctions-2" status="active" access="public" documented="true" meta="move-constructor" constexpr="true" noexcept="true" overload="true" overload-number="2" signature="CompilerGeneratedMemberFunctions(const QDocTests::CompilerGeneratedMemberFunctions &amp;&amp;other)">
+ <parameter type="const QDocTests::CompilerGeneratedMemberFunctions &amp;&amp;" name="other" default=""/>
+ </function>
+ </class>
+ </namespace>
+ <module name="QDocTests" href="qdoctests-module.html" status="active" documented="true" seen="true" title="" brief="Module contains test classes for QDoc"/>
+ </namespace>
+</INDEX>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/compiler_generated_member_functions/expected/html/qdoctests-compilergeneratedmemberfunctions-members.html b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/compiler_generated_member_functions/expected/html/qdoctests-compilergeneratedmemberfunctions-members.html
new file mode 100644
index 000000000..825a2352a
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/compiler_generated_member_functions/expected/html/qdoctests-compilergeneratedmemberfunctions-members.html
@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+<!-- compilergeneratedmemberfunctions.cpp -->
+ <meta name="description" content="A test class for compiler-generated member functions.">
+ <title>List of All Members for CompilerGeneratedMemberFunctions | Compiler generated member functions</title>
+</head>
+<body>
+<li>CompilerGeneratedMemberFunctions</li>
+<div class="sidebar"><div class="sidebar-content" id="sidebar-content"></div></div>
+<h1 class="title" translate="no">List of All Members for CompilerGeneratedMemberFunctions</h1>
+<p>This is the complete list of members for <a href="qdoctests-compilergeneratedmemberfunctions.html">QDocTests::CompilerGeneratedMemberFunctions</a>, including inherited members.</p>
+<ul>
+<li class="fn" translate="no"><span class="name"><b><a href="qdoctests-compilergeneratedmemberfunctions.html#CompilerGeneratedMemberFunctions" translate="no">CompilerGeneratedMemberFunctions</a></b></span>()</li>
+<li class="fn" translate="no"><span class="name"><b><a href="qdoctests-compilergeneratedmemberfunctions.html#CompilerGeneratedMemberFunctions-1" translate="no">CompilerGeneratedMemberFunctions</a></b></span>(const QDocTests::CompilerGeneratedMemberFunctions &amp;)</li>
+<li class="fn" translate="no"><span class="name"><b><a href="qdoctests-compilergeneratedmemberfunctions.html#CompilerGeneratedMemberFunctions-2" translate="no">CompilerGeneratedMemberFunctions</a></b></span>(const QDocTests::CompilerGeneratedMemberFunctions &amp;&amp;)</li>
+</ul>
+</body>
+</html>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/compiler_generated_member_functions/expected/html/qdoctests-compilergeneratedmemberfunctions.html b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/compiler_generated_member_functions/expected/html/qdoctests-compilergeneratedmemberfunctions.html
new file mode 100644
index 000000000..6a4fe947f
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/compiler_generated_member_functions/expected/html/qdoctests-compilergeneratedmemberfunctions.html
@@ -0,0 +1,59 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+<!-- compilergeneratedmemberfunctions.cpp -->
+ <meta name="description" content="A test class for compiler-generated member functions.">
+ <title>CompilerGeneratedMemberFunctions Class | Compiler generated member functions</title>
+</head>
+<body>
+<li>CompilerGeneratedMemberFunctions</li>
+<div class="sidebar">
+<div class="toc">
+<h3 id="toc">Contents</h3>
+<ul>
+<li class="level1"><a href="#public-functions">Public Functions</a></li>
+<li class="level1"><a href="#details">Detailed Description</a></li>
+</ul>
+</div>
+<div class="sidebar-content" id="sidebar-content"></div></div>
+<h1 class="title" translate="no">CompilerGeneratedMemberFunctions Class</h1>
+<span class="small-subtitle" translate="no">class <a href="qdoctests-module.html" translate="no">QDocTests</a>::CompilerGeneratedMemberFunctions</span>
+<!-- $$$CompilerGeneratedMemberFunctions-brief -->
+<p>A test class for compiler-generated member functions. <a href="#details">More...</a></p>
+<!-- @@@CompilerGeneratedMemberFunctions -->
+<div class="table"><table class="alignedsummary" translate="no">
+<tr><td class="memItemLeft rightAlign topAlign"> Header:</td><td class="memItemRight bottomAlign"> <span class="preprocessor">#include &lt;CompilerGeneratedMemberFunctions&gt;</span></td></tr>
+</table></div>
+<ul>
+<li><a href="qdoctests-compilergeneratedmemberfunctions-members.html">List of all members, including inherited members</a></li>
+</ul>
+<h2 id="public-functions">Public Functions</h2>
+<div class="table"><table class="alignedsummary" translate="no">
+<tr><td class="memItemLeft rightAlign topAlign"> </td><td class="memItemRight bottomAlign"><b><a href="qdoctests-compilergeneratedmemberfunctions.html#CompilerGeneratedMemberFunctions" translate="no">CompilerGeneratedMemberFunctions</a></b>()</td></tr>
+<tr><td class="memItemLeft rightAlign topAlign"> </td><td class="memItemRight bottomAlign"><b><a href="qdoctests-compilergeneratedmemberfunctions.html#CompilerGeneratedMemberFunctions-1" translate="no">CompilerGeneratedMemberFunctions</a></b>(const QDocTests::CompilerGeneratedMemberFunctions &amp;<i>other</i>)</td></tr>
+<tr><td class="memItemLeft rightAlign topAlign"> </td><td class="memItemRight bottomAlign"><b><a href="qdoctests-compilergeneratedmemberfunctions.html#CompilerGeneratedMemberFunctions-2" translate="no">CompilerGeneratedMemberFunctions</a></b>(const QDocTests::CompilerGeneratedMemberFunctions &amp;&amp;<i>other</i>)</td></tr>
+</table></div>
+<!-- $$$CompilerGeneratedMemberFunctions-description -->
+<div class="descr">
+<h2 id="details">Detailed Description</h2>
+<p>This class is used to test the documentation of compiler-generated member functions.</p>
+</div>
+<!-- @@@CompilerGeneratedMemberFunctions -->
+<div class="func">
+<h2>Member Function Documentation</h2>
+<!-- $$$CompilerGeneratedMemberFunctions[overload1]$$$CompilerGeneratedMemberFunctions -->
+<h3 class="fn" translate="no" id="CompilerGeneratedMemberFunctions"><code class="details extra" translate="no">[constexpr noexcept default]</code> CompilerGeneratedMemberFunctions::<span class="name">CompilerGeneratedMemberFunctions</span>()</h3>
+<p>Constructs a CompilerGeneratedMemberFunctions object.</p>
+<!-- @@@CompilerGeneratedMemberFunctions -->
+<!-- $$$CompilerGeneratedMemberFunctions$$$CompilerGeneratedMemberFunctionsconstQDocTests::CompilerGeneratedMemberFunctions& -->
+<h3 class="fn" translate="no" id="CompilerGeneratedMemberFunctions-1"><code class="details extra" translate="no">[constexpr noexcept default]</code> CompilerGeneratedMemberFunctions::<span class="name">CompilerGeneratedMemberFunctions</span>(const <span class="type"><a href="qdoctests-compilergeneratedmemberfunctions.html" translate="no">QDocTests::CompilerGeneratedMemberFunctions</a></span> &amp;<i>other</i>)</h3>
+<p>Copy-constructs a CompilerGeneratedMemberFunctions object from <i translate="no">other</i>.</p>
+<!-- @@@CompilerGeneratedMemberFunctions -->
+<!-- $$$CompilerGeneratedMemberFunctions$$$CompilerGeneratedMemberFunctionsconstQDocTests::CompilerGeneratedMemberFunctions&& -->
+<h3 class="fn" translate="no" id="CompilerGeneratedMemberFunctions-2"><code class="details extra" translate="no">[constexpr noexcept default]</code> CompilerGeneratedMemberFunctions::<span class="name">CompilerGeneratedMemberFunctions</span>(const <span class="type"><a href="qdoctests-compilergeneratedmemberfunctions.html" translate="no">QDocTests::CompilerGeneratedMemberFunctions</a></span> &amp;&amp;<i>other</i>)</h3>
+<p>Move-constructs a CompilerGeneratedMemberFunctions object from <i translate="no">other</i>.</p>
+<!-- @@@CompilerGeneratedMemberFunctions -->
+</div>
+</body>
+</html>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/compiler_generated_member_functions/expected/html/qdoctests-module.html b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/compiler_generated_member_functions/expected/html/qdoctests-module.html
new file mode 100644
index 000000000..d85654134
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/compiler_generated_member_functions/expected/html/qdoctests-module.html
@@ -0,0 +1,38 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+<!-- compilergeneratedmemberfunctions.cpp -->
+ <meta name="description" content="The QDocTests module contains test classes for QDoc.">
+ <title>Compiler generated member functions</title>
+</head>
+<body>
+<div class="sidebar">
+<div class="toc">
+<h3 id="toc">Contents</h3>
+<ul>
+<li class="level1"><a href="#namespaces">Namespaces</a></li>
+<li class="level1"><a href="#classes">Classes</a></li>
+<li class="level1"><a href="#details">Detailed Description</a></li>
+</ul>
+</div>
+<div class="sidebar-content" id="sidebar-content"></div></div>
+<!-- $$$QDocTests-brief -->
+<p>The QDocTests module contains test classes for QDoc. <a href="#details">More...</a></p>
+<!-- @@@QDocTests -->
+<h2 id="namespaces">Namespaces</h2>
+<div class="table"><table class="annotated">
+<tr class="odd topAlign"><td class="tblName" translate="no"><p><a href="qdoctests.html">QDocTests</a></p></td><td class="tblDescr"><p>Contains test classes for QDoc</p></td></tr>
+</table></div>
+<h2 id="classes">Classes</h2>
+<div class="table"><table class="annotated">
+<tr class="odd topAlign"><td class="tblName" translate="no"><p><a href="qdoctests-compilergeneratedmemberfunctions.html">QDocTests::CompilerGeneratedMemberFunctions</a></p></td><td class="tblDescr"><p>A test class for compiler-generated member functions</p></td></tr>
+</table></div>
+<!-- $$$QDocTests-description -->
+<div class="descr">
+<h2 id="details">Detailed Description</h2>
+<p>The QDocTests module contains test classes for QDoc.</p>
+</div>
+<!-- @@@QDocTests -->
+</body>
+</html>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/compiler_generated_member_functions/expected/html/qdoctests.html b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/compiler_generated_member_functions/expected/html/qdoctests.html
new file mode 100644
index 000000000..2f6741fa0
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/compiler_generated_member_functions/expected/html/qdoctests.html
@@ -0,0 +1,43 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+<!-- compilergeneratedmemberfunctions.cpp -->
+ <meta name="description" content="The QDocTests namespace contains test classes for QDoc.">
+ <title>QDocTests Namespace | Compiler generated member functions</title>
+</head>
+<body>
+<div class="sidebar">
+<div class="toc">
+<h3 id="toc">Contents</h3>
+<ul>
+<li class="level1"><a href="#classes">Classes</a></li>
+<li class="level1"><a href="#details">Detailed Description</a></li>
+</ul>
+</div>
+<div class="sidebar-content" id="sidebar-content"></div></div>
+<h1 class="title" translate="no">QDocTests Namespace</h1>
+<!-- $$$QDocTests-brief -->
+<p>The QDocTests namespace contains test classes for QDoc. <a href="#details">More...</a></p>
+<!-- @@@QDocTests -->
+<div class="table"><table class="alignedsummary" translate="no">
+<tr><td class="memItemLeft rightAlign topAlign"> Header:</td><td class="memItemRight bottomAlign"> <span class="preprocessor">#include &lt;QDocTests&gt;</span></td></tr>
+</table></div>
+<h2 id="classes">Classes</h2>
+<div class="table"><table class="alignedsummary" translate="no">
+<tr><td class="memItemLeft rightAlign topAlign"> class </td><td class="memItemRight bottomAlign"><b><a href="qdoctests-compilergeneratedmemberfunctions.html" translate="no">CompilerGeneratedMemberFunctions</a></b></td></tr>
+</table></div>
+<!-- $$$QDocTests-description -->
+<div class="descr">
+<h2 id="details">Detailed Description</h2>
+<p>The QDocTests namespace contains test classes for QDoc.</p>
+</div>
+<!-- @@@QDocTests -->
+<div class="classes">
+<h2>Classes</h2>
+<h3> class <a href="qdoctests-compilergeneratedmemberfunctions.html">CompilerGeneratedMemberFunctions</a></h3><!-- $$$CompilerGeneratedMemberFunctions-brief -->
+<p>A test class for compiler-generated member functions. <a href="qdoctests-compilergeneratedmemberfunctions.html#details">More...</a></p>
+<!-- @@@CompilerGeneratedMemberFunctions -->
+</div>
+</body>
+</html>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/compiler_generated_member_functions/expected/webxml/compiler-generated-member-functions.index b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/compiler_generated_member_functions/expected/webxml/compiler-generated-member-functions.index
new file mode 100644
index 000000000..30a694078
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/compiler_generated_member_functions/expected/webxml/compiler-generated-member-functions.index
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE QDOCINDEX>
+<INDEX url="" title="Compiler generated member functions Reference Documentation" version="" project="Compiler generated member functions">
+ <namespace name="" status="active" access="public" module="compiler generated member functions">
+ <namespace name="QDocTests" href="qdoctests.html" status="active" access="public" location="compilergeneratedmemberfunctions.h" documented="true" module="QDocTests" brief="Contains test classes for QDoc">
+ <class name="CompilerGeneratedMemberFunctions" fullname="QDocTests::CompilerGeneratedMemberFunctions" href="qdoctests-compilergeneratedmemberfunctions.html" status="active" access="public" location="compilergeneratedmemberfunctions.h" documented="true" module="QDocTests" brief="A test class for compiler-generated member functions">
+ <function name="CompilerGeneratedMemberFunctions" fullname="QDocTests::CompilerGeneratedMemberFunctions::CompilerGeneratedMemberFunctions" href="qdoctests-compilergeneratedmemberfunctions.html#CompilerGeneratedMemberFunctions" status="active" access="public" documented="true" meta="constructor" constexpr="true" noexcept="true" signature="CompilerGeneratedMemberFunctions()"/>
+ <function name="CompilerGeneratedMemberFunctions" fullname="QDocTests::CompilerGeneratedMemberFunctions::CompilerGeneratedMemberFunctions" href="qdoctests-compilergeneratedmemberfunctions.html#CompilerGeneratedMemberFunctions-1" status="active" access="public" documented="true" meta="copy-constructor" constexpr="true" noexcept="true" overload="true" overload-number="1" signature="CompilerGeneratedMemberFunctions(const QDocTests::CompilerGeneratedMemberFunctions &amp;other)">
+ <parameter type="const QDocTests::CompilerGeneratedMemberFunctions &amp;" name="other" default=""/>
+ </function>
+ <function name="CompilerGeneratedMemberFunctions" fullname="QDocTests::CompilerGeneratedMemberFunctions::CompilerGeneratedMemberFunctions" href="qdoctests-compilergeneratedmemberfunctions.html#CompilerGeneratedMemberFunctions-2" status="active" access="public" documented="true" meta="move-constructor" constexpr="true" noexcept="true" overload="true" overload-number="2" signature="CompilerGeneratedMemberFunctions(const QDocTests::CompilerGeneratedMemberFunctions &amp;&amp;other)">
+ <parameter type="const QDocTests::CompilerGeneratedMemberFunctions &amp;&amp;" name="other" default=""/>
+ </function>
+ </class>
+ </namespace>
+ <module name="QDocTests" href="qdoctests-module.html" status="active" documented="true" seen="true" title="" brief="Module contains test classes for QDoc"/>
+ </namespace>
+</INDEX>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/compiler_generated_member_functions/expected/webxml/qdoctests-compilergeneratedmemberfunctions.webxml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/compiler_generated_member_functions/expected/webxml/qdoctests-compilergeneratedmemberfunctions.webxml
new file mode 100644
index 000000000..2baf5256e
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/compiler_generated_member_functions/expected/webxml/qdoctests-compilergeneratedmemberfunctions.webxml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<WebXML>
+ <document>
+ <class name="CompilerGeneratedMemberFunctions" fullname="QDocTests::CompilerGeneratedMemberFunctions" href="qdoctests-compilergeneratedmemberfunctions.html" status="active" access="public" location="compilergeneratedmemberfunctions.h" documented="true" module="QDocTests" brief="A test class for compiler-generated member functions">
+ <description>
+ <brief>A test class for compiler-generated member functions.</brief>
+ <para>This class is used to test the documentation of compiler-generated member functions.</para>
+ </description>
+ <function name="CompilerGeneratedMemberFunctions" fullname="QDocTests::CompilerGeneratedMemberFunctions::CompilerGeneratedMemberFunctions" href="qdoctests-compilergeneratedmemberfunctions.html#CompilerGeneratedMemberFunctions" status="active" access="public" documented="true" meta="constructor" constexpr="true" noexcept="true" signature="CompilerGeneratedMemberFunctions()">
+ <description>
+ <para>Constructs a <link raw="CompilerGeneratedMemberFunctions" href="qdoctests-compilergeneratedmemberfunctions.html" type="class">CompilerGeneratedMemberFunctions</link> object.</para>
+ </description>
+ </function>
+ <function name="CompilerGeneratedMemberFunctions" fullname="QDocTests::CompilerGeneratedMemberFunctions::CompilerGeneratedMemberFunctions" href="qdoctests-compilergeneratedmemberfunctions.html#CompilerGeneratedMemberFunctions-1" status="active" access="public" documented="true" meta="copy-constructor" constexpr="true" noexcept="true" overload="true" overload-number="1" signature="CompilerGeneratedMemberFunctions(const QDocTests::CompilerGeneratedMemberFunctions &amp;other)">
+ <parameter type="const QDocTests::CompilerGeneratedMemberFunctions &amp;" name="other" default=""/>
+ <description>
+ <para>Copy-constructs a <link raw="CompilerGeneratedMemberFunctions" href="qdoctests-compilergeneratedmemberfunctions.html" type="class">CompilerGeneratedMemberFunctions</link> object from <argument>other</argument>.</para>
+ </description>
+ </function>
+ <function name="CompilerGeneratedMemberFunctions" fullname="QDocTests::CompilerGeneratedMemberFunctions::CompilerGeneratedMemberFunctions" href="qdoctests-compilergeneratedmemberfunctions.html#CompilerGeneratedMemberFunctions-2" status="active" access="public" documented="true" meta="move-constructor" constexpr="true" noexcept="true" overload="true" overload-number="2" signature="CompilerGeneratedMemberFunctions(const QDocTests::CompilerGeneratedMemberFunctions &amp;&amp;other)">
+ <parameter type="const QDocTests::CompilerGeneratedMemberFunctions &amp;&amp;" name="other" default=""/>
+ <description>
+ <para>Move-constructs a <link raw="CompilerGeneratedMemberFunctions" href="qdoctests-compilergeneratedmemberfunctions.html" type="class">CompilerGeneratedMemberFunctions</link> object from <argument>other</argument>.</para>
+ </description>
+ </function>
+ </class>
+ </document>
+</WebXML>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/compiler_generated_member_functions/expected/webxml/qdoctests-module.webxml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/compiler_generated_member_functions/expected/webxml/qdoctests-module.webxml
new file mode 100644
index 000000000..5d24b3077
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/compiler_generated_member_functions/expected/webxml/qdoctests-module.webxml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<WebXML>
+ <document/>
+</WebXML>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/compiler_generated_member_functions/expected/webxml/qdoctests.webxml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/compiler_generated_member_functions/expected/webxml/qdoctests.webxml
new file mode 100644
index 000000000..0485b5efd
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/compiler_generated_member_functions/expected/webxml/qdoctests.webxml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<WebXML>
+ <document>
+ <namespace name="QDocTests" href="qdoctests.html" status="active" access="public" location="compilergeneratedmemberfunctions.h" documented="true" module="QDocTests" brief="Contains test classes for QDoc">
+ <description>
+ <brief>The <link raw="QDocTests" href="qdoctests-module.html" type="module">QDocTests</link> namespace contains test classes for QDoc.</brief>
+ <para>The <link raw="QDocTests" href="qdoctests-module.html" type="module">QDocTests</link> namespace contains test classes for QDoc.</para>
+ </description>
+ <class name="CompilerGeneratedMemberFunctions" fullname="QDocTests::CompilerGeneratedMemberFunctions" href="qdoctests-compilergeneratedmemberfunctions.html" status="active" access="public" location="compilergeneratedmemberfunctions.h" documented="true" module="QDocTests" brief="A test class for compiler-generated member functions">
+ <description>
+ <brief>A test class for compiler-generated member functions.</brief>
+ <para>This class is used to test the documentation of compiler-generated member functions.</para>
+ </description>
+ <function name="CompilerGeneratedMemberFunctions" fullname="QDocTests::CompilerGeneratedMemberFunctions::CompilerGeneratedMemberFunctions" href="qdoctests-compilergeneratedmemberfunctions.html#CompilerGeneratedMemberFunctions" status="active" access="public" documented="true" meta="constructor" constexpr="true" noexcept="true" signature="CompilerGeneratedMemberFunctions()">
+ <description>
+ <para>Constructs a <link raw="CompilerGeneratedMemberFunctions" href="qdoctests-compilergeneratedmemberfunctions.html" type="class">CompilerGeneratedMemberFunctions</link> object.</para>
+ </description>
+ </function>
+ <function name="CompilerGeneratedMemberFunctions" fullname="QDocTests::CompilerGeneratedMemberFunctions::CompilerGeneratedMemberFunctions" href="qdoctests-compilergeneratedmemberfunctions.html#CompilerGeneratedMemberFunctions-1" status="active" access="public" documented="true" meta="copy-constructor" constexpr="true" noexcept="true" overload="true" overload-number="1" signature="CompilerGeneratedMemberFunctions(const QDocTests::CompilerGeneratedMemberFunctions &amp;other)">
+ <parameter type="const QDocTests::CompilerGeneratedMemberFunctions &amp;" name="other" default=""/>
+ <description>
+ <para>Copy-constructs a <link raw="CompilerGeneratedMemberFunctions" href="qdoctests-compilergeneratedmemberfunctions.html" type="class">CompilerGeneratedMemberFunctions</link> object from <argument>other</argument>.</para>
+ </description>
+ </function>
+ <function name="CompilerGeneratedMemberFunctions" fullname="QDocTests::CompilerGeneratedMemberFunctions::CompilerGeneratedMemberFunctions" href="qdoctests-compilergeneratedmemberfunctions.html#CompilerGeneratedMemberFunctions-2" status="active" access="public" documented="true" meta="move-constructor" constexpr="true" noexcept="true" overload="true" overload-number="2" signature="CompilerGeneratedMemberFunctions(const QDocTests::CompilerGeneratedMemberFunctions &amp;&amp;other)">
+ <parameter type="const QDocTests::CompilerGeneratedMemberFunctions &amp;&amp;" name="other" default=""/>
+ <description>
+ <para>Move-constructs a <link raw="CompilerGeneratedMemberFunctions" href="qdoctests-compilergeneratedmemberfunctions.html" type="class">CompilerGeneratedMemberFunctions</link> object from <argument>other</argument>.</para>
+ </description>
+ </function>
+ </class>
+ </namespace>
+ </document>
+</WebXML>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/compiler_generated_member_functions/src/compilergeneratedmemberfunctions.cpp b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/compiler_generated_member_functions/src/compilergeneratedmemberfunctions.cpp
new file mode 100644
index 000000000..fb846fb0c
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/compiler_generated_member_functions/src/compilergeneratedmemberfunctions.cpp
@@ -0,0 +1,45 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#include "compilergeneratedmemberfunctions.h"
+
+/*!
+ \module QDocTests
+ \brief The QDocTests module contains test classes for QDoc.
+
+ The QDocTests module contains test classes for QDoc.
+*/
+
+/*!
+ \namespace QDocTests
+ \inmodule QDocTests
+ \brief The QDocTests namespace contains test classes for QDoc.
+
+ The QDocTests namespace contains test classes for QDoc.
+*/
+
+/*!
+ \class QDocTests::CompilerGeneratedMemberFunctions
+ \inmodule QDocTests
+ \brief A test class for compiler-generated member functions.
+
+ This class is used to test the documentation of compiler-generated member functions.
+*/
+
+/*!
+ \fn constexpr QDocTests::CompilerGeneratedMemberFunctions::CompilerGeneratedMemberFunctions() noexcept
+
+ Constructs a CompilerGeneratedMemberFunctions object.
+*/
+
+/*!
+ \fn constexpr QDocTests::CompilerGeneratedMemberFunctions::CompilerGeneratedMemberFunctions(const CompilerGeneratedMemberFunctions &other) noexcept
+
+ Copy-constructs a CompilerGeneratedMemberFunctions object from \a other.
+*/
+
+/*!
+ \fn constexpr QDocTests::CompilerGeneratedMemberFunctions::CompilerGeneratedMemberFunctions(const CompilerGeneratedMemberFunctions &&other) noexcept
+
+ Move-constructs a CompilerGeneratedMemberFunctions object from \a other.
+*/
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/compiler_generated_member_functions/src/compilergeneratedmemberfunctions.h b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/compiler_generated_member_functions/src/compilergeneratedmemberfunctions.h
new file mode 100644
index 000000000..fce05b727
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/compiler_generated_member_functions/src/compilergeneratedmemberfunctions.h
@@ -0,0 +1,20 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#ifndef COMPILERGENERATEDMEMBERFUNCTIONS_H
+#define COMPILERGENERATEDMEMBERFUNCTIONS_H
+
+namespace QDocTests {
+
+class CompilerGeneratedMemberFunctions {
+public:
+ int number() const { return m_number; }
+
+private:
+ int m_number {42};
+
+};
+
+} // QDocTests
+
+#endif // COMPILERGENERATEDMEMBERFUNCTIONS_H
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/comprehensiveproject.qdocconf b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/comprehensiveproject.qdocconf
new file mode 100644
index 000000000..e6751e2b7
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/comprehensiveproject.qdocconf
@@ -0,0 +1,105 @@
+sources.fileextensions = "*.qml *.cpp *.qdoc"
+headers.fileextensions = "*.h"
+
+# images
+imagedirs = ./src/images
+
+# zero warning policy
+warninglimit = 0
+warninglimit.enabled = true
+
+# don't write host system-specific paths to index files
+locationinfo = false
+
+outputformats = HTML WebXML DocBook
+HTML.nosubdirs = true
+HTML.outputsubdir = html
+WebXML.nosubdirs = true
+WebXML.outputsubdir = webxml
+DocBook.nosubdirs = true
+DocBook.outputsubdir = docbook
+DocBook.its = true
+DocBook.usedocbookextensions = true
+
+includepaths += -I./src
+
+headerdirs = ./src \
+ ./src/qml
+sourcedirs = ./src \
+ ./src/qml
+exampledirs = ./src/snippets \
+ ./src/qml
+
+macro.CMDFN = \\\\fn
+macro.nothing = \\dontdocument ()
+macro.testnoautolist = \\if defined(test_noautolist)\n\\noautolist\n\\endif
+
+navigation.cppclassespage = "QDoc Test C++ Classes"
+
+defines += test_ignoresince
+defines += test_nestedmacro
+defines += test_properties
+
+macro.ver = "\1"
+macro.ver.match = "^(\\d+\\.\\d+)"
+macro.versionnote.HTML = "<p><b>This \1 was introduced in version \2.</b></p>\n"
+macro.versionnote.DocBook = "<db:para>This \1 was introduced in version \2.</db:para>\n"
+
+moduleheader = TestCPP
+
+project = Test
+description = "A test project for QDoc build artifacts"
+
+examples.fileextensions = "*.qml *.cpp"
+
+macro.begincomment = "\\c{/*}"
+macro.QDocTestVer = "1.1"
+
+navigation.qmltypespage = "QDoc.Test QML Module"
+navigation.qmltypestitle = "Types"
+warninglimit += 1
+
+manifestmeta.examplecategories = "Application Examples" \
+ "Desktop" \
+ "Mobile" \
+ "Embedded"
+tagfile = testtagfile.tags
+version = 6.2.11
+
+examplesinstallpath = test
+
+exampledirs += ./src/examples
+
+excludedirs += ./src/examples/demos/demo/excludes
+
+# Configure .qhp generation
+qhp.projects = Test
+
+qhp.Test.file = test.qhp
+qhp.Test.namespace = org.qt-project.test.001
+qhp.Test.virtualFolder = test
+qhp.Test.indexTitle = UI Components
+qhp.Test.indexRoot =
+
+qhp.Test.subprojects = test classes qmltypes undefined
+qhp.Test.subprojects.test.title = Test
+qhp.Test.subprojects.test.indexTitle = UI Components
+qhp.Test.subprojects.test.selectors = doc:page fake:example module qmlmodule
+qhp.Test.subprojects.test.sortPages = true
+
+qhp.Test.subprojects.classes.title = Classes
+qhp.Test.subprojects.classes.indexTitle = QDoc Test C++ Classes
+qhp.Test.subprojects.classes.selectors = class namespace doc:headerfile boop:whatever
+qhp.Test.subprojects.classes.sortPages = true
+
+qhp.Test.subprojects.qmltypes.title = QML Types
+qhp.Test.subprojects.qmltypes.indexTitle = UI Components
+qhp.Test.subprojects.qmltypes.selectors = qmlmodule:UIComponents,QDoc.Test
+qhp.Test.subprojects.qmltypes.sortPages = true
+
+# Add some meta-data to the example
+manifestmeta.filters = test
+
+manifestmeta.test.names = *
+manifestmeta.test.attributes = isTest:true
+manifestmeta.test.tags = test
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/docbook/autolinking.xml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/docbook/autolinking.xml
new file mode 100644
index 000000000..1fde6fac7
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/docbook/autolinking.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<db:article xmlns:db="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:its="http://www.w3.org/2005/11/its" version="5.2" xml:lang="en">
+<db:info>
+<db:title>Autolinking</db:title>
+<db:productname>Test</db:productname>
+<db:titleabbrev>A test project for QDoc build artifacts</db:titleabbrev>
+<db:abstract>
+<db:para>A test project for QDoc build artifacts.</db:para></db:abstract>
+</db:info>
+<db:section xml:id="testqdoc">
+<db:title>TestQDoc</db:title>
+<db:para>The string <db:link xlink:href="testqdoc.xml">TestQDoc</db:link> links to the C++ namespace unless linking explicitly, <db:link xlink:href="autolinking.xml#testqdoc">like this</db:link>, or <db:link xlink:href="testqdoc.xml">this</db:link>. Also,</db:para>
+<db:para>Autolinks:</db:para>
+<db:itemizedlist>
+<db:listitem>
+<db:para><db:link xlink:href="testqdoc-testderived.xml">TestQDoc::TestDerived</db:link></db:para>
+</db:listitem>
+</db:itemizedlist>
+<db:para>Explicit links:</db:para>
+<db:itemizedlist>
+<db:listitem>
+<db:para><db:link xlink:href="testqdoc-testderived.xml">TestQDoc::TestDerived</db:link></db:para>
+</db:listitem>
+<db:listitem>
+<db:para><db:link xlink:href="obsolete-classes.xml#testqdoc">Obsolete Classes#TestQDoc</db:link></db:para>
+</db:listitem>
+</db:itemizedlist>
+</db:section>
+<db:section xml:id="someprop">
+<db:title>someProp</db:title>
+</db:section>
+</db:article>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/docbook/classes.xml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/docbook/classes.xml
new file mode 100644
index 000000000..68d7a4616
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/docbook/classes.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<db:article xmlns:db="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:its="http://www.w3.org/2005/11/its" version="5.2" xml:lang="en">
+<db:info>
+<db:title>Classes</db:title>
+<db:productname>Test</db:productname>
+<db:titleabbrev>A test project for QDoc build artifacts</db:titleabbrev>
+<db:abstract>
+<db:para>A test project for QDoc build artifacts.</db:para></db:abstract>
+</db:info>
+<db:variablelist role="annotatedclasses">
+<db:varlistentry>
+<db:term><db:link xlink:href="seenclass.xml" xlink:role="class">SeenClass</db:link></db:term>
+<db:listitem>
+<db:para>A public but undocumented class.</db:para>
+</db:listitem>
+</db:varlistentry>
+<db:varlistentry>
+<db:term><db:link xlink:href="testqdoc-test.xml" xlink:role="class">TestQDoc::Test</db:link></db:term>
+<db:listitem>
+<db:para>A class in a namespace.</db:para>
+</db:listitem>
+</db:varlistentry>
+<db:varlistentry>
+<db:term><db:link xlink:href="testqdoc-testderived.xml" xlink:role="class">TestQDoc::TestDerived</db:link></db:term>
+<db:listitem>
+<db:para>A class in a namespace, derived from Test.</db:para>
+</db:listitem>
+</db:varlistentry>
+</db:variablelist>
+</db:article>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/docbook/cpptypes.xml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/docbook/cpptypes.xml
new file mode 100644
index 000000000..727d59fd0
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/docbook/cpptypes.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<db:article xmlns:db="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:its="http://www.w3.org/2005/11/its" version="5.2" xml:lang="en">
+<db:info>
+<db:title>Test C++ Types</db:title>
+<db:productname>Test</db:productname>
+<db:titleabbrev>A test project for QDoc build artifacts</db:titleabbrev>
+<db:abstract>
+<db:para>A test project for QDoc build artifacts.</db:para></db:abstract>
+</db:info>
+<db:anchor xml:id="details"/>
+<db:itemizedlist role="testgroup">
+<db:listitem>
+<db:para><db:link xlink:href="testqdoc-test.xml" xlink:role="class">TestQDoc::Test</db:link></db:para>
+</db:listitem>
+<db:listitem>
+<db:para><db:link xlink:href="testqdoc-test.xml#QDOCTEST_MACRO2" xlink:role="function">TestQDoc::Test::QDOCTEST_MACRO2</db:link></db:para>
+</db:listitem>
+<db:listitem>
+<db:para><db:link xlink:href="testqdoc-test.xml#operator-eq" xlink:role="function">TestQDoc::Test::operator=()</db:link></db:para>
+</db:listitem>
+<db:listitem>
+<db:para><db:link xlink:href="testqdoc-test.xml#someFunctionDefaultArg" xlink:role="function">TestQDoc::Test::someFunctionDefaultArg()</db:link></db:para>
+</db:listitem>
+</db:itemizedlist>
+<db:variablelist role="members">
+<db:varlistentry>
+<db:term><db:link xlink:href="testqdoc-test.xml" xlink:role="class">TestQDoc::Test</db:link></db:term>
+<db:listitem>
+<db:para>A class in a namespace.</db:para>
+</db:listitem>
+</db:varlistentry>
+</db:variablelist>
+</db:article>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/docbook/crossmoduleref.xml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/docbook/crossmoduleref.xml
new file mode 100644
index 000000000..19e6e28f2
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/docbook/crossmoduleref.xml
@@ -0,0 +1,59 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<db:article xmlns:db="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:its="http://www.w3.org/2005/11/its" version="5.2" xml:lang="en">
+<db:info>
+<db:title its:translate="no">CrossModuleRef Namespace</db:title>
+<db:productname>Test</db:productname>
+<db:titleabbrev>A test project for QDoc build artifacts</db:titleabbrev>
+<db:abstract>
+<db:para>Namespace that has documented functions in multiple modules.</db:para>
+<db:para>This namespace was introduced in Qt 3.0.</db:para>
+</db:abstract>
+</db:info>
+<db:variablelist its:translate="no">
+<db:varlistentry>
+<db:term>Header</db:term>
+<db:listitem>
+<db:para>CrossModuleRef</db:para>
+</db:listitem>
+</db:varlistentry>
+<db:varlistentry>
+<db:term>Since</db:term>
+<db:listitem>
+<db:para>Qt 3.0</db:para>
+</db:listitem>
+</db:varlistentry>
+<db:varlistentry>
+<db:term>CMake</db:term>
+<db:listitem>
+<db:para>find_package(Qt6 REQUIRED COMPONENTS QDocTest)</db:para>
+<db:para>target_link_libraries(mytarget PRIVATE Qt6::QDocTest)</db:para>
+</db:listitem>
+</db:varlistentry>
+<db:varlistentry>
+<db:term>qmake</db:term>
+<db:listitem>
+<db:para>QT += testcpp</db:para>
+</db:listitem>
+</db:varlistentry>
+</db:variablelist>
+<db:section xml:id="details">
+<db:title>Detailed Description</db:title>
+</db:section>
+<db:section xml:id="function-documentation">
+<db:title>Function Documentation</db:title>
+<db:section xml:id="documentMe">
+<db:title its:translate="no">void CrossModuleRef::documentMe()</db:title>
+<db:methodsynopsis>
+<db:void/>
+<db:methodname>documentMe</db:methodname>
+<db:void/>
+<db:synopsisinfo role="meta">plain</db:synopsisinfo>
+<db:synopsisinfo role="signature">void documentMe()</db:synopsisinfo>
+<db:synopsisinfo role="access">public</db:synopsisinfo>
+<db:synopsisinfo role="status">active</db:synopsisinfo>
+<db:synopsisinfo role="threadsafeness">unspecified</db:synopsisinfo>
+</db:methodsynopsis>
+<db:para>Document me!</db:para>
+</db:section>
+</db:section>
+</db:article>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/docbook/images/leonardo-da-vinci.png b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/docbook/images/leonardo-da-vinci.png
new file mode 100644
index 000000000..854acb4ca
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/docbook/images/leonardo-da-vinci.png
Binary files differ
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/docbook/obsolete-classes.xml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/docbook/obsolete-classes.xml
new file mode 100644
index 000000000..f90bcc5ca
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/docbook/obsolete-classes.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<db:article xmlns:db="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:its="http://www.w3.org/2005/11/its" version="5.2" xml:lang="en">
+<db:info>
+<db:title>Obsolete Classes</db:title>
+<db:productname>Test</db:productname>
+<db:titleabbrev>A test project for QDoc build artifacts</db:titleabbrev>
+<db:abstract>
+<db:para>A test project for QDoc build artifacts.</db:para></db:abstract>
+</db:info>
+<db:section xml:id="classes-with-obsolete-members">
+<db:title>Classes with obsolete members</db:title>
+<db:variablelist role="obsoletecppmembers">
+<db:varlistentry>
+<db:term><db:emphasis role="bold">T</db:emphasis></db:term>
+<db:listitem>
+<db:itemizedlist>
+<db:listitem>
+<db:para><db:link xlink:href="testqdoc-test.xml" role="class">Test</db:link> (<db:link xlink:href="testqdoc.xml" xlink:role="namespace">TestQDoc</db:link>)</db:para>
+</db:listitem>
+<db:listitem>
+<db:para><db:link xlink:href="testqdoc-testderived.xml" role="class">TestDerived</db:link> (<db:link xlink:href="testqdoc.xml" xlink:role="namespace">TestQDoc</db:link>)</db:para>
+</db:listitem>
+</db:itemizedlist>
+</db:listitem>
+</db:varlistentry>
+</db:variablelist>
+<db:section xml:id="testqdoc">
+<db:title>TestQDoc</db:title>
+</db:section>
+</db:section>
+</db:article>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/docbook/qdoc-test-qmlmodule.xml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/docbook/qdoc-test-qmlmodule.xml
new file mode 100644
index 000000000..793dc3ea1
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/docbook/qdoc-test-qmlmodule.xml
@@ -0,0 +1,54 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<db:article xmlns:db="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:its="http://www.w3.org/2005/11/its" version="5.2" xml:lang="en">
+<db:info>
+<db:title its:translate="no">QDoc.Test QML Module</db:title>
+<db:productname>Test</db:productname>
+<db:titleabbrev>A test project for QDoc build artifacts</db:titleabbrev>
+<db:abstract>
+<db:para>QML Types for the Test module.</db:para>
+<db:para><db:emphasis role="bold">This module is under development and is subject to change.</db:emphasis></db:para>
+<db:para>This module was introduced in Qt 1.1.</db:para>
+</db:abstract>
+</db:info>
+<db:para><db:emphasis role="bold">This module is under development and is subject to change.</db:emphasis></db:para>
+<db:para>This module was introduced in Qt 1.1.</db:para>
+<db:anchor xml:id="details"/>
+<db:variablelist role="members">
+<db:varlistentry>
+<db:term><db:link xlink:href="qml-qdoc-test-abstractparent.xml" xlink:role="">AbstractParent</db:link></db:term>
+<db:listitem>
+<db:para>Abstract base QML type.</db:para>
+</db:listitem>
+</db:varlistentry>
+<db:varlistentry>
+<db:term><db:link xlink:href="qml-qdoc-test-child.xml" xlink:role="">Child</db:link></db:term>
+<db:listitem>
+<db:para>A Child inheriting its parent.</db:para>
+</db:listitem>
+</db:varlistentry>
+<db:varlistentry>
+<db:term><db:link xlink:href="qml-qdoc-test-doctest.xml" xlink:role="">DocTest</db:link></db:term>
+<db:listitem>
+<db:para>Represents a doc test case.</db:para>
+</db:listitem>
+</db:varlistentry>
+<db:varlistentry>
+<db:term><db:link xlink:href="qml-qdoc-test-type.xml" xlink:role="">Type</db:link></db:term>
+<db:listitem>
+<db:para>A QML type documented in a .cpp file.</db:para>
+</db:listitem>
+</db:varlistentry>
+<db:varlistentry>
+<db:term><db:link xlink:href="qml-qdoc-test-yetanotherchild.xml" xlink:role="">YetAnotherChild</db:link></db:term>
+<db:listitem>
+<db:para>A type inheriting from internal abstract parent.</db:para>
+</db:listitem>
+</db:varlistentry>
+<db:varlistentry>
+<db:term><db:link xlink:href="qml-int.xml" xlink:role="">int</db:link></db:term>
+<db:listitem>
+<db:para>An integer value type.</db:para>
+</db:listitem>
+</db:varlistentry>
+</db:variablelist>
+</db:article>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/docbook/qml-int.xml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/docbook/qml-int.xml
new file mode 100644
index 000000000..f5e99f6ab
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/docbook/qml-int.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<db:article xmlns:db="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:its="http://www.w3.org/2005/11/its" version="5.2" xml:lang="en">
+<db:info>
+<db:title its:translate="no">int QML Value Type</db:title>
+<db:productname>Test</db:productname>
+<db:titleabbrev>A test project for QDoc build artifacts</db:titleabbrev>
+<db:abstract>
+<db:para>An integer value type.</db:para>
+<db:para>This type was introduced in Qt 1.1.</db:para>
+</db:abstract>
+</db:info>
+<db:variablelist its:translate="no">
+<db:varlistentry>
+<db:term>Import Statement</db:term>
+<db:listitem>
+<db:para>import QDoc.Test 1.1</db:para>
+</db:listitem>
+</db:varlistentry>
+<db:varlistentry>
+<db:term>Since:</db:term>
+<db:listitem>
+<db:para>Qt 1.1</db:para>
+</db:listitem>
+</db:varlistentry>
+</db:variablelist>
+<db:section xml:id="details">
+<db:title>Detailed Description</db:title>
+</db:section>
+<db:section xml:id="method-documentation">
+<db:title>Method Documentation</db:title>
+<db:section xml:id="abs-method">
+<db:title>int abs()</db:title>
+<db:para>Returns the absolute value of this integer.</db:para>
+</db:section>
+</db:section>
+</db:article>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/docbook/qml-qdoc-test-abstractparent.xml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/docbook/qml-qdoc-test-abstractparent.xml
new file mode 100644
index 000000000..695c7bfe4
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/docbook/qml-qdoc-test-abstractparent.xml
@@ -0,0 +1,79 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<db:article xmlns:db="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:its="http://www.w3.org/2005/11/its" version="5.2" xml:lang="en">
+<db:info>
+<db:title its:translate="no">AbstractParent QML Type</db:title>
+<db:productname>Test</db:productname>
+<db:titleabbrev>A test project for QDoc build artifacts</db:titleabbrev>
+<db:abstract>
+<db:para>Abstract base QML type.</db:para>
+<db:para>This type was introduced in Qt 1.1.</db:para>
+</db:abstract>
+</db:info>
+<db:variablelist its:translate="no">
+<db:varlistentry>
+<db:term>Import Statement</db:term>
+<db:listitem>
+<db:para>import QDoc.Test 1.1</db:para>
+</db:listitem>
+</db:varlistentry>
+<db:varlistentry>
+<db:term>Since:</db:term>
+<db:listitem>
+<db:para>Qt 1.1</db:para>
+</db:listitem>
+</db:varlistentry>
+<db:varlistentry>
+<db:term>Inherited By:</db:term>
+<db:listitem>
+<db:para><db:link xlink:href="qml-qdoc-test-child.xml" xlink:role="">Child</db:link></db:para>
+</db:listitem>
+</db:varlistentry>
+</db:variablelist>
+<db:section xml:id="details">
+<db:title>Detailed Description</db:title>
+</db:section>
+<db:section xml:id="property-documentation">
+<db:title>Property Documentation</db:title>
+<db:section xml:id="children-prop">
+<db:title>[default] children : list&lt;Child&gt;</db:title>
+<db:fieldsynopsis>
+<db:type>list&lt;Child&gt;</db:type>
+<db:varname>children</db:varname>
+<db:modifier>writable</db:modifier>
+<db:modifier>[default]</db:modifier>
+
+<db:synopsisinfo role="access">public</db:synopsisinfo>
+<db:synopsisinfo role="status">active</db:synopsisinfo>
+<db:synopsisinfo role="threadsafeness">unspecified</db:synopsisinfo>
+</db:fieldsynopsis>
+<db:para>Children of the type.</db:para>
+</db:section>
+<db:section xml:id="name-prop">
+<db:title>name : string</db:title>
+<db:fieldsynopsis>
+<db:type>string</db:type>
+<db:varname>name</db:varname>
+<db:modifier>writable</db:modifier>
+<db:synopsisinfo role="access">public</db:synopsisinfo>
+<db:synopsisinfo role="status">active</db:synopsisinfo>
+<db:synopsisinfo role="threadsafeness">unspecified</db:synopsisinfo>
+</db:fieldsynopsis>
+<db:para>Name of this parent.</db:para>
+</db:section>
+</db:section>
+<db:section xml:id="method-documentation">
+<db:title>Method Documentation</db:title>
+<db:section xml:id="name-method">
+<db:title>void name()</db:title>
+<db:para>Name all children with random names.</db:para>
+</db:section>
+<db:section xml:id="name-method-1">
+<db:title>void name(Child <db:emphasis>child</db:emphasis>, <db:emphasis>name</db:emphasis>)</db:title>
+<db:para>Name a <db:code its:translate="no" role="parameter">child</db:code> using <db:code its:translate="no" role="parameter">name</db:code>.</db:para>
+</db:section>
+<db:section xml:id="rear-method">
+<db:title>void rear(Child <db:emphasis>child</db:emphasis>, var <db:emphasis>method</db:emphasis> = Strict)</db:title>
+<db:para>Do some abstract parenting on <db:code its:translate="no" role="parameter">child</db:code> using a specific <db:code its:translate="no" role="parameter">method</db:code>.</db:para>
+</db:section>
+</db:section>
+</db:article>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/docbook/qml-qdoc-test-child.xml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/docbook/qml-qdoc-test-child.xml
new file mode 100644
index 000000000..26a78038e
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/docbook/qml-qdoc-test-child.xml
@@ -0,0 +1,79 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<db:article xmlns:db="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:its="http://www.w3.org/2005/11/its" version="5.2" xml:lang="en">
+<db:info>
+<db:title its:translate="no">Child QML Type</db:title>
+<db:productname>Test</db:productname>
+<db:titleabbrev>A test project for QDoc build artifacts</db:titleabbrev>
+<db:abstract>
+<db:para>A Child inheriting its parent.</db:para>
+<db:para>This type was introduced in Qt 1.1.</db:para>
+</db:abstract>
+</db:info>
+<db:variablelist its:translate="no">
+<db:varlistentry>
+<db:term>Import Statement</db:term>
+<db:listitem>
+<db:para>import QDoc.Test 1.1</db:para>
+</db:listitem>
+</db:varlistentry>
+<db:varlistentry>
+<db:term>Since:</db:term>
+<db:listitem>
+<db:para>Qt 1.1</db:para>
+</db:listitem>
+</db:varlistentry>
+<db:varlistentry>
+<db:term>Inherits:</db:term>
+<db:listitem>
+<db:para><db:link xlink:href="">AbstractParent</db:link></db:para>
+</db:listitem>
+</db:varlistentry>
+</db:variablelist>
+<db:section xml:id="details">
+<db:title>Detailed Description</db:title>
+</db:section>
+<db:section xml:id="property-documentation">
+<db:title>Property Documentation</db:title>
+<db:section xml:id="children-prop">
+<db:title>[default] children : list&lt;Child&gt;</db:title>
+<db:fieldsynopsis>
+<db:type>list&lt;Child&gt;</db:type>
+<db:varname>children</db:varname>
+<db:modifier>writable</db:modifier>
+<db:modifier>[default]</db:modifier>
+
+<db:synopsisinfo role="access">public</db:synopsisinfo>
+<db:synopsisinfo role="status">active</db:synopsisinfo>
+<db:synopsisinfo role="threadsafeness">unspecified</db:synopsisinfo>
+</db:fieldsynopsis>
+<db:para>Children of the type.</db:para>
+</db:section>
+<db:section xml:id="name-prop">
+<db:title>name : string</db:title>
+<db:fieldsynopsis>
+<db:type>string</db:type>
+<db:varname>name</db:varname>
+<db:modifier>writable</db:modifier>
+<db:synopsisinfo role="access">public</db:synopsisinfo>
+<db:synopsisinfo role="status">active</db:synopsisinfo>
+<db:synopsisinfo role="threadsafeness">unspecified</db:synopsisinfo>
+</db:fieldsynopsis>
+<db:para>Name of this child.</db:para>
+</db:section>
+</db:section>
+<db:section xml:id="method-documentation">
+<db:title>Method Documentation</db:title>
+<db:section xml:id="name-method">
+<db:title>void name(Child <db:emphasis>child</db:emphasis>, <db:emphasis>name</db:emphasis>)</db:title>
+<db:para>Name a <db:code its:translate="no" role="parameter">child</db:code> of this child using <db:code its:translate="no" role="parameter">name</db:code>.</db:para>
+</db:section>
+<db:section xml:id="name-method">
+<db:title>void name()</db:title>
+<db:para>Name all children with random names.</db:para>
+</db:section>
+<db:section xml:id="rear-method">
+<db:title>void rear(Child <db:emphasis>child</db:emphasis>, var <db:emphasis>method</db:emphasis> = Strict)</db:title>
+<db:para>Do some abstract parenting on <db:code its:translate="no" role="parameter">child</db:code> using a specific <db:code its:translate="no" role="parameter">method</db:code>.</db:para>
+</db:section>
+</db:section>
+</db:article>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/docbook/qml-qdoc-test-doctest.xml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/docbook/qml-qdoc-test-doctest.xml
new file mode 100644
index 000000000..9a021a6fc
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/docbook/qml-qdoc-test-doctest.xml
@@ -0,0 +1,117 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<db:article xmlns:db="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:its="http://www.w3.org/2005/11/its" version="5.2" xml:lang="en">
+<db:info>
+<db:title its:translate="no">DocTest QML Type</db:title>
+<db:productname>Test</db:productname>
+<db:titleabbrev>A test project for QDoc build artifacts</db:titleabbrev>
+<db:abstract>
+<db:para>Represents a doc test case.</db:para>
+<db:para>This type was introduced in QDoc.Test 0.9.</db:para>
+</db:abstract>
+</db:info>
+<db:variablelist its:translate="no">
+<db:varlistentry>
+<db:term>Import Statement</db:term>
+<db:listitem>
+<db:para>import QDoc.Test 1.1</db:para>
+</db:listitem>
+</db:varlistentry>
+<db:varlistentry>
+<db:term>Since:</db:term>
+<db:listitem>
+<db:para>QDoc.Test 0.9</db:para>
+</db:listitem>
+</db:varlistentry>
+</db:variablelist>
+<db:section xml:id="details">
+<db:title>Detailed Description</db:title>
+<db:section xml:id="introduction">
+<db:title>Introduction</db:title>
+<db:para>A documentation test case, itself documented inline in DocTest.qml.</db:para>
+</db:section>
+</db:section>
+<db:section xml:id="property-documentation">
+<db:title>Property Documentation</db:title>
+<db:section xml:id="active-prop">
+<db:title>[default: true] active : bool</db:title>
+<db:fieldsynopsis>
+<db:type>bool</db:type>
+<db:varname>active</db:varname>
+<db:modifier>writable</db:modifier>
+<db:synopsisinfo role="access">public</db:synopsisinfo>
+<db:synopsisinfo role="status">active</db:synopsisinfo>
+<db:synopsisinfo role="threadsafeness">unspecified</db:synopsisinfo>
+</db:fieldsynopsis>
+<db:para>Whether the test is active.</db:para>
+<db:section>
+<db:title>See Also</db:title>
+<db:para><db:emphasis>See also </db:emphasis>
+<db:simplelist type="vert" role="see-also">
+<db:member><db:link xlink:href="qml-qdoc-test-doctest.xml#name-prop">name</db:link></db:member>
+</db:simplelist>
+</db:para>
+</db:section>
+</db:section>
+<db:section xml:id="name-prop">
+<db:title>[required] name : string</db:title>
+<db:fieldsynopsis>
+<db:type>string</db:type>
+<db:varname>name</db:varname>
+<db:modifier>writable</db:modifier>
+<db:modifier>required</db:modifier>
+<db:synopsisinfo role="access">public</db:synopsisinfo>
+<db:synopsisinfo role="status">active</db:synopsisinfo>
+<db:synopsisinfo role="threadsafeness">unspecified</db:synopsisinfo>
+</db:fieldsynopsis>
+<db:para>Name of the test.</db:para>
+<db:programlisting language="qml" its:translate="no">DocTest {
+ name: &amp;quot;test&amp;quot;
+ // ...
+}
+</db:programlisting>
+</db:section>
+</db:section>
+<db:section xml:id="signal-documentation">
+<db:title>Signal Documentation</db:title>
+<db:section xml:id="completed-signal">
+<db:title>completed()</db:title>
+<db:note>
+<db:para>The corresponding handler is <db:code>onCompleted</db:code>.</db:para>
+</db:note>
+</db:section>
+<db:section xml:id="foo-signal">
+<db:title>foo(var <db:emphasis>bar</db:emphasis>)</db:title>
+<db:para>Signal with parameter <db:code its:translate="no" role="parameter">bar</db:code>.</db:para>
+<db:note>
+<db:para>The corresponding handler is <db:code>onFoo</db:code>.</db:para>
+</db:note>
+</db:section>
+<db:section xml:id="itsHappening-signal">
+<db:title>itsHappening(bool <db:emphasis>really</db:emphasis>)</db:title>
+<db:para>Signals that something is <db:code its:translate="no" role="parameter">really</db:code> happening.</db:para>
+<db:note>
+<db:para>The corresponding handler is <db:code>onItsHappening</db:code>.</db:para>
+</db:note>
+</db:section>
+</db:section>
+<db:section xml:id="method-documentation">
+<db:title>Method Documentation</db:title>
+<db:section xml:id="fail-method">
+<db:title>[since QDoc.Test 1.0] fail(<db:emphasis>message</db:emphasis> = &quot;oops&quot;)</db:title>
+<db:para>Fails the current test case, with the optional <db:code its:translate="no" role="parameter">message</db:code>.</db:para>
+<db:para>This method was introduced in QDoc.Test 1.0.</db:para>
+</db:section>
+<db:section xml:id="fail_hard-method">
+<db:title>fail_hard(<db:emphasis>msg</db:emphasis> = &quot;facepalm&quot;, <db:emphasis>option</db:emphasis> = 123)</db:title>
+<db:para>Fails the current test case, hard.</db:para>
+<db:itemizedlist>
+<db:listitem>
+<db:para>Prints out <db:code its:translate="no" role="parameter">msg</db:code>.</db:para>
+</db:listitem>
+<db:listitem>
+<db:para>Accepts a random <db:code its:translate="no" role="parameter">option</db:code>.</db:para>
+</db:listitem>
+</db:itemizedlist>
+</db:section>
+</db:section>
+</db:article>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/docbook/qml-qdoc-test-oldtype.xml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/docbook/qml-qdoc-test-oldtype.xml
new file mode 100644
index 000000000..e2d1059b4
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/docbook/qml-qdoc-test-oldtype.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<db:article xmlns:db="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:its="http://www.w3.org/2005/11/its" version="5.2" xml:lang="en">
+<db:info>
+<db:title its:translate="no">OldType QML Type</db:title>
+<db:productname>Test</db:productname>
+<db:titleabbrev>A test project for QDoc build artifacts</db:titleabbrev>
+<db:abstract>
+<db:para>Deprecated old type.</db:para>
+<db:para><db:emphasis role="bold">This type is deprecated since QDoc.Test 1.0. We strongly advise against using it in new code.</db:emphasis></db:para>
+<db:para>This type was introduced in Qt 1.1.</db:para>
+</db:abstract>
+</db:info>
+<db:variablelist its:translate="no">
+<db:varlistentry>
+<db:term>Import Statement</db:term>
+<db:listitem>
+<db:para>import QDoc.Test 1.1</db:para>
+</db:listitem>
+</db:varlistentry>
+<db:varlistentry>
+<db:term>Since:</db:term>
+<db:listitem>
+<db:para>Qt 1.1</db:para>
+</db:listitem>
+</db:varlistentry>
+<db:varlistentry>
+<db:term>Status:</db:term>
+<db:listitem>
+<db:para>Deprecated since 1.0</db:para>
+</db:listitem>
+</db:varlistentry>
+</db:variablelist>
+<db:para><db:emphasis role="bold">This type is deprecated since QDoc.Test 1.0. We strongly advise against using it in new code.</db:emphasis></db:para>
+<db:section xml:id="details">
+<db:title>Detailed Description</db:title>
+</db:section>
+</db:article>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/docbook/qml-qdoc-test-type.xml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/docbook/qml-qdoc-test-type.xml
new file mode 100644
index 000000000..826abfe3b
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/docbook/qml-qdoc-test-type.xml
@@ -0,0 +1,237 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<db:article xmlns:db="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:its="http://www.w3.org/2005/11/its" version="5.2" xml:lang="en">
+<db:info>
+<db:title its:translate="no">Type QML Type</db:title>
+<db:productname>Test</db:productname>
+<db:titleabbrev>A test project for QDoc build artifacts</db:titleabbrev>
+<db:abstract>
+<db:para>A QML type documented in a .cpp file.</db:para>
+<db:para>This type was introduced in Qt 1.1.</db:para>
+</db:abstract>
+</db:info>
+<db:variablelist its:translate="no">
+<db:varlistentry>
+<db:term>Import Statement</db:term>
+<db:listitem>
+<db:para>import QDoc.Test 1.1</db:para>
+</db:listitem>
+</db:varlistentry>
+<db:varlistentry>
+<db:term>Since:</db:term>
+<db:listitem>
+<db:para>Qt 1.1</db:para>
+</db:listitem>
+</db:varlistentry>
+<db:varlistentry>
+<db:term>In C++:</db:term>
+<db:listitem>
+<db:para><db:link xlink:href="testqdoc-test.xml">Test</db:link></db:para>
+</db:listitem>
+</db:varlistentry>
+<db:varlistentry>
+<db:term>Status:</db:term>
+<db:listitem>
+<db:para>&lt;Work In Progress&gt;</db:para>
+</db:listitem>
+</db:varlistentry>
+</db:variablelist>
+<db:section xml:id="details">
+<db:title>Detailed Description</db:title>
+</db:section>
+<db:section xml:id="property-documentation">
+<db:title>Property Documentation</db:title>
+<db:section xml:id="fifth-prop">
+<db:title>fifth : int</db:title>
+<db:fieldsynopsis>
+<db:type>int</db:type>
+<db:varname>fifth</db:varname>
+<db:modifier>writable</db:modifier>
+<db:synopsisinfo role="access">public</db:synopsisinfo>
+<db:synopsisinfo role="status">active</db:synopsisinfo>
+<db:synopsisinfo role="threadsafeness">unspecified</db:synopsisinfo>
+</db:fieldsynopsis>
+<db:bridgehead renderas="sect2">fourth : int</db:bridgehead><db:fieldsynopsis>
+<db:type>int</db:type>
+<db:varname>fourth</db:varname>
+<db:modifier>writable</db:modifier>
+<db:synopsisinfo role="access">public</db:synopsisinfo>
+<db:synopsisinfo role="status">active</db:synopsisinfo>
+<db:synopsisinfo role="threadsafeness">unspecified</db:synopsisinfo>
+</db:fieldsynopsis>
+<db:para>A group of properties sharing a documentation comment.</db:para>
+</db:section>
+<db:section xml:id="group-prop">
+<db:title>group group</db:title>
+<db:bridgehead renderas="sect2" xml:id="group.first-prop">group.first : int</db:bridgehead>
+<db:fieldsynopsis>
+<db:type>int</db:type>
+<db:varname>group.first</db:varname>
+<db:modifier>writable</db:modifier>
+<db:synopsisinfo role="access">public</db:synopsisinfo>
+<db:synopsisinfo role="status">active</db:synopsisinfo>
+<db:synopsisinfo role="threadsafeness">unspecified</db:synopsisinfo>
+</db:fieldsynopsis>
+<db:bridgehead renderas="sect2" xml:id="group.second-prop">group.second : int</db:bridgehead>
+<db:fieldsynopsis>
+<db:type>int</db:type>
+<db:varname>group.second</db:varname>
+<db:modifier>writable</db:modifier>
+<db:synopsisinfo role="access">public</db:synopsisinfo>
+<db:synopsisinfo role="status">active</db:synopsisinfo>
+<db:synopsisinfo role="threadsafeness">unspecified</db:synopsisinfo>
+</db:fieldsynopsis>
+<db:bridgehead renderas="sect2" xml:id="group.third-prop">group.third : int</db:bridgehead>
+<db:fieldsynopsis>
+<db:type>int</db:type>
+<db:varname>group.third</db:varname>
+<db:modifier>writable</db:modifier>
+<db:synopsisinfo role="access">public</db:synopsisinfo>
+<db:synopsisinfo role="status">active</db:synopsisinfo>
+<db:synopsisinfo role="threadsafeness">unspecified</db:synopsisinfo>
+</db:fieldsynopsis>
+<db:para>A property group.</db:para>
+</db:section>
+<db:section xml:id="id-prop">
+<db:title>[read-only] id : int</db:title>
+<db:fieldsynopsis>
+<db:type>int</db:type>
+<db:varname>id</db:varname>
+<db:modifier>[read-only]</db:modifier>
+
+<db:synopsisinfo role="access">public</db:synopsisinfo>
+<db:synopsisinfo role="status">active</db:synopsisinfo>
+<db:synopsisinfo role="threadsafeness">unspecified</db:synopsisinfo>
+</db:fieldsynopsis>
+<db:para>A read-only property.</db:para>
+</db:section>
+<db:section xml:id="name-prop">
+<db:title>[required] name : string</db:title>
+<db:fieldsynopsis>
+<db:type>string</db:type>
+<db:varname>name</db:varname>
+<db:modifier>writable</db:modifier>
+<db:modifier>required</db:modifier>
+<db:synopsisinfo role="access">public</db:synopsisinfo>
+<db:synopsisinfo role="status">active</db:synopsisinfo>
+<db:synopsisinfo role="threadsafeness">unspecified</db:synopsisinfo>
+</db:fieldsynopsis>
+<db:para>Name of the Test.</db:para>
+</db:section>
+</db:section>
+<db:section xml:id="attached-property-documentation">
+<db:title>Attached Property Documentation</db:title>
+<db:section xml:id="type-attached-prop">
+<db:title>[default: Type.NoType] Type.type : enumeration</db:title>
+<db:fieldsynopsis>
+<db:type>enumeration</db:type>
+<db:varname>Type.type</db:varname>
+<db:modifier>attached</db:modifier>
+<db:modifier>writable</db:modifier>
+<db:synopsisinfo role="access">public</db:synopsisinfo>
+<db:synopsisinfo role="status">active</db:synopsisinfo>
+<db:synopsisinfo role="threadsafeness">unspecified</db:synopsisinfo>
+</db:fieldsynopsis>
+<db:informaltable>
+<db:thead>
+<db:tr>
+<db:th>Constant</db:th>
+<db:th>Description</db:th>
+</db:tr>
+</db:thead>
+<db:tr>
+<db:td>
+<db:para its:translate="no">Type.NoType</db:para>
+</db:td>
+<db:td>
+<db:para>Nothing</db:para>
+</db:td>
+</db:tr>
+<db:tr>
+<db:td>
+<db:para its:translate="no">Type.SomeType</db:para>
+</db:td>
+<db:td>
+<db:para>Something</db:para>
+</db:td>
+</db:tr>
+</db:informaltable>
+</db:section>
+</db:section>
+<db:section xml:id="signal-documentation">
+<db:title>Signal Documentation</db:title>
+<db:section xml:id="completed-signal">
+<db:title>completed(int <db:emphasis>status</db:emphasis>)</db:title>
+<db:para>This signal is emitted when the operation completed with <db:code its:translate="no" role="parameter">status</db:code>.</db:para>
+<db:note>
+<db:para>The corresponding handler is <db:code>onCompleted</db:code>.</db:para>
+</db:note>
+</db:section>
+<db:section xml:id="group.created-signal">
+<db:title>group.created()</db:title>
+<db:para>This signal is prefixed with <db:emphasis>group</db:emphasis>.</db:para>
+<db:note>
+<db:para>The corresponding handler is <db:code>group.onCreated</db:code>.</db:para>
+</db:note>
+</db:section>
+</db:section>
+<db:section xml:id="attached-signal-documentation">
+<db:title>Attached Signal Documentation</db:title>
+<db:section xml:id="configured-signal">
+<db:title>configured()</db:title>
+<db:para>This attached signal is emitted when the type was configured.</db:para>
+<db:note>
+<db:para>The corresponding handler is <db:code>onConfigured</db:code>.</db:para>
+</db:note>
+</db:section>
+</db:section>
+<db:section xml:id="method-documentation">
+<db:title>Method Documentation</db:title>
+<db:section xml:id="disable-method">
+<db:title>disable()</db:title>
+<db:methodsynopsis>
+<db:type></db:type>
+<db:methodname>disable</db:methodname>
+<db:void/>
+<db:synopsisinfo role="meta">qmlmethod</db:synopsisinfo>
+<db:synopsisinfo role="signature">disable()</db:synopsisinfo>
+<db:synopsisinfo role="access">public</db:synopsisinfo>
+<db:synopsisinfo role="status">active</db:synopsisinfo>
+<db:synopsisinfo role="threadsafeness">unspecified</db:synopsisinfo>
+</db:methodsynopsis>
+<db:bridgehead renderas="sect2">enable()</db:bridgehead><db:methodsynopsis>
+<db:type></db:type>
+<db:methodname>enable</db:methodname>
+<db:void/>
+<db:synopsisinfo role="meta">qmlmethod</db:synopsisinfo>
+<db:synopsisinfo role="signature">enable()</db:synopsisinfo>
+<db:synopsisinfo role="access">public</db:synopsisinfo>
+<db:synopsisinfo role="status">active</db:synopsisinfo>
+<db:synopsisinfo role="threadsafeness">unspecified</db:synopsisinfo>
+</db:methodsynopsis>
+<db:para>Enables or disables this type.</db:para>
+</db:section>
+<db:section xml:id="copy-method">
+<db:title>Type copy(<db:emphasis>a</db:emphasis>)</db:title>
+<db:para>Returns another Type based on <db:code its:translate="no" role="parameter">a</db:code>.</db:para>
+</db:section>
+<db:section xml:id="futureDeprecated-method">
+<db:title>[until 6.3] futureDeprecated()</db:title>
+<db:para>This method is scheduled for deprecation in version 6.3.</db:para>
+<db:para>Use something else instead.</db:para>
+<db:para>This is a method that's marked for deprecation in a future version.</db:para>
+</db:section>
+</db:section>
+<db:section xml:id="obsolete">
+<db:title>Obsolete Members for Type</db:title>
+<db:para><db:emphasis role="bold">The following members of QML type <db:link xlink:href="qml-qdoc-test-type.xml">Type</db:link> are deprecated.</db:emphasis> We strongly advise against using them in new code.</db:para>
+<db:section xml:id="obsolete-method-documentation">
+<db:title>Obsolete Method Documentation</db:title>
+<db:section xml:id="deprecatedMethod-method">
+<db:title>[deprecated in 6.2] deprecatedMethod()</db:title>
+<db:para>This method is deprecated since QDoc.Test 6.2. We strongly advise against using it in new code.</db:para>
+<db:para>This method has no replacement.</db:para>
+<db:para>This is a method that should include information about being deprecated and that it has been so since 6.2 in its docs.</db:para>
+</db:section>
+</db:section>
+</db:section>
+</db:article>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/docbook/qml-qdoc-test-yetanotherchild.xml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/docbook/qml-qdoc-test-yetanotherchild.xml
new file mode 100644
index 000000000..040c7360b
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/docbook/qml-qdoc-test-yetanotherchild.xml
@@ -0,0 +1,44 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<db:article xmlns:db="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:its="http://www.w3.org/2005/11/its" version="5.2" xml:lang="en">
+<db:info>
+<db:title its:translate="no">YetAnotherChild QML Type</db:title>
+<db:productname>Test</db:productname>
+<db:titleabbrev>A test project for QDoc build artifacts</db:titleabbrev>
+<db:abstract>
+<db:para>A type inheriting from internal abstract parent.</db:para>
+<db:para>This type was introduced in Qt 1.1.</db:para>
+</db:abstract>
+</db:info>
+<db:variablelist its:translate="no">
+<db:varlistentry>
+<db:term>Import Statement</db:term>
+<db:listitem>
+<db:para>import QDoc.Test 1.1</db:para>
+</db:listitem>
+</db:varlistentry>
+<db:varlistentry>
+<db:term>Since:</db:term>
+<db:listitem>
+<db:para>Qt 1.1</db:para>
+</db:listitem>
+</db:varlistentry>
+</db:variablelist>
+<db:section xml:id="details">
+<db:title>Detailed Description</db:title>
+</db:section>
+<db:section xml:id="property-documentation">
+<db:title>Property Documentation</db:title>
+<db:section xml:id="prop-prop">
+<db:title>prop : int</db:title>
+<db:fieldsynopsis>
+<db:type>int</db:type>
+<db:varname>prop</db:varname>
+<db:modifier>writable</db:modifier>
+<db:synopsisinfo role="access">public</db:synopsisinfo>
+<db:synopsisinfo role="status">active</db:synopsisinfo>
+<db:synopsisinfo role="threadsafeness">unspecified</db:synopsisinfo>
+</db:fieldsynopsis>
+<db:para>Propagated to inheriting type docs.</db:para>
+</db:section>
+</db:section>
+</db:article>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/docbook/qml-test-nover-doctest.xml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/docbook/qml-test-nover-doctest.xml
new file mode 100644
index 000000000..d719d9f6f
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/docbook/qml-test-nover-doctest.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<db:article xmlns:db="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:its="http://www.w3.org/2005/11/its" version="5.2" xml:lang="en">
+<db:info>
+<db:title its:translate="no">DocTest QML Type</db:title>
+<db:productname>Test</db:productname>
+<db:titleabbrev>A test project for QDoc build artifacts</db:titleabbrev>
+<db:abstract>
+<db:para>Shadows the type name in QDoc.Test module.</db:para>
+<db:para>This type was introduced in Qt 1.1.</db:para>
+</db:abstract>
+</db:info>
+<db:variablelist its:translate="no">
+<db:varlistentry>
+<db:term>Import Statement</db:term>
+<db:listitem>
+<db:para>import Test.NoVer</db:para>
+</db:listitem>
+</db:varlistentry>
+<db:varlistentry>
+<db:term>Since:</db:term>
+<db:listitem>
+<db:para>Qt 1.1</db:para>
+</db:listitem>
+</db:varlistentry>
+<db:varlistentry>
+<db:term>Status:</db:term>
+<db:listitem>
+<db:para>Tech Preview</db:para>
+</db:listitem>
+</db:varlistentry>
+</db:variablelist>
+<db:section xml:id="details">
+<db:title>Detailed Description</db:title>
+</db:section>
+</db:article>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/docbook/qml-test-nover-typenoversion.xml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/docbook/qml-test-nover-typenoversion.xml
new file mode 100644
index 000000000..581207402
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/docbook/qml-test-nover-typenoversion.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<db:article xmlns:db="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:its="http://www.w3.org/2005/11/its" version="5.2" xml:lang="en">
+<db:info>
+<db:title its:translate="no">TypeNoVersion QML Type</db:title>
+<db:productname>Test</db:productname>
+<db:titleabbrev>A test project for QDoc build artifacts</db:titleabbrev>
+<db:abstract>
+<db:para>Another QML type documented in a .cpp file.</db:para>
+<db:para>This type was introduced in Qt 1.1.</db:para>
+</db:abstract>
+</db:info>
+<db:variablelist its:translate="no">
+<db:varlistentry>
+<db:term>Import Statement</db:term>
+<db:listitem>
+<db:para>import Test.NoVer</db:para>
+</db:listitem>
+</db:varlistentry>
+<db:varlistentry>
+<db:term>Since:</db:term>
+<db:listitem>
+<db:para>Qt 1.1</db:para>
+</db:listitem>
+</db:varlistentry>
+<db:varlistentry>
+<db:term>Status:</db:term>
+<db:listitem>
+<db:para>Tech Preview</db:para>
+</db:listitem>
+</db:varlistentry>
+</db:variablelist>
+<db:section xml:id="details">
+<db:title>Detailed Description</db:title>
+</db:section>
+</db:article>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/docbook/qml-themodule-thetype.xml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/docbook/qml-themodule-thetype.xml
new file mode 100644
index 000000000..b8a31eb9f
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/docbook/qml-themodule-thetype.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<db:article xmlns:db="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:its="http://www.w3.org/2005/11/its" version="5.2" xml:lang="en">
+<db:info>
+<db:title its:translate="no">TheType QML Type</db:title>
+<db:productname>Test</db:productname>
+<db:titleabbrev>A test project for QDoc build artifacts</db:titleabbrev>
+<db:abstract>
+<db:para>A test project for QDoc build artifacts.</db:para></db:abstract>
+</db:info>
+<db:variablelist its:translate="no">
+<db:varlistentry>
+<db:term>Import Statement</db:term>
+<db:listitem>
+<db:para>import TheModule</db:para>
+</db:listitem>
+</db:varlistentry>
+<db:varlistentry>
+<db:term>In C++:</db:term>
+<db:listitem>
+<db:para><db:link xlink:href="testqdoc-testderived.xml">TestDerived</db:link></db:para>
+</db:listitem>
+</db:varlistentry>
+</db:variablelist>
+<db:section xml:id="details">
+<db:title>Detailed Description</db:title>
+</db:section>
+<db:section xml:id="property-documentation">
+<db:title>Property Documentation</db:title>
+<db:section xml:id="name-prop">
+<db:title>[read-only] name : string</db:title>
+<db:fieldsynopsis>
+<db:type>string</db:type>
+<db:varname>name</db:varname>
+<db:modifier>[read-only]</db:modifier>
+
+<db:synopsisinfo role="access">public</db:synopsisinfo>
+<db:synopsisinfo role="status">active</db:synopsisinfo>
+<db:synopsisinfo role="threadsafeness">unspecified</db:synopsisinfo>
+</db:fieldsynopsis>
+<db:para>Read-only status of this property is resolved from Q_PROPERTY.</db:para>
+</db:section>
+</db:section>
+</db:article>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/docbook/qml-uicomponents-progressbar.xml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/docbook/qml-uicomponents-progressbar.xml
new file mode 100644
index 000000000..f1a8d4129
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/docbook/qml-uicomponents-progressbar.xml
@@ -0,0 +1,104 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<db:article xmlns:db="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:its="http://www.w3.org/2005/11/its" version="5.2" xml:lang="en">
+<db:info>
+<db:title its:translate="no">ProgressBar QML Type</db:title>
+<db:productname>Test</db:productname>
+<db:titleabbrev>A test project for QDoc build artifacts</db:titleabbrev>
+<db:abstract>
+<db:para>A component that shows the progress of an event.</db:para>
+</db:abstract>
+</db:info>
+<db:variablelist its:translate="no">
+<db:varlistentry>
+<db:term>Import Statement</db:term>
+<db:listitem>
+<db:para>import UIComponents 1.0</db:para>
+</db:listitem>
+</db:varlistentry>
+</db:variablelist>
+<db:section xml:id="details">
+<db:title>Detailed Description</db:title>
+<db:para>A <db:link xlink:href="qml-uicomponents-progressbar.xml">ProgressBar</db:link> shows the linear progress of an event as its <db:link xlink:href="qml-uicomponents-progressbar.xml#value-prop">value</db:link>. The range is specified using the <db:link xlink:href="qml-uicomponents-progressbar.xml#minimum-prop">minimum</db:link> and the <db:link xlink:href="qml-uicomponents-progressbar.xml#maximum-prop">maximum</db:link> values.</db:para>
+<db:para>The <db:link xlink:href="qml-uicomponents-progressbar.xml">ProgressBar</db:link> component is part of the <db:link xlink:href="uicomponents-qmlmodule.xml">UI Components</db:link> module.</db:para>
+<db:para>This documentation is part of the <db:link xlink:href="test-componentset-example.xml">UIComponents</db:link> example.</db:para>
+</db:section>
+<db:section xml:id="property-documentation">
+<db:title>Property Documentation</db:title>
+<db:section xml:id="color-prop">
+<db:title>color : color</db:title>
+<db:fieldsynopsis>
+<db:type>color</db:type>
+<db:varname>color</db:varname>
+<db:modifier>writable</db:modifier>
+<db:synopsisinfo role="access">public</db:synopsisinfo>
+<db:synopsisinfo role="status">active</db:synopsisinfo>
+<db:synopsisinfo role="threadsafeness">unspecified</db:synopsisinfo>
+</db:fieldsynopsis>
+<db:para>The color of the <db:link xlink:href="qml-uicomponents-progressbar.xml">ProgressBar</db:link>'s gradient. Must bind to a color type.</db:para>
+<db:section>
+<db:title>See Also</db:title>
+<db:para><db:emphasis>See also </db:emphasis>
+<db:simplelist type="vert" role="see-also">
+<db:member><db:link xlink:href="qml-uicomponents-progressbar.xml#secondColor-prop">secondColor</db:link></db:member>
+</db:simplelist>
+</db:para>
+</db:section>
+</db:section>
+<db:section xml:id="maximum-prop">
+<db:title>maximum : int</db:title>
+<db:fieldsynopsis>
+<db:type>int</db:type>
+<db:varname>maximum</db:varname>
+<db:modifier>writable</db:modifier>
+<db:synopsisinfo role="access">public</db:synopsisinfo>
+<db:synopsisinfo role="status">active</db:synopsisinfo>
+<db:synopsisinfo role="threadsafeness">unspecified</db:synopsisinfo>
+</db:fieldsynopsis>
+<db:para>The maximum value of the <db:link xlink:href="qml-uicomponents-progressbar.xml">ProgressBar</db:link> range. The <db:link xlink:href="qml-uicomponents-progressbar.xml#value-prop">value</db:link> must not be more than this value.</db:para>
+</db:section>
+<db:section xml:id="minimum-prop">
+<db:title>minimum : int</db:title>
+<db:fieldsynopsis>
+<db:type>int</db:type>
+<db:varname>minimum</db:varname>
+<db:modifier>writable</db:modifier>
+<db:synopsisinfo role="access">public</db:synopsisinfo>
+<db:synopsisinfo role="status">active</db:synopsisinfo>
+<db:synopsisinfo role="threadsafeness">unspecified</db:synopsisinfo>
+</db:fieldsynopsis>
+<db:para>The minimum value of the <db:link xlink:href="qml-uicomponents-progressbar.xml">ProgressBar</db:link> range. The <db:link xlink:href="qml-uicomponents-progressbar.xml#value-prop">value</db:link> must not be less than this value.</db:para>
+</db:section>
+<db:section xml:id="secondColor-prop">
+<db:title>secondColor : color</db:title>
+<db:fieldsynopsis>
+<db:type>color</db:type>
+<db:varname>secondColor</db:varname>
+<db:modifier>writable</db:modifier>
+<db:synopsisinfo role="access">public</db:synopsisinfo>
+<db:synopsisinfo role="status">active</db:synopsisinfo>
+<db:synopsisinfo role="threadsafeness">unspecified</db:synopsisinfo>
+</db:fieldsynopsis>
+<db:para>The second color of the <db:link xlink:href="qml-uicomponents-progressbar.xml">ProgressBar</db:link>'s gradient. Must bind to a color type.</db:para>
+<db:section>
+<db:title>See Also</db:title>
+<db:para><db:emphasis>See also </db:emphasis>
+<db:simplelist type="vert" role="see-also">
+<db:member><db:link xlink:href="qml-uicomponents-progressbar.xml#color-prop">color</db:link></db:member>
+</db:simplelist>
+</db:para>
+</db:section>
+</db:section>
+<db:section xml:id="value-prop">
+<db:title>value : int</db:title>
+<db:fieldsynopsis>
+<db:type>int</db:type>
+<db:varname>value</db:varname>
+<db:modifier>writable</db:modifier>
+<db:synopsisinfo role="access">public</db:synopsisinfo>
+<db:synopsisinfo role="status">active</db:synopsisinfo>
+<db:synopsisinfo role="threadsafeness">unspecified</db:synopsisinfo>
+</db:fieldsynopsis>
+<db:para>The value of the progress.</db:para>
+</db:section>
+</db:section>
+</db:article>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/docbook/qml-uicomponents-switch.xml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/docbook/qml-uicomponents-switch.xml
new file mode 100644
index 000000000..2ad3a31b5
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/docbook/qml-uicomponents-switch.xml
@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<db:article xmlns:db="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:its="http://www.w3.org/2005/11/its" version="5.2" xml:lang="en">
+<db:info>
+<db:title its:translate="no">Switch QML Type</db:title>
+<db:productname>Test</db:productname>
+<db:titleabbrev>A test project for QDoc build artifacts</db:titleabbrev>
+<db:abstract>
+<db:para>A component that can be turned on or off.</db:para>
+</db:abstract>
+</db:info>
+<db:variablelist its:translate="no">
+<db:varlistentry>
+<db:term>Import Statement</db:term>
+<db:listitem>
+<db:para>import UIComponents 1.0</db:para>
+</db:listitem>
+</db:varlistentry>
+</db:variablelist>
+<db:section xml:id="details">
+<db:title>Detailed Description</db:title>
+<db:para>A toggle switch has two states: an <db:code>on</db:code> and an <db:code>off</db:code> state. The <db:code>off</db:code> state is when the <db:link xlink:href="qml-uicomponents-switch.xml#on-prop">on</db:link> property is set to <db:code>false</db:code>.</db:para>
+<db:para>The ToggleSwitch component is part of the <db:link xlink:href="uicomponents-qmlmodule.xml">UI Components</db:link> module.</db:para>
+<db:para>This documentation is part of the <db:link xlink:href="test-componentset-example.xml">UIComponents</db:link> example.</db:para>
+</db:section>
+<db:section xml:id="property-documentation">
+<db:title>Property Documentation</db:title>
+<db:section xml:id="on-prop">
+<db:title>on : bool</db:title>
+<db:fieldsynopsis>
+<db:type>bool</db:type>
+<db:varname>on</db:varname>
+<db:modifier>writable</db:modifier>
+<db:synopsisinfo role="access">public</db:synopsisinfo>
+<db:synopsisinfo role="status">active</db:synopsisinfo>
+<db:synopsisinfo role="threadsafeness">unspecified</db:synopsisinfo>
+</db:fieldsynopsis>
+<db:para>Indicates the state of the switch. If <db:code>false</db:code>, then the switch is in the <db:code>off</db:code> state.</db:para>
+</db:section>
+</db:section>
+<db:section xml:id="method-documentation">
+<db:title>Method Documentation</db:title>
+<db:section xml:id="toggle-method">
+<db:title>toggle()</db:title>
+<db:para>A method to toggle the switch. If the switch is <db:code>on</db:code>, the toggling it will turn it <db:code>off</db:code>. Toggling a switch in the <db:code>off</db:code> position will turn it <db:code>on</db:code>.</db:para>
+</db:section>
+</db:section>
+</db:article>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/docbook/qml-uicomponents-tabwidget.xml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/docbook/qml-uicomponents-tabwidget.xml
new file mode 100644
index 000000000..f0bdf582a
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/docbook/qml-uicomponents-tabwidget.xml
@@ -0,0 +1,77 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<db:article xmlns:db="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:its="http://www.w3.org/2005/11/its" version="5.2" xml:lang="en">
+<db:info>
+<db:title its:translate="no">TabWidget QML Type</db:title>
+<db:productname>Test</db:productname>
+<db:titleabbrev>A test project for QDoc build artifacts</db:titleabbrev>
+<db:abstract>
+<db:para>A widget that places its children as tabs.</db:para>
+</db:abstract>
+</db:info>
+<db:variablelist its:translate="no">
+<db:varlistentry>
+<db:term>Import Statement</db:term>
+<db:listitem>
+<db:para>import UIComponents 1.0</db:para>
+</db:listitem>
+</db:varlistentry>
+</db:variablelist>
+<db:section xml:id="details">
+<db:title>Detailed Description</db:title>
+<db:para>A <db:link xlink:href="qml-uicomponents-tabwidget.xml">TabWidget</db:link> places its children as tabs in a view. Selecting a tab involves selecting the tab at the top.</db:para>
+<db:para>The <db:link xlink:href="qml-uicomponents-tabwidget.xml">TabWidget</db:link> component is part of the <db:link xlink:href="uicomponents-qmlmodule.xml">UI Components</db:link> module.</db:para>
+<db:para>This documentation is part of the <db:link xlink:href="test-componentset-example.xml">UIComponents</db:link> example.</db:para>
+<db:section xml:id="adding-tabs">
+<db:title>Adding Tabs</db:title>
+<db:para>To add a tab, declare the tab as a child of the <db:link xlink:href="qml-uicomponents-tabwidget.xml">TabWidget</db:link>.</db:para>
+<db:programlisting language="cpp" its:translate="no">TabWidget {
+ id: tabwidget
+
+ Rectangle {
+ id: tab1
+ color: &amp;quot;red&amp;quot;
+ //... omitted
+ }
+ Rectangle {
+ id: tab2
+ color: &amp;quot;blue&amp;quot;
+ //... omitted
+ }
+
+}
+</db:programlisting>
+</db:section>
+</db:section>
+<db:section xml:id="property-documentation">
+<db:title>Property Documentation</db:title>
+<db:section xml:id="current-prop">
+<db:title>current : int</db:title>
+<db:fieldsynopsis>
+<db:type>int</db:type>
+<db:varname>current</db:varname>
+<db:modifier>writable</db:modifier>
+<db:synopsisinfo role="access">public</db:synopsisinfo>
+<db:synopsisinfo role="status">active</db:synopsisinfo>
+<db:synopsisinfo role="threadsafeness">unspecified</db:synopsisinfo>
+</db:fieldsynopsis>
+<db:para>The currently active tab in the <db:link xlink:href="qml-uicomponents-tabwidget.xml">TabWidget</db:link>.</db:para>
+</db:section>
+<db:section xml:id="sampleReadOnlyProperty-prop">
+<db:title>[read-only] sampleReadOnlyProperty : int</db:title>
+<db:fieldsynopsis>
+<db:type>int</db:type>
+<db:varname>sampleReadOnlyProperty</db:varname>
+<db:modifier>[read-only]</db:modifier>
+
+<db:synopsisinfo role="access">public</db:synopsisinfo>
+<db:synopsisinfo role="status">active</db:synopsisinfo>
+<db:synopsisinfo role="threadsafeness">unspecified</db:synopsisinfo>
+</db:fieldsynopsis>
+<db:para>A sample <db:code>read-only</db:code> property. A contrived property to demonstrate QDoc's ability to detect read-only properties.</db:para>
+<db:para>The signature is:</db:para>
+<db:programlisting language="cpp" its:translate="no">readonly property int sampleReadOnlyProperty: 0
+</db:programlisting>
+<db:para>Note that the property must be initialized to a value.</db:para>
+</db:section>
+</db:section>
+</db:article>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/docbook/qmlmodules.xml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/docbook/qmlmodules.xml
new file mode 100644
index 000000000..af130ca0c
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/docbook/qmlmodules.xml
@@ -0,0 +1,103 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<db:article xmlns:db="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:its="http://www.w3.org/2005/11/its" version="5.2" xml:lang="en">
+<db:info>
+<db:title>QML Modules</db:title>
+<db:productname>Test</db:productname>
+<db:titleabbrev>A test project for QDoc build artifacts</db:titleabbrev>
+<db:abstract>
+<db:para>A test project for QDoc build artifacts.</db:para></db:abstract>
+</db:info>
+<db:variablelist role="qml-modules">
+<db:varlistentry>
+<db:term><db:link xlink:href="test-empty-qmlmodule.xml" xlink:role="">No QML Types Here</db:link></db:term>
+<db:listitem>
+<db:para>A QML module with no member types.</db:para>
+</db:listitem>
+</db:varlistentry>
+<db:varlistentry>
+<db:term><db:link xlink:href="qdoc-test-qmlmodule.xml" xlink:role="">QDoc.Test QML Module</db:link></db:term>
+<db:listitem>
+<db:para>QML Types for the Test module.</db:para>
+</db:listitem>
+</db:varlistentry>
+<db:varlistentry>
+<db:term><db:link xlink:href="themodule-qmlmodule.xml" xlink:role="">TheModule</db:link></db:term>
+<db:listitem>
+<db:para></db:para>
+</db:listitem>
+</db:varlistentry>
+<db:varlistentry>
+<db:term><db:link xlink:href="uicomponents-qmlmodule.xml" xlink:role="">UI Components</db:link></db:term>
+<db:listitem>
+<db:para>Basic set of UI components.</db:para>
+</db:listitem>
+</db:varlistentry>
+<db:varlistentry>
+<db:term><db:link xlink:href="test-nover-qmlmodule.xml" xlink:role="">Versionless QML Module</db:link></db:term>
+<db:listitem>
+<db:para>QML Types for the Test module without version.</db:para>
+</db:listitem>
+</db:varlistentry>
+</db:variablelist>
+<db:section xml:id="qml-types">
+<db:title>QML types</db:title>
+<db:variablelist role="qmltypesbymodule QDoc.Test">
+<db:varlistentry>
+<db:term><db:link xlink:href="qml-qdoc-test-abstractparent.xml" xlink:role="">AbstractParent</db:link></db:term>
+<db:listitem>
+<db:para>Abstract base QML type.</db:para>
+</db:listitem>
+</db:varlistentry>
+<db:varlistentry>
+<db:term><db:link xlink:href="qml-qdoc-test-child.xml" xlink:role="">Child</db:link></db:term>
+<db:listitem>
+<db:para>A Child inheriting its parent.</db:para>
+</db:listitem>
+</db:varlistentry>
+<db:varlistentry>
+<db:term><db:link xlink:href="qml-qdoc-test-doctest.xml" xlink:role="">DocTest</db:link></db:term>
+<db:listitem>
+<db:para>Represents a doc test case.</db:para>
+</db:listitem>
+</db:varlistentry>
+<db:varlistentry>
+<db:term><db:link xlink:href="qml-qdoc-test-type.xml" xlink:role="">Type</db:link></db:term>
+<db:listitem>
+<db:para>A QML type documented in a .cpp file.</db:para>
+</db:listitem>
+</db:varlistentry>
+<db:varlistentry>
+<db:term><db:link xlink:href="qml-qdoc-test-yetanotherchild.xml" xlink:role="">YetAnotherChild</db:link></db:term>
+<db:listitem>
+<db:para>A type inheriting from internal abstract parent.</db:para>
+</db:listitem>
+</db:varlistentry>
+</db:variablelist>
+</db:section>
+<db:section xml:id="qml-value-types">
+<db:title>QML value types</db:title>
+<db:simplelist>
+<db:member><db:link xlink:href="i">I</db:link></db:member>
+</db:simplelist>
+<db:variablelist role="qmlvaluetypes">
+<db:varlistentry xml:id="i">
+<db:term><db:emphasis role="bold">I</db:emphasis></db:term>
+<db:listitem>
+<db:itemizedlist>
+<db:listitem>
+<db:para><db:link xlink:href="qml-int.xml">int</db:link></db:para>
+</db:listitem>
+</db:itemizedlist>
+</db:listitem>
+</db:varlistentry>
+</db:variablelist>
+<db:variablelist role="qmlvaluetypesbymodule QDoc.Test">
+<db:varlistentry>
+<db:term><db:link xlink:href="qml-int.xml" xlink:role="">int</db:link></db:term>
+<db:listitem>
+<db:para>An integer value type.</db:para>
+</db:listitem>
+</db:varlistentry>
+</db:variablelist>
+</db:section>
+</db:article>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/docbook/seenclass.xml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/docbook/seenclass.xml
new file mode 100644
index 000000000..abb697567
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/docbook/seenclass.xml
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<db:article xmlns:db="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:its="http://www.w3.org/2005/11/its" version="5.2" xml:lang="en">
+<db:info>
+<db:title its:translate="no">SeenClass Class</db:title>
+<db:productname>Test</db:productname>
+<db:titleabbrev>A test project for QDoc build artifacts</db:titleabbrev>
+<db:abstract>
+<db:para>A public but undocumented class.</db:para>
+<db:para>This class was introduced in Qt 2.0.</db:para>
+</db:abstract>
+</db:info>
+<db:variablelist its:translate="no">
+<db:varlistentry>
+<db:term>Header</db:term>
+<db:listitem>
+<db:para>SeenClass</db:para>
+</db:listitem>
+</db:varlistentry>
+<db:varlistentry>
+<db:term>Since</db:term>
+<db:listitem>
+<db:para>Qt 2.0</db:para>
+</db:listitem>
+</db:varlistentry>
+<db:varlistentry>
+<db:term>CMake</db:term>
+<db:listitem>
+<db:para>find_package(Qt6 REQUIRED COMPONENTS QDocTest)</db:para>
+<db:para>target_link_libraries(mytarget PRIVATE Qt6::QDocTest)</db:para>
+</db:listitem>
+</db:varlistentry>
+<db:varlistentry>
+<db:term>qmake</db:term>
+<db:listitem>
+<db:para>QT += testcpp</db:para>
+</db:listitem>
+</db:varlistentry>
+</db:variablelist>
+<db:section xml:id="details">
+<db:title>Detailed Description</db:title>
+</db:section>
+</db:article>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/docbook/test-cmaketest-cmakelists-txt.xml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/docbook/test-cmaketest-cmakelists-txt.xml
new file mode 100644
index 000000000..a29ba9a12
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/docbook/test-cmaketest-cmakelists-txt.xml
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<db:article xmlns:db="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:its="http://www.w3.org/2005/11/its" version="5.2" xml:lang="en">
+<db:info>
+<db:title>CMake Example Project</db:title>
+<db:productname>Test</db:productname>
+<db:titleabbrev>A test project for QDoc build artifacts</db:titleabbrev>
+<db:abstract>
+<db:para>A test project for QDoc build artifacts.</db:para></db:abstract>
+</db:info>
+<db:programlisting language="cpp" its:translate="no">cmake_minimum_required(VERSION 3.16)
+project (QDOCTEST)
+
+</db:programlisting>
+</db:article>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/docbook/test-cmaketest-example.xml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/docbook/test-cmaketest-example.xml
new file mode 100644
index 000000000..549aa1fec
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/docbook/test-cmaketest-example.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<db:article xmlns:db="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:its="http://www.w3.org/2005/11/its" version="5.2" xml:lang="en">
+<db:info>
+<db:title>CMake Example Project</db:title>
+<db:productname>Test</db:productname>
+<db:titleabbrev>A test project for QDoc build artifacts</db:titleabbrev>
+<db:abstract>
+<db:para>A test project for QDoc build artifacts.</db:para></db:abstract>
+</db:info>
+<db:mediaobject>
+<db:imageobject>
+<db:imagedata fileref="images/leonardo-da-vinci.png"/>
+</db:imageobject>
+</db:mediaobject>
+<db:section>
+<db:title>List of Files</db:title>
+<db:para>Files:</db:para>
+<db:section>
+<db:title>List of Files</db:title>
+<db:itemizedlist>
+<db:listitem>
+<db:para><db:link xlink:href="cmaketest/CMakeLists.txt">cmaketest/CMakeLists.txt</db:link></db:para></db:listitem>
+<db:listitem>
+<db:para><db:link xlink:href="cmaketest/main.cpp">cmaketest/main.cpp</db:link></db:para></db:listitem>
+</db:itemizedlist>
+</db:section>
+</db:section></db:article>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/docbook/test-cmaketest-main-cpp.xml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/docbook/test-cmaketest-main-cpp.xml
new file mode 100644
index 000000000..d7e7029a2
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/docbook/test-cmaketest-main-cpp.xml
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<db:article xmlns:db="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:its="http://www.w3.org/2005/11/its" version="5.2" xml:lang="en">
+<db:info>
+<db:title>CMake Example Project</db:title>
+<db:productname>Test</db:productname>
+<db:titleabbrev>A test project for QDoc build artifacts</db:titleabbrev>
+<db:abstract>
+<db:para>A test project for QDoc build artifacts.</db:para></db:abstract>
+</db:info>
+<db:programlisting language="cpp" its:translate="no">void main(){}
+
+</db:programlisting>
+</db:article>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/docbook/test-componentset-componentset-pro.xml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/docbook/test-componentset-componentset-pro.xml
new file mode 100644
index 000000000..606a5ac94
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/docbook/test-componentset-componentset-pro.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<db:article xmlns:db="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:its="http://www.w3.org/2005/11/its" version="5.2" xml:lang="en">
+<db:info>
+<db:title>QML Documentation Example</db:title>
+<db:productname>Test</db:productname>
+<db:titleabbrev>A test project for QDoc build artifacts</db:titleabbrev>
+<db:abstract>
+<db:para>Example for documenting QML types.</db:para>
+</db:abstract>
+</db:info>
+<db:programlisting language="cpp" its:translate="no">SOURCES = componentset.pro \
+ ProgressBar.qml \
+ Switch.qml \
+ TabWidget.qml \
+ uicomponents.qdoc
+
+</db:programlisting>
+</db:article>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/docbook/test-componentset-componentset-qml.xml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/docbook/test-componentset-componentset-qml.xml
new file mode 100644
index 000000000..34f5e59b0
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/docbook/test-componentset-componentset-qml.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<db:article xmlns:db="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:its="http://www.w3.org/2005/11/its" version="5.2" xml:lang="en">
+<db:info>
+<db:title>QML Documentation Example</db:title>
+<db:productname>Test</db:productname>
+<db:titleabbrev>A test project for QDoc build artifacts</db:titleabbrev>
+<db:abstract>
+<db:para>Example for documenting QML types.</db:para>
+</db:abstract>
+</db:info>
+<db:programlisting language="qml" its:translate="no">// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+import QtQuick 2.0
+
+Item {
+}
+
+</db:programlisting>
+</db:article>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/docbook/test-componentset-example.xml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/docbook/test-componentset-example.xml
new file mode 100644
index 000000000..a5ffd1cd0
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/docbook/test-componentset-example.xml
@@ -0,0 +1,55 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<db:article xmlns:db="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:its="http://www.w3.org/2005/11/its" version="5.2" xml:lang="en">
+<db:info>
+<db:title>QML Documentation Example</db:title>
+<db:productname>Test</db:productname>
+<db:titleabbrev>A test project for QDoc build artifacts</db:titleabbrev>
+<db:abstract>
+<db:para>Example for documenting QML types.</db:para>
+</db:abstract>
+</db:info>
+<db:para>This example demonstrates one of the ways to document QML types. It also generates a warning about a missing example image, on purpose.</db:para>
+<db:para>In particular, there are sample types that are documented with QDoc commands comments. There are documentation comments for the QML types and their public interfaces. The types are grouped into a module, the <db:link xlink:href="uicomponents-qmlmodule.xml">UI Components</db:link> module.</db:para>
+<db:para>The uicomponents.qdoc file generates the overview page for the <db:link xlink:href="uicomponents-qmlmodule.xml">UI Components</db:link> module page.</db:para>
+<db:para>The generated documentation is available in the <db:link xlink:href="uicomponents-qmlmodule.xml">UI Components</db:link> module.</db:para>
+<db:section xml:id="qml-class">
+<db:title>QML Class</db:title>
+<db:para>The QML types use the \qmltype to document the type. In addition, they have the \inmodule command in order for QDoc to associate them to the <db:code>UIComponents</db:code> module.</db:para>
+<db:para>QDoc uses the \brief command to place a basic description when listing the types.</db:para>
+</db:section>
+<db:section xml:id="properties-signals-handlers-and-methods">
+<db:title>Properties, Signals, Handlers, and Methods</db:title>
+<db:para>The types have their properties, signals, handlers, and methods defined in their respective QML files. QDoc associates the properties and methods to the types, therefore, you only need to place the documentation above the property, method, or signal.</db:para>
+<db:para>To document the type of a <db:emphasis>property alias</db:emphasis>, you must use the \qmlproperty command to specify the data type.</db:para>
+<db:programlisting language="cpp" its:translate="no">\qmlproperty int anAliasedProperty
+An aliased property of type int.
+</db:programlisting>
+<db:section xml:id="internal-documentation">
+<db:title>Internal Documentation</db:title>
+<db:para>You may declare that a documentation is for internal use by placing the \internal command after the beginning QDoc comment <db:code>/*</db:code>. QDoc will prevent the internal documentation from appearing in the public API.</db:para>
+<db:para>If you wish to omit certain parts of the documentation, you may use the \omit and \endomit command.</db:para>
+</db:section>
+</db:section>
+<db:section xml:id="qml-types-with-c-implementation">
+<db:title>QML Types with C++ Implementation</db:title>
+<db:para>This example only demonstrates the documentation for types in QML files, but the regular QML commands may be placed inside C++ classes to define the public API of the QML type.</db:para>
+</db:section>
+<db:section>
+<db:title>List of Files</db:title>
+<db:para>Files:</db:para>
+<db:section>
+<db:title>List of Files</db:title>
+<db:itemizedlist>
+<db:listitem>
+<db:para><db:link xlink:href="componentset/ProgressBar.qml">componentset/ProgressBar.qml</db:link></db:para></db:listitem>
+<db:listitem>
+<db:para><db:link xlink:href="componentset/Switch.qml">componentset/Switch.qml</db:link></db:para></db:listitem>
+<db:listitem>
+<db:para><db:link xlink:href="componentset/TabWidget.qml">componentset/TabWidget.qml</db:link></db:para></db:listitem>
+<db:listitem>
+<db:para><db:link xlink:href="componentset/componentset.pro">componentset/componentset.pro</db:link></db:para></db:listitem>
+<db:listitem>
+<db:para><db:link xlink:href="componentset/componentset.qml">componentset/componentset.qml</db:link></db:para></db:listitem>
+</db:itemizedlist>
+</db:section>
+</db:section></db:article>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/docbook/test-componentset-progressbar-qml.xml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/docbook/test-componentset-progressbar-qml.xml
new file mode 100644
index 000000000..e284bd51a
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/docbook/test-componentset-progressbar-qml.xml
@@ -0,0 +1,111 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<db:article xmlns:db="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:its="http://www.w3.org/2005/11/its" version="5.2" xml:lang="en">
+<db:info>
+<db:title>QML Documentation Example</db:title>
+<db:productname>Test</db:productname>
+<db:titleabbrev>A test project for QDoc build artifacts</db:titleabbrev>
+<db:abstract>
+<db:para>Example for documenting QML types.</db:para>
+</db:abstract>
+</db:info>
+<db:programlisting language="qml" its:translate="no">// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick 1.0
+
+/*!
+ \qmltype ProgressBar
+ \inqmlmodule UIComponents
+ \brief A component that shows the progress of an event.
+
+ A ProgressBar shows the linear progress of an event as its \l value.
+ The range is specified using the \l {minimum} and the \l{maximum} values.
+
+ The ProgressBar component is part of the \l {UI Components} module.
+
+ This documentation is part of the \l{componentset}{UIComponents} example.
+*/
+Item {
+ id: progressbar
+
+ /*!
+ The minimum value of the ProgressBar range.
+ The \l value must not be less than this value.
+ */
+ property int minimum: 0
+
+ /*!
+ The maximum value of the ProgressBar range.
+ The \l value must not be more than this value.
+ */
+ property int maximum: 100
+
+ /*!
+ The value of the progress.
+ */
+ property int value: 0
+
+ /*!
+ \qmlproperty color ProgressBar::color
+ The color of the ProgressBar's gradient. Must bind to a color type.
+
+ \omit
+ The &amp;quot;\qmlproperty &amp;lt;type&amp;gt; &amp;lt;property name&amp;gt;&amp;quot; is needed because
+ property alias need to have their types manually entered.
+
+ QDoc will not publish the documentation within omit and endomit.
+ \endomit
+
+ \sa secondColor
+ */
+ property alias color: gradient1.color
+
+ /*!
+ \qmlproperty color ProgressBar::secondColor
+ The second color of the ProgressBar's gradient.
+ Must bind to a color type.
+
+ \omit
+ The &amp;quot;\qmlproperty &amp;lt;type&amp;gt; &amp;lt;property name&amp;gt;&amp;quot; is needed because
+ property alias need to have their types manually entered.
+
+ QDoc will not publish the documentation within omit and endomit.
+ \endomit
+
+ \sa color
+ */
+ property alias secondColor: gradient2.color
+
+ width: 250; height: 23
+ clip: true
+
+ Rectangle {
+ id: highlight
+
+ /*!
+ An internal documentation comment. The widthDest property is not
+ a public API and therefore will not be exposed.
+ */
+ property int widthDest: ((progressbar.width * (value - minimum)) / (maximum - minimum) - 6)
+
+ width: highlight.widthDest
+ Behavior on width { SmoothedAnimation { velocity: 1200 } }
+
+ anchors { left: parent.left; top: parent.top; bottom: parent.bottom; margins: 3 }
+ radius: 1
+ gradient: Gradient {
+ GradientStop { id: gradient1; position: 0.0 }
+ GradientStop { id: gradient2; position: 1.0 }
+ }
+
+ }
+ Text {
+ anchors { right: highlight.right; rightMargin: 6; verticalCenter: parent.verticalCenter }
+ color: &amp;quot;white&amp;quot;
+ font.bold: true
+ text: Math.floor((value - minimum) / (maximum - minimum) * 100) + '%'
+ }
+}
+
+</db:programlisting>
+</db:article>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/docbook/test-componentset-switch-qml.xml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/docbook/test-componentset-switch-qml.xml
new file mode 100644
index 000000000..93083a18a
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/docbook/test-componentset-switch-qml.xml
@@ -0,0 +1,116 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<db:article xmlns:db="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:its="http://www.w3.org/2005/11/its" version="5.2" xml:lang="en">
+<db:info>
+<db:title>QML Documentation Example</db:title>
+<db:productname>Test</db:productname>
+<db:titleabbrev>A test project for QDoc build artifacts</db:titleabbrev>
+<db:abstract>
+<db:para>Example for documenting QML types.</db:para>
+</db:abstract>
+</db:info>
+<db:programlisting language="qml" its:translate="no">// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick 1.0
+
+/*!
+ \qmltype ToggleSwitch
+ \inqmlmodule UIComponents
+ \brief A component that can be turned on or off.
+
+ A toggle switch has two states: an \c on and an \c off state. The \c off
+ state is when the \l on property is set to \c false.
+
+ The ToggleSwitch component is part of the \l {UI Components} module.
+
+ This documentation is part of the \l{componentset}{UIComponents} example.
+
+*/
+Item {
+ id: toggleswitch
+ width: background.width; height: background.height
+
+ /*!
+ Indicates the state of the switch. If \c false, then the switch is in
+ the \c off state.
+
+ \omit
+ The \qmlproperty &amp;lt;type&amp;gt; &amp;lt;propertyname&amp;gt; is not necessary as QDoc
+ will associate this property to the ToggleSwitch
+
+ QDoc will not publish the documentation within omit and endomit.
+ \endomit
+ */
+ property bool on: false
+
+ /*!
+ A method to toggle the switch. If the switch is \c on, the toggling it
+ will turn it \c off. Toggling a switch in the \c off position will
+ turn it \c on.
+ */
+ function toggle() {
+ if (toggleswitch.state == &amp;quot;on&amp;quot;)
+ toggleswitch.state = &amp;quot;off&amp;quot;;
+ else
+ toggleswitch.state = &amp;quot;on&amp;quot;;
+ }
+
+ /*!
+ \internal
+
+ An internal function to synchronize the switch's internals. This
+ function is not for public access. The \internal command will
+ prevent QDoc from publishing this comment in the public API.
+ */
+ function releaseSwitch() {
+ if (knob.x == 1) {
+ if (toggleswitch.state == &amp;quot;off&amp;quot;) return;
+ }
+ if (knob.x == 78) {
+ if (toggleswitch.state == &amp;quot;on&amp;quot;) return;
+ }
+ toggle();
+ }
+
+ Rectangle {
+ id: background
+ width: 130; height: 48
+ radius: 48
+ color: &amp;quot;lightsteelblue&amp;quot;
+ MouseArea { anchors.fill: parent; onClicked: toggle() }
+ }
+
+ Rectangle {
+ id: knob
+ width: 48; height: 48
+ radius: width
+ color: &amp;quot;lightblue&amp;quot;
+
+ MouseArea {
+ anchors.fill: parent
+ drag.target: knob; drag.axis: Drag.XAxis; drag.minimumX: 1; drag.maximumX: 78
+ onClicked: toggle()
+ onReleased: releaseSwitch()
+ }
+ }
+
+ states: [
+ State {
+ name: &amp;quot;on&amp;quot;
+ PropertyChanges { target: knob; x: 78 }
+ PropertyChanges { target: toggleswitch; on: true }
+ },
+ State {
+ name: &amp;quot;off&amp;quot;
+ PropertyChanges { target: knob; x: 1 }
+ PropertyChanges { target: toggleswitch; on: false }
+ }
+ ]
+
+ transitions: Transition {
+ NumberAnimation { properties: &amp;quot;x&amp;quot;; easing.type: Easing.InOutQuad; duration: 200 }
+ }
+}
+
+</db:programlisting>
+</db:article>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/docbook/test-componentset-tabwidget-qml.xml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/docbook/test-componentset-tabwidget-qml.xml
new file mode 100644
index 000000000..24c672b9f
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/docbook/test-componentset-tabwidget-qml.xml
@@ -0,0 +1,158 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<db:article xmlns:db="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:its="http://www.w3.org/2005/11/its" version="5.2" xml:lang="en">
+<db:info>
+<db:title>QML Documentation Example</db:title>
+<db:productname>Test</db:productname>
+<db:titleabbrev>A test project for QDoc build artifacts</db:titleabbrev>
+<db:abstract>
+<db:para>Example for documenting QML types.</db:para>
+</db:abstract>
+</db:info>
+<db:programlisting language="qml" its:translate="no">// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick 1.0
+
+/*!
+ \qmltype TabWidget
+ \inqmlmodule UIComponents
+ \brief A widget that places its children as tabs.
+
+ A TabWidget places its children as tabs in a view. Selecting
+ a tab involves selecting the tab at the top.
+
+ The TabWidget component is part of the \l {UI Components} module.
+
+ This documentation is part of the \l{componentset}{UIComponents} example.
+
+ \section1 Adding Tabs
+
+ To add a tab, declare the tab as a child of the TabWidget.
+
+ \code
+ TabWidget {
+ id: tabwidget
+
+ Rectangle {
+ id: tab1
+ color: &amp;quot;red&amp;quot;
+ //... omitted
+ }
+ Rectangle {
+ id: tab2
+ color: &amp;quot;blue&amp;quot;
+ //... omitted
+ }
+
+ }
+ \endcode
+
+*/
+Item {
+ id: tabWidget
+
+ /*!
+ \internal
+
+ Setting the default property to stack.children means any child items
+ of the TabWidget are actually added to the 'stack' item's children.
+
+ See the \l{&amp;quot;Property Binding in QML&amp;quot;}
+ documentation for details on default properties.
+
+ This is an implementation detail, not meant for public knowledge. Putting
+ the \internal command at the beginning will cause QDoc to not publish this
+ documentation in the public API page.
+
+ Normally, a property alias needs to have a
+ &amp;quot;\qmlproperty &amp;lt;type&amp;gt; &amp;lt;propertyname&amp;gt;&amp;quot; to assign the alias a type.
+
+ */
+ default property alias content: stack.children
+
+ /*!
+ The currently active tab in the TabWidget.
+ */
+ property int current: 0
+
+ /*!
+ A sample \c{read-only} property.
+ A contrived property to demonstrate QDoc's ability to detect
+ read-only properties.
+
+ The signature is:
+ \code
+ readonly property int sampleReadOnlyProperty: 0
+ \endcode
+
+ Note that the property must be initialized to a value.
+
+ */
+ readonly property int sampleReadOnlyProperty: 0
+
+ /*!
+ \internal
+
+ This handler is an implementation
+ detail. The \c{\internal} command will prevent QDoc from publishing this
+ documentation on the public API.
+ */
+ onCurrentChanged: setOpacities()
+ Component.onCompleted: setOpacities()
+
+ /*!
+ \internal
+
+ An internal function to set the opacity.
+ The \internal command will prevent QDoc from publishing this
+ documentation on the public API.
+ */
+ function setOpacities() {
+ for (var i = 0; i &amp;lt; stack.children.length; ++i) {
+ stack.children[i].opacity = (i == current ? 1 : 0)
+ }
+ }
+
+ Row {
+ id: header
+
+ Repeater {
+ model: stack.children.length
+ delegate: Rectangle {
+ width: tabWidget.width / stack.children.length; height: 36
+
+ Rectangle {
+ width: parent.width; height: 1
+ anchors { bottom: parent.bottom; bottomMargin: 1 }
+ color: &amp;quot;#acb2c2&amp;quot;
+ }
+ BorderImage {
+ anchors { fill: parent; leftMargin: 2; topMargin: 5; rightMargin: 1 }
+ border { left: 7; right: 7 }
+ source: &amp;quot;tab.png&amp;quot;
+ visible: tabWidget.current == index
+ }
+ Text {
+ horizontalAlignment: Qt.AlignHCenter; verticalAlignment: Qt.AlignVCenter
+ anchors.fill: parent
+ text: stack.children[index].title
+ elide: Text.ElideRight
+ font.bold: tabWidget.current == index
+ }
+ MouseArea {
+ anchors.fill: parent
+ onClicked: tabWidget.current = index
+ }
+ }
+ }
+ }
+
+ Item {
+ id: stack
+ width: tabWidget.width
+ anchors.top: header.bottom; anchors.bottom: tabWidget.bottom
+ }
+}
+
+</db:programlisting>
+</db:article>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/docbook/test-demos-demo-demo-cpp.xml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/docbook/test-demos-demo-demo-cpp.xml
new file mode 100644
index 000000000..e10690185
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/docbook/test-demos-demo-demo-cpp.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<db:article xmlns:db="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:its="http://www.w3.org/2005/11/its" version="5.2" xml:lang="en">
+<db:info>
+<db:title>Demo</db:title>
+<db:productname>Test</db:productname>
+<db:titleabbrev>A test project for QDoc build artifacts</db:titleabbrev>
+<db:abstract>
+<db:para>A test project for QDoc build artifacts.</db:para></db:abstract>
+</db:info>
+<db:programlisting language="cpp" its:translate="no">// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+bool isOverThousand(int n)
+{
+ if (n &amp;gt; 1'000)
+ return true;
+ return false;
+}
+
+</db:programlisting>
+</db:article>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/docbook/test-demos-demo-demo-pro.xml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/docbook/test-demos-demo-demo-pro.xml
new file mode 100644
index 000000000..a51cb5a2e
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/docbook/test-demos-demo-demo-pro.xml
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<db:article xmlns:db="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:its="http://www.w3.org/2005/11/its" version="5.2" xml:lang="en">
+<db:info>
+<db:title>Demo</db:title>
+<db:productname>Test</db:productname>
+<db:titleabbrev>A test project for QDoc build artifacts</db:titleabbrev>
+<db:abstract>
+<db:para>A test project for QDoc build artifacts.</db:para></db:abstract>
+</db:info>
+<db:programlisting language="cpp" its:translate="no">TEMPLATE = aux
+message(&amp;quot;Nothing to see here.&amp;quot;)
+
+</db:programlisting>
+</db:article>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/docbook/test-demos-demo-dontxclude-cmakelists-txt.xml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/docbook/test-demos-demo-dontxclude-cmakelists-txt.xml
new file mode 100644
index 000000000..4cfd84878
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/docbook/test-demos-demo-dontxclude-cmakelists-txt.xml
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<db:article xmlns:db="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:its="http://www.w3.org/2005/11/its" version="5.2" xml:lang="en">
+<db:info>
+<db:title>Demo</db:title>
+<db:productname>Test</db:productname>
+<db:titleabbrev>A test project for QDoc build artifacts</db:titleabbrev>
+<db:abstract>
+<db:para>A test project for QDoc build artifacts.</db:para></db:abstract>
+</db:info>
+<db:programlisting language="cpp" its:translate="no">cmake_minimum_required(VERSION 3.16)
+project (DONTXCLUDEDIRS_QDOCTEST)
+
+</db:programlisting>
+</db:article>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/docbook/test-demos-demo-example.xml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/docbook/test-demos-demo-example.xml
new file mode 100644
index 000000000..b81f636ba
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/docbook/test-demos-demo-example.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<db:article xmlns:db="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:its="http://www.w3.org/2005/11/its" version="5.2" xml:lang="en">
+<db:info>
+<db:title>Demo</db:title>
+<db:productname>Test</db:productname>
+<db:titleabbrev>A test project for QDoc build artifacts</db:titleabbrev>
+<db:abstract>
+<db:para>A test project for QDoc build artifacts.</db:para></db:abstract>
+</db:info>
+<db:mediaobject>
+<db:imageobject>
+<db:imagedata fileref="images/leonardo-da-vinci.png"/>
+</db:imageobject>
+</db:mediaobject>
+<db:programlisting language="cpp" its:translate="no"> if (n &amp;gt; 1'000)
+ return true;
+</db:programlisting>
+<db:section>
+<db:title>List of Files</db:title>
+<db:para>Files:</db:para>
+<db:section>
+<db:title>List of Files</db:title>
+<db:itemizedlist>
+<db:listitem>
+<db:para><db:link xlink:href="demos/demo/demo.cpp">demos/demo/demo.cpp</db:link></db:para></db:listitem>
+<db:listitem>
+<db:para><db:link xlink:href="demos/demo/demo.pro">demos/demo/demo.pro</db:link></db:para></db:listitem>
+<db:listitem>
+<db:para><db:link xlink:href="demos/demo/dontxclude/CMakeLists.txt">demos/demo/dontxclude/CMakeLists.txt</db:link></db:para></db:listitem>
+</db:itemizedlist>
+</db:section>
+</db:section></db:article>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/docbook/test-demos-hidden-example.xml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/docbook/test-demos-hidden-example.xml
new file mode 100644
index 000000000..b3a1e7996
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/docbook/test-demos-hidden-example.xml
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<db:article xmlns:db="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:its="http://www.w3.org/2005/11/its" version="5.2" xml:lang="en">
+<db:info>
+<db:title>Hidden Demo</db:title>
+<db:productname>Test</db:productname>
+<db:titleabbrev>A test project for QDoc build artifacts</db:titleabbrev>
+<db:abstract>
+<db:para>Tagged 'broken', does not appear in examples-manifest.xml.</db:para>
+</db:abstract>
+</db:info>
+<db:para>Also missing an image, but that's OK as it's broken anyway.</db:para>
+</db:article>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/docbook/test-empty-qmlmodule.xml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/docbook/test-empty-qmlmodule.xml
new file mode 100644
index 000000000..f58917730
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/docbook/test-empty-qmlmodule.xml
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<db:article xmlns:db="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:its="http://www.w3.org/2005/11/its" version="5.2" xml:lang="en">
+<db:info>
+<db:title its:translate="no">No QML Types Here</db:title>
+<db:productname>Test</db:productname>
+<db:titleabbrev>A test project for QDoc build artifacts</db:titleabbrev>
+<db:abstract>
+<db:para>A QML module with no member types.</db:para>
+</db:abstract>
+</db:info>
+<db:anchor xml:id="details"/>
+</db:article>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/docbook/test-nover-qmlmodule.xml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/docbook/test-nover-qmlmodule.xml
new file mode 100644
index 000000000..091fe4358
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/docbook/test-nover-qmlmodule.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<db:article xmlns:db="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:its="http://www.w3.org/2005/11/its" version="5.2" xml:lang="en">
+<db:info>
+<db:title its:translate="no">Versionless QML Module</db:title>
+<db:productname>Test</db:productname>
+<db:titleabbrev>A test project for QDoc build artifacts</db:titleabbrev>
+<db:abstract>
+<db:para>QML Types for the Test module without version.</db:para>
+<db:para>This module is in <db:emphasis>Tech Preview</db:emphasis> state.</db:para>
+<db:para>This module was introduced in Qt 1.1.</db:para>
+</db:abstract>
+</db:info>
+<db:para>This module is in <db:emphasis>Tech Preview</db:emphasis> state.</db:para>
+<db:para>This module was introduced in Qt 1.1.</db:para>
+<db:anchor xml:id="details"/>
+<db:variablelist role="members">
+<db:varlistentry>
+<db:term><db:link xlink:href="qml-test-nover-doctest.xml" xlink:role="">DocTest</db:link></db:term>
+<db:listitem>
+<db:para>Shadows the type name in QDoc.Test module.</db:para>
+</db:listitem>
+</db:varlistentry>
+<db:varlistentry>
+<db:term><db:link xlink:href="qml-test-nover-typenoversion.xml" xlink:role="">TypeNoVersion</db:link></db:term>
+<db:listitem>
+<db:para>Another QML type documented in a .cpp file.</db:para>
+</db:listitem>
+</db:varlistentry>
+</db:variablelist>
+</db:article>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/docbook/testcpp-module.xml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/docbook/testcpp-module.xml
new file mode 100644
index 000000000..d8e4647ac
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/docbook/testcpp-module.xml
@@ -0,0 +1,91 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<db:article xmlns:db="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:its="http://www.w3.org/2005/11/its" version="5.2" xml:lang="en">
+<db:info>
+<db:title its:translate="no">QDoc Test C++ Classes</db:title>
+<db:productname>Test</db:productname>
+<db:titleabbrev>A test project for QDoc build artifacts</db:titleabbrev>
+<db:abstract>
+<db:para>A test module page.</db:para>
+<db:para>This module was introduced in Qt 2.0.</db:para>
+</db:abstract>
+</db:info>
+<db:para>A test module page.</db:para>
+<db:para>This module was introduced in Qt 2.0.</db:para>
+<db:section xml:id="namespaces">
+<db:title>Namespaces</db:title>
+<db:variablelist role="namespaces">
+<db:varlistentry>
+<db:term><db:link xlink:href="crossmoduleref.xml" xlink:role="namespace">CrossModuleRef</db:link></db:term>
+<db:listitem>
+<db:para>Namespace that has documented functions in multiple modules.</db:para>
+</db:listitem>
+</db:varlistentry>
+<db:varlistentry>
+<db:term><db:link xlink:href="testqdoc.xml" xlink:role="namespace">TestQDoc</db:link></db:term>
+<db:listitem>
+<db:para>A namespace.</db:para>
+</db:listitem>
+</db:varlistentry>
+</db:variablelist>
+</db:section>
+<db:section xml:id="classes">
+<db:title>Classes</db:title>
+<db:variablelist role="classes">
+<db:varlistentry>
+<db:term><db:link xlink:href="seenclass.xml" xlink:role="class">SeenClass</db:link></db:term>
+<db:listitem>
+<db:para>A public but undocumented class.</db:para>
+</db:listitem>
+</db:varlistentry>
+<db:varlistentry>
+<db:term><db:link xlink:href="testqdoc-test.xml" xlink:role="class">TestQDoc::Test</db:link></db:term>
+<db:listitem>
+<db:para>A class in a namespace.</db:para>
+</db:listitem>
+</db:varlistentry>
+<db:varlistentry>
+<db:term><db:link xlink:href="testqdoc-testderived.xml" xlink:role="class">TestQDoc::TestDerived</db:link></db:term>
+<db:listitem>
+<db:para>A class in a namespace, derived from Test.</db:para>
+</db:listitem>
+</db:varlistentry>
+</db:variablelist>
+</db:section>
+<db:section xml:id="details">
+<db:title>Detailed Description</db:title>
+<db:note>
+<db:para>This is just a test. /* Look, Ma! {I'm made of arguments!} */</db:para>
+</db:note>
+<db:para>This module was introduced in version 5.15.</db:para>
+<db:para>1.0</db:para>
+<db:section xml:id="linking-to-function-like-things">
+<db:title>Linking to function-like things</db:title>
+<db:itemizedlist>
+<db:listitem>
+<db:para><db:link xlink:href="testqdoc-test.xml#someFunctionDefaultArg">someFunctionDefaultArg</db:link>()</db:para>
+</db:listitem>
+<db:listitem>
+<db:para><db:link xlink:href="testqdoc-test.xml#QDOCTEST_MACRO2">QDOCTEST_MACRO2</db:link>()</db:para>
+</db:listitem>
+<db:listitem>
+<db:para><db:link xlink:href="testqdoc-test.xml#QDOCTEST_MACRO2">QDOCTEST_MACRO2</db:link>(int &amp;x)</db:para>
+</db:listitem>
+<db:listitem>
+<db:para><db:link xlink:href="testcpp-module.xml#section">section()</db:link></db:para>
+</db:listitem>
+<db:listitem>
+<db:para><db:link xlink:href="testcpp-module.xml#section">section() is a section title</db:link></db:para>
+</db:listitem>
+<db:listitem>
+<db:para><db:link xlink:href="testqdoc-test.xml#Test">open( parenthesis</db:link></db:para>
+</db:listitem>
+<db:listitem>
+<db:para><db:link xlink:href="https://en.cppreference.com/w/cpp/utility/move">C++11 added std::move(T&amp;&amp; t)</db:link></db:para>
+</db:listitem>
+</db:itemizedlist>
+<db:section xml:id="section">
+<db:title>section()</db:title>
+</db:section>
+</db:section>
+</db:section>
+</db:article>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/docbook/testqdoc-test.xml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/docbook/testqdoc-test.xml
new file mode 100644
index 000000000..bcb583839
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/docbook/testqdoc-test.xml
@@ -0,0 +1,376 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<db:article xmlns:db="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:its="http://www.w3.org/2005/11/its" version="5.2" xml:lang="en">
+<db:info>
+<db:title its:translate="no">Test Class</db:title>
+<db:subtitle its:translate="no">TestQDoc::Test</db:subtitle>
+<db:productname>Test</db:productname>
+<db:titleabbrev>A test project for QDoc build artifacts</db:titleabbrev>
+<db:abstract>
+<db:para>A class in a namespace.</db:para>
+<db:para>This class was introduced in Qt 1.1.</db:para>
+<db:note>
+<db:para>All functions in this class are <db:link xlink:href="https://doc.qt.io/qt/17-qdoc-commands-thread.html#reentrant-command">reentrant</db:link> with the following exceptions:</db:para>
+<db:para>These functions are not <db:link xlink:href="https://doc.qt.io/qt/17-qdoc-commands-thread.html#reentrant-command">reentrant</db:link>:</db:para>
+<db:itemizedlist>
+<db:listitem>
+<db:para><db:link xlink:href="testqdoc-test.xml#someFunctionDefaultArg">someFunctionDefaultArg(int i, bool b) const</db:link></db:para>
+</db:listitem>
+</db:itemizedlist>
+</db:note>
+</db:abstract>
+</db:info>
+<db:variablelist its:translate="no">
+<db:varlistentry>
+<db:term>Header</db:term>
+<db:listitem>
+<db:para>Test</db:para>
+</db:listitem>
+</db:varlistentry>
+<db:varlistentry>
+<db:term>Since</db:term>
+<db:listitem>
+<db:para>Qt 1.1</db:para>
+</db:listitem>
+</db:varlistentry>
+<db:varlistentry>
+<db:term>CMake</db:term>
+<db:listitem>
+<db:para>find_package(Qt6 REQUIRED COMPONENTS QDocTest)</db:para>
+<db:para>target_link_libraries(mytarget PRIVATE Qt6::QDocTest)</db:para>
+</db:listitem>
+</db:varlistentry>
+<db:varlistentry>
+<db:term>qmake</db:term>
+<db:listitem>
+<db:para>QT += testcpp</db:para>
+</db:listitem>
+</db:varlistentry>
+<db:varlistentry>
+<db:term>In QML</db:term>
+<db:listitem>
+<db:para><db:link xlink:href="qml-qdoc-test-type.xml" xlink:role="">Type</db:link></db:para>
+</db:listitem>
+</db:varlistentry>
+<db:varlistentry>
+<db:term>Inherited By</db:term>
+<db:listitem>
+<db:para><db:link xlink:href="testqdoc-testderived.xml" xlink:role="class">TestQDoc::TestDerived</db:link></db:para>
+</db:listitem>
+</db:varlistentry>
+<db:varlistentry>
+<db:term>Group</db:term>
+<db:listitem>
+<db:para>Test is part of <db:simplelist><db:member>testgroup</db:member><db:member><db:link xlink:href="cpptypes.xml">Test C++ Types</db:link></db:member></db:simplelist>
+</db:para>
+</db:listitem>
+</db:varlistentry>
+</db:variablelist>
+<db:section xml:id="details">
+<db:title>Detailed Description</db:title>
+</db:section>
+<db:section xml:id="member-type-documentation">
+<db:title>Member Type Documentation</db:title>
+<db:section xml:id="SomeType-typedef">
+<db:title its:translate="no">Test::SomeType</db:title>
+<db:typedefsynopsis>
+<db:typedefname>SomeType</db:typedefname>
+<db:synopsisinfo role="access">public</db:synopsisinfo>
+<db:synopsisinfo role="status">active</db:synopsisinfo>
+<db:synopsisinfo role="threadsafeness">unspecified</db:synopsisinfo>
+</db:typedefsynopsis>
+<db:para>A typedef.</db:para>
+</db:section>
+</db:section>
+<db:section xml:id="member-function-documentation">
+<db:title>Member Function Documentation</db:title>
+<db:section xml:id="overload">
+<db:title its:translate="no">[protected] void Test::overload()</db:title>
+<db:bridgehead renderas="sect2" xml:id="overload-1" its:translate="no">[protected, since Test 1.2] void Test::overload(bool <db:emphasis>b</db:emphasis>)</db:bridgehead>
+<db:para>Overloads that share a documentation comment, optionally taking a parameter <db:code its:translate="no" role="parameter">b</db:code>.</db:para>
+</db:section>
+<db:section xml:id="Test">
+<db:title its:translate="no">Test::Test()</db:title>
+<db:constructorsynopsis>
+<db:methodname>Test</db:methodname>
+<db:void/>
+<db:synopsisinfo role="meta">constructor</db:synopsisinfo>
+<db:synopsisinfo role="signature">Test()</db:synopsisinfo>
+<db:synopsisinfo role="access">public</db:synopsisinfo>
+<db:synopsisinfo role="status">active</db:synopsisinfo>
+<db:synopsisinfo role="threadsafeness">unspecified</db:synopsisinfo>
+</db:constructorsynopsis>
+<db:para>The constructor is deleted.</db:para>
+</db:section>
+<db:section xml:id="funcPtr">
+<db:title its:translate="no">void (*)(bool) Test::funcPtr(bool <db:emphasis>b</db:emphasis>, const char *<db:emphasis>s</db:emphasis>)</db:title>
+<db:methodsynopsis>
+<db:type>void (*)(bool)</db:type>
+<db:methodname>funcPtr</db:methodname>
+<db:methodparam>
+<db:type>bool</db:type>
+<db:parameter>b</db:parameter>
+</db:methodparam>
+<db:methodparam>
+<db:type>const char *</db:type>
+<db:parameter>s</db:parameter>
+</db:methodparam>
+<db:synopsisinfo role="meta">plain</db:synopsisinfo>
+<db:synopsisinfo role="signature">void (*)(bool) funcPtr(bool b, const char *s)</db:synopsisinfo>
+<db:synopsisinfo role="access">public</db:synopsisinfo>
+<db:synopsisinfo role="status">active</db:synopsisinfo>
+<db:synopsisinfo role="threadsafeness">unspecified</db:synopsisinfo>
+</db:methodsynopsis>
+<db:para>Returns a pointer to a function that takes a boolean. Uses <db:code its:translate="no" role="parameter">b</db:code> and <db:code its:translate="no" role="parameter">s</db:code>.</db:para>
+</db:section>
+<db:section xml:id="inlineFunction">
+<db:title its:translate="no">void Test::inlineFunction()</db:title>
+<db:methodsynopsis>
+<db:void/>
+<db:methodname>inlineFunction</db:methodname>
+<db:void/>
+<db:synopsisinfo role="meta">plain</db:synopsisinfo>
+<db:synopsisinfo role="signature">void inlineFunction()</db:synopsisinfo>
+<db:synopsisinfo role="access">public</db:synopsisinfo>
+<db:synopsisinfo role="status">active</db:synopsisinfo>
+<db:synopsisinfo role="threadsafeness">unspecified</db:synopsisinfo>
+</db:methodsynopsis>
+<db:para>An inline function, documented using the \fn QDoc command.</db:para>
+</db:section>
+<db:section xml:id="methodWithEmDashInItsDocs">
+<db:title its:translate="no">void Test::methodWithEmDashInItsDocs()</db:title>
+<db:methodsynopsis>
+<db:void/>
+<db:methodname>methodWithEmDashInItsDocs</db:methodname>
+<db:void/>
+<db:synopsisinfo role="meta">plain</db:synopsisinfo>
+<db:synopsisinfo role="signature">void methodWithEmDashInItsDocs()</db:synopsisinfo>
+<db:synopsisinfo role="access">public</db:synopsisinfo>
+<db:synopsisinfo role="status">active</db:synopsisinfo>
+<db:synopsisinfo role="threadsafeness">unspecified</db:synopsisinfo>
+</db:methodsynopsis>
+<db:para>This method has em dashes in its documentation—as you'll find represented by <db:code>---</db:code> in the sources—here and there. The important bit to note is that when passed e.g. to the \c command, the three hyphens are processed as input to the command and not replaced by an em dash.</db:para>
+<db:para>-----------------------------------------------------------------------</db:para>
+<db:para>People can still add a bunch of dashes, though, without QDoc replacing them all with a series of em dashes.</db:para>
+<db:para>—You can also start a new paragraph with an em dash, if you want to.</db:para>
+<db:section>
+<db:title>See Also</db:title>
+<db:para><db:emphasis>See also </db:emphasis>
+<db:simplelist type="vert" role="see-also">
+<db:member><db:link xlink:href="testqdoc-test.xml#methodWithEnDashInItsDocs">methodWithEnDashInItsDocs</db:link></db:member>
+</db:simplelist>
+</db:para>
+</db:section>
+</db:section>
+<db:section xml:id="methodWithEnDashInItsDocs">
+<db:title its:translate="no">void Test::methodWithEnDashInItsDocs()</db:title>
+<db:methodsynopsis>
+<db:void/>
+<db:methodname>methodWithEnDashInItsDocs</db:methodname>
+<db:void/>
+<db:synopsisinfo role="meta">plain</db:synopsisinfo>
+<db:synopsisinfo role="signature">void methodWithEnDashInItsDocs()</db:synopsisinfo>
+<db:synopsisinfo role="access">public</db:synopsisinfo>
+<db:synopsisinfo role="status">active</db:synopsisinfo>
+<db:synopsisinfo role="threadsafeness">unspecified</db:synopsisinfo>
+</db:methodsynopsis>
+<db:para>This method has en dashes in its documentation – as you'll find represented by <db:code>--</db:code> in the sources – here and there. The important bit to note is that when passed e.g. to the \c command, the two hyphens are processed as input to the command and not replaced by an en dash. This also applies to code blocks, where otherwise, the decrement operator would get completely borked:</db:para>
+<db:programlisting language="cpp" its:translate="no">for (int i = 42; i &amp;gt; 0; --i)
+ // Do something cool during countdown.
+</db:programlisting>
+<db:para>...as it would be silly if this would output –i instead of <db:code>--i</db:code>.</db:para>
+<db:para>-----------------------------------------------------------------------</db:para>
+<db:para>It still allows people to add a bunch of dashes, though, without replacing them all with a series of en dashes. Of course, they might want to use the \hr command instead, like this:</db:para>
+<db:para>– You can also start a new paragraph with an en dash, if you want to.</db:para>
+<db:section>
+<db:title>See Also</db:title>
+<db:para><db:emphasis>See also </db:emphasis>
+<db:simplelist type="vert" role="see-also">
+<db:member><db:link xlink:href="">methodWithEnDashInItsDocs</db:link></db:member>
+</db:simplelist>
+</db:para>
+</db:section>
+</db:section>
+<db:section xml:id="someFunction">
+<db:title its:translate="no">[since Test 1.0] int Test::someFunction(<db:emphasis>int</db:emphasis>, int <db:emphasis>v</db:emphasis> = 0)</db:title>
+<db:methodsynopsis>
+<db:type>int</db:type>
+<db:methodname>someFunction</db:methodname>
+<db:methodparam>
+<db:type>int</db:type>
+<db:parameter></db:parameter>
+</db:methodparam>
+<db:methodparam>
+<db:type>int</db:type>
+<db:parameter>v</db:parameter>
+<db:initializer>0</db:initializer>
+</db:methodparam>
+<db:synopsisinfo role="meta">plain</db:synopsisinfo>
+<db:synopsisinfo role="signature">int someFunction(int, int v)</db:synopsisinfo>
+<db:synopsisinfo role="access">public</db:synopsisinfo>
+<db:synopsisinfo role="status">active</db:synopsisinfo>
+<db:synopsisinfo role="threadsafeness">unspecified</db:synopsisinfo>
+</db:methodsynopsis>
+<db:para>Function that takes a parameter <db:code its:translate="no" role="parameter">v</db:code>. Also returns the value of <db:code its:translate="no" role="parameter">v</db:code>.</db:para>
+<db:para>This function was introduced in Test 1.0.</db:para>
+</db:section>
+<db:section xml:id="someFunctionDefaultArg">
+<db:title its:translate="no">[since 2.0] void Test::someFunctionDefaultArg(int <db:emphasis>i</db:emphasis>, bool <db:emphasis>b</db:emphasis> = false) const</db:title>
+<db:methodsynopsis>
+<db:modifier>const</db:modifier>
+<db:void/>
+<db:methodname>someFunctionDefaultArg</db:methodname>
+<db:methodparam>
+<db:type>int</db:type>
+<db:parameter>i</db:parameter>
+</db:methodparam>
+<db:methodparam>
+<db:type>bool</db:type>
+<db:parameter>b</db:parameter>
+<db:initializer>false</db:initializer>
+</db:methodparam>
+<db:synopsisinfo role="meta">plain</db:synopsisinfo>
+<db:synopsisinfo role="signature">void someFunctionDefaultArg(int i, bool b) const</db:synopsisinfo>
+<db:synopsisinfo role="access">public</db:synopsisinfo>
+<db:synopsisinfo role="status">active</db:synopsisinfo>
+<db:synopsisinfo role="threadsafeness">non-reentrant</db:synopsisinfo>
+</db:methodsynopsis>
+<db:para>Function that takes a parameter <db:code its:translate="no" role="parameter">i</db:code> and <db:code its:translate="no" role="parameter">b</db:code>.</db:para>
+<db:warning>
+<db:para>This function is not <db:link xlink:href="https://doc.qt.io/qt/17-qdoc-commands-thread.html#reentrant-command">reentrant</db:link>.</db:para>
+</db:warning><db:para>This function was introduced in Qt 2.0.</db:para>
+</db:section>
+<db:section xml:id="virtualFun">
+<db:title its:translate="no">[virtual] void Test::virtualFun()</db:title>
+<db:methodsynopsis>
+<db:modifier>virtual</db:modifier>
+<db:void/>
+<db:methodname>virtualFun</db:methodname>
+<db:void/>
+<db:synopsisinfo role="meta">plain</db:synopsisinfo>
+<db:synopsisinfo role="signature">void virtualFun()</db:synopsisinfo>
+<db:synopsisinfo role="access">public</db:synopsisinfo>
+<db:synopsisinfo role="status">active</db:synopsisinfo>
+<db:synopsisinfo role="threadsafeness">unspecified</db:synopsisinfo>
+</db:methodsynopsis>
+<db:para>Function that must be reimplemented.</db:para>
+</db:section>
+<db:section xml:id="operator-eq">
+<db:title its:translate="no">TestQDoc::Test &amp;Test::operator=(TestQDoc::Test &amp;&amp;<db:emphasis>other</db:emphasis>)</db:title>
+<db:methodsynopsis>
+<db:type>TestQDoc::Test &amp;</db:type>
+<db:methodname>operator=</db:methodname>
+<db:methodparam>
+<db:type>TestQDoc::Test &amp;&amp;</db:type>
+<db:parameter>other</db:parameter>
+</db:methodparam>
+<db:synopsisinfo role="meta">move-assign</db:synopsisinfo>
+<db:synopsisinfo role="signature">TestQDoc::Test &amp; operator=(TestQDoc::Test &amp;&amp;other)</db:synopsisinfo>
+<db:synopsisinfo role="access">public</db:synopsisinfo>
+<db:synopsisinfo role="status">active</db:synopsisinfo>
+<db:synopsisinfo role="threadsafeness">unspecified</db:synopsisinfo>
+</db:methodsynopsis>
+<db:para>The move assignment operator is deleted. <db:code its:translate="no" role="parameter">other</db:code> cannot be moved from.</db:para>
+</db:section>
+</db:section>
+<db:section xml:id="related-non-members">
+<db:title>Related Non-Members</db:title>
+<db:section xml:id="operator-eq-eq">
+<db:title its:translate="no">bool operator==(const TestQDoc::Test &amp;<db:emphasis>lhs</db:emphasis>, const TestQDoc::Test &amp;<db:emphasis>rhs</db:emphasis>)</db:title>
+<db:methodsynopsis>
+<db:type>bool</db:type>
+<db:methodname>operator==</db:methodname>
+<db:methodparam>
+<db:type>const TestQDoc::Test &amp;</db:type>
+<db:parameter>lhs</db:parameter>
+</db:methodparam>
+<db:methodparam>
+<db:type>const TestQDoc::Test &amp;</db:type>
+<db:parameter>rhs</db:parameter>
+</db:methodparam>
+<db:synopsisinfo role="meta">plain</db:synopsisinfo>
+<db:synopsisinfo role="signature">bool operator==(const TestQDoc::Test &amp;lhs, const TestQDoc::Test &amp;rhs)</db:synopsisinfo>
+<db:synopsisinfo role="access">public</db:synopsisinfo>
+<db:synopsisinfo role="status">active</db:synopsisinfo>
+<db:synopsisinfo role="threadsafeness">unspecified</db:synopsisinfo>
+</db:methodsynopsis>
+<db:para>Returns true if <db:code its:translate="no" role="parameter">lhs</db:code> and <db:code its:translate="no" role="parameter">rhs</db:code> are equal.</db:para>
+</db:section>
+</db:section>
+<db:section xml:id="macro-documentation">
+<db:title>Macro Documentation</db:title>
+<db:section xml:id="QDOCTEST_MACRO2">
+<db:title its:translate="no">[since Test 1.1] QDOCTEST_MACRO2(int &amp;<db:emphasis>x</db:emphasis>)</db:title>
+<db:methodsynopsis>
+<db:methodname>QDOCTEST_MACRO2</db:methodname>
+<db:methodparam>
+<db:type>int &amp;</db:type>
+<db:parameter>x</db:parameter>
+</db:methodparam>
+<db:synopsisinfo role="meta">macrowithparams</db:synopsisinfo>
+<db:synopsisinfo role="signature">QDOCTEST_MACRO2(int &amp;x)</db:synopsisinfo>
+<db:synopsisinfo role="access">public</db:synopsisinfo>
+<db:synopsisinfo role="status">active</db:synopsisinfo>
+<db:synopsisinfo role="threadsafeness">unspecified</db:synopsisinfo>
+</db:methodsynopsis>
+<db:para>A macro with argument <db:code its:translate="no" role="parameter">x</db:code>.</db:para>
+<db:para>This macro was introduced in Test 1.1.</db:para>
+</db:section>
+</db:section>
+<db:section xml:id="obsolete">
+<db:title>Obsolete Members for Test</db:title>
+<db:para><db:emphasis role="bold">The following members of class <db:link xlink:href="testqdoc-test.xml">Test</db:link> are deprecated.</db:emphasis> We strongly advise against using them in new code.</db:para>
+<db:section xml:id="obsolete-member-function-documentation">
+<db:title>Obsolete Member Function Documentation</db:title>
+<db:section xml:id="operator-2b-2b">
+<db:title its:translate="no">[deprecated] TestQDoc::Test &amp;Test::operator++()</db:title>
+<db:bridgehead renderas="sect2" xml:id="operator--" its:translate="no">[deprecated] TestQDoc::Test &amp;Test::operator--()</db:bridgehead>
+<db:para>This function is deprecated. We strongly advise against using it in new code.</db:para>
+</db:section>
+<db:section xml:id="anotherObsoleteMember">
+<db:title its:translate="no">[deprecated] void Test::anotherObsoleteMember()</db:title>
+<db:methodsynopsis>
+<db:void/>
+<db:methodname>anotherObsoleteMember</db:methodname>
+<db:void/>
+<db:synopsisinfo role="meta">plain</db:synopsisinfo>
+<db:synopsisinfo role="signature">void anotherObsoleteMember()</db:synopsisinfo>
+<db:synopsisinfo role="access">public</db:synopsisinfo>
+<db:synopsisinfo role="status">deprecated</db:synopsisinfo>
+<db:synopsisinfo role="threadsafeness">unspecified</db:synopsisinfo>
+</db:methodsynopsis>
+<db:para>This function is deprecated. We strongly advise against using it in new code.</db:para>
+<db:para>Use <db:link xlink:href="testqdoc-test.xml#obsoleteMember">obsoleteMember</db:link>() instead.</db:para>
+</db:section>
+<db:section xml:id="deprecatedMember">
+<db:title its:translate="no">[deprecated in 6.0] void Test::deprecatedMember()</db:title>
+<db:methodsynopsis>
+<db:void/>
+<db:methodname>deprecatedMember</db:methodname>
+<db:void/>
+<db:synopsisinfo role="meta">plain</db:synopsisinfo>
+<db:synopsisinfo role="signature">void deprecatedMember()</db:synopsisinfo>
+<db:synopsisinfo role="access">public</db:synopsisinfo>
+<db:synopsisinfo role="status">deprecated</db:synopsisinfo>
+<db:synopsisinfo role="threadsafeness">unspecified</db:synopsisinfo>
+</db:methodsynopsis>
+<db:para>This function is deprecated since 6.0. We strongly advise against using it in new code.</db:para>
+<db:para>Use <db:link xlink:href="testqdoc-test.xml#someFunction">someFunction</db:link>() instead.</db:para>
+</db:section>
+<db:section xml:id="obsoleteMember">
+<db:title its:translate="no">[deprecated] void Test::obsoleteMember()</db:title>
+<db:methodsynopsis>
+<db:void/>
+<db:methodname>obsoleteMember</db:methodname>
+<db:void/>
+<db:synopsisinfo role="meta">plain</db:synopsisinfo>
+<db:synopsisinfo role="signature">void obsoleteMember()</db:synopsisinfo>
+<db:synopsisinfo role="access">public</db:synopsisinfo>
+<db:synopsisinfo role="status">deprecated</db:synopsisinfo>
+<db:synopsisinfo role="threadsafeness">unspecified</db:synopsisinfo>
+</db:methodsynopsis>
+<db:para>This function is deprecated. We strongly advise against using it in new code.</db:para>
+<db:para>Use <db:link xlink:href="testqdoc-test.xml#someFunction">someFunction</db:link>() instead.</db:para>
+</db:section>
+</db:section>
+</db:section>
+</db:article>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/docbook/testqdoc-testderived.xml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/docbook/testqdoc-testderived.xml
new file mode 100644
index 000000000..c6272cc7d
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/docbook/testqdoc-testderived.xml
@@ -0,0 +1,308 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<db:article xmlns:db="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:its="http://www.w3.org/2005/11/its" version="5.2" xml:lang="en">
+<db:info>
+<db:title its:translate="no">TestDerived Class</db:title>
+<db:subtitle its:translate="no">TestQDoc::TestDerived</db:subtitle>
+<db:productname>Test</db:productname>
+<db:titleabbrev>A test project for QDoc build artifacts</db:titleabbrev>
+<db:abstract>
+<db:para>A class in a namespace, derived from <db:link xlink:href="testqdoc-test.xml">Test</db:link>.</db:para>
+<db:para>This class was introduced in Qt 2.0.</db:para>
+</db:abstract>
+</db:info>
+<db:variablelist its:translate="no">
+<db:varlistentry>
+<db:term>Header</db:term>
+<db:listitem>
+<db:para>TestDerived</db:para>
+</db:listitem>
+</db:varlistentry>
+<db:varlistentry>
+<db:term>Since</db:term>
+<db:listitem>
+<db:para>Qt 2.0</db:para>
+</db:listitem>
+</db:varlistentry>
+<db:varlistentry>
+<db:term>CMake</db:term>
+<db:listitem>
+<db:para>find_package(Qt6 REQUIRED COMPONENTS QDocTest)</db:para>
+<db:para>target_link_libraries(mytarget PRIVATE Qt6::QDocTest)</db:para>
+</db:listitem>
+</db:varlistentry>
+<db:varlistentry>
+<db:term>qmake</db:term>
+<db:listitem>
+<db:para>QT += testcpp</db:para>
+</db:listitem>
+</db:varlistentry>
+<db:varlistentry>
+<db:term>In QML</db:term>
+<db:listitem>
+<db:para><db:link xlink:href="qml-themodule-thetype.xml" xlink:role="">TheType</db:link></db:para>
+</db:listitem>
+</db:varlistentry>
+<db:varlistentry>
+<db:term>Inherits</db:term>
+<db:listitem>
+<db:para><db:link xlink:href="testqdoc-test.xml" xlink:role="class">TestQDoc::Test</db:link></db:para>
+</db:listitem>
+</db:varlistentry>
+</db:variablelist>
+<db:section xml:id="details">
+<db:title>Detailed Description</db:title>
+</db:section>
+<db:section xml:id="member-type-documentation">
+<db:title>Member Type Documentation</db:title>
+<db:section xml:id="DerivedType-typedef">
+<db:title its:translate="no">[alias] TestDerived::DerivedType</db:title>
+<db:typedefsynopsis>
+<db:typedefname>DerivedType</db:typedefname>
+<db:synopsisinfo role="access">public</db:synopsisinfo>
+<db:synopsisinfo role="status">active</db:synopsisinfo>
+<db:synopsisinfo role="threadsafeness">unspecified</db:synopsisinfo>
+</db:typedefsynopsis>
+<db:para>An aliased typedef.</db:para>
+</db:section>
+<db:section xml:id="NotTypedef-typedef">
+<db:title its:translate="no">[alias] TestDerived::NotTypedef</db:title>
+<db:typedefsynopsis>
+<db:typedefname>NotTypedef</db:typedefname>
+<db:synopsisinfo role="access">public</db:synopsisinfo>
+<db:synopsisinfo role="status">active</db:synopsisinfo>
+<db:synopsisinfo role="threadsafeness">unspecified</db:synopsisinfo>
+</db:typedefsynopsis>
+<db:para>I'm an alias, not a typedef.</db:para>
+</db:section>
+</db:section>
+<db:section xml:id="property-documentation">
+<db:title>Property Documentation</db:title>
+<db:section xml:id="bindableProp-prop">
+<db:title its:translate="no">[bindable] bindableProp : QString</db:title>
+<db:fieldsynopsis>
+<db:modifier>(Qt property)</db:modifier>
+<db:type>QString</db:type>
+<db:varname>bindableProp</db:varname>
+<db:synopsisinfo role="access">public</db:synopsisinfo>
+<db:synopsisinfo role="status">active</db:synopsisinfo>
+<db:synopsisinfo role="threadsafeness">unspecified</db:synopsisinfo>
+<db:synopsisinfo role="getter">bindableProp</db:synopsisinfo>
+<db:synopsisinfo role="setter">setBindableProp</db:synopsisinfo>
+<db:synopsisinfo role="notifier">bindablePropChanged</db:synopsisinfo>
+</db:fieldsynopsis>
+<db:para>This property supports <db:link xlink:href="https://wiki.qt.io/QProperty">QProperty</db:link> bindings.</db:para>
+<db:para>Some property.</db:para>
+<db:section>
+<db:title>See Also</db:title>
+<db:para><db:emphasis>See also </db:emphasis>
+<db:simplelist type="vert" role="see-also">
+<db:member><db:link xlink:href="testqdoc-testderived.xml#someProp-prop">someProp</db:link></db:member>
+</db:simplelist>
+</db:para>
+</db:section>
+</db:section>
+<db:section xml:id="boolProp-prop">
+<db:title its:translate="no">boolProp : bool</db:title>
+<db:fieldsynopsis>
+<db:modifier>(Qt property)</db:modifier>
+<db:type>bool</db:type>
+<db:varname>boolProp</db:varname>
+<db:synopsisinfo role="access">public</db:synopsisinfo>
+<db:synopsisinfo role="status">active</db:synopsisinfo>
+<db:synopsisinfo role="threadsafeness">unspecified</db:synopsisinfo>
+<db:synopsisinfo role="getter">boolProp</db:synopsisinfo>
+<db:synopsisinfo role="setter">setBoolProp</db:synopsisinfo>
+<db:synopsisinfo role="resetter">resetBoolProp</db:synopsisinfo>
+<db:synopsisinfo role="notifier">boolPropChanged</db:synopsisinfo>
+</db:fieldsynopsis>
+<db:para>A boolean property.</db:para>
+<db:para>
+<db:emphasis role="bold">Access functions:
+</db:emphasis>
+</db:para>
+<db:itemizedlist its:translate="no">
+<db:listitem>
+<db:para><db:type>bool</db:type> <db:emphasis role="bold"><db:link xlink:href="">boolProp</db:link></db:emphasis>()</db:para>
+</db:listitem>
+<db:listitem>
+<db:para><db:type>void</db:type> <db:emphasis role="bold"><db:link xlink:href="">setBoolProp</db:link></db:emphasis>(<db:type>bool</db:type> <db:emphasis>b</db:emphasis>)</db:para>
+</db:listitem>
+<db:listitem>
+<db:para><db:type>void</db:type> <db:emphasis role="bold"><db:link xlink:href="">resetBoolProp</db:link></db:emphasis>()</db:para>
+</db:listitem>
+</db:itemizedlist>
+<db:para>
+<db:emphasis role="bold">Notifier signal:
+</db:emphasis>
+</db:para>
+<db:itemizedlist its:translate="no">
+<db:listitem>
+<db:para><db:type>void</db:type> <db:emphasis role="bold"><db:link xlink:href="">boolPropChanged</db:link></db:emphasis>()</db:para>
+</db:listitem>
+</db:itemizedlist>
+</db:section>
+<db:section xml:id="intProp-prop">
+<db:title its:translate="no">[read-only] intProp : int* const</db:title>
+<db:fieldsynopsis>
+<db:modifier>(Qt property)</db:modifier>
+<db:type>int*</db:type>
+<db:varname>intProp</db:varname>
+<db:synopsisinfo role="access">public</db:synopsisinfo>
+<db:synopsisinfo role="status">active</db:synopsisinfo>
+<db:synopsisinfo role="threadsafeness">unspecified</db:synopsisinfo>
+<db:synopsisinfo role="getter">getInt</db:synopsisinfo>
+</db:fieldsynopsis>
+<db:para>An integer property.</db:para>
+<db:para>
+<db:emphasis role="bold">Access functions:
+</db:emphasis>
+</db:para>
+<db:itemizedlist its:translate="no">
+<db:listitem>
+<db:para><db:type>int</db:type> *<db:emphasis role="bold"><db:link xlink:href="">getInt</db:link></db:emphasis>()</db:para>
+</db:listitem>
+</db:itemizedlist>
+</db:section>
+<db:section xml:id="name-prop">
+<db:title its:translate="no">[read-only] name : const QString*</db:title>
+<db:fieldsynopsis>
+<db:modifier>(Qt property)</db:modifier>
+<db:type>const QString*</db:type>
+<db:varname>name</db:varname>
+<db:synopsisinfo role="access">public</db:synopsisinfo>
+<db:synopsisinfo role="status">active</db:synopsisinfo>
+<db:synopsisinfo role="threadsafeness">unspecified</db:synopsisinfo>
+<db:synopsisinfo role="getter">name</db:synopsisinfo>
+</db:fieldsynopsis>
+<db:para>This property holds a name.</db:para>
+<db:para>
+<db:emphasis role="bold">Access functions:
+</db:emphasis>
+</db:para>
+<db:itemizedlist its:translate="no">
+<db:listitem>
+<db:para>const <db:type>QString</db:type> *<db:emphasis role="bold"><db:link xlink:href="">name</db:link></db:emphasis>() const</db:para>
+</db:listitem>
+</db:itemizedlist>
+</db:section>
+<db:section xml:id="someProp-prop">
+<db:title its:translate="no">[bindable read-only] someProp : QString</db:title>
+<db:fieldsynopsis>
+<db:modifier>(Qt property)</db:modifier>
+<db:type>QString</db:type>
+<db:varname>someProp</db:varname>
+<db:synopsisinfo role="access">public</db:synopsisinfo>
+<db:synopsisinfo role="status">active</db:synopsisinfo>
+<db:synopsisinfo role="threadsafeness">unspecified</db:synopsisinfo>
+<db:synopsisinfo role="getter">someProp</db:synopsisinfo>
+</db:fieldsynopsis>
+<db:para>This property supports <db:link xlink:href="https://wiki.qt.io/QProperty">QProperty</db:link> bindings.</db:para>
+<db:para>Another property.</db:para>
+</db:section>
+</db:section>
+<db:section xml:id="member-function-documentation">
+<db:title>Member Function Documentation</db:title>
+<db:section xml:id="emitSomething">
+<db:title its:translate="no">[private signal] void TestDerived::emitSomething()</db:title>
+<db:methodsynopsis>
+<db:void/>
+<db:methodname>emitSomething</db:methodname>
+<db:void/>
+<db:synopsisinfo role="meta">signal</db:synopsisinfo>
+<db:synopsisinfo role="signature">void emitSomething()</db:synopsisinfo>
+<db:synopsisinfo role="access">public</db:synopsisinfo>
+<db:synopsisinfo role="status">active</db:synopsisinfo>
+<db:synopsisinfo role="threadsafeness">unspecified</db:synopsisinfo>
+</db:methodsynopsis>
+<db:para>Emitted when things happen.</db:para>
+<db:note>
+<db:para>This is a private signal. It can be used in signal connections but cannot be emitted by the user.</db:para></db:note>
+</db:section>
+<db:section xml:id="id">
+<db:title its:translate="no">[override virtual] int TestDerived::id()</db:title>
+<db:methodsynopsis>
+<db:modifier>virtual</db:modifier>
+<db:type>int</db:type>
+<db:methodname>id</db:methodname>
+<db:void/>
+<db:modifier>override</db:modifier>
+<db:synopsisinfo role="meta">plain</db:synopsisinfo>
+<db:synopsisinfo role="signature">int id() override</db:synopsisinfo>
+<db:synopsisinfo role="access">public</db:synopsisinfo>
+<db:synopsisinfo role="status">active</db:synopsisinfo>
+<db:synopsisinfo role="threadsafeness">unspecified</db:synopsisinfo>
+</db:methodsynopsis>
+</db:section>
+<db:section xml:id="invokeMe">
+<db:title its:translate="no">[invokable] void TestDerived::invokeMe() const</db:title>
+<db:methodsynopsis>
+<db:modifier>const</db:modifier>
+<db:void/>
+<db:methodname>invokeMe</db:methodname>
+<db:void/>
+<db:synopsisinfo role="meta">plain</db:synopsisinfo>
+<db:synopsisinfo role="signature">void invokeMe() const</db:synopsisinfo>
+<db:synopsisinfo role="access">public</db:synopsisinfo>
+<db:synopsisinfo role="status">active</db:synopsisinfo>
+<db:synopsisinfo role="threadsafeness">unspecified</db:synopsisinfo>
+</db:methodsynopsis>
+<db:para>Something invokable.</db:para>
+<db:note>
+<db:para>This function can be invoked via the meta-object system and from QML. See <db:link xlink:href="">Q_INVOKABLE</db:link>.</db:para>
+</db:note>
+</db:section>
+<db:section xml:id="someValue">
+<db:title its:translate="no">TestQDoc::TestDerived::NotTypedef TestDerived::someValue()</db:title>
+<db:methodsynopsis>
+<db:type>TestQDoc::TestDerived::NotTypedef</db:type>
+<db:methodname>someValue</db:methodname>
+<db:void/>
+<db:synopsisinfo role="meta">plain</db:synopsisinfo>
+<db:synopsisinfo role="signature">TestQDoc::TestDerived::NotTypedef someValue()</db:synopsisinfo>
+<db:synopsisinfo role="access">public</db:synopsisinfo>
+<db:synopsisinfo role="status">active</db:synopsisinfo>
+<db:synopsisinfo role="threadsafeness">unspecified</db:synopsisinfo>
+</db:methodsynopsis>
+<db:para>Returns a value using an aliases type.</db:para>
+</db:section>
+<db:section xml:id="virtualFun">
+<db:title its:translate="no">[override virtual] void TestDerived::virtualFun()</db:title>
+<db:methodsynopsis>
+<db:modifier>virtual</db:modifier>
+<db:void/>
+<db:methodname>virtualFun</db:methodname>
+<db:void/>
+<db:modifier>override</db:modifier>
+<db:synopsisinfo role="meta">plain</db:synopsisinfo>
+<db:synopsisinfo role="signature">void virtualFun() override</db:synopsisinfo>
+<db:synopsisinfo role="access">public</db:synopsisinfo>
+<db:synopsisinfo role="status">active</db:synopsisinfo>
+<db:synopsisinfo role="threadsafeness">unspecified</db:synopsisinfo>
+</db:methodsynopsis>
+<db:para>Reimplements: <db:link xlink:href="testqdoc-test.xml#virtualFun" role="function">Test::virtualFun()</db:link>.</db:para>
+</db:section>
+</db:section>
+<db:section xml:id="obsolete">
+<db:title>Obsolete Members for TestDerived</db:title>
+<db:para><db:emphasis role="bold">The following members of class <db:link xlink:href="testqdoc-testderived.xml">TestDerived</db:link> are deprecated.</db:emphasis> We strongly advise against using them in new code.</db:para>
+<db:section xml:id="obsolete-member-function-documentation">
+<db:title>Obsolete Member Function Documentation</db:title>
+<db:section xml:id="staticObsoleteMember">
+<db:title its:translate="no">[static, deprecated] void TestDerived::staticObsoleteMember()</db:title>
+<db:methodsynopsis>
+<db:modifier>static</db:modifier>
+<db:void/>
+<db:methodname>staticObsoleteMember</db:methodname>
+<db:void/>
+<db:synopsisinfo role="meta">plain</db:synopsisinfo>
+<db:synopsisinfo role="signature">void staticObsoleteMember()</db:synopsisinfo>
+<db:synopsisinfo role="access">public</db:synopsisinfo>
+<db:synopsisinfo role="status">deprecated</db:synopsisinfo>
+<db:synopsisinfo role="threadsafeness">unspecified</db:synopsisinfo>
+</db:methodsynopsis>
+<db:para>This function is deprecated. We strongly advise against using it in new code.</db:para>
+<db:para>Static obsolete method.</db:para>
+</db:section>
+</db:section>
+</db:section>
+</db:article>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/docbook/testqdoc.xml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/docbook/testqdoc.xml
new file mode 100644
index 000000000..2322302a0
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/docbook/testqdoc.xml
@@ -0,0 +1,73 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<db:article xmlns:db="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:its="http://www.w3.org/2005/11/its" version="5.2" xml:lang="en">
+<db:info>
+<db:title its:translate="no">TestQDoc Namespace</db:title>
+<db:productname>Test</db:productname>
+<db:titleabbrev>A test project for QDoc build artifacts</db:titleabbrev>
+<db:abstract>
+<db:para>A namespace.</db:para>
+<db:para>This namespace was introduced in Qt 2.0.</db:para>
+</db:abstract>
+</db:info>
+<db:variablelist its:translate="no">
+<db:varlistentry>
+<db:term>Header</db:term>
+<db:listitem>
+<db:para>TestCPP</db:para>
+</db:listitem>
+</db:varlistentry>
+<db:varlistentry>
+<db:term>Since</db:term>
+<db:listitem>
+<db:para>Qt 2.0</db:para>
+</db:listitem>
+</db:varlistentry>
+<db:varlistentry>
+<db:term>CMake</db:term>
+<db:listitem>
+<db:para>find_package(Qt6 REQUIRED COMPONENTS QDocTest)</db:para>
+<db:para>target_link_libraries(mytarget PRIVATE Qt6::QDocTest)</db:para>
+</db:listitem>
+</db:varlistentry>
+<db:varlistentry>
+<db:term>qmake</db:term>
+<db:listitem>
+<db:para>QT += testcpp</db:para>
+</db:listitem>
+</db:varlistentry>
+</db:variablelist>
+<db:section xml:id="details">
+<db:title>Detailed Description</db:title>
+<db:section xml:id="usage">
+<db:title>Usage</db:title>
+<db:para>This namespace is for testing QDoc output.</db:para>
+</db:section>
+</db:section>
+<db:section xml:id="classes">
+<db:title>Classes</db:title>
+<db:section>
+<db:title>class <db:link xlink:href="testqdoc-test.xml" xlink:role="class">Test</db:link></db:title>
+<db:para>A class in a namespace.</db:para>
+</db:section>
+<db:section>
+<db:title>class <db:link xlink:href="testqdoc-testderived.xml" xlink:role="class">TestDerived</db:link></db:title>
+<db:para>A class in a namespace, derived from <db:link xlink:href="testqdoc-test.xml">Test</db:link>.</db:para>
+</db:section>
+</db:section>
+<db:section xml:id="macro-documentation">
+<db:title>Macro Documentation</db:title>
+<db:section xml:id="QDOCTEST_MACRO">
+<db:title its:translate="no">[since Test 0.9] QDOCTEST_MACRO</db:title>
+<db:methodsynopsis>
+<db:methodname>QDOCTEST_MACRO</db:methodname>
+<db:void/>
+<db:synopsisinfo role="meta">macrowithoutparams</db:synopsisinfo>
+<db:synopsisinfo role="signature">QDOCTEST_MACRO</db:synopsisinfo>
+<db:synopsisinfo role="access">public</db:synopsisinfo>
+<db:synopsisinfo role="status">active</db:synopsisinfo>
+<db:synopsisinfo role="threadsafeness">unspecified</db:synopsisinfo>
+</db:methodsynopsis>
+<db:para>This macro was introduced in Test 0.9.</db:para>
+</db:section>
+</db:section>
+</db:article>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/docbook/themodule-qmlmodule.xml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/docbook/themodule-qmlmodule.xml
new file mode 100644
index 000000000..d3c4ed894
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/docbook/themodule-qmlmodule.xml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<db:article xmlns:db="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:its="http://www.w3.org/2005/11/its" version="5.2" xml:lang="en">
+<db:info>
+<db:title its:translate="no"></db:title>
+<db:productname>Test</db:productname>
+<db:titleabbrev>A test project for QDoc build artifacts</db:titleabbrev>
+<db:abstract>
+<db:para>A test project for QDoc build artifacts.</db:para></db:abstract>
+</db:info>
+<db:anchor xml:id="details"/>
+<db:itemizedlist role="members">
+<db:listitem>
+<db:para><db:link xlink:href="qml-themodule-thetype.xml" xlink:role="">TheType</db:link></db:para>
+</db:listitem>
+</db:itemizedlist>
+</db:article>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/docbook/uicomponents-qmlmodule.xml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/docbook/uicomponents-qmlmodule.xml
new file mode 100644
index 000000000..80e5b0cdc
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/docbook/uicomponents-qmlmodule.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<db:article xmlns:db="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:its="http://www.w3.org/2005/11/its" version="5.2" xml:lang="en">
+<db:info>
+<db:title its:translate="no">UI Components</db:title>
+<db:productname>Test</db:productname>
+<db:titleabbrev>A test project for QDoc build artifacts</db:titleabbrev>
+<db:abstract>
+<db:para>Basic set of UI components.</db:para>
+</db:abstract>
+</db:info>
+<db:anchor xml:id="details"/>
+<db:para>This is a listing of a list of UI components implemented by QML types. These files are available for general import and they are based on the Qt Quick Code Samples.</db:para>
+<db:para>This module is part of the <db:link xlink:href="test-componentset-example.xml">UIComponents</db:link> example.</db:para>
+<db:variablelist role="members">
+<db:varlistentry>
+<db:term><db:link xlink:href="qml-uicomponents-progressbar.xml" xlink:role="">ProgressBar</db:link></db:term>
+<db:listitem>
+<db:para>A component that shows the progress of an event.</db:para>
+</db:listitem>
+</db:varlistentry>
+<db:varlistentry>
+<db:term><db:link xlink:href="qml-uicomponents-switch.xml" xlink:role="">Switch</db:link></db:term>
+<db:listitem>
+<db:para>A component that can be turned on or off.</db:para>
+</db:listitem>
+</db:varlistentry>
+<db:varlistentry>
+<db:term><db:link xlink:href="qml-uicomponents-tabwidget.xml" xlink:role="">TabWidget</db:link></db:term>
+<db:listitem>
+<db:para>A widget that places its children as tabs.</db:para>
+</db:listitem>
+</db:varlistentry>
+</db:variablelist>
+</db:article>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/autolinking.html b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/autolinking.html
new file mode 100644
index 000000000..ead0896fe
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/autolinking.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+<!-- classlists.qdoc -->
+ <title>Autolinking | Test 6.2.11</title>
+</head>
+<body>
+<div class="sidebar">
+<div class="toc">
+<h3 id="toc">Contents</h3>
+<ul>
+<li class="level1"><a href="#testqdoc">TestQDoc</a></li>
+<li class="level1"><a href="#someprop">someProp</a></li>
+</ul>
+</div>
+<div class="sidebar-content" id="sidebar-content"></div></div>
+<h1 class="title">Autolinking</h1>
+<!-- $$$autolinking.html-description -->
+<div class="descr" id="details">
+<h2 id="testqdoc">TestQDoc</h2>
+<p>The string <a href="testqdoc.html" translate="no">TestQDoc</a> links to the C++ namespace unless linking explicitly, <a href="autolinking.html#testqdoc">like this</a>, or <a href="testqdoc.html" translate="no">this</a>. Also,</p>
+<p>Autolinks:</p>
+<ul>
+<li><a href="testqdoc-testderived.html" translate="no">TestQDoc::TestDerived</a></li>
+</ul>
+<p>Explicit links:</p>
+<ul>
+<li><a href="testqdoc-testderived.html" translate="no">TestQDoc::TestDerived</a></li>
+<li><a href="obsolete-classes.html#testqdoc">Obsolete Classes#TestQDoc</a></li>
+</ul>
+<h2 id="someprop">someProp</h2>
+</div>
+<!-- @@@autolinking.html -->
+</body>
+</html>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/classes.html b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/classes.html
new file mode 100644
index 000000000..6a8df3334
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/classes.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+<!-- unseenclass.qdoc -->
+ <title>Classes | Test 6.2.11</title>
+</head>
+<body>
+<div class="sidebar"><div class="sidebar-content" id="sidebar-content"></div></div>
+<h1 class="title">Classes</h1>
+<!-- $$$classes.html-description -->
+<div class="descr" id="details">
+<div class="table"><table class="annotated">
+<tr class="odd topAlign"><td class="tblName" translate="no"><p><a href="seenclass.html">SeenClass</a></p></td><td class="tblDescr"><p>A public but undocumented class</p></td></tr>
+<tr class="even topAlign"><td class="tblName" translate="no"><p><a href="testqdoc-test.html">TestQDoc::Test</a></p></td><td class="tblDescr"><p>A class in a namespace</p></td></tr>
+<tr class="odd topAlign"><td class="tblName" translate="no"><p><a href="testqdoc-testderived.html">TestQDoc::TestDerived</a></p></td><td class="tblDescr"><p>A class in a namespace, derived from Test</p></td></tr>
+</table></div>
+</div>
+<!-- @@@classes.html -->
+</body>
+</html>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/cpptypes.html b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/cpptypes.html
new file mode 100644
index 000000000..3bd7789ff
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/cpptypes.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+<!-- classlists.qdoc -->
+ <title>Test C++ Types | Test 6.2.11</title>
+</head>
+<body>
+<div class="sidebar"><div class="sidebar-content" id="sidebar-content"></div></div>
+<h1 class="title">Test C++ Types</h1>
+<!-- $$$cpptypes-description -->
+<div class="descr" id="details">
+<ul>
+<li translate="no"><a href="testqdoc-test.html">TestQDoc::Test</a></li>
+<li translate="no"><a href="testqdoc-test.html#QDOCTEST_MACRO2">TestQDoc::Test::QDOCTEST_MACRO2</a></li>
+<li translate="no"><a href="testqdoc-test.html#operator-eq">TestQDoc::Test::operator=()</a></li>
+<li translate="no"><a href="testqdoc-test.html#someFunctionDefaultArg">TestQDoc::Test::someFunctionDefaultArg()</a></li>
+</ul>
+</div>
+<!-- @@@cpptypes -->
+<div class="table"><table class="annotated">
+<tr class="odd topAlign"><td class="tblName" translate="no"><p><a href="testqdoc-test.html">TestQDoc::Test</a></p></td><td class="tblDescr"><p>A class in a namespace</p></td></tr>
+</table></div>
+</body>
+</html>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/crossmoduleref.html b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/crossmoduleref.html
new file mode 100644
index 000000000..f681dbd69
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/crossmoduleref.html
@@ -0,0 +1,47 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+<!-- testcpp.cpp -->
+ <meta name="description" content="Namespace that has documented functions in multiple modules.">
+ <title>CrossModuleRef Namespace | Test 6.2.11</title>
+</head>
+<body>
+<div class="sidebar">
+<div class="toc">
+<h3 id="toc">Contents</h3>
+<ul>
+<li class="level1"><a href="#functions">Functions</a></li>
+<li class="level1"><a href="#details">Detailed Description</a></li>
+</ul>
+</div>
+<div class="sidebar-content" id="sidebar-content"></div></div>
+<h1 class="title" translate="no">CrossModuleRef Namespace</h1>
+<!-- $$$CrossModuleRef-brief -->
+<p>Namespace that has documented functions in multiple modules. <a href="#details">More...</a></p>
+<!-- @@@CrossModuleRef -->
+<div class="table"><table class="alignedsummary" translate="no">
+<tr><td class="memItemLeft rightAlign topAlign"> Header:</td><td class="memItemRight bottomAlign"> <span class="preprocessor">#include &lt;CrossModuleRef&gt;</span></td></tr>
+<tr><td class="memItemLeft rightAlign topAlign"> CMake:</td><td class="memItemRight bottomAlign"> find_package(Qt6 REQUIRED COMPONENTS QDocTest) <br/>
+target_link_libraries(mytarget PRIVATE Qt6::QDocTest)</td></tr>
+<tr><td class="memItemLeft rightAlign topAlign"> qmake:</td><td class="memItemRight bottomAlign"> QT += testcpp</td></tr>
+<tr><td class="memItemLeft rightAlign topAlign"> Since:</td><td class="memItemRight bottomAlign"> Qt 3.0</td></tr>
+</table></div>
+<h2 id="functions">Functions</h2>
+<div class="table"><table class="alignedsummary" translate="no">
+<tr><td class="memItemLeft rightAlign topAlign"> void </td><td class="memItemRight bottomAlign"><b><a href="crossmoduleref.html#documentMe" translate="no">documentMe</a></b>()</td></tr>
+</table></div>
+<!-- $$$CrossModuleRef-description -->
+<div class="descr">
+<h2 id="details">Detailed Description</h2>
+</div>
+<!-- @@@CrossModuleRef -->
+<div class="func">
+<h2>Function Documentation</h2>
+<!-- $$$documentMe[overload1]$$$documentMe -->
+<h3 class="fn" translate="no" id="documentMe"><span class="type">void</span> CrossModuleRef::<span class="name">documentMe</span>()</h3>
+<p>Document me!</p>
+<!-- @@@documentMe -->
+</div>
+</body>
+</html>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/images/leonardo-da-vinci.png b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/images/leonardo-da-vinci.png
new file mode 100644
index 000000000..854acb4ca
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/images/leonardo-da-vinci.png
Binary files differ
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/obsolete-classes.html b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/obsolete-classes.html
new file mode 100644
index 000000000..575b6e45c
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/obsolete-classes.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+<!-- classlists.qdoc -->
+ <title>Obsolete Classes | Test 6.2.11</title>
+</head>
+<body>
+<div class="sidebar">
+<div class="toc">
+<h3 id="toc">Contents</h3>
+<ul>
+<li class="level1"><a href="#classes-with-obsolete-members">Classes with obsolete members</a></li>
+<li class="level2"><a href="#testqdoc">TestQDoc</a></li>
+</ul>
+</div>
+<div class="sidebar-content" id="sidebar-content"></div></div>
+<h1 class="title">Obsolete Classes</h1>
+<!-- $$$obsolete-classes.html-description -->
+<div class="descr" id="details">
+<h2 id="classes-with-obsolete-members">Classes with obsolete members</h2>
+<div class="flowListDiv" translate="no">
+<dl class="flowList odd"><dt class="alphaChar"><b>T</b></dt>
+<dd><a href="testqdoc-test-obsolete.html">Test</a> (<a href="testqdoc.html">TestQDoc</a>)</dd>
+<dd><a href="testqdoc-testderived-obsolete.html">TestDerived</a> (<a href="testqdoc.html">TestQDoc</a>)</dd>
+</dl>
+</div>
+<h3 id="testqdoc">TestQDoc</h3>
+</div>
+<!-- @@@obsolete-classes.html -->
+</body>
+</html>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/qdoc-test-qmlmodule.html b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/qdoc-test-qmlmodule.html
new file mode 100644
index 000000000..6be64618b
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/qdoc-test-qmlmodule.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+<!-- type.cpp -->
+ <meta name="description" content="QML Types for the Test module.">
+ <title>QDoc.Test QML Module | Test 6.2.11</title>
+</head>
+<body>
+<div class="sidebar"><div class="sidebar-content" id="sidebar-content"></div></div>
+<h1 class="title" translate="no">QDoc.Test QML Module</h1>
+<p><b>This module is under development and is subject to change.</b></p>
+<p>This module was introduced in Qt 1.1.</p>
+<!-- $$$QDoc.Test-description -->
+<div class="descr" id="details">
+</div>
+<!-- @@@QDoc.Test -->
+<div class="table"><table class="annotated">
+<tr class="odd topAlign"><td class="tblName" translate="no"><p><a href="qml-qdoc-test-abstractparent.html">AbstractParent</a></p></td><td class="tblDescr"><p>Abstract base QML type</p></td></tr>
+<tr class="even topAlign"><td class="tblName" translate="no"><p><a href="qml-qdoc-test-child.html">Child</a></p></td><td class="tblDescr"><p>A Child inheriting its parent</p></td></tr>
+<tr class="odd topAlign"><td class="tblName" translate="no"><p><a href="qml-qdoc-test-doctest.html">DocTest</a></p></td><td class="tblDescr"><p>Represents a doc test case</p></td></tr>
+<tr class="even topAlign"><td class="tblName" translate="no"><p><a href="qml-qdoc-test-type.html">Type</a></p></td><td class="tblDescr"><p>A QML type documented in a .cpp file</p></td></tr>
+<tr class="odd topAlign"><td class="tblName" translate="no"><p><a href="qml-qdoc-test-yetanotherchild.html">YetAnotherChild</a></p></td><td class="tblDescr"><p>A type inheriting from internal abstract parent</p></td></tr>
+<tr class="even topAlign"><td class="tblName" translate="no"><p><a href="qml-int.html">int</a></p></td><td class="tblDescr"><p>An integer value type</p></td></tr>
+</table></div>
+</body>
+</html>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/qml-int.html b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/qml-int.html
new file mode 100644
index 000000000..63ea49e7d
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/qml-int.html
@@ -0,0 +1,45 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+<!-- parent.qdoc -->
+ <meta name="description" content="An integer value type.">
+ <title>int QML Value Type | Test 6.2.11</title>
+</head>
+<body>
+<li><a href="qdoc-test-qmlmodule.html" translate="no">Types</a></li>
+<li>int</li>
+<div class="sidebar">
+<div class="toc">
+<h3 id="toc">Contents</h3>
+<ul>
+<li class="level1"><a href="#methods">Methods</a></li>
+<li class="level1"><a href="#details">Detailed Description</a></li>
+</ul>
+</div>
+<div class="sidebar-content" id="sidebar-content"></div></div>
+<h1 class="title" translate="no">int QML Value Type</h1>
+<!-- $$$int-brief -->
+<p>An integer value type. <a href="#details">More...</a></p>
+<!-- @@@int -->
+<div class="table"><table class="alignedsummary" translate="no">
+<tr><td class="memItemLeft rightAlign topAlign"> Import Statement:</td><td class="memItemRight bottomAlign"> import QDoc.Test 1.1</td></tr><tr><td class="memItemLeft rightAlign topAlign"> Since:</td><td class="memItemRight bottomAlign"> Qt 1.1</td></tr></table></div><h2 id="methods">Methods</h2>
+<ul>
+<li class="fn" translate="no">int <b><a href="qml-int.html#abs-method" translate="no">abs</a></b>()</li>
+</ul>
+<!-- $$$int-description -->
+<h2 id="details">Detailed Description</h2>
+<!-- @@@int -->
+<h2>Method Documentation</h2>
+<!-- $$$abs[overload1]$$$abs -->
+<div class="qmlitem"><div class="qmlproto" translate="no">
+<div class="table"><table class="qmlname">
+<tr valign="top" class="odd" id="abs-method">
+<td class="tblQmlFuncNode"><p>
+<span class="type"><a href="qml-int.html" translate="no">int</a></span> <span class="name">abs</span>()</p></td></tr>
+</table></div></div>
+<div class="qmldoc"><p>Returns the absolute value of this integer.</p>
+</div></div><!-- @@@abs -->
+<br/>
+</body>
+</html>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/qml-qdoc-test-abstractparent-members.html b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/qml-qdoc-test-abstractparent-members.html
new file mode 100644
index 000000000..af49329a3
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/qml-qdoc-test-abstractparent-members.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+<!-- parent.qdoc -->
+ <meta name="description" content="Abstract base QML type.">
+ <title>List of All Members for AbstractParent | Test 6.2.11</title>
+</head>
+<body>
+<li><a href="qdoc-test-qmlmodule.html" translate="no">Types</a></li>
+<li>AbstractParent</li>
+<div class="sidebar"><div class="sidebar-content" id="sidebar-content"></div></div>
+<h1 class="title" translate="no">List of All Members for AbstractParent</h1>
+<p>This is the complete list of members for <a href="qml-qdoc-test-abstractparent.html">AbstractParent</a>, including inherited members.</p>
+<ul>
+<li class="fn" translate="no"><b><a href="qml-qdoc-test-abstractparent.html#children-prop" translate="no">children</a></b> : list&lt;Child&gt; [default]</li>
+<li class="fn" translate="no"><b><a href="qml-qdoc-test-abstractparent.html#name-prop" translate="no">name</a></b> : string</li>
+<li class="fn" translate="no">void <b><a href="qml-qdoc-test-abstractparent.html#name-method" translate="no">name</a></b>()</li>
+<li class="fn" translate="no">void <b><a href="qml-qdoc-test-abstractparent.html#name-method-1" translate="no">name</a></b>(Child <i>child</i>, <i>name</i>)</li>
+<li class="fn" translate="no">void <b><a href="qml-qdoc-test-abstractparent.html#rear-method" translate="no">rear</a></b>(Child <i>child</i>, var <i>method</i>)</li>
+</ul>
+</body>
+</html>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/qml-qdoc-test-abstractparent.html b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/qml-qdoc-test-abstractparent.html
new file mode 100644
index 000000000..8fe88bee7
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/qml-qdoc-test-abstractparent.html
@@ -0,0 +1,98 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+<!-- parent.qdoc -->
+ <meta name="description" content="Abstract base QML type.">
+ <title>AbstractParent QML Type | Test 6.2.11</title>
+</head>
+<body>
+<li><a href="qdoc-test-qmlmodule.html" translate="no">Types</a></li>
+<li>AbstractParent</li>
+<div class="sidebar">
+<div class="toc">
+<h3 id="toc">Contents</h3>
+<ul>
+<li class="level1"><a href="#properties">Properties</a></li>
+<li class="level1"><a href="#methods">Methods</a></li>
+<li class="level1"><a href="#details">Detailed Description</a></li>
+</ul>
+</div>
+<div class="sidebar-content" id="sidebar-content"></div></div>
+<h1 class="title" translate="no">AbstractParent QML Type</h1>
+<!-- $$$AbstractParent-brief -->
+<p>Abstract base QML type. <a href="#details">More...</a></p>
+<!-- @@@AbstractParent -->
+<div class="table"><table class="alignedsummary" translate="no">
+<tr><td class="memItemLeft rightAlign topAlign"> Import Statement:</td><td class="memItemRight bottomAlign"> import QDoc.Test 1.1</td></tr><tr><td class="memItemLeft rightAlign topAlign"> Since:</td><td class="memItemRight bottomAlign"> Qt 1.1</td></tr><tr><td class="memItemLeft rightAlign topAlign"> Inherited By:</td><td class="memItemRight bottomAlign"> <p><a href="qml-qdoc-test-child.html" translate="no">Child</a></p>
+</td></tr></table></div><ul>
+<li><a href="qml-qdoc-test-abstractparent-members.html">List of all members, including inherited members</a></li>
+</ul>
+<h2 id="properties">Properties</h2>
+<ul>
+<li class="fn" translate="no"><b><a href="qml-qdoc-test-abstractparent.html#children-prop" translate="no">children</a></b> : list&lt;Child&gt;</li>
+<li class="fn" translate="no"><b><a href="qml-qdoc-test-abstractparent.html#name-prop" translate="no">name</a></b> : string</li>
+</ul>
+<h2 id="methods">Methods</h2>
+<ul>
+<li class="fn" translate="no">void <b><a href="qml-qdoc-test-abstractparent.html#name-method" translate="no">name</a></b>()</li>
+<li class="fn" translate="no">void <b><a href="qml-qdoc-test-abstractparent.html#name-method-1" translate="no">name</a></b>(Child <i>child</i>, <i>name</i>)</li>
+<li class="fn" translate="no">void <b><a href="qml-qdoc-test-abstractparent.html#rear-method" translate="no">rear</a></b>(Child <i>child</i>, var <i>method</i>)</li>
+</ul>
+<!-- $$$AbstractParent-description -->
+<h2 id="details">Detailed Description</h2>
+<!-- @@@AbstractParent -->
+<h2>Property Documentation</h2>
+<!-- $$$children -->
+<div class="qmlitem"><div class="qmlproto" translate="no">
+<div class="table"><table class="qmlname">
+<tr valign="top" class="odd" id="children-prop">
+<td class="tblQmlPropNode"><p>
+<span class="name">children</span> : <span class="type">list</span>&lt;<span class="type"><a href="qml-qdoc-test-child.html" translate="no">Child</a></span>&gt; <code class="details extra" translate="no">[default]</code></p></td></tr>
+</table></div></div>
+<div class="qmldoc"><p>Children of the type.</p>
+</div></div><!-- @@@children -->
+<br/>
+<!-- $$$name -->
+<div class="qmlitem"><div class="qmlproto" translate="no">
+<div class="table"><table class="qmlname">
+<tr valign="top" class="odd" id="name-prop">
+<td class="tblQmlPropNode"><p>
+<span class="name">name</span> : <span class="type">string</span></p></td></tr>
+</table></div></div>
+<div class="qmldoc"><p>Name of this parent.</p>
+</div></div><!-- @@@name -->
+<br/>
+<h2>Method Documentation</h2>
+<!-- $$$name[overload1]$$$name -->
+<div class="qmlitem"><div class="qmlproto" translate="no">
+<div class="table"><table class="qmlname">
+<tr valign="top" class="odd" id="name-method">
+<td class="tblQmlFuncNode"><p>
+<span class="type">void</span> <span class="name">name</span>()</p></td></tr>
+</table></div></div>
+<div class="qmldoc"><p>Name all children with random names.</p>
+</div></div><!-- @@@name -->
+<br/>
+<!-- $$$name$$$nameChild -->
+<div class="qmlitem"><div class="qmlproto" translate="no">
+<div class="table"><table class="qmlname">
+<tr valign="top" class="odd" id="name-method-1">
+<td class="tblQmlFuncNode"><p>
+<span class="type">void</span> <span class="name">name</span>(<span class="type"><a href="qml-qdoc-test-child.html" translate="no">Child</a></span> <i>child</i>, <i>name</i>)</p></td></tr>
+</table></div></div>
+<div class="qmldoc"><p>Name a <i translate="no">child</i> using <i translate="no">name</i>.</p>
+</div></div><!-- @@@name -->
+<br/>
+<!-- $$$rear[overload1]$$$rearChildvar -->
+<div class="qmlitem"><div class="qmlproto" translate="no">
+<div class="table"><table class="qmlname">
+<tr valign="top" class="odd" id="rear-method">
+<td class="tblQmlFuncNode"><p>
+<span class="type">void</span> <span class="name">rear</span>(<span class="type"><a href="qml-qdoc-test-child.html" translate="no">Child</a></span> <i>child</i>, <span class="type">var</span> <i>method</i> = Strict)</p></td></tr>
+</table></div></div>
+<div class="qmldoc"><p>Do some abstract parenting on <i translate="no">child</i> using a specific <i translate="no">method</i>.</p>
+</div></div><!-- @@@rear -->
+<br/>
+</body>
+</html>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/qml-qdoc-test-child-members.html b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/qml-qdoc-test-child-members.html
new file mode 100644
index 000000000..c9a9b0a5c
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/qml-qdoc-test-child-members.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+<!-- parent.qdoc -->
+ <meta name="description" content="A Child inheriting its parent.">
+ <title>List of All Members for Child | Test 6.2.11</title>
+</head>
+<body>
+<li><a href="qdoc-test-qmlmodule.html" translate="no">Types</a></li>
+<li>Child</li>
+<div class="sidebar"><div class="sidebar-content" id="sidebar-content"></div></div>
+<h1 class="title" translate="no">List of All Members for Child</h1>
+<p>This is the complete list of members for <a href="qml-qdoc-test-child.html">Child</a>, including inherited members.</p>
+<ul>
+<li class="fn" translate="no"><b><a href="qml-qdoc-test-child.html#children-prop" translate="no">children</a></b> : list&lt;Child&gt; [default]</li>
+<li class="fn" translate="no"><b><a href="qml-qdoc-test-child.html#name-prop" translate="no">name</a></b> : string</li>
+<li class="fn" translate="no">void <b><a href="qml-qdoc-test-child.html#name-method" translate="no">name</a></b>(Child <i>child</i>, <i>name</i>)</li>
+<li class="fn" translate="no">void <b><a href="qml-qdoc-test-child.html#name-method" translate="no">name</a></b>()</li>
+<li class="fn" translate="no">void <b><a href="qml-qdoc-test-child.html#rear-method" translate="no">rear</a></b>(Child <i>child</i>, var <i>method</i>)</li>
+</ul>
+</body>
+</html>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/qml-qdoc-test-child.html b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/qml-qdoc-test-child.html
new file mode 100644
index 000000000..578c2f829
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/qml-qdoc-test-child.html
@@ -0,0 +1,98 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+<!-- parent.qdoc -->
+ <meta name="description" content="A Child inheriting its parent.">
+ <title>Child QML Type | Test 6.2.11</title>
+</head>
+<body>
+<li><a href="qdoc-test-qmlmodule.html" translate="no">Types</a></li>
+<li>Child</li>
+<div class="sidebar">
+<div class="toc">
+<h3 id="toc">Contents</h3>
+<ul>
+<li class="level1"><a href="#properties">Properties</a></li>
+<li class="level1"><a href="#methods">Methods</a></li>
+<li class="level1"><a href="#details">Detailed Description</a></li>
+</ul>
+</div>
+<div class="sidebar-content" id="sidebar-content"></div></div>
+<h1 class="title" translate="no">Child QML Type</h1>
+<!-- $$$Child-brief -->
+<p>A Child inheriting its parent. <a href="#details">More...</a></p>
+<!-- @@@Child -->
+<div class="table"><table class="alignedsummary" translate="no">
+<tr><td class="memItemLeft rightAlign topAlign"> Import Statement:</td><td class="memItemRight bottomAlign"> import QDoc.Test 1.1</td></tr><tr><td class="memItemLeft rightAlign topAlign"> Since:</td><td class="memItemRight bottomAlign"> Qt 1.1</td></tr><tr><td class="memItemLeft rightAlign topAlign"> Inherits:</td><td class="memItemRight bottomAlign"> <p><a href="qml-qdoc-test-abstractparent.html" translate="no">AbstractParent</a></p>
+</td></tr></table></div><ul>
+<li><a href="qml-qdoc-test-child-members.html">List of all members, including inherited members</a></li>
+</ul>
+<h2 id="properties">Properties</h2>
+<ul>
+<li class="fn" translate="no"><b><a href="qml-qdoc-test-child.html#children-prop" translate="no">children</a></b> : list&lt;Child&gt;</li>
+<li class="fn" translate="no"><b><a href="qml-qdoc-test-child.html#name-prop" translate="no">name</a></b> : string</li>
+</ul>
+<h2 id="methods">Methods</h2>
+<ul>
+<li class="fn" translate="no">void <b><a href="qml-qdoc-test-child.html#name-method" translate="no">name</a></b>(Child <i>child</i>, <i>name</i>)</li>
+<li class="fn" translate="no">void <b><a href="qml-qdoc-test-child.html#name-method" translate="no">name</a></b>()</li>
+<li class="fn" translate="no">void <b><a href="qml-qdoc-test-child.html#rear-method" translate="no">rear</a></b>(Child <i>child</i>, var <i>method</i>)</li>
+</ul>
+<!-- $$$Child-description -->
+<h2 id="details">Detailed Description</h2>
+<!-- @@@Child -->
+<h2>Property Documentation</h2>
+<!-- $$$children -->
+<div class="qmlitem"><div class="qmlproto" translate="no">
+<div class="table"><table class="qmlname">
+<tr valign="top" class="odd" id="children-prop">
+<td class="tblQmlPropNode"><p>
+<span class="name">children</span> : <span class="type">list</span>&lt;<span class="type"><a href="qml-qdoc-test-child.html" translate="no">Child</a></span>&gt; <code class="details extra" translate="no">[default]</code></p></td></tr>
+</table></div></div>
+<div class="qmldoc"><p>Children of the type.</p>
+</div></div><!-- @@@children -->
+<br/>
+<!-- $$$name -->
+<div class="qmlitem"><div class="qmlproto" translate="no">
+<div class="table"><table class="qmlname">
+<tr valign="top" class="odd" id="name-prop">
+<td class="tblQmlPropNode"><p>
+<span class="name">name</span> : <span class="type">string</span></p></td></tr>
+</table></div></div>
+<div class="qmldoc"><p>Name of this child.</p>
+</div></div><!-- @@@name -->
+<br/>
+<h2>Method Documentation</h2>
+<!-- $$$name[overload1]$$$nameChild -->
+<div class="qmlitem"><div class="qmlproto" translate="no">
+<div class="table"><table class="qmlname">
+<tr valign="top" class="odd" id="name-method">
+<td class="tblQmlFuncNode"><p>
+<span class="type">void</span> <span class="name">name</span>(<span class="type"><a href="qml-qdoc-test-child.html" translate="no">Child</a></span> <i>child</i>, <i>name</i>)</p></td></tr>
+</table></div></div>
+<div class="qmldoc"><p>Name a <i translate="no">child</i> of this child using <i translate="no">name</i>.</p>
+</div></div><!-- @@@name -->
+<br/>
+<!-- $$$name[overload1]$$$name -->
+<div class="qmlitem"><div class="qmlproto" translate="no">
+<div class="table"><table class="qmlname">
+<tr valign="top" class="odd" id="name-method">
+<td class="tblQmlFuncNode"><p>
+<span class="type">void</span> <span class="name">name</span>()</p></td></tr>
+</table></div></div>
+<div class="qmldoc"><p>Name all children with random names.</p>
+</div></div><!-- @@@name -->
+<br/>
+<!-- $$$rear[overload1]$$$rearChildvar -->
+<div class="qmlitem"><div class="qmlproto" translate="no">
+<div class="table"><table class="qmlname">
+<tr valign="top" class="odd" id="rear-method">
+<td class="tblQmlFuncNode"><p>
+<span class="type">void</span> <span class="name">rear</span>(<span class="type"><a href="qml-qdoc-test-child.html" translate="no">Child</a></span> <i>child</i>, <span class="type">var</span> <i>method</i> = Strict)</p></td></tr>
+</table></div></div>
+<div class="qmldoc"><p>Do some abstract parenting on <i translate="no">child</i> using a specific <i translate="no">method</i>.</p>
+</div></div><!-- @@@rear -->
+<br/>
+</body>
+</html>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/qml-qdoc-test-doctest-members.html b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/qml-qdoc-test-doctest-members.html
new file mode 100644
index 000000000..706b65b20
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/qml-qdoc-test-doctest-members.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+<!-- DocTest.qml -->
+ <meta name="description" content="Represents a doc test case.">
+ <title>List of All Members for DocTest | Test 6.2.11</title>
+</head>
+<body>
+<li><a href="qdoc-test-qmlmodule.html" translate="no">Types</a></li>
+<li>DocTest</li>
+<div class="sidebar"><div class="sidebar-content" id="sidebar-content"></div></div>
+<h1 class="title" translate="no">List of All Members for DocTest</h1>
+<p>This is the complete list of members for <a href="qml-qdoc-test-doctest.html">DocTest</a>, including inherited members.</p>
+<ul>
+<li class="fn" translate="no"><b><a href="qml-qdoc-test-doctest.html#active-prop" translate="no">active</a></b> : bool</li>
+<li class="fn" translate="no"><b><a href="qml-qdoc-test-doctest.html#name-prop" translate="no">name</a></b> : string</li>
+<li class="fn" translate="no"><b><a href="qml-qdoc-test-doctest.html#completed-signal" translate="no">completed</a></b>()</li>
+<li class="fn" translate="no"><b><a href="qml-qdoc-test-doctest.html#fail-method" translate="no">fail</a></b>(<i>message</i>) <code class="summary extra" translate="no">(since QDoc.Test 1.0)</code></li>
+<li class="fn" translate="no"><b><a href="qml-qdoc-test-doctest.html#fail_hard-method" translate="no">fail_hard</a></b>(<i>msg</i>, <i>option</i>)</li>
+<li class="fn" translate="no"><b><a href="qml-qdoc-test-doctest.html#foo-signal" translate="no">foo</a></b>(var <i>bar</i>)</li>
+<li class="fn" translate="no"><b><a href="qml-qdoc-test-doctest.html#itsHappening-signal" translate="no">itsHappening</a></b>(bool <i>really</i>)</li>
+</ul>
+</body>
+</html>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/qml-qdoc-test-doctest.html b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/qml-qdoc-test-doctest.html
new file mode 100644
index 000000000..37174fb59
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/qml-qdoc-test-doctest.html
@@ -0,0 +1,139 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+<!-- DocTest.qml -->
+ <meta name="description" content="Represents a doc test case.">
+ <title>DocTest QML Type | Test 6.2.11</title>
+</head>
+<body>
+<li><a href="qdoc-test-qmlmodule.html" translate="no">Types</a></li>
+<li>DocTest</li>
+<div class="sidebar">
+<div class="toc">
+<h3 id="toc">Contents</h3>
+<ul>
+<li class="level1"><a href="#properties">Properties</a></li>
+<li class="level1"><a href="#signals">Signals</a></li>
+<li class="level1"><a href="#methods">Methods</a></li>
+<li class="level1"><a href="#details">Detailed Description</a></li>
+<li class="level2"><a href="#introduction">Introduction</a></li>
+</ul>
+</div>
+<div class="sidebar-content" id="sidebar-content"></div></div>
+<h1 class="title" translate="no">DocTest QML Type</h1>
+<!-- $$$DocTest-brief -->
+<p>Represents a doc test case. <a href="#details">More...</a></p>
+<!-- @@@DocTest -->
+<div class="table"><table class="alignedsummary" translate="no">
+<tr><td class="memItemLeft rightAlign topAlign"> Import Statement:</td><td class="memItemRight bottomAlign"> import QDoc.Test 1.1</td></tr><tr><td class="memItemLeft rightAlign topAlign"> Since:</td><td class="memItemRight bottomAlign"> QDoc.Test 0.9</td></tr></table></div><ul>
+<li><a href="qml-qdoc-test-doctest-members.html">List of all members, including inherited members</a></li>
+</ul>
+<h2 id="properties">Properties</h2>
+<ul>
+<li class="fn" translate="no"><b><a href="qml-qdoc-test-doctest.html#active-prop" translate="no">active</a></b> : bool</li>
+<li class="fn" translate="no"><b><a href="qml-qdoc-test-doctest.html#name-prop" translate="no">name</a></b> : string</li>
+</ul>
+<h2 id="signals">Signals</h2>
+<ul>
+<li class="fn" translate="no"><b><a href="qml-qdoc-test-doctest.html#completed-signal" translate="no">completed</a></b>()</li>
+<li class="fn" translate="no"><b><a href="qml-qdoc-test-doctest.html#foo-signal" translate="no">foo</a></b>(var <i>bar</i>)</li>
+<li class="fn" translate="no"><b><a href="qml-qdoc-test-doctest.html#itsHappening-signal" translate="no">itsHappening</a></b>(bool <i>really</i>)</li>
+</ul>
+<h2 id="methods">Methods</h2>
+<ul>
+<li class="fn" translate="no"><b><a href="qml-qdoc-test-doctest.html#fail-method" translate="no">fail</a></b>(<i>message</i>) <code class="summary extra" translate="no">(since QDoc.Test 1.0)</code></li>
+<li class="fn" translate="no"><b><a href="qml-qdoc-test-doctest.html#fail_hard-method" translate="no">fail_hard</a></b>(<i>msg</i>, <i>option</i>)</li>
+</ul>
+<!-- $$$DocTest-description -->
+<h2 id="details">Detailed Description</h2>
+<h2 id="introduction">Introduction</h2>
+<p>A documentation test case, itself documented inline in DocTest.qml.</p>
+<!-- @@@DocTest -->
+<h2>Property Documentation</h2>
+<!-- $$$active -->
+<div class="qmlitem"><div class="qmlproto" translate="no">
+<div class="table"><table class="qmlname">
+<tr valign="top" class="odd" id="active-prop">
+<td class="tblQmlPropNode"><p>
+<span class="name">active</span> : <span class="type">bool</span> <code class="details extra" translate="no">[default: true]</code></p></td></tr>
+</table></div></div>
+<div class="qmldoc"><p>Whether the test is active.</p>
+<p><b>See also </b><a href="qml-qdoc-test-doctest.html#name-prop" translate="no">name</a>.</p>
+</div></div><!-- @@@active -->
+<br/>
+<!-- $$$name -->
+<div class="qmlitem"><div class="qmlproto" translate="no">
+<div class="table"><table class="qmlname">
+<tr valign="top" class="odd" id="name-prop">
+<td class="tblQmlPropNode"><p>
+<span class="name">name</span> : <span class="type">string</span> <code class="details extra" translate="no">[required]</code></p></td></tr>
+</table></div></div>
+<div class="qmldoc"><p>Name of the test.</p>
+<pre class="qml" translate="no"><span class="type"><a href="qml-test-nover-doctest.html" translate="no">DocTest</a></span> {
+ <span class="name">name</span>: <span class="string">&quot;test&quot;</span>
+ <span class="comment">// ...</span>
+}</pre>
+</div></div><!-- @@@name -->
+<br/>
+<h2>Signal Documentation</h2>
+<!-- $$$completed[overload1]$$$completed -->
+<div class="qmlitem"><div class="qmlproto" translate="no">
+<div class="table"><table class="qmlname">
+<tr valign="top" class="odd" id="completed-signal">
+<td class="tblQmlFuncNode"><p>
+<span class="name">completed</span>()</p></td></tr>
+</table></div></div>
+<div class="qmldoc"><div class="admonition note"><p><b>Note: </b>The corresponding handler is <code translate="no">onCompleted</code>.</p>
+</div></div></div><!-- @@@completed -->
+<br/>
+<!-- $$$foo[overload1]$$$foovar -->
+<div class="qmlitem"><div class="qmlproto" translate="no">
+<div class="table"><table class="qmlname">
+<tr valign="top" class="odd" id="foo-signal">
+<td class="tblQmlFuncNode"><p>
+<span class="name">foo</span>(<span class="type">var</span> <i>bar</i>)</p></td></tr>
+</table></div></div>
+<div class="qmldoc"><p>Signal with parameter <i translate="no">bar</i>.</p>
+<div class="admonition note"><p><b>Note: </b>The corresponding handler is <code translate="no">onFoo</code>.</p>
+</div></div></div><!-- @@@foo -->
+<br/>
+<!-- $$$itsHappening[overload1]$$$itsHappeningbool -->
+<div class="qmlitem"><div class="qmlproto" translate="no">
+<div class="table"><table class="qmlname">
+<tr valign="top" class="odd" id="itsHappening-signal">
+<td class="tblQmlFuncNode"><p>
+<span class="name">itsHappening</span>(<span class="type">bool</span> <i>really</i>)</p></td></tr>
+</table></div></div>
+<div class="qmldoc"><p>Signals that something is <i translate="no">really</i> happening.</p>
+<div class="admonition note"><p><b>Note: </b>The corresponding handler is <code translate="no">onItsHappening</code>.</p>
+</div></div></div><!-- @@@itsHappening -->
+<br/>
+<h2>Method Documentation</h2>
+<!-- $$$fail[overload1]$$$fail -->
+<div class="qmlitem"><div class="qmlproto" translate="no">
+<div class="table"><table class="qmlname">
+<tr valign="top" class="odd" id="fail-method">
+<td class="tblQmlFuncNode"><p>
+<code class="details extra" translate="no">[since QDoc.Test 1.0]</code> <span class="name">fail</span>(<i>message</i> = &quot;oops&quot;)</p></td></tr>
+</table></div></div>
+<div class="qmldoc"><p>Fails the current test case, with the optional <i translate="no">message</i>.</p>
+<p>This method was introduced in QDoc.Test 1.0.</p>
+</div></div><!-- @@@fail -->
+<br/>
+<!-- $$$fail_hard[overload1]$$$fail_hard -->
+<div class="qmlitem"><div class="qmlproto" translate="no">
+<div class="table"><table class="qmlname">
+<tr valign="top" class="odd" id="fail_hard-method">
+<td class="tblQmlFuncNode"><p>
+<span class="name">fail_hard</span>(<i>msg</i> = &quot;facepalm&quot;, <i>option</i> = 123)</p></td></tr>
+</table></div></div>
+<div class="qmldoc"><p>Fails the current test case, hard.</p>
+<ul>
+<li>Prints out <i translate="no">msg</i>.</li>
+<li>Accepts a random <i translate="no">option</i>.</li>
+</ul>
+</div></div><!-- @@@fail_hard -->
+<br/>
+</body>
+</html>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/qml-qdoc-test-oldtype-members.html b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/qml-qdoc-test-oldtype-members.html
new file mode 100644
index 000000000..6527f5a6c
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/qml-qdoc-test-oldtype-members.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+<!-- type.cpp -->
+ <meta name="description" content="Deprecated old type.">
+ <title>List of All Members for OldType | Test 6.2.11</title>
+</head>
+<body>
+<li><a href="qdoc-test-qmlmodule.html" translate="no">Types</a></li>
+<li>OldType</li>
+<div class="sidebar"><div class="sidebar-content" id="sidebar-content"></div></div>
+<h1 class="title" translate="no">List of All Members for OldType</h1>
+<p>This is the complete list of members for <a href="qml-qdoc-test-oldtype.html" class="obsolete">OldType</a>, including inherited members.</p>
+</body>
+</html>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/qml-qdoc-test-oldtype.html b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/qml-qdoc-test-oldtype.html
new file mode 100644
index 000000000..4fe99b259
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/qml-qdoc-test-oldtype.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+<!-- type.cpp -->
+ <meta name="description" content="Deprecated old type.">
+ <title>OldType QML Type | Test 6.2.11</title>
+</head>
+<body>
+<li><a href="qdoc-test-qmlmodule.html" translate="no">Types</a></li>
+<li>OldType</li>
+<div class="sidebar">
+<div class="toc">
+<h3 id="toc">Contents</h3>
+<ul>
+<li class="level1"><a href="#details">Detailed Description</a></li>
+</ul>
+</div>
+<div class="sidebar-content" id="sidebar-content"></div></div>
+<h1 class="title" translate="no">OldType QML Type</h1>
+<!-- $$$OldType-brief -->
+<p>Deprecated old type. <a href="#details">More...</a></p>
+<!-- @@@OldType -->
+<div class="table"><table class="alignedsummary" translate="no">
+<tr><td class="memItemLeft rightAlign topAlign"> Import Statement:</td><td class="memItemRight bottomAlign"> import QDoc.Test 1.1</td></tr><tr><td class="memItemLeft rightAlign topAlign"> Since:</td><td class="memItemRight bottomAlign"> Qt 1.1</td></tr><tr><td class="memItemLeft rightAlign topAlign"> Status:</td><td class="memItemRight bottomAlign"> Deprecated since 1.0<span class="status deprecated"></span></td></tr></table></div><p><b>This type is deprecated since QDoc.Test 1.0. We strongly advise against using it in new code.</b></p>
+<ul>
+<li><a href="qml-qdoc-test-oldtype-members.html">List of all members, including inherited members</a></li>
+</ul>
+<!-- $$$OldType-description -->
+<h2 id="details">Detailed Description</h2>
+<!-- @@@OldType -->
+</body>
+</html>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/qml-qdoc-test-type-members.html b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/qml-qdoc-test-type-members.html
new file mode 100644
index 000000000..eb3180a88
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/qml-qdoc-test-type-members.html
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+<!-- type.cpp -->
+ <meta name="description" content="A QML type documented in a .cpp file.">
+ <title>List of All Members for Type | Test 6.2.11</title>
+</head>
+<body>
+<li><a href="qdoc-test-qmlmodule.html" translate="no">Types</a></li>
+<li>Type</li>
+<div class="sidebar"><div class="sidebar-content" id="sidebar-content"></div></div>
+<h1 class="title" translate="no">List of All Members for Type</h1>
+<p>This is the complete list of members for <a href="qml-qdoc-test-type.html">Type</a>, including inherited members.</p>
+<ul>
+<li class="fn" translate="no"><b><a href="qml-qdoc-test-type.html#fifth-prop" translate="no">fifth</a></b> : int</li>
+<li class="fn" translate="no"><b><a href="qml-qdoc-test-type.html#fourth-prop" translate="no">fourth</a></b> : int</li>
+<li class="fn" translate="no"><b><a href="qml-qdoc-test-type.html#group-prop" translate="no">group</a></b><ul>
+<li class="fn" translate="no"><b><a href="qml-qdoc-test-type.html#group.first-prop" translate="no">group.first</a></b> : int</li>
+<li class="fn" translate="no"><b><a href="qml-qdoc-test-type.html#group.second-prop" translate="no">group.second</a></b> : int</li>
+<li class="fn" translate="no"><b><a href="qml-qdoc-test-type.html#group.third-prop" translate="no">group.third</a></b> : int</li>
+</ul>
+</li>
+<li class="fn" translate="no"><b><a href="qml-qdoc-test-type.html#id-prop" translate="no">id</a></b> : int</li>
+<li class="fn" translate="no"><b><a href="qml-qdoc-test-type.html#name-prop" translate="no">name</a></b> : string</li>
+<li class="fn" translate="no"><b><a href="qml-qdoc-test-type.html#type-attached-prop" translate="no">type</a></b> : enumeration [attached]</li>
+<li class="fn" translate="no"><b><a href="qml-qdoc-test-type.html#completed-signal" translate="no">completed</a></b>(int <i>status</i>)</li>
+<li class="fn" translate="no"><b><a href="qml-qdoc-test-type.html#configured-signal" translate="no">configured</a></b>() [attached]</li>
+<li class="fn" translate="no">Type <b><a href="qml-qdoc-test-type.html#copy-method" translate="no">copy</a></b>(<i>a</i>)</li>
+<li class="fn" translate="no"><b><a href="qml-qdoc-test-type-obsolete.html#deprecatedMethod-method" translate="no">deprecatedMethod</a></b>() <code class="summary extra" translate="no">(deprecated in 6.2)</code></li>
+<li class="fn" translate="no"><b><a href="qml-qdoc-test-type.html#disable-method" translate="no">disable</a></b>()</li>
+<li class="fn" translate="no"><b><a href="qml-qdoc-test-type.html#enable-method" translate="no">enable</a></b>()</li>
+<li class="fn" translate="no"><b><a href="qml-qdoc-test-type.html#futureDeprecated-method" translate="no">futureDeprecated</a></b>() <code class="summary extra" translate="no">(until 6.3)</code></li>
+<li class="fn" translate="no"><b><a href="qml-qdoc-test-type.html#group.created-signal" translate="no">group.created</a></b>()</li>
+</ul>
+</body>
+</html>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/qml-qdoc-test-type-obsolete.html b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/qml-qdoc-test-type-obsolete.html
new file mode 100644
index 000000000..acd56759a
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/qml-qdoc-test-type-obsolete.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+<!-- type.cpp -->
+ <meta name="description" content="A QML type documented in a .cpp file.">
+ <title>Obsolete Members for Type | Test 6.2.11</title>
+</head>
+<body>
+<li><a href="qdoc-test-qmlmodule.html" translate="no">Types</a></li>
+<li>Type</li>
+<div class="sidebar"><div class="sidebar-content" id="sidebar-content"></div></div>
+<h1 class="title" translate="no">Obsolete Members for Type</h1>
+<p><b>The following members of QML type <a href="qml-qdoc-test-type.html">Type</a> are deprecated.</b> They are provided to keep old source code working. We strongly advise against using them in new code.</p>
+<h2 id="methods">Methods</h2>
+<ul>
+<li class="fn" translate="no"><b><a href="qml-qdoc-test-type-obsolete.html#deprecatedMethod-method" translate="no">deprecatedMethod</a></b>() <code class="summary extra" translate="no">(deprecated in 6.2)</code></li>
+</ul>
+<h2>Method Documentation</h2>
+<!-- $$$deprecatedMethod[overload1]$$$deprecatedMethod -->
+<div class="qmlitem"><div class="qmlproto" translate="no">
+<div class="table"><table class="qmlname">
+<tr valign="top" class="odd" id="deprecatedMethod-method">
+<td class="tblQmlFuncNode"><p>
+<code class="details extra" translate="no">[deprecated in 6.2]</code> <span class="name">deprecatedMethod</span>()</p></td></tr>
+</table></div></div>
+<div class="qmldoc"><p>This method is deprecated since QDoc.Test 6.2. We strongly advise against using it in new code.</p>
+<p>This method has no replacement.</p>
+<p>This is a method that should include information about being deprecated and that it has been so since 6.2 in its docs.</p>
+</div></div><!-- @@@deprecatedMethod -->
+<br/>
+</body>
+</html>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/qml-qdoc-test-type.html b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/qml-qdoc-test-type.html
new file mode 100644
index 000000000..7fb53266d
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/qml-qdoc-test-type.html
@@ -0,0 +1,209 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+<!-- type.cpp -->
+ <meta name="description" content="A QML type documented in a .cpp file.">
+ <title>Type QML Type | Test 6.2.11</title>
+</head>
+<body>
+<li><a href="qdoc-test-qmlmodule.html" translate="no">Types</a></li>
+<li>Type</li>
+<div class="sidebar">
+<div class="toc">
+<h3 id="toc">Contents</h3>
+<ul>
+<li class="level1"><a href="#properties">Properties</a></li>
+<li class="level1"><a href="#attached-properties">Attached Properties</a></li>
+<li class="level1"><a href="#signals">Signals</a></li>
+<li class="level1"><a href="#attached-signals">Attached Signals</a></li>
+<li class="level1"><a href="#methods">Methods</a></li>
+<li class="level1"><a href="#details">Detailed Description</a></li>
+</ul>
+</div>
+<div class="sidebar-content" id="sidebar-content"></div></div>
+<h1 class="title" translate="no">Type QML Type</h1>
+<!-- $$$Type-brief -->
+<p>A QML type documented in a .cpp file. <a href="#details">More...</a></p>
+<!-- @@@Type -->
+<div class="table"><table class="alignedsummary" translate="no">
+<tr><td class="memItemLeft rightAlign topAlign"> Import Statement:</td><td class="memItemRight bottomAlign"> import QDoc.Test 1.1</td></tr><tr><td class="memItemLeft rightAlign topAlign"> Since:</td><td class="memItemRight bottomAlign"> Qt 1.1</td></tr><tr><td class="memItemLeft rightAlign topAlign"> In C++:</td><td class="memItemRight bottomAlign"> <a href="testqdoc-test.html" translate="no">Test</a></td></tr><tr><td class="memItemLeft rightAlign topAlign"> Status:</td><td class="memItemRight bottomAlign"> &lt;Work In Progress&gt;<span class="status work-in-progress"></span></td></tr></table></div><ul>
+<li><a href="qml-qdoc-test-type-members.html">List of all members, including inherited members</a></li>
+<li><a href="qml-qdoc-test-type-obsolete.html">Deprecated members</a></li>
+</ul>
+<h2 id="properties">Properties</h2>
+<ul>
+<li class="fn" translate="no"><b><a href="qml-qdoc-test-type.html#fifth-prop" translate="no">fifth</a></b> : int</li>
+<li class="fn" translate="no"><b><a href="qml-qdoc-test-type.html#fourth-prop" translate="no">fourth</a></b> : int</li>
+<li class="fn" translate="no"><b><a href="qml-qdoc-test-type.html#group-prop" translate="no">group</a></b><ul>
+<li class="fn" translate="no"><b><a href="qml-qdoc-test-type.html#group.first-prop" translate="no">group.first</a></b> : int</li>
+<li class="fn" translate="no"><b><a href="qml-qdoc-test-type.html#group.second-prop" translate="no">group.second</a></b> : int</li>
+<li class="fn" translate="no"><b><a href="qml-qdoc-test-type.html#group.third-prop" translate="no">group.third</a></b> : int</li>
+</ul>
+</li>
+<li class="fn" translate="no"><b><a href="qml-qdoc-test-type.html#id-prop" translate="no">id</a></b> : int</li>
+<li class="fn" translate="no"><b><a href="qml-qdoc-test-type.html#name-prop" translate="no">name</a></b> : string</li>
+</ul>
+<h2 id="attached-properties">Attached Properties</h2>
+<ul>
+<li class="fn" translate="no"><b><a href="qml-qdoc-test-type.html#type-attached-prop" translate="no">type</a></b> : enumeration</li>
+</ul>
+<h2 id="signals">Signals</h2>
+<ul>
+<li class="fn" translate="no"><b><a href="qml-qdoc-test-type.html#completed-signal" translate="no">completed</a></b>(int <i>status</i>)</li>
+<li class="fn" translate="no"><b><a href="qml-qdoc-test-type.html#group.created-signal" translate="no">group.created</a></b>()</li>
+</ul>
+<h2 id="attached-signals">Attached Signals</h2>
+<ul>
+<li class="fn" translate="no"><b><a href="qml-qdoc-test-type.html#configured-signal" translate="no">configured</a></b>()</li>
+</ul>
+<h2 id="methods">Methods</h2>
+<ul>
+<li class="fn" translate="no">Type <b><a href="qml-qdoc-test-type.html#copy-method" translate="no">copy</a></b>(<i>a</i>)</li>
+<li class="fn" translate="no"><b><a href="qml-qdoc-test-type.html#disable-method" translate="no">disable</a></b>()</li>
+<li class="fn" translate="no"><b><a href="qml-qdoc-test-type.html#enable-method" translate="no">enable</a></b>()</li>
+<li class="fn" translate="no"><b><a href="qml-qdoc-test-type.html#futureDeprecated-method" translate="no">futureDeprecated</a></b>() <code class="summary extra" translate="no">(until 6.3)</code></li>
+</ul>
+<!-- $$$Type-description -->
+<h2 id="details">Detailed Description</h2>
+<!-- @@@Type -->
+<h2>Property Documentation</h2>
+<!-- $$$ -->
+<div class="qmlitem"><div class="fngroup">
+<div class="qmlproto" translate="no">
+<div class="table"><table class="qmlname">
+<tr valign="top" class="odd" id="fifth-prop">
+<td class="tblQmlPropNode"><p>
+<span class="name">fifth</span> : <span class="type"><a href="qml-int.html" translate="no">int</a></span></p></td></tr>
+<tr valign="top" class="odd" id="fourth-prop">
+<td class="tblQmlPropNode"><p>
+<span class="name">fourth</span> : <span class="type"><a href="qml-int.html" translate="no">int</a></span></p></td></tr>
+</table></div></div>
+</div><div class="qmldoc"><p>A group of properties sharing a documentation comment.</p>
+</div></div><!-- @@@ -->
+<br/>
+<!-- $$$group -->
+<div class="qmlitem"><div class="qmlproto" translate="no">
+<div class="table"><table class="qmlname">
+<tr valign="top" class="even" id="group-prop"><th class="centerAlign"><p><b>group group</b></p></th></tr>
+<tr valign="top" class="odd" id="group.first-prop">
+<td class="tblQmlPropNode"><p>
+<span class="name">group.first</span> : <span class="type"><a href="qml-int.html" translate="no">int</a></span></p></td></tr>
+<tr valign="top" class="odd" id="group.second-prop">
+<td class="tblQmlPropNode"><p>
+<span class="name">group.second</span> : <span class="type"><a href="qml-int.html" translate="no">int</a></span></p></td></tr>
+<tr valign="top" class="odd" id="group.third-prop">
+<td class="tblQmlPropNode"><p>
+<span class="name">group.third</span> : <span class="type"><a href="qml-int.html" translate="no">int</a></span></p></td></tr>
+</table></div></div>
+<div class="qmldoc"><p>A property group.</p>
+</div></div><!-- @@@group -->
+<br/>
+<!-- $$$id -->
+<div class="qmlitem"><div class="qmlproto" translate="no">
+<div class="table"><table class="qmlname">
+<tr valign="top" class="odd" id="id-prop">
+<td class="tblQmlPropNode"><p>
+<span class="name">id</span> : <span class="type"><a href="qml-int.html" translate="no">int</a></span> <code class="details extra" translate="no">[read-only]</code></p></td></tr>
+</table></div></div>
+<div class="qmldoc"><p>A read-only property.</p>
+</div></div><!-- @@@id -->
+<br/>
+<!-- $$$name -->
+<div class="qmlitem"><div class="qmlproto" translate="no">
+<div class="table"><table class="qmlname">
+<tr valign="top" class="odd" id="name-prop">
+<td class="tblQmlPropNode"><p>
+<span class="name">name</span> : <span class="type">string</span> <code class="details extra" translate="no">[required]</code></p></td></tr>
+</table></div></div>
+<div class="qmldoc"><p>Name of the Test.</p>
+</div></div><!-- @@@name -->
+<br/>
+<h2>Attached Property Documentation</h2>
+<!-- $$$type -->
+<div class="qmlitem"><div class="qmlproto" translate="no">
+<div class="table"><table class="qmlname">
+<tr valign="top" class="odd" id="type-attached-prop">
+<td class="tblQmlPropNode"><p>
+<span class="name">Type.type</span> : <span class="type">enumeration</span> <code class="details extra" translate="no">[default: Type.NoType]</code></p></td></tr>
+</table></div></div>
+<div class="qmldoc"><div class="table"><table class="valuelist"><tr valign="top" class="odd"><th class="tblConst">Constant</th><th class="tbldscr">Description</th></tr>
+<tr><td class="topAlign"><code translate="no">Type.NoType</code></td><td class="topAlign">Nothing</td></tr>
+<tr><td class="topAlign"><code translate="no">Type.SomeType</code></td><td class="topAlign">Something</td></tr>
+</table></div>
+</div></div><!-- @@@type -->
+<br/>
+<h2>Signal Documentation</h2>
+<!-- $$$completed[overload1]$$$completedint -->
+<div class="qmlitem"><div class="qmlproto" translate="no">
+<div class="table"><table class="qmlname">
+<tr valign="top" class="odd" id="completed-signal">
+<td class="tblQmlFuncNode"><p>
+<span class="name">completed</span>(<span class="type"><a href="qml-int.html" translate="no">int</a></span> <i>status</i>)</p></td></tr>
+</table></div></div>
+<div class="qmldoc"><p>This signal is emitted when the operation completed with <i translate="no">status</i>.</p>
+<div class="admonition note"><p><b>Note: </b>The corresponding handler is <code translate="no">onCompleted</code>.</p>
+</div></div></div><!-- @@@completed -->
+<br/>
+<!-- $$$group.created[overload1]$$$group.created -->
+<div class="qmlitem"><div class="qmlproto" translate="no">
+<div class="table"><table class="qmlname">
+<tr valign="top" class="odd" id="group.created-signal">
+<td class="tblQmlFuncNode"><p>
+<span class="name">group.created</span>()</p></td></tr>
+</table></div></div>
+<div class="qmldoc"><p>This signal is prefixed with <i>group</i>.</p>
+<div class="admonition note"><p><b>Note: </b>The corresponding handler is <code translate="no">group.onCreated</code>.</p>
+</div></div></div><!-- @@@group.created -->
+<br/>
+<h2>Attached Signal Documentation</h2>
+<!-- $$$configured[overload1]$$$configured -->
+<div class="qmlitem"><div class="qmlproto" translate="no">
+<div class="table"><table class="qmlname">
+<tr valign="top" class="odd" id="configured-signal">
+<td class="tblQmlFuncNode"><p>
+<span class="name">configured</span>()</p></td></tr>
+</table></div></div>
+<div class="qmldoc"><p>This attached signal is emitted when the type was configured.</p>
+<div class="admonition note"><p><b>Note: </b>The corresponding handler is <code translate="no">onConfigured</code>.</p>
+</div></div></div><!-- @@@configured -->
+<br/>
+<h2>Method Documentation</h2>
+<!-- $$$ -->
+<div class="qmlitem"><div class="fngroup">
+<div class="qmlproto" translate="no">
+<div class="table"><table class="qmlname">
+<tr valign="top" class="odd" id="disable-method">
+<td class="tblQmlFuncNode"><p>
+<span class="name">disable</span>()</p></td></tr>
+<tr valign="top" class="odd" id="enable-method">
+<td class="tblQmlFuncNode"><p>
+<span class="name">enable</span>()</p></td></tr>
+</table></div></div>
+</div><div class="qmldoc"><p>Enables or disables this type.</p>
+</div></div><!-- @@@ -->
+<br/>
+<!-- $$$copy[overload1]$$$copy -->
+<div class="qmlitem"><div class="qmlproto" translate="no">
+<div class="table"><table class="qmlname">
+<tr valign="top" class="odd" id="copy-method">
+<td class="tblQmlFuncNode"><p>
+<span class="type"><a href="qml-qdoc-test-type.html" translate="no">Type</a></span> <span class="name">copy</span>(<i>a</i>)</p></td></tr>
+</table></div></div>
+<div class="qmldoc"><p>Returns another Type based on <i translate="no">a</i>.</p>
+</div></div><!-- @@@copy -->
+<br/>
+<!-- $$$futureDeprecated[overload1]$$$futureDeprecated -->
+<div class="qmlitem"><div class="qmlproto" translate="no">
+<div class="table"><table class="qmlname">
+<tr valign="top" class="odd" id="futureDeprecated-method">
+<td class="tblQmlFuncNode"><p>
+<code class="details extra" translate="no">[until 6.3]</code> <span class="name">futureDeprecated</span>()</p></td></tr>
+</table></div></div>
+<div class="qmldoc"><p>This method is scheduled for deprecation in version 6.3.</p>
+<p>Use something else instead.</p>
+<p>This is a method that's marked for deprecation in a future version.</p>
+</div></div><!-- @@@futureDeprecated -->
+<br/>
+</body>
+</html>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/qml-qdoc-test-yetanotherchild-members.html b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/qml-qdoc-test-yetanotherchild-members.html
new file mode 100644
index 000000000..fbab41876
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/qml-qdoc-test-yetanotherchild-members.html
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+<!-- parent.qdoc -->
+ <meta name="description" content="A type inheriting from internal abstract parent.">
+ <title>List of All Members for YetAnotherChild | Test 6.2.11</title>
+</head>
+<body>
+<li><a href="qdoc-test-qmlmodule.html" translate="no">Types</a></li>
+<li>YetAnotherChild</li>
+<div class="sidebar"><div class="sidebar-content" id="sidebar-content"></div></div>
+<h1 class="title" translate="no">List of All Members for YetAnotherChild</h1>
+<p>This is the complete list of members for <a href="qml-qdoc-test-yetanotherchild.html">YetAnotherChild</a>, including inherited members.</p>
+<ul>
+<li class="fn" translate="no"><b><a href="qml-qdoc-test-yetanotherchild.html#prop-prop" translate="no">prop</a></b> : int</li>
+</ul>
+</body>
+</html>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/qml-qdoc-test-yetanotherchild.html b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/qml-qdoc-test-yetanotherchild.html
new file mode 100644
index 000000000..2ecc87355
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/qml-qdoc-test-yetanotherchild.html
@@ -0,0 +1,48 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+<!-- parent.qdoc -->
+ <meta name="description" content="A type inheriting from internal abstract parent.">
+ <title>YetAnotherChild QML Type | Test 6.2.11</title>
+</head>
+<body>
+<li><a href="qdoc-test-qmlmodule.html" translate="no">Types</a></li>
+<li>YetAnotherChild</li>
+<div class="sidebar">
+<div class="toc">
+<h3 id="toc">Contents</h3>
+<ul>
+<li class="level1"><a href="#properties">Properties</a></li>
+<li class="level1"><a href="#details">Detailed Description</a></li>
+</ul>
+</div>
+<div class="sidebar-content" id="sidebar-content"></div></div>
+<h1 class="title" translate="no">YetAnotherChild QML Type</h1>
+<!-- $$$YetAnotherChild-brief -->
+<p>A type inheriting from internal abstract parent. <a href="#details">More...</a></p>
+<!-- @@@YetAnotherChild -->
+<div class="table"><table class="alignedsummary" translate="no">
+<tr><td class="memItemLeft rightAlign topAlign"> Import Statement:</td><td class="memItemRight bottomAlign"> import QDoc.Test 1.1</td></tr><tr><td class="memItemLeft rightAlign topAlign"> Since:</td><td class="memItemRight bottomAlign"> Qt 1.1</td></tr></table></div><ul>
+<li><a href="qml-qdoc-test-yetanotherchild-members.html">List of all members, including inherited members</a></li>
+</ul>
+<h2 id="properties">Properties</h2>
+<ul>
+<li class="fn" translate="no"><b><a href="qml-qdoc-test-yetanotherchild.html#prop-prop" translate="no">prop</a></b> : int</li>
+</ul>
+<!-- $$$YetAnotherChild-description -->
+<h2 id="details">Detailed Description</h2>
+<!-- @@@YetAnotherChild -->
+<h2>Property Documentation</h2>
+<!-- $$$prop -->
+<div class="qmlitem"><div class="qmlproto" translate="no">
+<div class="table"><table class="qmlname">
+<tr valign="top" class="odd" id="prop-prop">
+<td class="tblQmlPropNode"><p>
+<span class="name">prop</span> : <span class="type"><a href="qml-int.html" translate="no">int</a></span></p></td></tr>
+</table></div></div>
+<div class="qmldoc"><p>Propagated to inheriting type docs.</p>
+</div></div><!-- @@@prop -->
+<br/>
+</body>
+</html>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/qml-test-nover-doctest-members.html b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/qml-test-nover-doctest-members.html
new file mode 100644
index 000000000..491427692
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/qml-test-nover-doctest-members.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+<!-- DocTest.qml -->
+ <meta name="description" content="Shadows the type name in QDoc.Test module.">
+ <title>List of All Members for DocTest | Test 6.2.11</title>
+</head>
+<body>
+<li><a href="qdoc-test-qmlmodule.html" translate="no">Types</a></li>
+<li><a href="test-nover-qmlmodule.html" translate="no">Test.NoVer (Tech Preview)</a></li>
+<li>DocTest</li>
+<div class="sidebar"><div class="sidebar-content" id="sidebar-content"></div></div>
+<h1 class="title" translate="no">List of All Members for DocTest</h1>
+<p>This is the complete list of members for <a href="qml-test-nover-doctest.html">DocTest</a>, including inherited members.</p>
+</body>
+</html>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/qml-test-nover-doctest.html b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/qml-test-nover-doctest.html
new file mode 100644
index 000000000..2543b3ce3
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/qml-test-nover-doctest.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+<!-- DocTest.qml -->
+ <meta name="description" content="Shadows the type name in QDoc.Test module.">
+ <title>DocTest QML Type | Test 6.2.11</title>
+</head>
+<body>
+<li><a href="qdoc-test-qmlmodule.html" translate="no">Types</a></li>
+<li><a href="test-nover-qmlmodule.html" translate="no">Test.NoVer (Tech Preview)</a></li>
+<li>DocTest</li>
+<div class="sidebar">
+<div class="toc">
+<h3 id="toc">Contents</h3>
+<ul>
+<li class="level1"><a href="#details">Detailed Description</a></li>
+</ul>
+</div>
+<div class="sidebar-content" id="sidebar-content"></div></div>
+<h1 class="title" translate="no">DocTest QML Type</h1>
+<!-- $$$DocTest-brief -->
+<p>Shadows the type name in QDoc.Test module. <a href="#details">More...</a></p>
+<!-- @@@DocTest -->
+<div class="table"><table class="alignedsummary" translate="no">
+<tr><td class="memItemLeft rightAlign topAlign"> Import Statement:</td><td class="memItemRight bottomAlign"> import Test.NoVer</td></tr><tr><td class="memItemLeft rightAlign topAlign"> Since:</td><td class="memItemRight bottomAlign"> Qt 1.1</td></tr><tr><td class="memItemLeft rightAlign topAlign"> Status:</td><td class="memItemRight bottomAlign"> Tech Preview<span class="status tech-preview"></span></td></tr></table></div><ul>
+<li><a href="qml-test-nover-doctest-members.html">List of all members, including inherited members</a></li>
+</ul>
+<!-- $$$DocTest-description -->
+<h2 id="details">Detailed Description</h2>
+<!-- @@@DocTest -->
+</body>
+</html>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/qml-test-nover-typenoversion-members.html b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/qml-test-nover-typenoversion-members.html
new file mode 100644
index 000000000..ca2308045
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/qml-test-nover-typenoversion-members.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+<!-- type.cpp -->
+ <meta name="description" content="Another QML type documented in a .cpp file.">
+ <title>List of All Members for TypeNoVersion | Test 6.2.11</title>
+</head>
+<body>
+<li><a href="qdoc-test-qmlmodule.html" translate="no">Types</a></li>
+<li><a href="test-nover-qmlmodule.html" translate="no">Test.NoVer (Tech Preview)</a></li>
+<li>TypeNoVersion</li>
+<div class="sidebar"><div class="sidebar-content" id="sidebar-content"></div></div>
+<h1 class="title" translate="no">List of All Members for TypeNoVersion</h1>
+<p>This is the complete list of members for <a href="qml-test-nover-typenoversion.html">TypeNoVersion</a>, including inherited members.</p>
+</body>
+</html>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/qml-test-nover-typenoversion.html b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/qml-test-nover-typenoversion.html
new file mode 100644
index 000000000..b754038d9
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/qml-test-nover-typenoversion.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+<!-- type.cpp -->
+ <meta name="description" content="Another QML type documented in a .cpp file.">
+ <title>TypeNoVersion QML Type | Test 6.2.11</title>
+</head>
+<body>
+<li><a href="qdoc-test-qmlmodule.html" translate="no">Types</a></li>
+<li><a href="test-nover-qmlmodule.html" translate="no">Test.NoVer (Tech Preview)</a></li>
+<li>TypeNoVersion</li>
+<div class="sidebar">
+<div class="toc">
+<h3 id="toc">Contents</h3>
+<ul>
+<li class="level1"><a href="#details">Detailed Description</a></li>
+</ul>
+</div>
+<div class="sidebar-content" id="sidebar-content"></div></div>
+<h1 class="title" translate="no">TypeNoVersion QML Type</h1>
+<!-- $$$TypeNoVersion-brief -->
+<p>Another QML type documented in a .cpp file. <a href="#details">More...</a></p>
+<!-- @@@TypeNoVersion -->
+<div class="table"><table class="alignedsummary" translate="no">
+<tr><td class="memItemLeft rightAlign topAlign"> Import Statement:</td><td class="memItemRight bottomAlign"> import Test.NoVer</td></tr><tr><td class="memItemLeft rightAlign topAlign"> Since:</td><td class="memItemRight bottomAlign"> Qt 1.1</td></tr><tr><td class="memItemLeft rightAlign topAlign"> Status:</td><td class="memItemRight bottomAlign"> Tech Preview<span class="status tech-preview"></span></td></tr></table></div><ul>
+<li><a href="qml-test-nover-typenoversion-members.html">List of all members, including inherited members</a></li>
+</ul>
+<!-- $$$TypeNoVersion-description -->
+<h2 id="details">Detailed Description</h2>
+<!-- @@@TypeNoVersion -->
+</body>
+</html>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/qml-themodule-thetype-members.html b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/qml-themodule-thetype-members.html
new file mode 100644
index 000000000..de3143219
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/qml-themodule-thetype-members.html
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+<!-- properties.qdoc -->
+ <title>List of All Members for TheType | Test 6.2.11</title>
+</head>
+<body>
+<li><a href="qdoc-test-qmlmodule.html" translate="no">Types</a></li>
+<li><a href="themodule-qmlmodule.html" translate="no">TheModule</a></li>
+<li>TheType</li>
+<div class="sidebar"><div class="sidebar-content" id="sidebar-content"></div></div>
+<h1 class="title" translate="no">List of All Members for TheType</h1>
+<p>This is the complete list of members for <a href="qml-themodule-thetype.html">TheType</a>, including inherited members.</p>
+<ul>
+<li class="fn" translate="no"><b><a href="qml-themodule-thetype.html#name-prop" translate="no">name</a></b> : string</li>
+</ul>
+</body>
+</html>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/qml-themodule-thetype.html b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/qml-themodule-thetype.html
new file mode 100644
index 000000000..86b8fd066
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/qml-themodule-thetype.html
@@ -0,0 +1,45 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+<!-- properties.qdoc -->
+ <title>TheType QML Type | Test 6.2.11</title>
+</head>
+<body>
+<li><a href="qdoc-test-qmlmodule.html" translate="no">Types</a></li>
+<li><a href="themodule-qmlmodule.html" translate="no">TheModule</a></li>
+<li>TheType</li>
+<div class="sidebar">
+<div class="toc">
+<h3 id="toc">Contents</h3>
+<ul>
+<li class="level1"><a href="#properties">Properties</a></li>
+<li class="level1"><a href="#details">Detailed Description</a></li>
+</ul>
+</div>
+<div class="sidebar-content" id="sidebar-content"></div></div>
+<h1 class="title" translate="no">TheType QML Type</h1>
+<div class="table"><table class="alignedsummary" translate="no">
+<tr><td class="memItemLeft rightAlign topAlign"> Import Statement:</td><td class="memItemRight bottomAlign"> import TheModule</td></tr><tr><td class="memItemLeft rightAlign topAlign"> In C++:</td><td class="memItemRight bottomAlign"> <a href="testqdoc-testderived.html" translate="no">TestDerived</a></td></tr></table></div><ul>
+<li><a href="qml-themodule-thetype-members.html">List of all members, including inherited members</a></li>
+</ul>
+<h2 id="properties">Properties</h2>
+<ul>
+<li class="fn" translate="no"><b><a href="qml-themodule-thetype.html#name-prop" translate="no">name</a></b> : string</li>
+</ul>
+<!-- $$$TheType-description -->
+<h2 id="details">Detailed Description</h2>
+<!-- @@@TheType -->
+<h2>Property Documentation</h2>
+<!-- $$$name -->
+<div class="qmlitem"><div class="qmlproto" translate="no">
+<div class="table"><table class="qmlname">
+<tr valign="top" class="odd" id="name-prop">
+<td class="tblQmlPropNode"><p>
+<span class="name">name</span> : <span class="type">string</span> <code class="details extra" translate="no">[read-only]</code></p></td></tr>
+</table></div></div>
+<div class="qmldoc"><p>Read-only status of this property is resolved from Q_PROPERTY.</p>
+</div></div><!-- @@@name -->
+<br/>
+</body>
+</html>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/qml-uicomponents-progressbar-members.html b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/qml-uicomponents-progressbar-members.html
new file mode 100644
index 000000000..77c4ef164
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/qml-uicomponents-progressbar-members.html
@@ -0,0 +1,24 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+<!-- ProgressBar.qml -->
+ <meta name="description" content="A component that shows the progress of an event.">
+ <title>List of All Members for ProgressBar | Test 6.2.11</title>
+</head>
+<body>
+<li><a href="qdoc-test-qmlmodule.html" translate="no">Types</a></li>
+<li><a href="uicomponents-qmlmodule.html" translate="no">UIComponents</a></li>
+<li>ProgressBar</li>
+<div class="sidebar"><div class="sidebar-content" id="sidebar-content"></div></div>
+<h1 class="title" translate="no">List of All Members for ProgressBar</h1>
+<p>This is the complete list of members for <a href="qml-uicomponents-progressbar.html">ProgressBar</a>, including inherited members.</p>
+<ul>
+<li class="fn" translate="no"><b><a href="qml-uicomponents-progressbar.html#color-prop" translate="no">color</a></b> : color</li>
+<li class="fn" translate="no"><b><a href="qml-uicomponents-progressbar.html#maximum-prop" translate="no">maximum</a></b> : int</li>
+<li class="fn" translate="no"><b><a href="qml-uicomponents-progressbar.html#minimum-prop" translate="no">minimum</a></b> : int</li>
+<li class="fn" translate="no"><b><a href="qml-uicomponents-progressbar.html#secondColor-prop" translate="no">secondColor</a></b> : color</li>
+<li class="fn" translate="no"><b><a href="qml-uicomponents-progressbar.html#value-prop" translate="no">value</a></b> : int</li>
+</ul>
+</body>
+</html>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/qml-uicomponents-progressbar.html b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/qml-uicomponents-progressbar.html
new file mode 100644
index 000000000..b491e2eec
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/qml-uicomponents-progressbar.html
@@ -0,0 +1,98 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+<!-- ProgressBar.qml -->
+ <meta name="description" content="A component that shows the progress of an event.">
+ <title>ProgressBar QML Type | Test 6.2.11</title>
+</head>
+<body>
+<li><a href="qdoc-test-qmlmodule.html" translate="no">Types</a></li>
+<li><a href="uicomponents-qmlmodule.html" translate="no">UIComponents</a></li>
+<li>ProgressBar</li>
+<div class="sidebar">
+<div class="toc">
+<h3 id="toc">Contents</h3>
+<ul>
+<li class="level1"><a href="#properties">Properties</a></li>
+<li class="level1"><a href="#details">Detailed Description</a></li>
+</ul>
+</div>
+<div class="sidebar-content" id="sidebar-content"></div></div>
+<h1 class="title" translate="no">ProgressBar QML Type</h1>
+<!-- $$$ProgressBar-brief -->
+<p>A component that shows the progress of an event. <a href="#details">More...</a></p>
+<!-- @@@ProgressBar -->
+<div class="table"><table class="alignedsummary" translate="no">
+<tr><td class="memItemLeft rightAlign topAlign"> Import Statement:</td><td class="memItemRight bottomAlign"> import UIComponents 1.0</td></tr></table></div><ul>
+<li><a href="qml-uicomponents-progressbar-members.html">List of all members, including inherited members</a></li>
+</ul>
+<h2 id="properties">Properties</h2>
+<ul>
+<li class="fn" translate="no"><b><a href="qml-uicomponents-progressbar.html#color-prop" translate="no">color</a></b> : color</li>
+<li class="fn" translate="no"><b><a href="qml-uicomponents-progressbar.html#maximum-prop" translate="no">maximum</a></b> : int</li>
+<li class="fn" translate="no"><b><a href="qml-uicomponents-progressbar.html#minimum-prop" translate="no">minimum</a></b> : int</li>
+<li class="fn" translate="no"><b><a href="qml-uicomponents-progressbar.html#secondColor-prop" translate="no">secondColor</a></b> : color</li>
+<li class="fn" translate="no"><b><a href="qml-uicomponents-progressbar.html#value-prop" translate="no">value</a></b> : int</li>
+</ul>
+<!-- $$$ProgressBar-description -->
+<h2 id="details">Detailed Description</h2>
+<p>A ProgressBar shows the linear progress of an event as its <a href="qml-uicomponents-progressbar.html#value-prop" translate="no">value</a>. The range is specified using the <a href="qml-uicomponents-progressbar.html#minimum-prop" translate="no">minimum</a> and the <a href="qml-uicomponents-progressbar.html#maximum-prop" translate="no">maximum</a> values.</p>
+<p>The ProgressBar component is part of the <a href="uicomponents-qmlmodule.html" translate="no">UI Components</a> module.</p>
+<p>This documentation is part of the <a href="test-componentset-example.html">UIComponents</a> example.</p>
+<!-- @@@ProgressBar -->
+<h2>Property Documentation</h2>
+<!-- $$$color -->
+<div class="qmlitem"><div class="qmlproto" translate="no">
+<div class="table"><table class="qmlname">
+<tr valign="top" class="odd" id="color-prop">
+<td class="tblQmlPropNode"><p>
+<span class="name">color</span> : <span class="type">color</span></p></td></tr>
+</table></div></div>
+<div class="qmldoc"><p>The color of the <a href="qml-uicomponents-progressbar.html" translate="no">ProgressBar</a>'s gradient. Must bind to a color type.</p>
+<p><b>See also </b><a href="qml-uicomponents-progressbar.html#secondColor-prop" translate="no">secondColor</a>.</p>
+</div></div><!-- @@@color -->
+<br/>
+<!-- $$$maximum -->
+<div class="qmlitem"><div class="qmlproto" translate="no">
+<div class="table"><table class="qmlname">
+<tr valign="top" class="odd" id="maximum-prop">
+<td class="tblQmlPropNode"><p>
+<span class="name">maximum</span> : <span class="type"><a href="qml-int.html" translate="no">int</a></span></p></td></tr>
+</table></div></div>
+<div class="qmldoc"><p>The maximum value of the <a href="qml-uicomponents-progressbar.html" translate="no">ProgressBar</a> range. The <a href="qml-uicomponents-progressbar.html#value-prop" translate="no">value</a> must not be more than this value.</p>
+</div></div><!-- @@@maximum -->
+<br/>
+<!-- $$$minimum -->
+<div class="qmlitem"><div class="qmlproto" translate="no">
+<div class="table"><table class="qmlname">
+<tr valign="top" class="odd" id="minimum-prop">
+<td class="tblQmlPropNode"><p>
+<span class="name">minimum</span> : <span class="type"><a href="qml-int.html" translate="no">int</a></span></p></td></tr>
+</table></div></div>
+<div class="qmldoc"><p>The minimum value of the <a href="qml-uicomponents-progressbar.html" translate="no">ProgressBar</a> range. The <a href="qml-uicomponents-progressbar.html#value-prop" translate="no">value</a> must not be less than this value.</p>
+</div></div><!-- @@@minimum -->
+<br/>
+<!-- $$$secondColor -->
+<div class="qmlitem"><div class="qmlproto" translate="no">
+<div class="table"><table class="qmlname">
+<tr valign="top" class="odd" id="secondColor-prop">
+<td class="tblQmlPropNode"><p>
+<span class="name">secondColor</span> : <span class="type">color</span></p></td></tr>
+</table></div></div>
+<div class="qmldoc"><p>The second color of the <a href="qml-uicomponents-progressbar.html" translate="no">ProgressBar</a>'s gradient. Must bind to a color type.</p>
+<p><b>See also </b><a href="qml-uicomponents-progressbar.html#color-prop" translate="no">color</a>.</p>
+</div></div><!-- @@@secondColor -->
+<br/>
+<!-- $$$value -->
+<div class="qmlitem"><div class="qmlproto" translate="no">
+<div class="table"><table class="qmlname">
+<tr valign="top" class="odd" id="value-prop">
+<td class="tblQmlPropNode"><p>
+<span class="name">value</span> : <span class="type"><a href="qml-int.html" translate="no">int</a></span></p></td></tr>
+</table></div></div>
+<div class="qmldoc"><p>The value of the progress.</p>
+</div></div><!-- @@@value -->
+<br/>
+</body>
+</html>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/qml-uicomponents-switch-members.html b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/qml-uicomponents-switch-members.html
new file mode 100644
index 000000000..df1cd735f
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/qml-uicomponents-switch-members.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+<!-- Switch.qml -->
+ <meta name="description" content="A component that can be turned on or off.">
+ <title>List of All Members for Switch | Test 6.2.11</title>
+</head>
+<body>
+<li><a href="qdoc-test-qmlmodule.html" translate="no">Types</a></li>
+<li><a href="uicomponents-qmlmodule.html" translate="no">UIComponents</a></li>
+<li>Switch</li>
+<div class="sidebar"><div class="sidebar-content" id="sidebar-content"></div></div>
+<h1 class="title" translate="no">List of All Members for Switch</h1>
+<p>This is the complete list of members for <a href="qml-uicomponents-switch.html">Switch</a>, including inherited members.</p>
+<ul>
+<li class="fn" translate="no"><b><a href="qml-uicomponents-switch.html#on-prop" translate="no">on</a></b> : bool</li>
+<li class="fn" translate="no"><b><a href="qml-uicomponents-switch.html#toggle-method" translate="no">toggle</a></b>()</li>
+</ul>
+</body>
+</html>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/qml-uicomponents-switch.html b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/qml-uicomponents-switch.html
new file mode 100644
index 000000000..8e3917a21
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/qml-uicomponents-switch.html
@@ -0,0 +1,68 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+<!-- Switch.qml -->
+ <meta name="description" content="A component that can be turned on or off.">
+ <title>Switch QML Type | Test 6.2.11</title>
+</head>
+<body>
+<li><a href="qdoc-test-qmlmodule.html" translate="no">Types</a></li>
+<li><a href="uicomponents-qmlmodule.html" translate="no">UIComponents</a></li>
+<li>Switch</li>
+<div class="sidebar">
+<div class="toc">
+<h3 id="toc">Contents</h3>
+<ul>
+<li class="level1"><a href="#properties">Properties</a></li>
+<li class="level1"><a href="#methods">Methods</a></li>
+<li class="level1"><a href="#details">Detailed Description</a></li>
+</ul>
+</div>
+<div class="sidebar-content" id="sidebar-content"></div></div>
+<h1 class="title" translate="no">Switch QML Type</h1>
+<!-- $$$Switch-brief -->
+<p>A component that can be turned on or off. <a href="#details">More...</a></p>
+<!-- @@@Switch -->
+<div class="table"><table class="alignedsummary" translate="no">
+<tr><td class="memItemLeft rightAlign topAlign"> Import Statement:</td><td class="memItemRight bottomAlign"> import UIComponents 1.0</td></tr></table></div><ul>
+<li><a href="qml-uicomponents-switch-members.html">List of all members, including inherited members</a></li>
+</ul>
+<h2 id="properties">Properties</h2>
+<ul>
+<li class="fn" translate="no"><b><a href="qml-uicomponents-switch.html#on-prop" translate="no">on</a></b> : bool</li>
+</ul>
+<h2 id="methods">Methods</h2>
+<ul>
+<li class="fn" translate="no"><b><a href="qml-uicomponents-switch.html#toggle-method" translate="no">toggle</a></b>()</li>
+</ul>
+<!-- $$$Switch-description -->
+<h2 id="details">Detailed Description</h2>
+<p>A toggle switch has two states: an <code translate="no">on</code> and an <code translate="no">off</code> state. The <code translate="no">off</code> state is when the <a href="qml-uicomponents-switch.html#on-prop" translate="no">on</a> property is set to <code translate="no">false</code>.</p>
+<p>The ToggleSwitch component is part of the <a href="uicomponents-qmlmodule.html" translate="no">UI Components</a> module.</p>
+<p>This documentation is part of the <a href="test-componentset-example.html">UIComponents</a> example.</p>
+<!-- @@@Switch -->
+<h2>Property Documentation</h2>
+<!-- $$$on -->
+<div class="qmlitem"><div class="qmlproto" translate="no">
+<div class="table"><table class="qmlname">
+<tr valign="top" class="odd" id="on-prop">
+<td class="tblQmlPropNode"><p>
+<span class="name">on</span> : <span class="type">bool</span></p></td></tr>
+</table></div></div>
+<div class="qmldoc"><p>Indicates the state of the switch. If <code translate="no">false</code>, then the switch is in the <code translate="no">off</code> state.</p>
+</div></div><!-- @@@on -->
+<br/>
+<h2>Method Documentation</h2>
+<!-- $$$toggle[overload1]$$$toggle -->
+<div class="qmlitem"><div class="qmlproto" translate="no">
+<div class="table"><table class="qmlname">
+<tr valign="top" class="odd" id="toggle-method">
+<td class="tblQmlFuncNode"><p>
+<span class="name">toggle</span>()</p></td></tr>
+</table></div></div>
+<div class="qmldoc"><p>A method to toggle the switch. If the switch is <code translate="no">on</code>, the toggling it will turn it <code translate="no">off</code>. Toggling a switch in the <code translate="no">off</code> position will turn it <code translate="no">on</code>.</p>
+</div></div><!-- @@@toggle -->
+<br/>
+</body>
+</html>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/qml-uicomponents-tabwidget-members.html b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/qml-uicomponents-tabwidget-members.html
new file mode 100644
index 000000000..cd459ceff
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/qml-uicomponents-tabwidget-members.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+<!-- TabWidget.qml -->
+ <meta name="description" content="A widget that places its children as tabs.">
+ <title>List of All Members for TabWidget | Test 6.2.11</title>
+</head>
+<body>
+<li><a href="qdoc-test-qmlmodule.html" translate="no">Types</a></li>
+<li><a href="uicomponents-qmlmodule.html" translate="no">UIComponents</a></li>
+<li>TabWidget</li>
+<div class="sidebar"><div class="sidebar-content" id="sidebar-content"></div></div>
+<h1 class="title" translate="no">List of All Members for TabWidget</h1>
+<p>This is the complete list of members for <a href="qml-uicomponents-tabwidget.html">TabWidget</a>, including inherited members.</p>
+<ul>
+<li class="fn" translate="no"><b><a href="qml-uicomponents-tabwidget.html#current-prop" translate="no">current</a></b> : int</li>
+<li class="fn" translate="no"><b><a href="qml-uicomponents-tabwidget.html#sampleReadOnlyProperty-prop" translate="no">sampleReadOnlyProperty</a></b> : int</li>
+</ul>
+</body>
+</html>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/qml-uicomponents-tabwidget.html b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/qml-uicomponents-tabwidget.html
new file mode 100644
index 000000000..466262eca
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/qml-uicomponents-tabwidget.html
@@ -0,0 +1,84 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+<!-- TabWidget.qml -->
+ <meta name="description" content="A widget that places its children as tabs.">
+ <title>TabWidget QML Type | Test 6.2.11</title>
+</head>
+<body>
+<li><a href="qdoc-test-qmlmodule.html" translate="no">Types</a></li>
+<li><a href="uicomponents-qmlmodule.html" translate="no">UIComponents</a></li>
+<li>TabWidget</li>
+<div class="sidebar">
+<div class="toc">
+<h3 id="toc">Contents</h3>
+<ul>
+<li class="level1"><a href="#properties">Properties</a></li>
+<li class="level1"><a href="#details">Detailed Description</a></li>
+<li class="level2"><a href="#adding-tabs">Adding Tabs</a></li>
+</ul>
+</div>
+<div class="sidebar-content" id="sidebar-content"></div></div>
+<h1 class="title" translate="no">TabWidget QML Type</h1>
+<!-- $$$TabWidget-brief -->
+<p>A widget that places its children as tabs. <a href="#details">More...</a></p>
+<!-- @@@TabWidget -->
+<div class="table"><table class="alignedsummary" translate="no">
+<tr><td class="memItemLeft rightAlign topAlign"> Import Statement:</td><td class="memItemRight bottomAlign"> import UIComponents 1.0</td></tr></table></div><ul>
+<li><a href="qml-uicomponents-tabwidget-members.html">List of all members, including inherited members</a></li>
+</ul>
+<h2 id="properties">Properties</h2>
+<ul>
+<li class="fn" translate="no"><b><a href="qml-uicomponents-tabwidget.html#current-prop" translate="no">current</a></b> : int</li>
+<li class="fn" translate="no"><b><a href="qml-uicomponents-tabwidget.html#sampleReadOnlyProperty-prop" translate="no">sampleReadOnlyProperty</a></b> : int</li>
+</ul>
+<!-- $$$TabWidget-description -->
+<h2 id="details">Detailed Description</h2>
+<p>A TabWidget places its children as tabs in a view. Selecting a tab involves selecting the tab at the top.</p>
+<p>The TabWidget component is part of the <a href="uicomponents-qmlmodule.html" translate="no">UI Components</a> module.</p>
+<p>This documentation is part of the <a href="test-componentset-example.html">UIComponents</a> example.</p>
+<h2 id="adding-tabs">Adding Tabs</h2>
+<p>To add a tab, declare the tab as a child of the TabWidget.</p>
+<pre class="cpp" translate="no"><span class="type"><a href="qml-uicomponents-tabwidget.html" translate="no">TabWidget</a></span> {
+ <span class="name">id</span>: <span class="name">tabwidget</span>
+
+ <span class="type">Rectangle</span> {
+ <span class="name">id</span>: <span class="name">tab1</span>
+ <span class="name">color</span>: <span class="string">&quot;red&quot;</span>
+ <span class="comment">//... omitted</span>
+ }
+ <span class="type">Rectangle</span> {
+ <span class="name">id</span>: <span class="name">tab2</span>
+ <span class="name">color</span>: <span class="string">&quot;blue&quot;</span>
+ <span class="comment">//... omitted</span>
+ }
+
+}</pre>
+<!-- @@@TabWidget -->
+<h2>Property Documentation</h2>
+<!-- $$$current -->
+<div class="qmlitem"><div class="qmlproto" translate="no">
+<div class="table"><table class="qmlname">
+<tr valign="top" class="odd" id="current-prop">
+<td class="tblQmlPropNode"><p>
+<span class="name">current</span> : <span class="type"><a href="qml-int.html" translate="no">int</a></span></p></td></tr>
+</table></div></div>
+<div class="qmldoc"><p>The currently active tab in the <a href="qml-uicomponents-tabwidget.html" translate="no">TabWidget</a>.</p>
+</div></div><!-- @@@current -->
+<br/>
+<!-- $$$sampleReadOnlyProperty -->
+<div class="qmlitem"><div class="qmlproto" translate="no">
+<div class="table"><table class="qmlname">
+<tr valign="top" class="odd" id="sampleReadOnlyProperty-prop">
+<td class="tblQmlPropNode"><p>
+<span class="name">sampleReadOnlyProperty</span> : <span class="type"><a href="qml-int.html" translate="no">int</a></span> <code class="details extra" translate="no">[read-only]</code></p></td></tr>
+</table></div></div>
+<div class="qmldoc"><p>A sample <code translate="no">read-only</code> property. A contrived property to demonstrate QDoc's ability to detect read-only properties.</p>
+<p>The signature is:</p>
+<pre class="cpp" translate="no">readonly property <span class="type"><a href="qml-int.html" translate="no">int</a></span> sampleReadOnlyProperty: <span class="number">0</span></pre>
+<p>Note that the property must be initialized to a value.</p>
+</div></div><!-- @@@sampleReadOnlyProperty -->
+<br/>
+</body>
+</html>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/qmlmodules.html b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/qmlmodules.html
new file mode 100644
index 000000000..bd0238d40
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/qmlmodules.html
@@ -0,0 +1,49 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+<!-- modules.qdoc -->
+ <title>QML Modules | Test 6.2.11</title>
+</head>
+<body>
+<div class="sidebar">
+<div class="toc">
+<h3 id="toc">Contents</h3>
+<ul>
+<li class="level1"><a href="#qml-types">QML types</a></li>
+<li class="level1"><a href="#qml-value-types">QML value types</a></li>
+</ul>
+</div>
+<div class="sidebar-content" id="sidebar-content"></div></div>
+<h1 class="title">QML Modules</h1>
+<!-- $$$qmlmodules.html-description -->
+<div class="descr" id="details">
+<div class="table"><table class="annotated">
+<tr class="odd topAlign"><td class="tblName" translate="no"><p><a href="test-empty-qmlmodule.html">No QML Types Here</a></p></td><td class="tblDescr"><p>A QML module with no member types.</p></td></tr>
+<tr class="even topAlign"><td class="tblName" translate="no"><p><a href="qdoc-test-qmlmodule.html">QDoc.Test QML Module</a></p></td><td class="tblDescr"><p>QML Types for the Test module.</p></td></tr>
+<tr class="odd topAlign"><td class="tblName" translate="no"><p><a href="themodule-qmlmodule.html">TheModule</a></p></td><td class="tblDescr"><p></p></td></tr>
+<tr class="even topAlign"><td class="tblName" translate="no"><p><a href="uicomponents-qmlmodule.html">UI Components</a></p></td><td class="tblDescr"><p>Basic set of UI components.</p></td></tr>
+<tr class="odd topAlign"><td class="tblName" translate="no"><p><a href="test-nover-qmlmodule.html">Versionless QML Module</a></p></td><td class="tblDescr"><p>QML Types for the Test module without version.</p></td></tr>
+</table></div>
+<h2 id="qml-types">QML types</h2>
+<div class="table"><table class="annotated">
+<tr class="odd topAlign"><td class="tblName" translate="no"><p><a href="qml-qdoc-test-abstractparent.html">AbstractParent</a></p></td><td class="tblDescr"><p>Abstract base QML type</p></td></tr>
+<tr class="even topAlign"><td class="tblName" translate="no"><p><a href="qml-qdoc-test-child.html">Child</a></p></td><td class="tblDescr"><p>A Child inheriting its parent</p></td></tr>
+<tr class="odd topAlign"><td class="tblName" translate="no"><p><a href="qml-qdoc-test-doctest.html">DocTest</a></p></td><td class="tblDescr"><p>Represents a doc test case</p></td></tr>
+<tr class="even topAlign"><td class="tblName" translate="no"><p><a href="qml-qdoc-test-type.html">Type</a></p></td><td class="tblDescr"><p>A QML type documented in a .cpp file</p></td></tr>
+<tr class="odd topAlign"><td class="tblName" translate="no"><p><a href="qml-qdoc-test-yetanotherchild.html">YetAnotherChild</a></p></td><td class="tblDescr"><p>A type inheriting from internal abstract parent</p></td></tr>
+</table></div>
+<h2 id="qml-value-types">QML value types</h2>
+<p class="centerAlign functionIndex" translate="no"><b><a href="#i">I</a>&nbsp;</b></p>
+<div class="flowListDiv" translate="no">
+<dl class="flowList odd"><dt class="alphaChar" id="i"><b>I</b></dt>
+<dd><a href="qml-int.html">int</a></dd>
+</dl>
+</div>
+<div class="table"><table class="annotated">
+<tr class="odd topAlign"><td class="tblName" translate="no"><p><a href="qml-int.html">int</a></p></td><td class="tblDescr"><p>An integer value type</p></td></tr>
+</table></div>
+</div>
+<!-- @@@qmlmodules.html -->
+</body>
+</html>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/seenclass.html b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/seenclass.html
new file mode 100644
index 000000000..ece82ceb7
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/seenclass.html
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+<!-- dont.cpp -->
+ <meta name="description" content="A public but undocumented class.">
+ <title>SeenClass Class | Test 6.2.11</title>
+</head>
+<body>
+<li><a href="testcpp-module.html" translate="no">C++ Classes</a></li>
+<li>SeenClass</li>
+<div class="sidebar">
+<div class="toc">
+<h3 id="toc">Contents</h3>
+<ul>
+<li class="level1"><a href="#details">Detailed Description</a></li>
+</ul>
+</div>
+<div class="sidebar-content" id="sidebar-content"></div></div>
+<h1 class="title" translate="no">SeenClass Class</h1>
+<!-- $$$SeenClass-brief -->
+<p>A public but undocumented class. <a href="#details">More...</a></p>
+<!-- @@@SeenClass -->
+<div class="table"><table class="alignedsummary" translate="no">
+<tr><td class="memItemLeft rightAlign topAlign"> Header:</td><td class="memItemRight bottomAlign"> <span class="preprocessor">#include &lt;SeenClass&gt;</span></td></tr>
+<tr><td class="memItemLeft rightAlign topAlign"> CMake:</td><td class="memItemRight bottomAlign"> find_package(Qt6 REQUIRED COMPONENTS QDocTest) <br/>
+target_link_libraries(mytarget PRIVATE Qt6::QDocTest)</td></tr>
+<tr><td class="memItemLeft rightAlign topAlign"> qmake:</td><td class="memItemRight bottomAlign"> QT += testcpp</td></tr>
+<tr><td class="memItemLeft rightAlign topAlign"> Since:</td><td class="memItemRight bottomAlign"> Qt 2.0</td></tr>
+</table></div>
+<!-- $$$SeenClass-description -->
+<div class="descr">
+<h2 id="details">Detailed Description</h2>
+</div>
+<!-- @@@SeenClass -->
+</body>
+</html>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/test-cmaketest-cmakelists-txt.html b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/test-cmaketest-cmakelists-txt.html
new file mode 100644
index 000000000..8072df6db
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/test-cmaketest-cmakelists-txt.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+<!-- cmaketest.qdoc -->
+ <title>CMake Example Project | Test 6.2.11</title>
+</head>
+<body>
+<h1 class="title">CMake Example Project</h1>
+<pre class="cpp" translate="no">cmake_minimum_required(VERSION 3.16)
+project (QDOCTEST)</pre>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/test-cmaketest-example.html b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/test-cmaketest-example.html
new file mode 100644
index 000000000..a9ebe9784
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/test-cmaketest-example.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+<!-- cmaketest.qdoc -->
+ <title>CMake Example Project | Test 6.2.11</title>
+</head>
+<body>
+<div class="sidebar"><div class="sidebar-content" id="sidebar-content"></div></div>
+<h1 class="title">CMake Example Project</h1>
+<!-- $$$cmaketest-description -->
+<div class="descr" id="details">
+<p class="centerAlign"><img src="images/leonardo-da-vinci.png" alt="" /></p><p>Files:</p>
+<ul>
+<li><a href="test-cmaketest-cmakelists-txt.html" translate="no">cmaketest/CMakeLists.txt</a></li>
+<li><a href="test-cmaketest-main-cpp.html" translate="no">cmaketest/main.cpp</a></li>
+</ul>
+</div>
+<!-- @@@cmaketest -->
+</body>
+</html>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/test-cmaketest-main-cpp.html b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/test-cmaketest-main-cpp.html
new file mode 100644
index 000000000..0acb90579
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/test-cmaketest-main-cpp.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+<!-- cmaketest.qdoc -->
+ <title>CMake Example Project | Test 6.2.11</title>
+</head>
+<body>
+<h1 class="title">CMake Example Project</h1>
+<pre class="cpp" translate="no"><span class="type">void</span> main(){}</pre>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/test-componentset-componentset-pro.html b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/test-componentset-componentset-pro.html
new file mode 100644
index 000000000..0589f516e
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/test-componentset-componentset-pro.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+<!-- examples.qdoc -->
+ <meta name="description" content="Example for documenting QML types.">
+ <title>QML Documentation Example | Test 6.2.11</title>
+</head>
+<body>
+<h1 class="title">QML Documentation Example</h1>
+<pre class="cpp" translate="no">SOURCES = componentset.pro \
+ ProgressBar.qml \
+ Switch.qml \
+ TabWidget.qml \
+ uicomponents.qdoc</pre>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/test-componentset-componentset-qml.html b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/test-componentset-componentset-qml.html
new file mode 100644
index 000000000..8d50b2a3a
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/test-componentset-componentset-qml.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+<!-- examples.qdoc -->
+ <meta name="description" content="Example for documenting QML types.">
+ <title>QML Documentation Example | Test 6.2.11</title>
+</head>
+<body>
+<h1 class="title">QML Documentation Example</h1>
+<pre class="qml" translate="no"><span class="comment">// Copyright (C) 2023 The Qt Company Ltd.</span>
+<span class="comment">// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0</span>
+
+import QtQuick 2.0
+
+<span class="type">Item</span> {
+}</pre>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/test-componentset-example.html b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/test-componentset-example.html
new file mode 100644
index 000000000..8227ff937
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/test-componentset-example.html
@@ -0,0 +1,55 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+<!-- examples.qdoc -->
+ <meta name="description" content="Example for documenting QML types.">
+ <title>QML Documentation Example | Test 6.2.11</title>
+</head>
+<body>
+<div class="sidebar">
+<div class="toc">
+<h3 id="toc">Contents</h3>
+<ul>
+<li class="level1"><a href="#qml-class">QML Class</a></li>
+<li class="level1"><a href="#properties-signals-handlers-and-methods">Properties, Signals, Handlers, and Methods</a></li>
+<li class="level2"><a href="#internal-documentation">Internal Documentation</a></li>
+<li class="level1"><a href="#qml-types-with-c-implementation">QML Types with C++ Implementation</a></li>
+</ul>
+</div>
+<div class="sidebar-content" id="sidebar-content"></div></div>
+<h1 class="title">QML Documentation Example</h1>
+<!-- $$$componentset-brief -->
+<p>Example for documenting QML types.</p>
+<!-- @@@componentset -->
+<!-- $$$componentset-description -->
+<div class="descr" id="details">
+<p>This example demonstrates one of the ways to document QML types. It also generates a warning about a missing example image, on purpose.</p>
+<p>In particular, there are sample types that are documented with QDoc commands comments. There are documentation comments for the QML types and their public interfaces. The types are grouped into a module, the <a href="uicomponents-qmlmodule.html" translate="no">UI Components</a> module.</p>
+<p>The uicomponents.qdoc file generates the overview page for the <a href="uicomponents-qmlmodule.html" translate="no">UI Components</a> module page.</p>
+<p>The generated documentation is available in the <a href="uicomponents-qmlmodule.html" translate="no">UI Components</a> module.</p>
+<h4 id="qml-class">QML Class</h4>
+<p>The QML types use the \qmltype to document the type. In addition, they have the \inmodule command in order for QDoc to associate them to the <code translate="no">UIComponents</code> module.</p>
+<p>QDoc uses the \brief command to place a basic description when listing the types.</p>
+<h4 id="properties-signals-handlers-and-methods">Properties, Signals, Handlers, and Methods</h4>
+<p>The types have their properties, signals, handlers, and methods defined in their respective QML files. QDoc associates the properties and methods to the types, therefore, you only need to place the documentation above the property, method, or signal.</p>
+<p>To document the type of a <i>property alias</i>, you must use the \qmlproperty command to specify the data type.</p>
+<pre class="cpp" translate="no">\qmlproperty <span class="type">int</span> anAliasedProperty
+An aliased property of type <span class="type">int</span><span class="operator">.</span></pre>
+<h5 id="internal-documentation">Internal Documentation</h5>
+<p>You may declare that a documentation is for internal use by placing the \internal command after the beginning QDoc comment <code translate="no">/*</code>. QDoc will prevent the internal documentation from appearing in the public API.</p>
+<p>If you wish to omit certain parts of the documentation, you may use the \omit and \endomit command.</p>
+<h4 id="qml-types-with-c-implementation">QML Types with C++ Implementation</h4>
+<p>This example only demonstrates the documentation for types in QML files, but the regular QML commands may be placed inside C++ classes to define the public API of the QML type.</p>
+<p>Files:</p>
+<ul>
+<li><a href="test-componentset-progressbar-qml.html" translate="no">componentset/ProgressBar.qml</a></li>
+<li><a href="test-componentset-switch-qml.html" translate="no">componentset/Switch.qml</a></li>
+<li><a href="test-componentset-tabwidget-qml.html" translate="no">componentset/TabWidget.qml</a></li>
+<li><a href="test-componentset-componentset-pro.html" translate="no">componentset/componentset.pro</a></li>
+<li><a href="test-componentset-componentset-qml.html" translate="no">componentset/componentset.qml</a></li>
+</ul>
+</div>
+<!-- @@@componentset -->
+</body>
+</html>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/test-componentset-progressbar-qml.html b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/test-componentset-progressbar-qml.html
new file mode 100644
index 000000000..41c9c506a
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/test-componentset-progressbar-qml.html
@@ -0,0 +1,108 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+<!-- examples.qdoc -->
+ <meta name="description" content="Example for documenting QML types.">
+ <title>QML Documentation Example | Test 6.2.11</title>
+</head>
+<body>
+<h1 class="title">QML Documentation Example</h1>
+<pre class="qml" translate="no"><span class="comment">// Copyright (C) 2016 The Qt Company Ltd.</span>
+<span class="comment">// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause</span>
+
+import QtQuick 1.0
+
+<span class="comment">/*!
+ \qmltype ProgressBar
+ \inqmlmodule UIComponents
+ \brief A component that shows the progress of an event.
+
+ A ProgressBar shows the linear progress of an event as its \l value.
+ The range is specified using the \l {minimum} and the \l{maximum} values.
+
+ The ProgressBar component is part of the \l {UI Components} module.
+
+ This documentation is part of the \l{componentset}{UIComponents} example.
+*/</span>
+<span class="type">Item</span> {
+ <span class="name">id</span>: <span class="name">progressbar</span>
+
+ <span class="comment">/*!
+ The minimum value of the ProgressBar range.
+ The \l value must not be less than this value.
+ */</span>
+ property <span class="type"><a href="qml-int.html" translate="no">int</a></span> <span class="name">minimum</span>: <span class="number">0</span>
+
+ <span class="comment">/*!
+ The maximum value of the ProgressBar range.
+ The \l value must not be more than this value.
+ */</span>
+ property <span class="type"><a href="qml-int.html" translate="no">int</a></span> <span class="name">maximum</span>: <span class="number">100</span>
+
+ <span class="comment">/*!
+ The value of the progress.
+ */</span>
+ property <span class="type"><a href="qml-int.html" translate="no">int</a></span> <span class="name">value</span>: <span class="number">0</span>
+
+ <span class="comment">/*!
+ \qmlproperty color ProgressBar::color
+ The color of the ProgressBar's gradient. Must bind to a color type.
+
+ \omit
+ The &quot;\qmlproperty &lt;type&gt; &lt;property name&gt;&quot; is needed because
+ property alias need to have their types manually entered.
+
+ QDoc will not publish the documentation within omit and endomit.
+ \endomit
+
+ \sa secondColor
+ */</span>
+ property <span class="type">alias</span> <span class="name">color</span>: <span class="name">gradient1</span>.<span class="name">color</span>
+
+ <span class="comment">/*!
+ \qmlproperty color ProgressBar::secondColor
+ The second color of the ProgressBar's gradient.
+ Must bind to a color type.
+
+ \omit
+ The &quot;\qmlproperty &lt;type&gt; &lt;property name&gt;&quot; is needed because
+ property alias need to have their types manually entered.
+
+ QDoc will not publish the documentation within omit and endomit.
+ \endomit
+
+ \sa color
+ */</span>
+ property <span class="type">alias</span> <span class="name">secondColor</span>: <span class="name">gradient2</span>.<span class="name">color</span>
+
+ <span class="name">width</span>: <span class="number">250</span>; <span class="name">height</span>: <span class="number">23</span>
+ <span class="name">clip</span>: <span class="number">true</span>
+
+ <span class="type">Rectangle</span> {
+ <span class="name">id</span>: <span class="name">highlight</span>
+
+ <span class="comment">/*!
+ An internal documentation comment. The widthDest property is not
+ a public API and therefore will not be exposed.
+ */</span>
+ property <span class="type"><a href="qml-int.html" translate="no">int</a></span> <span class="name">widthDest</span>: ((<span class="name">progressbar</span>.<span class="name">width</span> <span class="operator">*</span> (<span class="name">value</span> <span class="operator">-</span> <span class="name">minimum</span>)) <span class="operator">/</span> (<span class="name">maximum</span> <span class="operator">-</span> <span class="name">minimum</span>) <span class="operator">-</span> <span class="number">6</span>)
+
+ <span class="name">width</span>: <span class="name">highlight</span>.<span class="name">widthDest</span>
+ Behavior on <span class="name">width</span> { <span class="type">SmoothedAnimation</span> { <span class="name">velocity</span>: <span class="number">1200</span> } }
+
+ <span class="type">anchors</span> { <span class="name">left</span>: <span class="name">parent</span>.<span class="name">left</span>; <span class="name">top</span>: <span class="name">parent</span>.<span class="name">top</span>; <span class="name">bottom</span>: <span class="name">parent</span>.<span class="name">bottom</span>; <span class="name">margins</span>: <span class="number">3</span> }
+ <span class="name">radius</span>: <span class="number">1</span>
+ <span class="name">gradient</span>: <span class="name">Gradient</span> {
+ <span class="type">GradientStop</span> { <span class="name">id</span>: <span class="name">gradient1</span>; <span class="name">position</span>: <span class="number">0.0</span> }
+ <span class="type">GradientStop</span> { <span class="name">id</span>: <span class="name">gradient2</span>; <span class="name">position</span>: <span class="number">1.0</span> }
+ }
+
+ }
+ <span class="type">Text</span> {
+ <span class="type">anchors</span> { <span class="name">right</span>: <span class="name">highlight</span>.<span class="name">right</span>; <span class="name">rightMargin</span>: <span class="number">6</span>; <span class="name">verticalCenter</span>: <span class="name">parent</span>.<span class="name">verticalCenter</span> }
+ <span class="name">color</span>: <span class="string">&quot;white&quot;</span>
+ <span class="name">font</span>.bold: <span class="number">true</span>
+ <span class="name">text</span>: <span class="name">Math</span>.<span class="name">floor</span>((<span class="name">value</span> <span class="operator">-</span> <span class="name">minimum</span>) <span class="operator">/</span> (<span class="name">maximum</span> <span class="operator">-</span> <span class="name">minimum</span>) <span class="operator">*</span> <span class="number">100</span>) <span class="operator">+</span> <span class="string">'%'</span>
+ }
+}</pre>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/test-componentset-switch-qml.html b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/test-componentset-switch-qml.html
new file mode 100644
index 000000000..958f2f19b
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/test-componentset-switch-qml.html
@@ -0,0 +1,113 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+<!-- examples.qdoc -->
+ <meta name="description" content="Example for documenting QML types.">
+ <title>QML Documentation Example | Test 6.2.11</title>
+</head>
+<body>
+<h1 class="title">QML Documentation Example</h1>
+<pre class="qml" translate="no"><span class="comment">// Copyright (C) 2016 The Qt Company Ltd.</span>
+<span class="comment">// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause</span>
+
+import QtQuick 1.0
+
+<span class="comment">/*!
+ \qmltype ToggleSwitch
+ \inqmlmodule UIComponents
+ \brief A component that can be turned on or off.
+
+ A toggle switch has two states: an \c on and an \c off state. The \c off
+ state is when the \l on property is set to \c false.
+
+ The ToggleSwitch component is part of the \l {UI Components} module.
+
+ This documentation is part of the \l{componentset}{UIComponents} example.
+
+*/</span>
+<span class="type">Item</span> {
+ <span class="name">id</span>: <span class="name">toggleswitch</span>
+ <span class="name">width</span>: <span class="name">background</span>.<span class="name">width</span>; <span class="name">height</span>: <span class="name">background</span>.<span class="name">height</span>
+
+ <span class="comment">/*!
+ Indicates the state of the switch. If \c false, then the switch is in
+ the \c off state.
+
+ \omit
+ The \qmlproperty &lt;type&gt; &lt;propertyname&gt; is not necessary as QDoc
+ will associate this property to the ToggleSwitch
+
+ QDoc will not publish the documentation within omit and endomit.
+ \endomit
+ */</span>
+ property <span class="type">bool</span> <span class="name">on</span>: <span class="number">false</span>
+
+ <span class="comment">/*!
+ A method to toggle the switch. If the switch is \c on, the toggling it
+ will turn it \c off. Toggling a switch in the \c off position will
+ turn it \c on.
+ */</span>
+ <span class="keyword">function </span><span class="name">toggle</span>() {
+ <span class="keyword">if</span> (<span class="name">toggleswitch</span>.<span class="name">state</span> <span class="operator">==</span> <span class="string">&quot;on&quot;</span>)
+ <span class="name">toggleswitch</span>.<span class="name">state</span> <span class="operator">=</span> <span class="string">&quot;off&quot;</span>;
+ <span class="keyword">else</span>
+ <span class="name">toggleswitch</span>.<span class="name">state</span> <span class="operator">=</span> <span class="string">&quot;on&quot;</span>;
+ }
+
+ <span class="comment">/*!
+ \internal
+
+ An internal function to synchronize the switch's internals. This
+ function is not for public access. The \internal command will
+ prevent QDoc from publishing this comment in the public API.
+ */</span>
+ <span class="keyword">function </span><span class="name">releaseSwitch</span>() {
+ <span class="keyword">if</span> (<span class="name">knob</span>.<span class="name">x</span> <span class="operator">==</span> <span class="number">1</span>) {
+ <span class="keyword">if</span> (<span class="name">toggleswitch</span>.<span class="name">state</span> <span class="operator">==</span> <span class="string">&quot;off&quot;</span>) <span class="keyword">return</span>;
+ }
+ <span class="keyword">if</span> (<span class="name">knob</span>.<span class="name">x</span> <span class="operator">==</span> <span class="number">78</span>) {
+ <span class="keyword">if</span> (<span class="name">toggleswitch</span>.<span class="name">state</span> <span class="operator">==</span> <span class="string">&quot;on&quot;</span>) <span class="keyword">return</span>;
+ }
+ <span class="name">toggle</span>();
+ }
+
+ <span class="type">Rectangle</span> {
+ <span class="name">id</span>: <span class="name">background</span>
+ <span class="name">width</span>: <span class="number">130</span>; <span class="name">height</span>: <span class="number">48</span>
+ <span class="name">radius</span>: <span class="number">48</span>
+ <span class="name">color</span>: <span class="string">&quot;lightsteelblue&quot;</span>
+ <span class="type">MouseArea</span> { <span class="name">anchors</span>.fill: <span class="name">parent</span>; <span class="name">onClicked</span>: <span class="name">toggle</span>() }
+ }
+
+ <span class="type">Rectangle</span> {
+ <span class="name">id</span>: <span class="name">knob</span>
+ <span class="name">width</span>: <span class="number">48</span>; <span class="name">height</span>: <span class="number">48</span>
+ <span class="name">radius</span>: <span class="name">width</span>
+ <span class="name">color</span>: <span class="string">&quot;lightblue&quot;</span>
+
+ <span class="type">MouseArea</span> {
+ <span class="name">anchors</span>.fill: <span class="name">parent</span>
+ <span class="name">drag</span>.target: <span class="name">knob</span>; <span class="name">drag</span>.axis: <span class="name">Drag</span>.<span class="name">XAxis</span>; <span class="name">drag</span>.minimumX: <span class="number">1</span>; <span class="name">drag</span>.maximumX: <span class="number">78</span>
+ <span class="name">onClicked</span>: <span class="name">toggle</span>()
+ <span class="name">onReleased</span>: <span class="name">releaseSwitch</span>()
+ }
+ }
+
+ <span class="name">states</span>: [
+ <span class="type">State</span> {
+ <span class="name">name</span>: <span class="string">&quot;on&quot;</span>
+ <span class="type">PropertyChanges</span> { <span class="name">target</span>: <span class="name">knob</span>; <span class="name">x</span>: <span class="number">78</span> }
+ <span class="type">PropertyChanges</span> { <span class="name">target</span>: <span class="name">toggleswitch</span>; <span class="name">on</span>: <span class="number">true</span> }
+ },
+ <span class="type">State</span> {
+ <span class="name">name</span>: <span class="string">&quot;off&quot;</span>
+ <span class="type">PropertyChanges</span> { <span class="name">target</span>: <span class="name">knob</span>; <span class="name">x</span>: <span class="number">1</span> }
+ <span class="type">PropertyChanges</span> { <span class="name">target</span>: <span class="name">toggleswitch</span>; <span class="name">on</span>: <span class="number">false</span> }
+ }
+ ]
+
+ <span class="name">transitions</span>: <span class="name">Transition</span> {
+ <span class="type">NumberAnimation</span> { <span class="name">properties</span>: <span class="string">&quot;x&quot;</span>; <span class="name">easing</span>.type: <span class="name">Easing</span>.<span class="name">InOutQuad</span>; <span class="name">duration</span>: <span class="number">200</span> }
+ }
+}</pre>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/test-componentset-tabwidget-qml.html b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/test-componentset-tabwidget-qml.html
new file mode 100644
index 000000000..ea6116dca
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/test-componentset-tabwidget-qml.html
@@ -0,0 +1,155 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+<!-- examples.qdoc -->
+ <meta name="description" content="Example for documenting QML types.">
+ <title>QML Documentation Example | Test 6.2.11</title>
+</head>
+<body>
+<h1 class="title">QML Documentation Example</h1>
+<pre class="qml" translate="no"><span class="comment">// Copyright (C) 2016 The Qt Company Ltd.</span>
+<span class="comment">// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause</span>
+
+import QtQuick 1.0
+
+<span class="comment">/*!
+ \qmltype TabWidget
+ \inqmlmodule UIComponents
+ \brief A widget that places its children as tabs.
+
+ A TabWidget places its children as tabs in a view. Selecting
+ a tab involves selecting the tab at the top.
+
+ The TabWidget component is part of the \l {UI Components} module.
+
+ This documentation is part of the \l{componentset}{UIComponents} example.
+
+ \section1 Adding Tabs
+
+ To add a tab, declare the tab as a child of the TabWidget.
+
+ \code
+ TabWidget {
+ id: tabwidget
+
+ Rectangle {
+ id: tab1
+ color: &quot;red&quot;
+ //... omitted
+ }
+ Rectangle {
+ id: tab2
+ color: &quot;blue&quot;
+ //... omitted
+ }
+
+ }
+ \endcode
+
+*/</span>
+<span class="type">Item</span> {
+ <span class="name">id</span>: <span class="name">tabWidget</span>
+
+ <span class="comment">/*!
+ \internal
+
+ Setting the default property to stack.children means any child items
+ of the TabWidget are actually added to the 'stack' item's children.
+
+ See the \l{&quot;Property Binding in QML&quot;}
+ documentation for details on default properties.
+
+ This is an implementation detail, not meant for public knowledge. Putting
+ the \internal command at the beginning will cause QDoc to not publish this
+ documentation in the public API page.
+
+ Normally, a property alias needs to have a
+ &quot;\qmlproperty &lt;type&gt; &lt;propertyname&gt;&quot; to assign the alias a type.
+
+ */</span>
+ default property <span class="type">alias</span> <span class="name">content</span>: <span class="name">stack</span>.<span class="name">children</span>
+
+ <span class="comment">/*!
+ The currently active tab in the TabWidget.
+ */</span>
+ property <span class="type"><a href="qml-int.html" translate="no">int</a></span> <span class="name">current</span>: <span class="number">0</span>
+
+ <span class="comment">/*!
+ A sample \c{read-only} property.
+ A contrived property to demonstrate QDoc's ability to detect
+ read-only properties.
+
+ The signature is:
+ \code
+ readonly property int sampleReadOnlyProperty: 0
+ \endcode
+
+ Note that the property must be initialized to a value.
+
+ */</span>
+ readonly property <span class="type"><a href="qml-int.html" translate="no">int</a></span> <span class="name">sampleReadOnlyProperty</span>: <span class="number">0</span>
+
+ <span class="comment">/*!
+ \internal
+
+ This handler is an implementation
+ detail. The \c{\internal} command will prevent QDoc from publishing this
+ documentation on the public API.
+ */</span>
+ <span class="name">onCurrentChanged</span>: <span class="name">setOpacities</span>()
+ <span class="name">Component</span>.onCompleted: <span class="name">setOpacities</span>()
+
+ <span class="comment">/*!
+ \internal
+
+ An internal function to set the opacity.
+ The \internal command will prevent QDoc from publishing this
+ documentation on the public API.
+ */</span>
+ <span class="keyword">function </span><span class="name">setOpacities</span>() {
+ <span class="keyword">for</span> (var i = 0; <span class="name">i</span> <span class="operator">&lt;</span> <span class="name">stack</span>.<span class="name">children</span>.<span class="name">length</span>; ++<span class="name">i</span>) {
+ <span class="name">stack</span>.<span class="name">children</span>[<span class="name">i</span>].<span class="name">opacity</span> <span class="operator">=</span> (<span class="name">i</span> <span class="operator">==</span> <span class="name">current</span> ? <span class="number">1</span> : <span class="number">0</span>)
+ }
+ }
+
+ <span class="type">Row</span> {
+ <span class="name">id</span>: <span class="name">header</span>
+
+ <span class="type">Repeater</span> {
+ <span class="name">model</span>: <span class="name">stack</span>.<span class="name">children</span>.<span class="name">length</span>
+ <span class="name">delegate</span>: <span class="name">Rectangle</span> {
+ <span class="name">width</span>: <span class="name">tabWidget</span>.<span class="name">width</span> <span class="operator">/</span> <span class="name">stack</span>.<span class="name">children</span>.<span class="name">length</span>; <span class="name">height</span>: <span class="number">36</span>
+
+ <span class="type">Rectangle</span> {
+ <span class="name">width</span>: <span class="name">parent</span>.<span class="name">width</span>; <span class="name">height</span>: <span class="number">1</span>
+ <span class="type">anchors</span> { <span class="name">bottom</span>: <span class="name">parent</span>.<span class="name">bottom</span>; <span class="name">bottomMargin</span>: <span class="number">1</span> }
+ <span class="name">color</span>: <span class="string">&quot;#acb2c2&quot;</span>
+ }
+ <span class="type">BorderImage</span> {
+ <span class="type">anchors</span> { <span class="name">fill</span>: <span class="name">parent</span>; <span class="name">leftMargin</span>: <span class="number">2</span>; <span class="name">topMargin</span>: <span class="number">5</span>; <span class="name">rightMargin</span>: <span class="number">1</span> }
+ <span class="type">border</span> { <span class="name">left</span>: <span class="number">7</span>; <span class="name">right</span>: <span class="number">7</span> }
+ <span class="name">source</span>: <span class="string">&quot;tab.png&quot;</span>
+ <span class="name">visible</span>: <span class="name">tabWidget</span>.<span class="name">current</span> <span class="operator">==</span> <span class="name">index</span>
+ }
+ <span class="type">Text</span> {
+ <span class="name">horizontalAlignment</span>: <span class="name">Qt</span>.<span class="name">AlignHCenter</span>; <span class="name">verticalAlignment</span>: <span class="name">Qt</span>.<span class="name">AlignVCenter</span>
+ <span class="name">anchors</span>.fill: <span class="name">parent</span>
+ <span class="name">text</span>: <span class="name">stack</span>.<span class="name">children</span>[<span class="name">index</span>].<span class="name">title</span>
+ <span class="name">elide</span>: <span class="name">Text</span>.<span class="name">ElideRight</span>
+ <span class="name">font</span>.bold: <span class="name">tabWidget</span>.<span class="name">current</span> <span class="operator">==</span> <span class="name">index</span>
+ }
+ <span class="type">MouseArea</span> {
+ <span class="name">anchors</span>.fill: <span class="name">parent</span>
+ <span class="name">onClicked</span>: <span class="name">tabWidget</span>.<span class="name">current</span> <span class="operator">=</span> <span class="name">index</span>
+ }
+ }
+ }
+ }
+
+ <span class="type">Item</span> {
+ <span class="name">id</span>: <span class="name">stack</span>
+ <span class="name">width</span>: <span class="name">tabWidget</span>.<span class="name">width</span>
+ <span class="name">anchors</span>.top: <span class="name">header</span>.<span class="name">bottom</span>; <span class="name">anchors</span>.bottom: <span class="name">tabWidget</span>.<span class="name">bottom</span>
+ }
+}</pre>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/test-demos-demo-demo-cpp.html b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/test-demos-demo-demo-cpp.html
new file mode 100644
index 000000000..6e1f68232
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/test-demos-demo-demo-cpp.html
@@ -0,0 +1,18 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+<!-- demo.qdoc -->
+ <title>Demo | Test 6.2.11</title>
+</head>
+<body>
+<h1 class="title">Demo</h1>
+<pre class="cpp" translate="no"><span class="comment">// Copyright (C) 2023 The Qt Company Ltd.</span>
+<span class="comment">// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0</span>
+
+<span class="type">bool</span> isOverThousand(<span class="type">int</span> n)
+{
+ <span class="keyword">if</span> (n <span class="operator">&gt;</span> <span class="number">1'000</span>)
+ <span class="keyword">return</span> <span class="keyword">true</span>;
+ <span class="keyword">return</span> <span class="keyword">false</span>;
+}</pre>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/test-demos-demo-demo-pro.html b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/test-demos-demo-demo-pro.html
new file mode 100644
index 000000000..3d59c65b3
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/test-demos-demo-demo-pro.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+<!-- demo.qdoc -->
+ <title>Demo | Test 6.2.11</title>
+</head>
+<body>
+<h1 class="title">Demo</h1>
+<pre class="cpp" translate="no">TEMPLATE = aux
+message(&quot;Nothing to see here.&quot;)</pre>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/test-demos-demo-dontxclude-cmakelists-txt.html b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/test-demos-demo-dontxclude-cmakelists-txt.html
new file mode 100644
index 000000000..c654a3768
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/test-demos-demo-dontxclude-cmakelists-txt.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+<!-- demo.qdoc -->
+ <title>Demo | Test 6.2.11</title>
+</head>
+<body>
+<h1 class="title">Demo</h1>
+<pre class="cpp" translate="no">cmake_minimum_required(VERSION 3.16)
+project (DONTXCLUDEDIRS_QDOCTEST)</pre>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/test-demos-demo-example.html b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/test-demos-demo-example.html
new file mode 100644
index 000000000..8f7c9ace8
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/test-demos-demo-example.html
@@ -0,0 +1,24 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+<!-- demo.qdoc -->
+ <title>Demo | Test 6.2.11</title>
+</head>
+<body>
+<div class="sidebar"><div class="sidebar-content" id="sidebar-content"></div></div>
+<h1 class="title">Demo</h1>
+<!-- $$$demos/demo-description -->
+<div class="descr" id="details">
+<p class="centerAlign"><img src="images/leonardo-da-vinci.png" alt="" /></p><pre class="cpp" translate="no"> <span class="keyword">if</span> (n <span class="operator">&gt;</span> <span class="number">1'000</span>)
+ <span class="keyword">return</span> <span class="keyword">true</span>;</pre>
+<p>Files:</p>
+<ul>
+<li><a href="test-demos-demo-demo-cpp.html" translate="no">demos/demo/demo.cpp</a></li>
+<li><a href="test-demos-demo-demo-pro.html" translate="no">demos/demo/demo.pro</a></li>
+<li><a href="test-demos-demo-dontxclude-cmakelists-txt.html" translate="no">demos/demo/dontxclude/CMakeLists.txt</a></li>
+</ul>
+</div>
+<!-- @@@demos/demo -->
+</body>
+</html>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/test-demos-hidden-example.html b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/test-demos-hidden-example.html
new file mode 100644
index 000000000..fc123ba60
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/test-demos-hidden-example.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+<!-- hidden.qdoc -->
+ <meta name="description" content="Tagged 'broken', does not appear in examples-manifest.xml.">
+ <title>Hidden Demo | Test 6.2.11</title>
+</head>
+<body>
+<div class="sidebar"><div class="sidebar-content" id="sidebar-content"></div></div>
+<h1 class="title">Hidden Demo</h1>
+<!-- $$$demos/hidden-brief -->
+<p>Tagged 'broken', does not appear in examples-manifest.xml.</p>
+<!-- @@@demos/hidden -->
+<!-- $$$demos/hidden-description -->
+<div class="descr" id="details">
+<p>Also missing an image, but that's OK as it's broken anyway.</p>
+</div>
+<!-- @@@demos/hidden -->
+</body>
+</html>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/test-empty-qmlmodule.html b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/test-empty-qmlmodule.html
new file mode 100644
index 000000000..eabda8b7a
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/test-empty-qmlmodule.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+<!-- type.cpp -->
+ <meta name="description" content="A QML module with no member types.">
+ <title>No QML Types Here | Test 6.2.11</title>
+</head>
+<body>
+<div class="sidebar"><div class="sidebar-content" id="sidebar-content"></div></div>
+<h1 class="title" translate="no">No QML Types Here</h1>
+<!-- $$$Test.Empty-description -->
+<div class="descr" id="details">
+</div>
+<!-- @@@Test.Empty -->
+</body>
+</html>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/test-nover-qmlmodule.html b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/test-nover-qmlmodule.html
new file mode 100644
index 000000000..68cdf82d4
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/test-nover-qmlmodule.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+<!-- type.cpp -->
+ <meta name="description" content="QML Types for the Test module without version.">
+ <title>Versionless QML Module | Test 6.2.11</title>
+</head>
+<body>
+<div class="sidebar"><div class="sidebar-content" id="sidebar-content"></div></div>
+<h1 class="title" translate="no">Versionless QML Module</h1>
+<p>This module is in <i>Tech Preview</i> state.</p>
+<p>This module was introduced in Qt 1.1.</p>
+<!-- $$$Test.NoVer-description -->
+<div class="descr" id="details">
+</div>
+<!-- @@@Test.NoVer -->
+<div class="table"><table class="annotated">
+<tr class="odd topAlign"><td class="tblName" translate="no"><p><a href="qml-test-nover-doctest.html">DocTest</a></p></td><td class="tblDescr"><p>Shadows the type name in QDoc.Test module</p></td></tr>
+<tr class="even topAlign"><td class="tblName" translate="no"><p><a href="qml-test-nover-typenoversion.html">TypeNoVersion</a></p></td><td class="tblDescr"><p>Another QML type documented in a .cpp file</p></td></tr>
+</table></div>
+</body>
+</html>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/test.index b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/test.index
new file mode 100644
index 000000000..55637861e
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/test.index
@@ -0,0 +1,238 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE QDOCINDEX>
+<INDEX url="" title="A test project for QDoc build artifacts" version="6.2.11" project="Test">
+ <namespace name="" status="active" access="public" module="test">
+ <function name="QDOCTEST_MACRO" href="testqdoc.html#QDOCTEST_MACRO" status="active" access="public" documented="true" related="0" since="Test 0.9" meta="macrowithoutparams" signature="QDOCTEST_MACRO"/>
+ <function name="QDOCTEST_MACRO2" href="testqdoc-test.html#QDOCTEST_MACRO2" status="active" access="public" documented="true" related="1" since="Test 1.1" meta="macrowithparams" brief="A macro with argument x" signature="QDOCTEST_MACRO2(int &amp;x)" groups="testgroup">
+ <parameter type="int &amp;" name="x" default=""/>
+ </function>
+ <qmlclass name="AbstractParent" qml-module-name="QDoc.Test" fullname="QDoc.Test.AbstractParent" href="qml-qdoc-test-abstractparent.html" status="active" access="public" abstract="true" location="parent.qdoc" since="1.1" documented="true" title="AbstractParent" fulltitle="AbstractParent" subtitle="" brief="Abstract base QML type">
+ <function name="name" fullname="QDoc.Test.AbstractParent.name" href="qml-qdoc-test-abstractparent.html#name-method" status="active" access="public" location="parent.qdoc" documented="true" meta="qmlmethod" type="void"/>
+ <function name="name" fullname="QDoc.Test.AbstractParent.name" href="qml-qdoc-test-abstractparent.html#name-method" status="active" access="public" location="parent.qdoc" documented="true" meta="qmlmethod" type="void">
+ <parameter type="Child" name="child" default=""/>
+ <parameter type="" name="name" default=""/>
+ </function>
+ <function name="rear" fullname="QDoc.Test.AbstractParent.rear" href="qml-qdoc-test-abstractparent.html#rear-method" status="active" access="public" location="parent.qdoc" documented="true" meta="qmlmethod" type="void">
+ <parameter type="Child" name="child" default=""/>
+ <parameter type="var" name="method" default="Strict"/>
+ </function>
+ <qmlproperty name="children" fullname="QDoc.Test.AbstractParent.children" status="active" access="public" location="parent.qdoc" documented="true" type="list&lt;Child&gt;" attached="false" writable="true" brief="Children of the type"/>
+ <qmlproperty name="name" fullname="QDoc.Test.AbstractParent.name" status="active" access="public" location="parent.qdoc" documented="true" type="string" attached="false" writable="true" brief="Name of this parent"/>
+ </qmlclass>
+ <page name="autolinking.html" href="autolinking.html" status="active" location="classlists.qdoc" documented="true" subtype="page" title="Autolinking" fulltitle="Autolinking" subtitle="">
+ <contents name="testqdoc" title="TestQDoc" level="1"/>
+ <contents name="someprop" title="someProp" level="1"/>
+ </page>
+ <page name="cmaketest" href="test-cmaketest-example.html" status="active" location="cmaketest.qdoc" documented="true" subtype="example" title="CMake Example Project" fulltitle="CMake Example Project" subtitle="">
+ <page name="cmaketest/main.cpp" href="test-cmaketest-main-cpp.html" status="active" subtype="file" title="" fulltitle="main.cpp Example File" subtitle="cmaketest/main.cpp"/>
+ <page name="cmaketest/CMakeLists.txt" href="test-cmaketest-cmakelists-txt.html" status="active" subtype="file" title="" fulltitle="CMakeLists.txt Example File" subtitle="cmaketest/CMakeLists.txt"/>
+ </page>
+ <qmlclass name="Child" qml-module-name="QDoc.Test" qml-base-type="QDoc.Test::AbstractParent" fullname="QDoc.Test.Child" href="qml-qdoc-test-child.html" status="active" access="public" location="parent.qdoc" since="1.1" documented="true" title="Child" fulltitle="Child" subtitle="" brief="A Child inheriting its parent">
+ <function name="name" fullname="QDoc.Test.Child.name" href="qml-qdoc-test-child.html#name-method" status="active" access="public" location="parent.qdoc" documented="true" meta="qmlmethod" type="void">
+ <parameter type="Child" name="child" default=""/>
+ <parameter type="" name="name" default=""/>
+ </function>
+ <qmlproperty name="name" fullname="QDoc.Test.Child.name" href="qml-qdoc-test-child.html#name-prop" status="active" access="public" location="parent.qdoc" documented="true" type="string" attached="false" writable="true" brief="Name of this child"/>
+ </qmlclass>
+ <page name="classes.html" href="classes.html" status="active" location="unseenclass.qdoc" documented="true" subtype="page" title="Classes" fulltitle="Classes" subtitle=""/>
+ <namespace name="CrossModuleRef" href="crossmoduleref.html" status="active" access="public" location="testcpp.h" since="3.0" documented="true" module="TestCPP" brief="Namespace that has documented functions in multiple modules">
+ <function name="documentMe" fullname="CrossModuleRef::documentMe" href="crossmoduleref.html#documentMe" status="active" access="public" location="testcpp.h" documented="true" meta="plain" type="void" signature="void documentMe()"/>
+ </namespace>
+ <page name="demos/demo" href="test-demos-demo-example.html" status="active" location="demo.qdoc" documented="true" subtype="example" title="Demo" fulltitle="Demo" subtitle="">
+ <page name="demos/demo/demo.cpp" href="test-demos-demo-demo-cpp.html" status="active" subtype="file" title="" fulltitle="demo.cpp Example File" subtitle="demos/demo/demo.cpp"/>
+ <page name="demos/demo/demo.pro" href="test-demos-demo-demo-pro.html" status="active" subtype="file" title="" fulltitle="demo.pro Example File" subtitle="demos/demo/demo.pro"/>
+ <page name="demos/demo/dontxclude/CMakeLists.txt" href="test-demos-demo-dontxclude-cmakelists-txt.html" status="active" subtype="file" title="" fulltitle="CMakeLists.txt Example File" subtitle="demos/demo/dontxclude/CMakeLists.txt"/>
+ </page>
+ <qmlclass name="DocTest" qml-module-name="QDoc.Test" fullname="QDoc.Test.DocTest" href="qml-qdoc-test-doctest.html" status="active" access="public" location="DocTest.qml" since="QDoc.Test 0.9" documented="true" title="DocTest" fulltitle="DocTest" subtitle="" brief="Represents a doc test case">
+ <contents name="introduction" title="Introduction" level="1"/>
+ <function name="completed" fullname="QDoc.Test.DocTest.completed" href="qml-qdoc-test-doctest.html#completed-signal" status="active" access="public" documented="true" meta="qmlsignal"/>
+ <function name="fail" fullname="QDoc.Test.DocTest.fail" href="qml-qdoc-test-doctest.html#fail-method" status="active" access="public" location="DocTest.qml" documented="true" since="QDoc.Test 1.0" meta="qmlmethod">
+ <parameter type="" name="message" default="&quot;oops&quot;"/>
+ </function>
+ <function name="fail_hard" fullname="QDoc.Test.DocTest.fail_hard" href="qml-qdoc-test-doctest.html#fail_hard-method" status="active" access="public" documented="true" meta="qmlmethod">
+ <parameter type="" name="msg" default="&quot;facepalm&quot;"/>
+ <parameter type="" name="option" default="123"/>
+ </function>
+ <function name="foo" fullname="QDoc.Test.DocTest.foo" href="qml-qdoc-test-doctest.html#foo-signal" status="active" access="public" location="DocTest.qml" documented="true" meta="qmlsignal">
+ <parameter type="var" name="bar" default=""/>
+ </function>
+ <function name="itsHappening" fullname="QDoc.Test.DocTest.itsHappening" href="qml-qdoc-test-doctest.html#itsHappening-signal" status="active" access="public" documented="true" meta="qmlsignal">
+ <parameter type="bool" name="really" default=""/>
+ </function>
+ <qmlproperty name="active" fullname="QDoc.Test.DocTest.active" href="qml-qdoc-test-doctest.html#active-prop" status="active" access="public" documented="true" type="bool" attached="false" writable="true"/>
+ <qmlproperty name="name" fullname="QDoc.Test.DocTest.name" href="qml-qdoc-test-doctest.html#name-prop" status="active" access="public" documented="true" type="string" attached="false" writable="true" required="true"/>
+ </qmlclass>
+ <qmlclass name="DocTest" qml-module-name="Test.NoVer" fullname="Test.NoVer.DocTest" href="qml-test-nover-doctest.html" status="active" access="public" location="DocTest.qml" since="1.1" documented="true" title="DocTest" fulltitle="DocTest" subtitle="" brief="Shadows the type name in QDoc.Test module"/>
+ <class name="DontLinkToMe" href="dontlinktome.html" status="ignored" access="public" location="testcpp.h" since="2.0" documented="true" module="TestCPP" brief="Class that does not generate documentation"/>
+ <page name="demos/hidden" href="test-demos-hidden-example.html" status="active" location="hidden.qdoc" documented="true" subtype="example" title="Hidden Demo" fulltitle="Hidden Demo" subtitle="" brief="Tagged 'broken', does not appear in examples-manifest.xml"/>
+ <page name="obsolete-classes.html" href="obsolete-classes.html" status="active" location="classlists.qdoc" documented="true" subtype="page" title="Obsolete Classes" fulltitle="Obsolete Classes" subtitle="">
+ <contents name="classes-with-obsolete-members" title="Classes with obsolete members" level="1"/>
+ <contents name="testqdoc" title="TestQDoc" level="2"/>
+ </page>
+ <qmlclass name="OldType" qml-module-name="QDoc.Test" fullname="QDoc.Test.OldType" href="qml-qdoc-test-oldtype.html" status="deprecated" access="public" since="1.1" documented="true" title="OldType" fulltitle="OldType" subtitle="" brief="Deprecated old type"/>
+ <qmlclass name="ProgressBar" qml-module-name="UIComponents" fullname="UIComponents.ProgressBar" href="qml-uicomponents-progressbar.html" status="active" access="public" location="ProgressBar.qml" documented="true" title="ProgressBar" fulltitle="ProgressBar" subtitle="" brief="A component that shows the progress of an event">
+ <qmlproperty name="color" fullname="UIComponents.ProgressBar.color" href="qml-uicomponents-progressbar.html#color-prop" status="active" access="public" documented="true" type="color" attached="false" writable="true"/>
+ <qmlproperty name="maximum" fullname="UIComponents.ProgressBar.maximum" href="qml-uicomponents-progressbar.html#maximum-prop" status="active" access="public" documented="true" type="int" attached="false" writable="true"/>
+ <qmlproperty name="minimum" fullname="UIComponents.ProgressBar.minimum" href="qml-uicomponents-progressbar.html#minimum-prop" status="active" access="public" documented="true" type="int" attached="false" writable="true"/>
+ <qmlproperty name="secondColor" fullname="UIComponents.ProgressBar.secondColor" href="qml-uicomponents-progressbar.html#secondColor-prop" status="active" access="public" documented="true" type="color" attached="false" writable="true"/>
+ <qmlproperty name="value" fullname="UIComponents.ProgressBar.value" href="qml-uicomponents-progressbar.html#value-prop" status="active" access="public" documented="true" type="int" attached="false" writable="true"/>
+ </qmlclass>
+ <page name="componentset" href="test-componentset-example.html" status="active" location="examples.qdoc" documented="true" subtype="example" title="QML Documentation Example" fulltitle="QML Documentation Example" subtitle="" brief="Example for documenting QML types">
+ <contents name="qml-class" title="QML Class" level="1"/>
+ <contents name="properties-signals-handlers-and-methods" title="Properties, Signals, Handlers, and Methods" level="1"/>
+ <contents name="internal-documentation" title="Internal Documentation" level="2"/>
+ <contents name="qml-types-with-c-implementation" title="QML Types with C++ Implementation" level="1"/>
+ <page name="componentset/ProgressBar.qml" href="test-componentset-progressbar-qml.html" status="active" subtype="file" title="" fulltitle="ProgressBar.qml Example File" subtitle="componentset/ProgressBar.qml"/>
+ <page name="componentset/Switch.qml" href="test-componentset-switch-qml.html" status="active" subtype="file" title="" fulltitle="Switch.qml Example File" subtitle="componentset/Switch.qml"/>
+ <page name="componentset/TabWidget.qml" href="test-componentset-tabwidget-qml.html" status="active" subtype="file" title="" fulltitle="TabWidget.qml Example File" subtitle="componentset/TabWidget.qml"/>
+ <page name="componentset/componentset.qml" href="test-componentset-componentset-qml.html" status="active" subtype="file" title="" fulltitle="componentset.qml Example File" subtitle="componentset/componentset.qml"/>
+ <page name="componentset/componentset.pro" href="test-componentset-componentset-pro.html" status="active" subtype="file" title="" fulltitle="componentset.pro Example File" subtitle="componentset/componentset.pro"/>
+ </page>
+ <page name="qmlmodules.html" href="qmlmodules.html" status="active" location="modules.qdoc" documented="true" subtype="page" title="QML Modules" fulltitle="QML Modules" subtitle="">
+ <contents name="qml-types" title="QML types" level="1"/>
+ <contents name="qml-value-types" title="QML value types" level="1"/>
+ </page>
+ <page name="https://wiki.qt.io/QProperty" href="https://wiki.qt.io/QProperty" status="active" location="properties.qdoc" documented="true" subtype="externalpage" title="QProperty" fulltitle="QProperty" subtitle=""/>
+ <class name="SeenClass" href="seenclass.html" status="active" access="public" location="dont.h" since="2.0" documented="true" module="TestCPP" brief="A public but undocumented class"/>
+ <qmlclass name="Switch" qml-module-name="UIComponents" fullname="UIComponents.Switch" href="qml-uicomponents-switch.html" status="active" access="public" location="Switch.qml" documented="true" title="Switch" fulltitle="Switch" subtitle="" brief="A component that can be turned on or off">
+ <function name="toggle" fullname="UIComponents.Switch.toggle" href="qml-uicomponents-switch.html#toggle-method" status="active" access="public" documented="true" meta="qmlmethod"/>
+ <qmlproperty name="on" fullname="UIComponents.Switch.on" href="qml-uicomponents-switch.html#on-prop" status="active" access="public" documented="true" type="bool" attached="false" writable="true"/>
+ </qmlclass>
+ <qmlclass name="TabWidget" qml-module-name="UIComponents" fullname="UIComponents.TabWidget" href="qml-uicomponents-tabwidget.html" status="active" access="public" location="TabWidget.qml" documented="true" title="TabWidget" fulltitle="TabWidget" subtitle="" brief="A widget that places its children as tabs">
+ <contents name="adding-tabs" title="Adding Tabs" level="1"/>
+ <qmlproperty name="current" fullname="UIComponents.TabWidget.current" href="qml-uicomponents-tabwidget.html#current-prop" status="active" access="public" documented="true" type="int" attached="false" writable="true"/>
+ <qmlproperty name="sampleReadOnlyProperty" fullname="UIComponents.TabWidget.sampleReadOnlyProperty" href="qml-uicomponents-tabwidget.html#sampleReadOnlyProperty-prop" status="active" access="public" documented="true" type="int" attached="false" writable="false"/>
+ </qmlclass>
+ <namespace name="TestQDoc" href="testqdoc.html" status="active" access="public" location="testcpp.h" since="2.0" documented="true" module="TestCPP" brief="A namespace">
+ <contents name="usage" title="Usage" level="1"/>
+ <function name="QDOCTEST_MACRO" href="testqdoc.html#QDOCTEST_MACRO" status="active" access="public" documented="true" related="0" since="Test 0.9" meta="macrowithoutparams" signature="QDOCTEST_MACRO"/>
+ <class threadsafety="reentrant" name="Test" fullname="TestQDoc::Test" href="testqdoc-test.html" status="active" access="public" location="testcpp.h" since="1.1" documented="true" groups="cpptypes,testgroup" module="TestCPP" brief="A class in a namespace">
+ <function name="QDOCTEST_MACRO2" href="testqdoc-test.html#QDOCTEST_MACRO2" status="active" access="public" documented="true" related="1" since="Test 1.1" meta="macrowithparams" brief="A macro with argument x" signature="QDOCTEST_MACRO2(int &amp;x)" groups="testgroup">
+ <parameter type="int &amp;" name="x" default=""/>
+ </function>
+ <function name="Test" fullname="TestQDoc::Test::Test" href="testqdoc-test.html#Test" status="active" access="public" location="testcpp.h" documented="true" meta="constructor" signature="Test()"/>
+ <function name="anotherObsoleteMember" fullname="TestQDoc::Test::anotherObsoleteMember" href="testqdoc-test-obsolete.html#anotherObsoleteMember" status="deprecated" access="public" location="testcpp.h" documented="true" meta="plain" type="void" signature="void anotherObsoleteMember()"/>
+ <function name="deprecatedMember" fullname="TestQDoc::Test::deprecatedMember" href="testqdoc-test-obsolete.html#deprecatedMember" status="deprecated" access="public" location="testcpp.h" documented="true" meta="plain" type="void" signature="void deprecatedMember()"/>
+ <function name="funcPtr" fullname="TestQDoc::Test::funcPtr" href="testqdoc-test.html#funcPtr" status="active" access="public" location="testcpp.h" documented="true" meta="plain" type="void (*)(bool)" signature="void (*)(bool) funcPtr(bool b, const char *s)">
+ <parameter type="bool" name="b" default=""/>
+ <parameter type="const char *" name="s" default=""/>
+ </function>
+ <function name="inlineFunction" fullname="TestQDoc::Test::inlineFunction" href="testqdoc-test.html#inlineFunction" status="active" access="public" location="testcpp.h" documented="true" meta="plain" type="void" brief="An inline function, documented using the \fn QDoc command" signature="void inlineFunction()"/>
+ <function name="methodWithEmDashInItsDocs" fullname="TestQDoc::Test::methodWithEmDashInItsDocs" href="testqdoc-test.html#methodWithEmDashInItsDocs" status="active" access="public" location="testcpp.h" documented="true" meta="plain" type="void" signature="void methodWithEmDashInItsDocs()"/>
+ <function name="methodWithEnDashInItsDocs" fullname="TestQDoc::Test::methodWithEnDashInItsDocs" href="testqdoc-test.html#methodWithEnDashInItsDocs" status="active" access="public" location="testcpp.h" documented="true" meta="plain" type="void" signature="void methodWithEnDashInItsDocs()"/>
+ <function name="obsoleteMember" fullname="TestQDoc::Test::obsoleteMember" href="testqdoc-test-obsolete.html#obsoleteMember" status="deprecated" access="public" location="testcpp.h" documented="true" meta="plain" type="void" signature="void obsoleteMember()"/>
+ <function name="operator++" fullname="TestQDoc::Test::operator++" href="testqdoc-test-obsolete.html#operator-2b-2b" status="deprecated" access="public" location="testcpp.h" documented="true" meta="plain" type="TestQDoc::Test &amp;" signature="TestQDoc::Test &amp; operator++()"/>
+ <function name="operator--" fullname="TestQDoc::Test::operator--" href="testqdoc-test-obsolete.html#operator--" status="deprecated" access="public" location="testcpp.h" documented="true" meta="plain" type="TestQDoc::Test &amp;" signature="TestQDoc::Test &amp; operator--()"/>
+ <function name="operator=" fullname="TestQDoc::Test::operator=" href="testqdoc-test.html#operator-eq" status="active" access="public" location="testcpp.h" documented="true" meta="move-assign" type="TestQDoc::Test &amp;" signature="TestQDoc::Test &amp; operator=(TestQDoc::Test &amp;&amp;other)" groups="testgroup">
+ <parameter type="TestQDoc::Test &amp;&amp;" name="other" default=""/>
+ </function>
+ <function name="operator==" href="testqdoc-test.html#operator-eq-eq" status="active" access="public" location="testcpp.h" documented="true" related="2" meta="plain" type="bool" signature="bool operator==(const TestQDoc::Test &amp;lhs, const TestQDoc::Test &amp;rhs)">
+ <parameter type="const TestQDoc::Test &amp;" name="lhs" default=""/>
+ <parameter type="const TestQDoc::Test &amp;" name="rhs" default=""/>
+ </function>
+ <function name="overload" fullname="TestQDoc::Test::overload" href="testqdoc-test.html#overload" status="active" access="protected" location="testcpp.h" documented="true" meta="plain" type="void" signature="void overload()"/>
+ <function name="overload" fullname="TestQDoc::Test::overload" href="testqdoc-test.html#overload-1" status="active" access="protected" location="testcpp.h" documented="true" since="Test 1.2" meta="plain" overload="true" overload-number="1" type="void" signature="void overload(bool b)">
+ <parameter type="bool" name="b" default=""/>
+ </function>
+ <function name="someFunction" fullname="TestQDoc::Test::someFunction" href="testqdoc-test.html#someFunction" status="active" access="public" location="testcpp.h" documented="true" since="Test 1.0" meta="plain" type="int" signature="int someFunction(int, int v)">
+ <parameter type="int" name="" default=""/>
+ <parameter type="int" name="v" default="0"/>
+ </function>
+ <function name="someFunctionDefaultArg" fullname="TestQDoc::Test::someFunctionDefaultArg" href="testqdoc-test.html#someFunctionDefaultArg" threadsafety="non-reentrant" status="active" access="public" location="testcpp.h" documented="true" since="2.0" meta="plain" const="true" type="void" signature="void someFunctionDefaultArg(int i, bool b) const" groups="testgroup">
+ <parameter type="int" name="i" default=""/>
+ <parameter type="bool" name="b" default="false"/>
+ </function>
+ <function name="virtualFun" fullname="TestQDoc::Test::virtualFun" href="testqdoc-test.html#virtualFun" status="active" access="public" location="testcpp.h" documented="true" meta="plain" virtual="virtual" type="void" signature="void virtualFun()"/>
+ <typedef name="SomeType" fullname="TestQDoc::Test::SomeType" href="testqdoc-test.html#SomeType-typedef" status="active" access="public" location="testcpp.h" documented="true"/>
+ </class>
+ <class name="TestDerived" fullname="TestQDoc::TestDerived" href="testqdoc-testderived.html" status="active" access="public" location="testcpp.h" since="2.0" documented="true" bases="TestQDoc::Test" module="TestCPP" brief="A class in a namespace, derived from Test">
+ <function name="bindableProp" fullname="TestQDoc::TestDerived::bindableProp" href="testqdoc-testderived.html#bindableProp-prop" status="active" access="public" location="testcpp.h" meta="plain" associated-property="bindableProp" type="QBindable&lt;QString&gt;" signature="QBindable&lt;QString&gt; bindableProp()"/>
+ <function name="bindablePropChanged" fullname="TestQDoc::TestDerived::bindablePropChanged" href="testqdoc-testderived.html#bindableProp-prop" status="active" access="public" location="testcpp.h" meta="signal" associated-property="bindableProp" type="void" signature="void bindablePropChanged()"/>
+ <function name="boolProp" fullname="TestQDoc::TestDerived::boolProp" href="testqdoc-testderived.html#boolProp-prop" status="active" access="public" location="testcpp.h" meta="plain" associated-property="boolProp" type="bool" signature="bool boolProp()"/>
+ <function name="boolPropChanged" fullname="TestQDoc::TestDerived::boolPropChanged" href="testqdoc-testderived.html#boolProp-prop" status="active" access="public" location="testcpp.h" meta="signal" associated-property="boolProp" type="void" signature="void boolPropChanged()"/>
+ <function name="emitSomething" fullname="TestQDoc::TestDerived::emitSomething" href="testqdoc-testderived.html#emitSomething" status="active" access="public" location="testcpp.h" documented="true" meta="signal" type="void" signature="void emitSomething()"/>
+ <function name="getInt" fullname="TestQDoc::TestDerived::getInt" href="testqdoc-testderived.html#intProp-prop" status="active" access="public" location="testcpp.h" meta="plain" associated-property="intProp" type="int *" signature="int * getInt()"/>
+ <function name="id" fullname="TestQDoc::TestDerived::id" href="testqdoc-testderived.html#id" status="active" access="public" location="testcpp.h" documented="true" meta="plain" virtual="virtual" override="true" type="int" signature="int id() override"/>
+ <function name="invokeMe" fullname="TestQDoc::TestDerived::invokeMe" href="testqdoc-testderived.html#invokeMe" status="active" access="public" location="testcpp.h" documented="true" meta="plain" const="true" type="void" brief="Something invokable" signature="void invokeMe() const"/>
+ <function name="name" fullname="TestQDoc::TestDerived::name" href="testqdoc-testderived.html#name-prop" status="active" access="public" location="testcpp.h" meta="plain" const="true" associated-property="name" type="const QString *" signature="const QString * name() const"/>
+ <function name="resetBoolProp" fullname="TestQDoc::TestDerived::resetBoolProp" href="testqdoc-testderived.html#boolProp-prop" status="active" access="public" location="testcpp.h" meta="slot" associated-property="boolProp" type="void" signature="void resetBoolProp()"/>
+ <function name="setBindableProp" fullname="TestQDoc::TestDerived::setBindableProp" href="testqdoc-testderived.html#bindableProp-prop" status="active" access="public" location="testcpp.h" meta="slot" associated-property="bindableProp" type="void" signature="void setBindableProp(const QString &amp;s)">
+ <parameter type="const QString &amp;" name="s" default=""/>
+ </function>
+ <function name="setBoolProp" fullname="TestQDoc::TestDerived::setBoolProp" href="testqdoc-testderived.html#boolProp-prop" status="active" access="public" location="testcpp.h" meta="slot" associated-property="boolProp" type="void" signature="void setBoolProp(bool b)">
+ <parameter type="bool" name="b" default=""/>
+ </function>
+ <function name="someProp" fullname="TestQDoc::TestDerived::someProp" href="testqdoc-testderived.html#someProp-prop" status="active" access="public" location="testcpp.h" meta="plain" associated-property="someProp" type="const QString &amp;" signature="const QString &amp; someProp()"/>
+ <function name="someValue" fullname="TestQDoc::TestDerived::someValue" href="testqdoc-testderived.html#someValue" status="active" access="public" location="testcpp.h" documented="true" meta="plain" type="TestQDoc::TestDerived::NotTypedef" signature="TestQDoc::TestDerived::NotTypedef someValue()"/>
+ <function name="staticObsoleteMember" fullname="TestQDoc::TestDerived::staticObsoleteMember" href="testqdoc-testderived-obsolete.html#staticObsoleteMember" status="deprecated" access="public" location="testcpp.h" documented="true" meta="plain" static="true" type="void" signature="void staticObsoleteMember()"/>
+ <function name="virtualFun" fullname="TestQDoc::TestDerived::virtualFun" href="testqdoc-testderived.html#virtualFun" status="active" access="public" location="testcpp.h" documented="true" meta="plain" virtual="virtual" override="true" type="void" signature="void virtualFun() override"/>
+ <typedef name="DerivedType" fullname="TestQDoc::TestDerived::DerivedType" href="testqdoc-testderived.html#DerivedType-typedef" status="active" access="public" location="testcpp.h" documented="true" aliasedtype="Test::SomeType"/>
+ <typedef name="NotTypedef" fullname="TestQDoc::TestDerived::NotTypedef" href="testqdoc-testderived.html#NotTypedef-typedef" status="active" access="public" location="testcpp.h" documented="true" aliasedtype="int"/>
+ <property name="bindableProp" fullname="TestQDoc::TestDerived::bindableProp" href="testqdoc-testderived.html#bindableProp-prop" status="active" access="public" location="testcpp.h" documented="true" bindable="true">
+ <getter name="bindableProp"/>
+ <setter name="setBindableProp"/>
+ <notifier name="bindablePropChanged"/>
+ </property>
+ <property name="boolProp" fullname="TestQDoc::TestDerived::boolProp" href="testqdoc-testderived.html#boolProp-prop" status="active" access="public" location="testcpp.h" documented="true">
+ <getter name="boolProp"/>
+ <setter name="setBoolProp"/>
+ <resetter name="resetBoolProp"/>
+ <notifier name="boolPropChanged"/>
+ </property>
+ <property name="intProp" fullname="TestQDoc::TestDerived::intProp" href="testqdoc-testderived.html#intProp-prop" status="active" access="public" location="testcpp.h" documented="true" writable="false">
+ <getter name="getInt"/>
+ </property>
+ <property name="name" fullname="TestQDoc::TestDerived::name" href="testqdoc-testderived.html#name-prop" status="active" access="public" location="testcpp.h" documented="true" writable="false" brief="This property holds a name">
+ <getter name="name"/>
+ </property>
+ <property name="someProp" fullname="TestQDoc::TestDerived::someProp" href="testqdoc-testderived.html#someProp-prop" status="active" access="public" location="testcpp.h" documented="true" bindable="true" writable="false">
+ <getter name="someProp"/>
+ </property>
+ </class>
+ </namespace>
+ <qmlclass name="TheType" qml-module-name="TheModule" fullname="TheModule.TheType" href="qml-themodule-thetype.html" status="active" access="public" location="properties.qdoc" documented="true" title="TheType" fulltitle="TheType" subtitle="">
+ <qmlproperty name="name" fullname="TheModule.TheType.name" href="qml-themodule-thetype.html#name-prop" status="active" access="public" location="properties.qdoc" documented="true" type="string" attached="false" writable="false" brief="Read-only status of this property is resolved from Q_PROPERTY"/>
+ </qmlclass>
+ <qmlclass name="Type" qml-module-name="QDoc.Test" fullname="QDoc.Test.Type" href="qml-qdoc-test-type.html" status="active" access="public" since="1.1" documented="true" title="Type" fulltitle="Type" subtitle="" brief="A QML type documented in a .cpp file">
+ <function name="completed" fullname="QDoc.Test.Type.completed" href="qml-qdoc-test-type.html#completed-signal" status="active" access="public" documented="true" meta="qmlsignal">
+ <parameter type="int" name="status" default=""/>
+ </function>
+ <function name="configured" fullname="QDoc.Test.Type.configured" href="qml-qdoc-test-type.html#configured-signal" status="active" access="public" documented="true" meta="qmlsignal"/>
+ <function name="copy" fullname="QDoc.Test.Type.copy" href="qml-qdoc-test-type.html#copy-method" status="active" access="public" documented="true" meta="qmlmethod" type="Type">
+ <parameter type="" name="a" default=""/>
+ </function>
+ <function name="deprecatedMethod" fullname="QDoc.Test.Type.deprecatedMethod" href="qml-qdoc-test-type-obsolete.html#deprecatedMethod-method" status="deprecated" access="public" documented="true" meta="qmlmethod"/>
+ <function name="disable" fullname="QDoc.Test.Type.disable" href="qml-qdoc-test-type.html#disable-method" status="active" access="public" documented="true" meta="qmlmethod"/>
+ <function name="enable" fullname="QDoc.Test.Type.enable" href="qml-qdoc-test-type.html#enable-method" status="active" access="public" documented="true" meta="qmlmethod"/>
+ <function name="futureDeprecated" fullname="QDoc.Test.Type.futureDeprecated" href="qml-qdoc-test-type.html#futureDeprecated-method" status="active" access="public" documented="true" meta="qmlmethod"/>
+ <function name="group.created" fullname="QDoc.Test.Type.group.created" href="qml-qdoc-test-type.html#group.created-signal" status="active" access="public" documented="true" meta="qmlsignal"/>
+ <qmlproperty name="fifth" fullname="QDoc.Test.Type.fifth" href="qml-qdoc-test-type.html#fifth-prop" status="active" access="public" documented="true" type="int" attached="false" writable="true" brief="A group of properties sharing a documentation comment"/>
+ <qmlproperty name="fourth" fullname="QDoc.Test.Type.fourth" href="qml-qdoc-test-type.html#fourth-prop" status="active" access="public" documented="true" type="int" attached="false" writable="true" brief="A group of properties sharing a documentation comment"/>
+ <qmlproperty name="group.first" fullname="QDoc.Test.Type.group.first" href="qml-qdoc-test-type.html#group.first-prop" status="active" access="public" documented="true" type="int" attached="false" writable="true" brief="A property group"/>
+ <qmlproperty name="group.second" fullname="QDoc.Test.Type.group.second" href="qml-qdoc-test-type.html#group.second-prop" status="active" access="public" documented="true" type="int" attached="false" writable="true" brief="A property group"/>
+ <qmlproperty name="group.third" fullname="QDoc.Test.Type.group.third" href="qml-qdoc-test-type.html#group.third-prop" status="active" access="public" documented="true" type="int" attached="false" writable="true" brief="A property group"/>
+ <qmlproperty name="id" fullname="QDoc.Test.Type.id" href="qml-qdoc-test-type.html#id-prop" status="active" access="public" documented="true" type="int" attached="false" writable="false" brief="A read-only property"/>
+ <qmlproperty name="name" fullname="QDoc.Test.Type.name" href="qml-qdoc-test-type.html#name-prop" status="active" access="public" documented="true" type="string" attached="false" writable="true" required="true" brief="Name of the Test"/>
+ <qmlproperty name="type" fullname="QDoc.Test.Type.type" href="qml-qdoc-test-type.html#type-attached-prop" status="active" access="public" documented="true" type="enumeration" attached="true" writable="true"/>
+ <qmlproperty name="group" fullname="QDoc.Test.Type.group" href="qml-qdoc-test-type.html#group-prop" status="active" access="public" documented="true"/>
+ </qmlclass>
+ <qmlclass name="TypeNoVersion" qml-module-name="Test.NoVer" fullname="Test.NoVer.TypeNoVersion" href="qml-test-nover-typenoversion.html" status="active" access="public" since="1.1" documented="true" title="TypeNoVersion" fulltitle="TypeNoVersion" subtitle="" brief="Another QML type documented in a .cpp file"/>
+ <class name="UnseenClass" href="unseenclass.html" status="ignored" access="public" location="dont.h" since="2.0" documented="true" module="TestCPP" brief="A public but undocumented class"/>
+ <qmlclass name="YetAnotherChild" qml-module-name="QDoc.Test" qml-base-type="QDoc.Test::InternParent" fullname="QDoc.Test.YetAnotherChild" href="qml-qdoc-test-yetanotherchild.html" status="active" access="public" location="parent.qdoc" since="1.1" documented="true" title="YetAnotherChild" fulltitle="YetAnotherChild" subtitle="" brief="A type inheriting from internal abstract parent"/>
+ <qmlvaluetype name="int" qml-module-name="QDoc.Test" fullname="QDoc.Test.int" href="qml-int.html" status="active" access="public" location="parent.qdoc" since="1.1" documented="true" title="int" fulltitle="int" subtitle="" brief="An integer value type">
+ <function name="abs" fullname="QDoc.Test.int.abs" href="qml-int.html#abs-method" status="active" access="public" location="parent.qdoc" documented="true" meta="qmlmethod" type="int"/>
+ </qmlvaluetype>
+ <page name="https://doc.qt.io/qt/17-qdoc-commands-thread.html#reentrant-command" href="https://doc.qt.io/qt/17-qdoc-commands-thread.html#reentrant-command" status="active" location="classlists.qdoc" documented="true" subtype="externalpage" title="reentrant" fulltitle="reentrant" subtitle=""/>
+ <group name="cpptypes" href="cpptypes.html" status="active" location="classlists.qdoc" documented="true" seen="true" title="Test C++ Types"/>
+ <group name="testgroup" href="testgroup.html" status="internal" seen="false" title=""/>
+ <module name="TestCPP" href="testcpp-module.html" status="active" since="2.0" documented="true" seen="true" title="QDoc Test C++ Classes" brief="A test module page">
+ <contents name="linking-to-function-like-things" title="Linking to function-like things" level="1"/>
+ <contents name="section" title="section()" level="2"/>
+ </module>
+ <qmlmodule name="QDoc.Test" qml-module-name="QDoc.Test" qml-module-version="1.1" href="qdoc-test-qmlmodule.html" status="preliminary" since="1.1" documented="true" seen="true" title="QDoc.Test QML Module" brief="QML Types for the Test module"/>
+ <qmlmodule name="Test.Empty" qml-module-name="Test.Empty" qml-module-version="1.0" href="test-empty-qmlmodule.html" status="active" documented="true" seen="true" title="No QML Types Here" brief="A QML module with no member types"/>
+ <qmlmodule name="Test.NoVer" qml-module-name="Test.NoVer" href="test-nover-qmlmodule.html" status="active" since="1.1" documented="true" seen="true" title="Versionless QML Module" brief="QML Types for the Test module without version"/>
+ <qmlmodule name="TheModule" qml-module-name="TheModule" href="themodule-qmlmodule.html" status="active" location="properties.qdoc" documented="true" seen="true" title=""/>
+ <qmlmodule name="UIComponents" qml-module-name="UIComponents" qml-module-version="1.0" href="uicomponents-qmlmodule.html" status="active" location="examples.qdoc" documented="true" seen="true" title="UI Components" brief="Basic set of UI components"/>
+ </namespace>
+</INDEX>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/test.qhp b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/test.qhp
new file mode 100644
index 000000000..ba54e85ea
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/test.qhp
@@ -0,0 +1,260 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<QtHelpProject version="1.0">
+ <namespace>org.qt-project.test.001</namespace>
+ <virtualFolder>test</virtualFolder>
+ <metaData name="version" value="6.2.11"/>
+ <filterSection>
+ <toc>
+ <section ref="uicomponents-qmlmodule.html" title="UI Components">
+ <section ref="uicomponents-qmlmodule.html" title="Test">
+ <section ref="themodule-qmlmodule.html" title=""/>
+ <section ref="autolinking.html" title="Autolinking"/>
+ <section ref="test-cmaketest-example.html" title="CMake Example Project"/>
+ <section ref="classes.html" title="Classes"/>
+ <section ref="test-demos-demo-example.html" title="Demo"/>
+ <section ref="test-demos-hidden-example.html" title="Hidden Demo"/>
+ <section ref="test-empty-qmlmodule.html" title="No QML Types Here"/>
+ <section ref="obsolete-classes.html" title="Obsolete Classes"/>
+ <section ref="testcpp-module.html" title="QDoc Test C++ Classes"/>
+ <section ref="qdoc-test-qmlmodule.html" title="QDoc.Test QML Module"/>
+ <section ref="test-componentset-example.html" title="QML Documentation Example"/>
+ <section ref="qmlmodules.html" title="QML Modules"/>
+ <section ref="uicomponents-qmlmodule.html" title="UI Components"/>
+ <section ref="test-nover-qmlmodule.html" title="Versionless QML Module"/>
+ </section>
+ <section ref="testcpp-module.html" title="Classes">
+ <section ref="crossmoduleref.html" title="CrossModuleRef"/>
+ <section ref="seenclass.html" title="SeenClass Class Reference"/>
+ <section ref="testqdoc.html" title="TestQDoc"/>
+ <section ref="testqdoc-test.html" title="TestQDoc::Test Class Reference">
+ <section ref="testqdoc-test-members.html" title="List of all members"/>
+ <section ref="testqdoc-test-obsolete.html" title="Obsolete members"/>
+ </section>
+ <section ref="testqdoc-testderived.html" title="TestQDoc::TestDerived Class Reference">
+ <section ref="testqdoc-testderived-members.html" title="List of all members"/>
+ <section ref="testqdoc-testderived-obsolete.html" title="Obsolete members"/>
+ </section>
+ </section>
+ <section ref="uicomponents-qmlmodule.html" title="QML Types">
+ <section ref="qml-qdoc-test-abstractparent.html" title="AbstractParent Type Reference">
+ <section ref="qml-qdoc-test-abstractparent-members.html" title="List of all members"/>
+ </section>
+ <section ref="qml-qdoc-test-child.html" title="Child Type Reference">
+ <section ref="qml-qdoc-test-child-members.html" title="List of all members"/>
+ </section>
+ <section ref="qml-qdoc-test-doctest.html" title="DocTest Type Reference">
+ <section ref="qml-qdoc-test-doctest-members.html" title="List of all members"/>
+ </section>
+ <section ref="qml-qdoc-test-oldtype.html" title="OldType Type Reference">
+ <section ref="qml-qdoc-test-oldtype-members.html" title="List of all members"/>
+ </section>
+ <section ref="qml-qdoc-test-type.html" title="Type Type Reference">
+ <section ref="qml-qdoc-test-type-members.html" title="List of all members"/>
+ <section ref="qml-qdoc-test-type-obsolete.html" title="Obsolete members"/>
+ </section>
+ <section ref="qml-qdoc-test-yetanotherchild.html" title="YetAnotherChild Type Reference">
+ <section ref="qml-qdoc-test-yetanotherchild-members.html" title="List of all members"/>
+ </section>
+ <section ref="qml-int.html" title="int Type Reference"/>
+ <section ref="qml-uicomponents-progressbar.html" title="ProgressBar Type Reference">
+ <section ref="qml-uicomponents-progressbar-members.html" title="List of all members"/>
+ </section>
+ <section ref="qml-uicomponents-switch.html" title="Switch Type Reference">
+ <section ref="qml-uicomponents-switch-members.html" title="List of all members"/>
+ </section>
+ <section ref="qml-uicomponents-tabwidget.html" title="TabWidget Type Reference">
+ <section ref="qml-uicomponents-tabwidget-members.html" title="List of all members"/>
+ </section>
+ </section>
+ </section>
+ </toc>
+ <keywords>
+ <keyword name="AbstractParent" id="QML.AbstractParent" ref="qml-qdoc-test-abstractparent.html"/>
+ <keyword name="AbstractParent" id="QML.QDoc.Test1.AbstractParent" ref="qml-qdoc-test-abstractparent.html"/>
+ <keyword name="Autolinking" id="Autolinking" ref="autolinking.html"/>
+ <keyword name="Child" id="QML.Child" ref="qml-qdoc-test-child.html"/>
+ <keyword name="Child" id="QML.QDoc.Test1.Child" ref="qml-qdoc-test-child.html"/>
+ <keyword name="Classes" id="Classes" ref="classes.html"/>
+ <keyword name="CrossModuleRef" id="CrossModuleRef" ref="crossmoduleref.html"/>
+ <keyword name="DocTest" id="QML.DocTest" ref="qml-qdoc-test-doctest.html"/>
+ <keyword name="DocTest" id="QML.QDoc.Test1.DocTest" ref="qml-qdoc-test-doctest.html"/>
+ <keyword name="DocTest" id="QML.DocTest" ref="qml-test-nover-doctest.html"/>
+ <keyword name="DocTest" id="QML.Test.NoVer.DocTest" ref="qml-test-nover-doctest.html"/>
+ <keyword name="Obsolete Classes" id="Obsolete Classes" ref="obsolete-classes.html"/>
+ <keyword name="OldType" id="QML.OldType" ref="qml-qdoc-test-oldtype.html"/>
+ <keyword name="OldType" id="QML.QDoc.Test1.OldType" ref="qml-qdoc-test-oldtype.html"/>
+ <keyword name="ProgressBar" id="QML.ProgressBar" ref="qml-uicomponents-progressbar.html"/>
+ <keyword name="ProgressBar" id="QML.UIComponents1.ProgressBar" ref="qml-uicomponents-progressbar.html"/>
+ <keyword name="QDOCTEST_MACRO" id="QDOCTEST_MACRO" ref="testqdoc.html#QDOCTEST_MACRO"/>
+ <keyword name="QDOCTEST_MACRO2" id="QDOCTEST_MACRO2" ref="testqdoc-test.html#QDOCTEST_MACRO2"/>
+ <keyword name="QDoc Test C++ Classes" id="QDoc Test C++ Classes" ref="testcpp-module.html"/>
+ <keyword name="QDoc.Test" id="QML.Test.QDoc" ref="qdoc-test-qmlmodule.html"/>
+ <keyword name="QML Modules" id="QML Modules" ref="qmlmodules.html"/>
+ <keyword name="SeenClass" id="SeenClass" ref="seenclass.html"/>
+ <keyword name="Switch" id="QML.Switch" ref="qml-uicomponents-switch.html"/>
+ <keyword name="Switch" id="QML.UIComponents1.Switch" ref="qml-uicomponents-switch.html"/>
+ <keyword name="TabWidget" id="QML.TabWidget" ref="qml-uicomponents-tabwidget.html"/>
+ <keyword name="TabWidget" id="QML.UIComponents1.TabWidget" ref="qml-uicomponents-tabwidget.html"/>
+ <keyword name="Test" id="TestQDoc::Test" ref="testqdoc-test.html"/>
+ <keyword name="Test C++ Types" id="Test C++ Types" ref="cpptypes.html"/>
+ <keyword name="Test.Empty" id="QML.Empty.Test" ref="test-empty-qmlmodule.html"/>
+ <keyword name="Test.NoVer" id="QML.NoVer.Test" ref="test-nover-qmlmodule.html"/>
+ <keyword name="Test::SomeType" id="Test::SomeType" ref="testqdoc-test.html#SomeType-typedef"/>
+ <keyword name="TestDerived" id="TestQDoc::TestDerived" ref="testqdoc-testderived.html"/>
+ <keyword name="TestDerived::DerivedType" id="TestDerived::DerivedType" ref="testqdoc-testderived.html#DerivedType-typedef"/>
+ <keyword name="TestDerived::NotTypedef" id="TestDerived::NotTypedef" ref="testqdoc-testderived.html#NotTypedef-typedef"/>
+ <keyword name="TestQDoc" id="TestQDoc" ref="testqdoc.html"/>
+ <keyword name="TheType" id="QML.TheType" ref="qml-themodule-thetype.html"/>
+ <keyword name="TheType" id="QML.TheModule.TheType" ref="qml-themodule-thetype.html"/>
+ <keyword name="Type" id="QML.Type" ref="qml-qdoc-test-type.html"/>
+ <keyword name="Type" id="QML.QDoc.Test1.Type" ref="qml-qdoc-test-type.html"/>
+ <keyword name="TypeNoVersion" id="QML.TypeNoVersion" ref="qml-test-nover-typenoversion.html"/>
+ <keyword name="TypeNoVersion" id="QML.Test.NoVer.TypeNoVersion" ref="qml-test-nover-typenoversion.html"/>
+ <keyword name="UIComponents" id="QML.UIComponents" ref="uicomponents-qmlmodule.html"/>
+ <keyword name="YetAnotherChild" id="QML.YetAnotherChild" ref="qml-qdoc-test-yetanotherchild.html"/>
+ <keyword name="YetAnotherChild" id="QML.QDoc.Test1.YetAnotherChild" ref="qml-qdoc-test-yetanotherchild.html"/>
+ <keyword name="abs" id="int::abs" ref="qml-int.html#abs-method"/>
+ <keyword name="active" id="DocTest::active" ref="qml-qdoc-test-doctest.html#active-prop"/>
+ <keyword name="anotherObsoleteMember" id="Test::anotherObsoleteMember" ref="testqdoc-test-obsolete.html#anotherObsoleteMember"/>
+ <keyword name="bindableProp" id="TestDerived::bindableProp" ref="testqdoc-testderived.html#bindableProp-prop"/>
+ <keyword name="bindableProp" id="TestDerived::bindableProp" ref="testqdoc-testderived.html#bindableProp-prop"/>
+ <keyword name="bindablePropChanged" id="TestDerived::bindablePropChanged" ref="testqdoc-testderived.html#bindableProp-prop"/>
+ <keyword name="boolProp" id="TestDerived::boolProp" ref="testqdoc-testderived.html#boolProp-prop"/>
+ <keyword name="boolProp" id="TestDerived::boolProp" ref="testqdoc-testderived.html#boolProp-prop"/>
+ <keyword name="boolPropChanged" id="TestDerived::boolPropChanged" ref="testqdoc-testderived.html#boolProp-prop"/>
+ <keyword name="children" id="AbstractParent::children" ref="qml-qdoc-test-abstractparent.html#children-prop"/>
+ <keyword name="color" id="ProgressBar::color" ref="qml-uicomponents-progressbar.html#color-prop"/>
+ <keyword name="completed" id="DocTest::completed" ref="qml-qdoc-test-doctest.html#completed-signal"/>
+ <keyword name="completed" id="Type::completed" ref="qml-qdoc-test-type.html#completed-signal"/>
+ <keyword name="configured" id="Type::configured" ref="qml-qdoc-test-type.html#configured-signal"/>
+ <keyword name="copy" id="Type::copy" ref="qml-qdoc-test-type.html#copy-method"/>
+ <keyword name="current" id="TabWidget::current" ref="qml-uicomponents-tabwidget.html#current-prop"/>
+ <keyword name="deprecatedMember" id="Test::deprecatedMember" ref="testqdoc-test-obsolete.html#deprecatedMember"/>
+ <keyword name="deprecatedMethod" id="Type::deprecatedMethod" ref="qml-qdoc-test-type-obsolete.html#deprecatedMethod-method"/>
+ <keyword name="disable" id="Type::disable" ref="qml-qdoc-test-type.html#disable-method"/>
+ <keyword name="documentMe" id="CrossModuleRef::documentMe" ref="crossmoduleref.html#documentMe"/>
+ <keyword name="emitSomething" id="TestDerived::emitSomething" ref="testqdoc-testderived.html#emitSomething"/>
+ <keyword name="enable" id="Type::enable" ref="qml-qdoc-test-type.html#enable-method"/>
+ <keyword name="fail" id="DocTest::fail" ref="qml-qdoc-test-doctest.html#fail-method"/>
+ <keyword name="fail_hard" id="DocTest::fail_hard" ref="qml-qdoc-test-doctest.html#fail_hard-method"/>
+ <keyword name="fifth" id="Type::fifth" ref="qml-qdoc-test-type.html#fifth-prop"/>
+ <keyword name="foo" id="DocTest::foo" ref="qml-qdoc-test-doctest.html#foo-signal"/>
+ <keyword name="fourth" id="Type::fourth" ref="qml-qdoc-test-type.html#fourth-prop"/>
+ <keyword name="funcPtr" id="Test::funcPtr" ref="testqdoc-test.html#funcPtr"/>
+ <keyword name="futureDeprecated" id="Type::futureDeprecated" ref="qml-qdoc-test-type.html#futureDeprecated-method"/>
+ <keyword name="getInt" id="TestDerived::getInt" ref="testqdoc-testderived.html#intProp-prop"/>
+ <keyword name="group.created" id="Type::group.created" ref="qml-qdoc-test-type.html#group.created-signal"/>
+ <keyword name="group.first" id="Type::group.first" ref="qml-qdoc-test-type.html#group.first-prop"/>
+ <keyword name="group.second" id="Type::group.second" ref="qml-qdoc-test-type.html#group.second-prop"/>
+ <keyword name="group.third" id="Type::group.third" ref="qml-qdoc-test-type.html#group.third-prop"/>
+ <keyword name="id" id="Type::id" ref="qml-qdoc-test-type.html#id-prop"/>
+ <keyword name="id" id="TestDerived::id" ref="testqdoc-testderived.html#id"/>
+ <keyword name="inlineFunction" id="Test::inlineFunction" ref="testqdoc-test.html#inlineFunction"/>
+ <keyword name="int" id="QML.int" ref="qml-int.html"/>
+ <keyword name="int" id="QML.QDoc.Test1.int" ref="qml-int.html"/>
+ <keyword name="intProp" id="TestDerived::intProp" ref="testqdoc-testderived.html#intProp-prop"/>
+ <keyword name="invokeMe" id="TestDerived::invokeMe" ref="testqdoc-testderived.html#invokeMe"/>
+ <keyword name="itsHappening" id="DocTest::itsHappening" ref="qml-qdoc-test-doctest.html#itsHappening-signal"/>
+ <keyword name="maximum" id="ProgressBar::maximum" ref="qml-uicomponents-progressbar.html#maximum-prop"/>
+ <keyword name="methodWithEmDashInItsDocs" id="Test::methodWithEmDashInItsDocs" ref="testqdoc-test.html#methodWithEmDashInItsDocs"/>
+ <keyword name="methodWithEnDashInItsDocs" id="Test::methodWithEnDashInItsDocs" ref="testqdoc-test.html#methodWithEnDashInItsDocs"/>
+ <keyword name="minimum" id="ProgressBar::minimum" ref="qml-uicomponents-progressbar.html#minimum-prop"/>
+ <keyword name="name" id="AbstractParent::name" ref="qml-qdoc-test-abstractparent.html#name-method"/>
+ <keyword name="name" id="AbstractParent::name" ref="qml-qdoc-test-abstractparent.html#name-prop"/>
+ <keyword name="name" id="Child::name" ref="qml-qdoc-test-child.html#name-method"/>
+ <keyword name="name" id="Child::name" ref="qml-qdoc-test-child.html#name-prop"/>
+ <keyword name="name" id="DocTest::name" ref="qml-qdoc-test-doctest.html#name-prop"/>
+ <keyword name="name" id="Type::name" ref="qml-qdoc-test-type.html#name-prop"/>
+ <keyword name="name" id="TheType::name" ref="qml-themodule-thetype.html#name-prop"/>
+ <keyword name="name" id="TestDerived::name" ref="testqdoc-testderived.html#name-prop"/>
+ <keyword name="name" id="TestDerived::name" ref="testqdoc-testderived.html#name-prop"/>
+ <keyword name="obsoleteMember" id="Test::obsoleteMember" ref="testqdoc-test-obsolete.html#obsoleteMember"/>
+ <keyword name="on" id="Switch::on" ref="qml-uicomponents-switch.html#on-prop"/>
+ <keyword name="operator++" id="Test::operator++" ref="testqdoc-test-obsolete.html#operator-2b-2b"/>
+ <keyword name="operator--" id="Test::operator--" ref="testqdoc-test-obsolete.html#operator--"/>
+ <keyword name="operator=" id="Test::operator=" ref="testqdoc-test.html#operator-eq"/>
+ <keyword name="operator==" id="operator==" ref="testqdoc-test.html#operator-eq-eq"/>
+ <keyword name="overload" id="Test::overload" ref="testqdoc-test.html#overload"/>
+ <keyword name="rear" id="AbstractParent::rear" ref="qml-qdoc-test-abstractparent.html#rear-method"/>
+ <keyword name="resetBoolProp" id="TestDerived::resetBoolProp" ref="testqdoc-testderived.html#boolProp-prop"/>
+ <keyword name="sampleReadOnlyProperty" id="TabWidget::sampleReadOnlyProperty" ref="qml-uicomponents-tabwidget.html#sampleReadOnlyProperty-prop"/>
+ <keyword name="secondColor" id="ProgressBar::secondColor" ref="qml-uicomponents-progressbar.html#secondColor-prop"/>
+ <keyword name="setBindableProp" id="TestDerived::setBindableProp" ref="testqdoc-testderived.html#bindableProp-prop"/>
+ <keyword name="setBoolProp" id="TestDerived::setBoolProp" ref="testqdoc-testderived.html#boolProp-prop"/>
+ <keyword name="someFunction" id="Test::someFunction" ref="testqdoc-test.html#someFunction"/>
+ <keyword name="someFunctionDefaultArg" id="Test::someFunctionDefaultArg" ref="testqdoc-test.html#someFunctionDefaultArg"/>
+ <keyword name="someProp" id="TestDerived::someProp" ref="testqdoc-testderived.html#someProp-prop"/>
+ <keyword name="someProp" id="TestDerived::someProp" ref="testqdoc-testderived.html#someProp-prop"/>
+ <keyword name="someValue" id="TestDerived::someValue" ref="testqdoc-testderived.html#someValue"/>
+ <keyword name="staticObsoleteMember" id="TestDerived::staticObsoleteMember" ref="testqdoc-testderived-obsolete.html#staticObsoleteMember"/>
+ <keyword name="toggle" id="Switch::toggle" ref="qml-uicomponents-switch.html#toggle-method"/>
+ <keyword name="type" id="Type::type" ref="qml-qdoc-test-type.html#type-attached-prop"/>
+ <keyword name="value" id="ProgressBar::value" ref="qml-uicomponents-progressbar.html#value-prop"/>
+ <keyword name="virtualFun" id="Test::virtualFun" ref="testqdoc-test.html#virtualFun"/>
+ <keyword name="virtualFun" id="TestDerived::virtualFun" ref="testqdoc-testderived.html#virtualFun"/>
+ </keywords>
+ <files>
+ <file>autolinking.html</file>
+ <file>classes.html</file>
+ <file>cpptypes.html</file>
+ <file>crossmoduleref.html</file>
+ <file>images/leonardo-da-vinci.png</file>
+ <file>obsolete-classes.html</file>
+ <file>qdoc-test-qmlmodule.html</file>
+ <file>qml-int.html</file>
+ <file>qml-qdoc-test-abstractparent-members.html</file>
+ <file>qml-qdoc-test-abstractparent.html</file>
+ <file>qml-qdoc-test-child-members.html</file>
+ <file>qml-qdoc-test-child.html</file>
+ <file>qml-qdoc-test-doctest-members.html</file>
+ <file>qml-qdoc-test-doctest.html</file>
+ <file>qml-qdoc-test-oldtype-members.html</file>
+ <file>qml-qdoc-test-oldtype.html</file>
+ <file>qml-qdoc-test-type-members.html</file>
+ <file>qml-qdoc-test-type-obsolete.html</file>
+ <file>qml-qdoc-test-type.html</file>
+ <file>qml-qdoc-test-yetanotherchild-members.html</file>
+ <file>qml-qdoc-test-yetanotherchild.html</file>
+ <file>qml-test-nover-doctest-members.html</file>
+ <file>qml-test-nover-doctest.html</file>
+ <file>qml-test-nover-typenoversion-members.html</file>
+ <file>qml-test-nover-typenoversion.html</file>
+ <file>qml-themodule-thetype-members.html</file>
+ <file>qml-themodule-thetype.html</file>
+ <file>qml-uicomponents-progressbar-members.html</file>
+ <file>qml-uicomponents-progressbar.html</file>
+ <file>qml-uicomponents-switch-members.html</file>
+ <file>qml-uicomponents-switch.html</file>
+ <file>qml-uicomponents-tabwidget-members.html</file>
+ <file>qml-uicomponents-tabwidget.html</file>
+ <file>qmlmodules.html</file>
+ <file>seenclass.html</file>
+ <file>test-cmaketest-cmakelists-txt.html</file>
+ <file>test-cmaketest-example.html</file>
+ <file>test-cmaketest-main-cpp.html</file>
+ <file>test-componentset-componentset-pro.html</file>
+ <file>test-componentset-componentset-qml.html</file>
+ <file>test-componentset-example.html</file>
+ <file>test-componentset-progressbar-qml.html</file>
+ <file>test-componentset-switch-qml.html</file>
+ <file>test-componentset-tabwidget-qml.html</file>
+ <file>test-demos-demo-demo-cpp.html</file>
+ <file>test-demos-demo-demo-pro.html</file>
+ <file>test-demos-demo-dontxclude-cmakelists-txt.html</file>
+ <file>test-demos-demo-example.html</file>
+ <file>test-demos-hidden-example.html</file>
+ <file>test-empty-qmlmodule.html</file>
+ <file>test-nover-qmlmodule.html</file>
+ <file>testcpp-module.html</file>
+ <file>testqdoc-test-members.html</file>
+ <file>testqdoc-test-obsolete.html</file>
+ <file>testqdoc-test.html</file>
+ <file>testqdoc-testderived-members.html</file>
+ <file>testqdoc-testderived-obsolete.html</file>
+ <file>testqdoc-testderived.html</file>
+ <file>testqdoc.html</file>
+ <file>themodule-qmlmodule.html</file>
+ <file>uicomponents-qmlmodule.html</file>
+ </files>
+ </filterSection>
+</QtHelpProject>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/testcpp-module.html b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/testcpp-module.html
new file mode 100644
index 000000000..828bfd508
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/testcpp-module.html
@@ -0,0 +1,60 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+<!-- testcpp.cpp -->
+ <meta name="description" content="A test module page.">
+ <title>QDoc Test C++ Classes | Test 6.2.11</title>
+</head>
+<body>
+<div class="sidebar">
+<div class="toc">
+<h3 id="toc">Contents</h3>
+<ul>
+<li class="level1"><a href="#namespaces">Namespaces</a></li>
+<li class="level1"><a href="#classes">Classes</a></li>
+<li class="level1"><a href="#details">Detailed Description</a></li>
+<li class="level2"><a href="#linking-to-function-like-things">Linking to function-like things</a></li>
+<li class="level3"><a href="#section">section()</a></li>
+</ul>
+</div>
+<div class="sidebar-content" id="sidebar-content"></div></div>
+<h1 class="title" translate="no">QDoc Test C++ Classes</h1>
+<!-- $$$TestCPP-brief -->
+<p>A test module page. <a href="#details">More...</a></p>
+<!-- @@@TestCPP -->
+<p>This module was introduced in Qt 2.0.</p>
+<h2 id="namespaces">Namespaces</h2>
+<div class="table"><table class="annotated">
+<tr class="odd topAlign"><td class="tblName" translate="no"><p><a href="crossmoduleref.html">CrossModuleRef</a></p></td><td class="tblDescr"><p>Namespace that has documented functions in multiple modules</p></td></tr>
+<tr class="even topAlign"><td class="tblName" translate="no"><p><a href="testqdoc.html">TestQDoc</a></p></td><td class="tblDescr"><p>A namespace</p></td></tr>
+</table></div>
+<h2 id="classes">Classes</h2>
+<div class="table"><table class="annotated">
+<tr class="odd topAlign"><td class="tblName" translate="no"><p><a href="seenclass.html">SeenClass</a></p></td><td class="tblDescr"><p>A public but undocumented class</p></td></tr>
+<tr class="even topAlign"><td class="tblName" translate="no"><p><a href="testqdoc-test.html">TestQDoc::Test</a></p></td><td class="tblDescr"><p>A class in a namespace</p></td></tr>
+<tr class="odd topAlign"><td class="tblName" translate="no"><p><a href="testqdoc-testderived.html">TestQDoc::TestDerived</a></p></td><td class="tblDescr"><p>A class in a namespace, derived from Test</p></td></tr>
+</table></div>
+<!-- $$$TestCPP-description -->
+<div class="descr">
+<h2 id="details">Detailed Description</h2>
+<div class="admonition note">
+<p><b>Note: </b>This is just a test. /* Look, Ma! {I'm made of arguments!} */</p>
+</div>
+<p><b>This module was introduced in version 5.15.</b></p>
+<p>1.0</p>
+<h3 id="linking-to-function-like-things">Linking to function-like things</h3>
+<ul>
+<li><a href="testqdoc-test.html#someFunctionDefaultArg" translate="no">someFunctionDefaultArg</a>()</li>
+<li><a href="testqdoc-test.html#QDOCTEST_MACRO2" translate="no">QDOCTEST_MACRO2</a>()</li>
+<li><a href="testqdoc-test.html#QDOCTEST_MACRO2" translate="no">QDOCTEST_MACRO2</a>(int &amp;x)</li>
+<li><a href="testcpp-module.html#section" translate="no">section()</a></li>
+<li><a href="testcpp-module.html#section" translate="no">section() is a section title</a></li>
+<li><a href="testqdoc-test.html#Test" translate="no">open( parenthesis</a></li>
+<li><a href="https://en.cppreference.com/w/cpp/utility/move">C++11 added std::move(T&amp;&amp; t)</a></li>
+</ul>
+<h4 id="section">section()</h4>
+</div>
+<!-- @@@TestCPP -->
+</body>
+</html>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/testqdoc-test-members.html b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/testqdoc-test-members.html
new file mode 100644
index 000000000..3b34571c7
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/testqdoc-test-members.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+<!-- testcpp.cpp -->
+ <meta name="description" content="A class in a namespace.">
+ <title>List of All Members for Test | Test 6.2.11</title>
+</head>
+<body>
+<li><a href="testcpp-module.html" translate="no">C++ Classes</a></li>
+<li>Test</li>
+<div class="sidebar"><div class="sidebar-content" id="sidebar-content"></div></div>
+<h1 class="title" translate="no">List of All Members for Test</h1>
+<p>This is the complete list of members for <a href="testqdoc-test.html">TestQDoc::Test</a>, including inherited members.</p>
+<ul>
+<li class="fn" translate="no"><span class="name"><b><a href="testqdoc-test.html#SomeType-typedef" translate="no">SomeType</a></b></span></li>
+<li class="fn" translate="no"><span class="name"><b><a href="testqdoc-test.html#Test" translate="no">Test</a></b></span>()</li>
+<li class="fn" translate="no"><span class="name"><b><a href="testqdoc-test.html#funcPtr" translate="no">funcPtr</a></b></span>(bool, const char *) : void (*)(bool)</li>
+<li class="fn" translate="no"><span class="name"><b><a href="testqdoc-test.html#inlineFunction" translate="no">inlineFunction</a></b></span>()</li>
+<li class="fn" translate="no"><span class="name"><b><a href="testqdoc-test.html#methodWithEmDashInItsDocs" translate="no">methodWithEmDashInItsDocs</a></b></span>()</li>
+<li class="fn" translate="no"><span class="name"><b><a href="testqdoc-test.html#methodWithEnDashInItsDocs" translate="no">methodWithEnDashInItsDocs</a></b></span>()</li>
+<li class="fn" translate="no"><span class="name"><b><a href="testqdoc-test.html#overload" translate="no">overload</a></b></span>()</li>
+<li class="fn" translate="no"><span class="name"><b><a href="testqdoc-test.html#overload-1" translate="no">overload</a></b></span>(bool)</li>
+<li class="fn" translate="no"><span class="name"><b><a href="testqdoc-test.html#someFunction" translate="no">someFunction</a></b></span>(int, int) : int</li>
+<li class="fn" translate="no"><span class="name"><b><a href="testqdoc-test.html#someFunctionDefaultArg" translate="no">someFunctionDefaultArg</a></b></span>(int, bool) const</li>
+<li class="fn" translate="no"><span class="name"><b><a href="testqdoc-test.html#virtualFun" translate="no">virtualFun</a></b></span>()</li>
+<li class="fn" translate="no"><span class="name"><b><a href="testqdoc-test.html#operator-eq" translate="no">operator=</a></b></span>(TestQDoc::Test &amp;&amp;) : TestQDoc::Test &amp;</li>
+</ul>
+</body>
+</html>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/testqdoc-test-obsolete.html b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/testqdoc-test-obsolete.html
new file mode 100644
index 000000000..81cc19f7d
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/testqdoc-test-obsolete.html
@@ -0,0 +1,45 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+<!-- testcpp.cpp -->
+ <meta name="description" content="A class in a namespace.">
+ <title>Obsolete Members for Test | Test 6.2.11</title>
+</head>
+<body>
+<li><a href="testcpp-module.html" translate="no">C++ Classes</a></li>
+<li>Test</li>
+<div class="sidebar"><div class="sidebar-content" id="sidebar-content"></div></div>
+<h1 class="title" translate="no">Obsolete Members for Test</h1>
+<p><b>The following members of class <a href="testqdoc-test.html" translate="no">Test</a> are deprecated.</b> They are provided to keep old source code working. We strongly advise against using them in new code.</p>
+<h2>Public Functions</h2>
+<div class="table"><table class="alignedsummary" translate="no">
+<tr><td class="memItemLeft topAlign rightAlign"> <code class="summary extra" translate="no">(deprecated)</code> void </td><td class="memItemRight bottomAlign"><b><a href="testqdoc-test-obsolete.html#anotherObsoleteMember" translate="no">anotherObsoleteMember</a></b>()</td></tr>
+<tr><td class="memItemLeft topAlign rightAlign"> <code class="summary extra" translate="no">(deprecated in 6.0)</code> void </td><td class="memItemRight bottomAlign"><b><a href="testqdoc-test-obsolete.html#deprecatedMember" translate="no">deprecatedMember</a></b>()</td></tr>
+<tr><td class="memItemLeft topAlign rightAlign"> <code class="summary extra" translate="no">(deprecated)</code> void </td><td class="memItemRight bottomAlign"><b><a href="testqdoc-test-obsolete.html#obsoleteMember" translate="no">obsoleteMember</a></b>()</td></tr>
+<tr><td class="memItemLeft topAlign rightAlign"> <code class="summary extra" translate="no">(deprecated)</code> TestQDoc::Test &amp;</td><td class="memItemRight bottomAlign"><b><a href="testqdoc-test-obsolete.html#operator-2b-2b" translate="no">operator++</a></b>()</td></tr>
+<tr><td class="memItemLeft topAlign rightAlign"> <code class="summary extra" translate="no">(deprecated)</code> TestQDoc::Test &amp;</td><td class="memItemRight bottomAlign"><b><a href="testqdoc-test-obsolete.html#operator--" translate="no">operator--</a></b>()</td></tr>
+</table></div>
+<h2>Member Function Documentation</h2>
+<!-- $$$ -->
+<div class="fngroup">
+<h3 class="fn fngroupitem" translate="no" id="operator-2b-2b"><code class="details extra" translate="no">[deprecated]</code> <span class="type"><a href="testqdoc-test.html" translate="no">TestQDoc::Test</a></span> &amp;Test::<span class="name">operator++</span>()</h3><h3 class="fn fngroupitem" translate="no" id="operator--"><code class="details extra" translate="no">[deprecated]</code> <span class="type"><a href="testqdoc-test.html" translate="no">TestQDoc::Test</a></span> &amp;Test::<span class="name">operator--</span>()</h3></div>
+<p>This function is deprecated. We strongly advise against using it in new code.</p>
+<!-- @@@ -->
+<!-- $$$anotherObsoleteMember[overload1]$$$anotherObsoleteMember -->
+<h3 class="fn" translate="no" id="anotherObsoleteMember"><code class="details extra" translate="no">[deprecated]</code> <span class="type">void</span> Test::<span class="name">anotherObsoleteMember</span>()</h3>
+<p>This function is deprecated. We strongly advise against using it in new code.</p>
+<p>Use <a href="testqdoc-test-obsolete.html#obsoleteMember" translate="no">obsoleteMember</a>() instead.</p>
+<!-- @@@anotherObsoleteMember -->
+<!-- $$$deprecatedMember[overload1]$$$deprecatedMember -->
+<h3 class="fn" translate="no" id="deprecatedMember"><code class="details extra" translate="no">[deprecated in 6.0]</code> <span class="type">void</span> Test::<span class="name">deprecatedMember</span>()</h3>
+<p>This function is deprecated since 6.0. We strongly advise against using it in new code.</p>
+<p>Use <a href="testqdoc-test.html#someFunction" translate="no">someFunction</a>() instead.</p>
+<!-- @@@deprecatedMember -->
+<!-- $$$obsoleteMember[overload1]$$$obsoleteMember -->
+<h3 class="fn" translate="no" id="obsoleteMember"><code class="details extra" translate="no">[deprecated]</code> <span class="type">void</span> Test::<span class="name">obsoleteMember</span>()</h3>
+<p>This function is deprecated. We strongly advise against using it in new code.</p>
+<p>Use <a href="testqdoc-test.html#someFunction" translate="no">someFunction</a>() instead.</p>
+<!-- @@@obsoleteMember -->
+</body>
+</html>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/testqdoc-test.html b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/testqdoc-test.html
new file mode 100644
index 000000000..25e1966aa
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/testqdoc-test.html
@@ -0,0 +1,165 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+<!-- testcpp.cpp -->
+ <meta name="description" content="A class in a namespace.">
+ <title>Test Class | Test 6.2.11</title>
+</head>
+<body>
+<li><a href="testcpp-module.html" translate="no">C++ Classes</a></li>
+<li>Test</li>
+<div class="sidebar">
+<div class="toc">
+<h3 id="toc">Contents</h3>
+<ul>
+<li class="level1"><a href="#public-types">Public Types</a></li>
+<li class="level1"><a href="#public-functions">Public Functions</a></li>
+<li class="level1"><a href="#protected-functions">Protected Functions</a></li>
+<li class="level1"><a href="#related-non-members">Related Non-Members</a></li>
+<li class="level1"><a href="#macros">Macros</a></li>
+<li class="level1"><a href="#details">Detailed Description</a></li>
+</ul>
+</div>
+<div class="sidebar-content" id="sidebar-content"></div></div>
+<h1 class="title" translate="no">Test Class</h1>
+<span class="small-subtitle" translate="no">class <a href="testqdoc.html" translate="no">TestQDoc</a>::Test</span>
+<!-- $$$Test-brief -->
+<p>A class in a namespace. <a href="#details">More...</a></p>
+<!-- @@@Test -->
+<div class="table"><table class="alignedsummary" translate="no">
+<tr><td class="memItemLeft rightAlign topAlign"> Header:</td><td class="memItemRight bottomAlign"> <span class="preprocessor">#include &lt;Test&gt;</span></td></tr>
+<tr><td class="memItemLeft rightAlign topAlign"> CMake:</td><td class="memItemRight bottomAlign"> find_package(Qt6 REQUIRED COMPONENTS QDocTest) <br/>
+target_link_libraries(mytarget PRIVATE Qt6::QDocTest)</td></tr>
+<tr><td class="memItemLeft rightAlign topAlign"> qmake:</td><td class="memItemRight bottomAlign"> QT += testcpp</td></tr>
+<tr><td class="memItemLeft rightAlign topAlign"> Since:</td><td class="memItemRight bottomAlign"> Qt 1.1</td></tr>
+<tr><td class="memItemLeft rightAlign topAlign"> In QML:</td><td class="memItemRight bottomAlign"> <a href="qml-qdoc-test-type.html" translate="no">Type</a></td></tr>
+<tr><td class="memItemLeft rightAlign topAlign"> Inherited By:</td><td class="memItemRight bottomAlign"> <p><a href="testqdoc-testderived.html" translate="no">TestQDoc::TestDerived</a></p>
+</td></tr>
+</table></div>
+<ul>
+<li><a href="testqdoc-test-members.html">List of all members, including inherited members</a></li>
+<li><a href="testqdoc-test-obsolete.html">Deprecated members</a></li>
+<li>Test is part of <a href="cpptypes.html">Test C++ Types</a>.</li>
+</ul>
+<p><b>Note:</b> All functions in this class are <a href="https://doc.qt.io/qt/17-qdoc-commands-thread.html#reentrant-command">reentrant</a> with the following exceptions:</p>
+<ul>
+<li><a href="testqdoc-test.html#someFunctionDefaultArg" translate="no">someFunctionDefaultArg</a>(int i, bool b) const</li>
+</ul>
+<h2 id="public-types">Public Types</h2>
+<div class="table"><table class="alignedsummary" translate="no">
+<tr><td class="memItemLeft rightAlign topAlign"> </td><td class="memItemRight bottomAlign"><b><a href="testqdoc-test.html#SomeType-typedef" translate="no">SomeType</a></b></td></tr>
+</table></div>
+<h2 id="public-functions">Public Functions</h2>
+<div class="table"><table class="alignedsummary" translate="no">
+<tr><td class="memItemLeft rightAlign topAlign"> </td><td class="memItemRight bottomAlign"><b><a href="testqdoc-test.html#Test" translate="no">Test</a></b>()</td></tr>
+<tr><td class="memItemLeft rightAlign topAlign"> void (*)(bool) </td><td class="memItemRight bottomAlign"><b><a href="testqdoc-test.html#funcPtr" translate="no">funcPtr</a></b>(bool <i>b</i>, const char *<i>s</i>)</td></tr>
+<tr><td class="memItemLeft rightAlign topAlign"> void </td><td class="memItemRight bottomAlign"><b><a href="testqdoc-test.html#inlineFunction" translate="no">inlineFunction</a></b>()</td></tr>
+<tr><td class="memItemLeft rightAlign topAlign"> void </td><td class="memItemRight bottomAlign"><b><a href="testqdoc-test.html#methodWithEmDashInItsDocs" translate="no">methodWithEmDashInItsDocs</a></b>()</td></tr>
+<tr><td class="memItemLeft rightAlign topAlign"> void </td><td class="memItemRight bottomAlign"><b><a href="testqdoc-test.html#methodWithEnDashInItsDocs" translate="no">methodWithEnDashInItsDocs</a></b>()</td></tr>
+<tr><td class="memItemLeft rightAlign topAlign"> <code class="summary extra" translate="no">(since Test 1.0)</code> int </td><td class="memItemRight bottomAlign"><b><a href="testqdoc-test.html#someFunction" translate="no">someFunction</a></b>(int, int <i>v</i> = 0)</td></tr>
+<tr><td class="memItemLeft rightAlign topAlign"> <code class="summary extra" translate="no">(since 2.0)</code> void </td><td class="memItemRight bottomAlign"><b><a href="testqdoc-test.html#someFunctionDefaultArg" translate="no">someFunctionDefaultArg</a></b>(int <i>i</i>, bool <i>b</i> = false) const</td></tr>
+<tr><td class="memItemLeft rightAlign topAlign"> virtual void </td><td class="memItemRight bottomAlign"><b><a href="testqdoc-test.html#virtualFun" translate="no">virtualFun</a></b>()</td></tr>
+<tr><td class="memItemLeft rightAlign topAlign"> TestQDoc::Test &amp;</td><td class="memItemRight bottomAlign"><b><a href="testqdoc-test.html#operator-eq" translate="no">operator=</a></b>(TestQDoc::Test &amp;&amp;<i>other</i>)</td></tr>
+</table></div>
+<h2 id="protected-functions">Protected Functions</h2>
+<div class="table"><table class="alignedsummary" translate="no">
+<tr><td class="memItemLeft rightAlign topAlign"> void </td><td class="memItemRight bottomAlign"><b><a href="testqdoc-test.html#overload" translate="no">overload</a></b>()</td></tr>
+<tr><td class="memItemLeft rightAlign topAlign"> <code class="summary extra" translate="no">(since Test 1.2)</code> void </td><td class="memItemRight bottomAlign"><b><a href="testqdoc-test.html#overload-1" translate="no">overload</a></b>(bool <i>b</i>)</td></tr>
+</table></div>
+<h2 id="related-non-members">Related Non-Members</h2>
+<div class="table"><table class="alignedsummary" translate="no">
+<tr><td class="memItemLeft rightAlign topAlign"> bool </td><td class="memItemRight bottomAlign"><b><a href="testqdoc-test.html#operator-eq-eq" translate="no">operator==</a></b>(const TestQDoc::Test &amp;<i>lhs</i>, const TestQDoc::Test &amp;<i>rhs</i>)</td></tr>
+</table></div>
+<h2 id="macros">Macros</h2>
+<div class="table"><table class="alignedsummary" translate="no">
+<tr><td class="memItemLeft rightAlign topAlign"> <code class="summary extra" translate="no">(since Test 1.1)</code> </td><td class="memItemRight bottomAlign"><b><a href="testqdoc-test.html#QDOCTEST_MACRO2" translate="no">QDOCTEST_MACRO2</a></b>(int &amp;<i>x</i>)</td></tr>
+</table></div>
+<!-- $$$Test-description -->
+<div class="descr">
+<h2 id="details">Detailed Description</h2>
+</div>
+<!-- @@@Test -->
+<div class="types">
+<h2>Member Type Documentation</h2>
+<!-- $$$SomeType -->
+<h3 class="fn" translate="no" id="SomeType-typedef">Test::<span class="name">SomeType</span></h3>
+<p>A typedef.</p>
+<!-- @@@SomeType -->
+</div>
+<div class="func">
+<h2>Member Function Documentation</h2>
+<!-- $$$ -->
+<div class="fngroup">
+<h3 class="fn fngroupitem" translate="no" id="overload"><code class="details extra" translate="no">[protected]</code> <span class="type">void</span> Test::<span class="name">overload</span>()</h3><h3 class="fn fngroupitem" translate="no" id="overload-1"><code class="details extra" translate="no">[protected, since Test 1.2]</code> <span class="type">void</span> Test::<span class="name">overload</span>(<span class="type">bool</span> <i>b</i>)</h3></div>
+<p>Overloads that share a documentation comment, optionally taking a parameter <i translate="no">b</i>.</p>
+<!-- @@@ -->
+<!-- $$$Test[overload1]$$$Test -->
+<h3 class="fn" translate="no" id="Test">Test::<span class="name">Test</span>()</h3>
+<p>The constructor is deleted.</p>
+<!-- @@@Test -->
+<!-- $$$funcPtr[overload1]$$$funcPtrboolconstchar* -->
+<h3 class="fn" translate="no" id="funcPtr"><span class="type">void</span> (*)(<span class="type">bool</span>) Test::<span class="name">funcPtr</span>(<span class="type">bool</span> <i>b</i>, const <span class="type">char</span> *<i>s</i>)</h3>
+<p>Returns a pointer to a function that takes a boolean. Uses <i translate="no">b</i> and <i translate="no">s</i>.</p>
+<!-- @@@funcPtr -->
+<!-- $$$inlineFunction[overload1]$$$inlineFunction -->
+<h3 class="fn" translate="no" id="inlineFunction"><span class="type">void</span> Test::<span class="name">inlineFunction</span>()</h3>
+<p>An inline function, documented using the \fn QDoc command.</p>
+<!-- @@@inlineFunction -->
+<!-- $$$methodWithEmDashInItsDocs[overload1]$$$methodWithEmDashInItsDocs -->
+<h3 class="fn" translate="no" id="methodWithEmDashInItsDocs"><span class="type">void</span> Test::<span class="name">methodWithEmDashInItsDocs</span>()</h3>
+<p>This method has em dashes in its documentation&mdash;as you'll find represented by <code translate="no">---</code> in the sources&mdash;here and there. The important bit to note is that when passed e.g. to the \c command, the three hyphens are processed as input to the command and not replaced by an em dash.</p>
+<p>-----------------------------------------------------------------------</p>
+<p>People can still add a bunch of dashes, though, without QDoc replacing them all with a series of em dashes.</p>
+<p>&mdash;You can also start a new paragraph with an em dash, if you want to.</p>
+<p><b>See also </b><a href="testqdoc-test.html#methodWithEnDashInItsDocs" translate="no">methodWithEnDashInItsDocs</a>.</p>
+<!-- @@@methodWithEmDashInItsDocs -->
+<!-- $$$methodWithEnDashInItsDocs[overload1]$$$methodWithEnDashInItsDocs -->
+<h3 class="fn" translate="no" id="methodWithEnDashInItsDocs"><span class="type">void</span> Test::<span class="name">methodWithEnDashInItsDocs</span>()</h3>
+<p>This method has en dashes in its documentation &ndash; as you'll find represented by <code translate="no">--</code> in the sources &ndash; here and there. The important bit to note is that when passed e.g. to the \c command, the two hyphens are processed as input to the command and not replaced by an en dash. This also applies to code blocks, where otherwise, the decrement operator would get completely borked:</p>
+<pre class="cpp" translate="no"><span class="keyword">for</span> (<span class="type">int</span> i <span class="operator">=</span> <span class="number">42</span>; i <span class="operator">&gt;</span> <span class="number">0</span>; <span class="operator">-</span><span class="operator">-</span>i)
+ <span class="comment">// Do something cool during countdown.</span></pre>
+<p>...as it would be silly if this would output &ndash;i instead of <code translate="no">--i</code>.</p>
+<p>-----------------------------------------------------------------------</p>
+<p>It still allows people to add a bunch of dashes, though, without replacing them all with a series of en dashes. Of course, they might want to use the \hr command instead, like this:</p>
+<hr />
+<p>&ndash; You can also start a new paragraph with an en dash, if you want to.</p>
+<p><b>See also </b>methodWithEnDashInItsDocs.</p>
+<!-- @@@methodWithEnDashInItsDocs -->
+<!-- $$$someFunction[overload1]$$$someFunctionintint -->
+<h3 class="fn" translate="no" id="someFunction"><code class="details extra" translate="no">[since Test 1.0]</code> <span class="type">int</span> Test::<span class="name">someFunction</span>(<span class="type">int</span>, <span class="type">int</span> <i>v</i> = 0)</h3>
+<p>Function that takes a parameter <i translate="no">v</i>. Also returns the value of <i translate="no">v</i>.</p>
+<p>This function was introduced in Test 1.0.</p>
+<!-- @@@someFunction -->
+<!-- $$$someFunctionDefaultArg[overload1]$$$someFunctionDefaultArgintbool -->
+<h3 class="fn" translate="no" id="someFunctionDefaultArg"><code class="details extra" translate="no">[since 2.0]</code> <span class="type">void</span> Test::<span class="name">someFunctionDefaultArg</span>(<span class="type">int</span> <i>i</i>, <span class="type">bool</span> <i>b</i> = false) const</h3>
+<p>Function that takes a parameter <i translate="no">i</i> and <i translate="no">b</i>.</p>
+<p><b>Warning:</b> This function is not <a href="https://doc.qt.io/qt/17-qdoc-commands-thread.html#reentrant-command">reentrant</a>.</p>
+<p>This function was introduced in Qt 2.0.</p>
+<!-- @@@someFunctionDefaultArg -->
+<!-- $$$virtualFun[overload1]$$$virtualFun -->
+<h3 class="fn" translate="no" id="virtualFun"><code class="details extra" translate="no">[virtual]</code> <span class="type">void</span> Test::<span class="name">virtualFun</span>()</h3>
+<p>Function that must be reimplemented.</p>
+<!-- @@@virtualFun -->
+<!-- $$$operator=[overload1]$$$operator=TestQDoc::Test&& -->
+<h3 class="fn" translate="no" id="operator-eq"><span class="type"><a href="testqdoc-test.html" translate="no">TestQDoc::Test</a></span> &amp;Test::<span class="name">operator=</span>(<span class="type"><a href="testqdoc-test.html" translate="no">TestQDoc::Test</a></span> &amp;&amp;<i>other</i>)</h3>
+<p>The move assignment operator is deleted. <i translate="no">other</i> cannot be moved from.</p>
+<!-- @@@operator= -->
+</div>
+<div class="relnonmem">
+<h2>Related Non-Members</h2>
+<!-- $$$operator==[overload1]$$$operator==constTestQDoc::Test&constTestQDoc::Test& -->
+<h3 class="fn" translate="no" id="operator-eq-eq"><span class="type">bool</span> <span class="name">operator==</span>(const <span class="type"><a href="testqdoc-test.html" translate="no">TestQDoc::Test</a></span> &amp;<i>lhs</i>, const <span class="type"><a href="testqdoc-test.html" translate="no">TestQDoc::Test</a></span> &amp;<i>rhs</i>)</h3>
+<p>Returns true if <i translate="no">lhs</i> and <i translate="no">rhs</i> are equal.</p>
+<!-- @@@operator== -->
+</div>
+<div class="macros">
+<h2>Macro Documentation</h2>
+<!-- $$$QDOCTEST_MACRO2[overload1]$$$QDOCTEST_MACRO2int& -->
+<h3 class="fn" translate="no" id="QDOCTEST_MACRO2"><code class="details extra" translate="no">[since Test 1.1]</code> <span class="name">QDOCTEST_MACRO2</span>(<span class="type">int</span> &amp;<i>x</i>)</h3>
+<p>A macro with argument <i translate="no">x</i>.</p>
+<p>This macro was introduced in Test 1.1.</p>
+<!-- @@@QDOCTEST_MACRO2 -->
+</div>
+</body>
+</html>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/testqdoc-testderived-members.html b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/testqdoc-testderived-members.html
new file mode 100644
index 000000000..f263a0b7b
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/testqdoc-testderived-members.html
@@ -0,0 +1,50 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+<!-- testcpp.cpp -->
+ <meta name="description" content="A class in a namespace, derived from Test.">
+ <title>List of All Members for TestDerived | Test 6.2.11</title>
+</head>
+<body>
+<li><a href="testcpp-module.html" translate="no">C++ Classes</a></li>
+<li>TestDerived</li>
+<div class="sidebar"><div class="sidebar-content" id="sidebar-content"></div></div>
+<h1 class="title" translate="no">List of All Members for TestDerived</h1>
+<p>This is the complete list of members for <a href="testqdoc-testderived.html">TestQDoc::TestDerived</a>, including inherited members.</p>
+<div class="table"><table class="propsummary" translate="no">
+<tr><td class="topAlign"><ul>
+<li class="fn" translate="no"><span class="name"><b><a href="testqdoc-testderived.html#DerivedType-typedef" translate="no">DerivedType</a></b></span></li>
+<li class="fn" translate="no"><span class="name"><b><a href="testqdoc-testderived.html#NotTypedef-typedef" translate="no">NotTypedef</a></b></span></li>
+<li class="fn" translate="no"><span class="name"><b><a href="testqdoc-test.html#SomeType-typedef" translate="no">SomeType</a></b></span></li>
+<li class="fn" translate="no"><span class="name"><b><a href="testqdoc-testderived.html#bindableProp-prop" translate="no">bindableProp</a></b></span>() : QBindable&lt;QString&gt;</li>
+<li class="fn" translate="no"><span class="name"><b><a href="testqdoc-testderived.html#bindableProp-prop" translate="no">bindablePropChanged</a></b></span>()</li>
+<li class="fn" translate="no"><span class="name"><b><a href="testqdoc-testderived.html#boolProp-prop" translate="no">boolProp</a></b></span>() : bool</li>
+<li class="fn" translate="no"><span class="name"><b><a href="testqdoc-testderived.html#boolProp-prop" translate="no">boolPropChanged</a></b></span>()</li>
+<li class="fn" translate="no"><span class="name"><b><a href="testqdoc-testderived.html#emitSomething" translate="no">emitSomething</a></b></span>()</li>
+<li class="fn" translate="no"><span class="name"><b><a href="testqdoc-test.html#funcPtr" translate="no">funcPtr</a></b></span>(bool, const char *) : void (*)(bool)</li>
+<li class="fn" translate="no"><span class="name"><b><a href="testqdoc-testderived.html#intProp-prop" translate="no">getInt</a></b></span>() : int *</li>
+<li class="fn" translate="no"><span class="name"><b><a href="testqdoc-testderived.html#id" translate="no">id</a></b></span>() : int</li>
+<li class="fn" translate="no"><span class="name"><b><a href="testqdoc-test.html#inlineFunction" translate="no">inlineFunction</a></b></span>()</li>
+<li class="fn" translate="no"><span class="name"><b><a href="testqdoc-testderived.html#invokeMe" translate="no">invokeMe</a></b></span>() const</li>
+<li class="fn" translate="no"><span class="name"><b><a href="testqdoc-test.html#methodWithEmDashInItsDocs" translate="no">methodWithEmDashInItsDocs</a></b></span>()</li>
+</ul></td><td class="topAlign"><ul>
+<li class="fn" translate="no"><span class="name"><b><a href="testqdoc-test.html#methodWithEnDashInItsDocs" translate="no">methodWithEnDashInItsDocs</a></b></span>()</li>
+<li class="fn" translate="no"><span class="name"><b><a href="testqdoc-testderived.html#name-prop" translate="no">name</a></b></span>() const : const QString *</li>
+<li class="fn" translate="no"><span class="name"><b><a href="testqdoc-test.html#overload" translate="no">overload</a></b></span>()</li>
+<li class="fn" translate="no"><span class="name"><b><a href="testqdoc-test.html#overload-1" translate="no">overload</a></b></span>(bool)</li>
+<li class="fn" translate="no"><span class="name"><b><a href="testqdoc-testderived.html#boolProp-prop" translate="no">resetBoolProp</a></b></span>()</li>
+<li class="fn" translate="no"><span class="name"><b><a href="testqdoc-testderived.html#bindableProp-prop" translate="no">setBindableProp</a></b></span>(const QString &amp;)</li>
+<li class="fn" translate="no"><span class="name"><b><a href="testqdoc-testderived.html#boolProp-prop" translate="no">setBoolProp</a></b></span>(bool)</li>
+<li class="fn" translate="no"><span class="name"><b><a href="testqdoc-test.html#someFunction" translate="no">someFunction</a></b></span>(int, int) : int</li>
+<li class="fn" translate="no"><span class="name"><b><a href="testqdoc-test.html#someFunctionDefaultArg" translate="no">someFunctionDefaultArg</a></b></span>(int, bool) const</li>
+<li class="fn" translate="no"><span class="name"><b><a href="testqdoc-testderived.html#someProp-prop" translate="no">someProp</a></b></span>() : const QString &amp;</li>
+<li class="fn" translate="no"><span class="name"><b><a href="testqdoc-testderived.html#someValue" translate="no">someValue</a></b></span>() : TestQDoc::TestDerived::NotTypedef</li>
+<li class="fn" translate="no"><span class="name"><b><a href="testqdoc-testderived.html#virtualFun" translate="no">virtualFun</a></b></span>()</li>
+<li class="fn" translate="no"><span class="name"><b><a href="testqdoc-test.html#virtualFun" translate="no">virtualFun</a></b></span>()</li>
+<li class="fn" translate="no"><span class="name"><b><a href="testqdoc-test.html#operator-eq" translate="no">operator=</a></b></span>(TestQDoc::Test &amp;&amp;) : TestQDoc::Test &amp;</li>
+</ul>
+</td></tr>
+</table></div>
+</body>
+</html>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/testqdoc-testderived-obsolete.html b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/testqdoc-testderived-obsolete.html
new file mode 100644
index 000000000..e293b8595
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/testqdoc-testderived-obsolete.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+<!-- testcpp.cpp -->
+ <meta name="description" content="A class in a namespace, derived from Test.">
+ <title>Obsolete Members for TestDerived | Test 6.2.11</title>
+</head>
+<body>
+<li><a href="testcpp-module.html" translate="no">C++ Classes</a></li>
+<li>TestDerived</li>
+<div class="sidebar"><div class="sidebar-content" id="sidebar-content"></div></div>
+<h1 class="title" translate="no">Obsolete Members for TestDerived</h1>
+<p><b>The following members of class <a href="testqdoc-testderived.html" translate="no">TestDerived</a> are deprecated.</b> They are provided to keep old source code working. We strongly advise against using them in new code.</p>
+<h2>Static Public Members</h2>
+<div class="table"><table class="alignedsummary" translate="no">
+<tr><td class="memItemLeft topAlign rightAlign"> <code class="summary extra" translate="no">(deprecated)</code> void </td><td class="memItemRight bottomAlign"><b><a href="testqdoc-testderived-obsolete.html#staticObsoleteMember" translate="no">staticObsoleteMember</a></b>()</td></tr>
+</table></div>
+<h2>Member Function Documentation</h2>
+<!-- $$$staticObsoleteMember[overload1]$$$staticObsoleteMember -->
+<h3 class="fn" translate="no" id="staticObsoleteMember"><code class="details extra" translate="no">[static, deprecated]</code> <span class="type">void</span> TestDerived::<span class="name">staticObsoleteMember</span>()</h3>
+<p>This function is deprecated. We strongly advise against using it in new code.</p>
+<p>Static obsolete method.</p>
+<!-- @@@staticObsoleteMember -->
+</body>
+</html>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/testqdoc-testderived.html b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/testqdoc-testderived.html
new file mode 100644
index 000000000..8c1c895e3
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/testqdoc-testderived.html
@@ -0,0 +1,173 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+<!-- testcpp.cpp -->
+ <meta name="description" content="A class in a namespace, derived from Test.">
+ <title>TestDerived Class | Test 6.2.11</title>
+</head>
+<body>
+<li><a href="testcpp-module.html" translate="no">C++ Classes</a></li>
+<li>TestDerived</li>
+<div class="sidebar">
+<div class="toc">
+<h3 id="toc">Contents</h3>
+<ul>
+<li class="level1"><a href="#public-types">Public Types</a></li>
+<li class="level1"><a href="#properties">Properties</a></li>
+<li class="level1"><a href="#public-functions">Public Functions</a></li>
+<li class="level1"><a href="#reimplemented-public-functions">Reimplemented Public Functions</a></li>
+<li class="level1"><a href="#public-slots">Public Slots</a></li>
+<li class="level1"><a href="#signals">Signals</a></li>
+<li class="level1"><a href="#details">Detailed Description</a></li>
+</ul>
+</div>
+<div class="sidebar-content" id="sidebar-content"></div></div>
+<h1 class="title" translate="no">TestDerived Class</h1>
+<span class="small-subtitle" translate="no">class <a href="testqdoc.html" translate="no">TestQDoc</a>::TestDerived</span>
+<!-- $$$TestDerived-brief -->
+<p>A class in a namespace, derived from <a href="testqdoc-test.html" translate="no">Test</a>. <a href="#details">More...</a></p>
+<!-- @@@TestDerived -->
+<div class="table"><table class="alignedsummary" translate="no">
+<tr><td class="memItemLeft rightAlign topAlign"> Header:</td><td class="memItemRight bottomAlign"> <span class="preprocessor">#include &lt;TestDerived&gt;</span></td></tr>
+<tr><td class="memItemLeft rightAlign topAlign"> CMake:</td><td class="memItemRight bottomAlign"> find_package(Qt6 REQUIRED COMPONENTS QDocTest) <br/>
+target_link_libraries(mytarget PRIVATE Qt6::QDocTest)</td></tr>
+<tr><td class="memItemLeft rightAlign topAlign"> qmake:</td><td class="memItemRight bottomAlign"> QT += testcpp</td></tr>
+<tr><td class="memItemLeft rightAlign topAlign"> Since:</td><td class="memItemRight bottomAlign"> Qt 2.0</td></tr>
+<tr><td class="memItemLeft rightAlign topAlign"> In QML:</td><td class="memItemRight bottomAlign"> <a href="qml-themodule-thetype.html" translate="no">TheType</a></td></tr>
+<tr><td class="memItemLeft rightAlign topAlign"> Inherits:</td><td class="memItemRight bottomAlign"> <a href="testqdoc-test.html" translate="no">TestQDoc::Test</a></td></tr>
+</table></div>
+<ul>
+<li><a href="testqdoc-testderived-members.html">List of all members, including inherited members</a></li>
+<li><a href="testqdoc-testderived-obsolete.html">Deprecated members</a></li>
+</ul>
+<h2 id="public-types">Public Types</h2>
+<div class="table"><table class="alignedsummary" translate="no">
+<tr><td class="memItemLeft rightAlign topAlign"> </td><td class="memItemRight bottomAlign"><b><a href="testqdoc-testderived.html#DerivedType-typedef" translate="no">DerivedType</a></b></td></tr>
+<tr><td class="memItemLeft rightAlign topAlign"> </td><td class="memItemRight bottomAlign"><b><a href="testqdoc-testderived.html#NotTypedef-typedef" translate="no">NotTypedef</a></b></td></tr>
+</table></div>
+<h2 id="properties">Properties</h2>
+<div class="table"><table class="propsummary" translate="no">
+<tr><td class="topAlign"><ul>
+<li class="fn" translate="no"><b><a href="testqdoc-testderived.html#bindableProp-prop" translate="no">bindableProp</a></b> : QString</li>
+<li class="fn" translate="no"><b><a href="testqdoc-testderived.html#boolProp-prop" translate="no">boolProp</a></b> : bool</li>
+<li class="fn" translate="no"><b><a href="testqdoc-testderived.html#intProp-prop" translate="no">intProp</a></b> : int* const</li>
+</ul></td><td class="topAlign"><ul>
+<li class="fn" translate="no"><b><a href="testqdoc-testderived.html#name-prop" translate="no">name</a></b> : const QString*</li>
+<li class="fn" translate="no"><b><a href="testqdoc-testderived.html#someProp-prop" translate="no">someProp</a></b> : QString</li>
+</ul>
+</td></tr>
+</table></div>
+<h2 id="public-functions">Public Functions</h2>
+<div class="table"><table class="alignedsummary" translate="no">
+<tr><td class="memItemLeft rightAlign topAlign"> QBindable&lt;QString&gt; </td><td class="memItemRight bottomAlign"><b><a href="testqdoc-testderived.html#bindableProp-prop" translate="no">bindableProp</a></b>()</td></tr>
+<tr><td class="memItemLeft rightAlign topAlign"> bool </td><td class="memItemRight bottomAlign"><b><a href="testqdoc-testderived.html#boolProp-prop" translate="no">boolProp</a></b>()</td></tr>
+<tr><td class="memItemLeft rightAlign topAlign"> int *</td><td class="memItemRight bottomAlign"><b><a href="testqdoc-testderived.html#intProp-prop" translate="no">getInt</a></b>()</td></tr>
+<tr><td class="memItemLeft rightAlign topAlign"> void </td><td class="memItemRight bottomAlign"><b><a href="testqdoc-testderived.html#invokeMe" translate="no">invokeMe</a></b>() const</td></tr>
+<tr><td class="memItemLeft rightAlign topAlign"> const QString *</td><td class="memItemRight bottomAlign"><b><a href="testqdoc-testderived.html#name-prop" translate="no">name</a></b>() const</td></tr>
+<tr><td class="memItemLeft rightAlign topAlign"> const QString &amp;</td><td class="memItemRight bottomAlign"><b><a href="testqdoc-testderived.html#someProp-prop" translate="no">someProp</a></b>()</td></tr>
+<tr><td class="memItemLeft rightAlign topAlign"> TestQDoc::TestDerived::NotTypedef </td><td class="memItemRight bottomAlign"><b><a href="testqdoc-testderived.html#someValue" translate="no">someValue</a></b>()</td></tr>
+</table></div>
+<h2 id="reimplemented-public-functions">Reimplemented Public Functions</h2>
+<div class="table"><table class="alignedsummary" translate="no">
+<tr><td class="memItemLeft rightAlign topAlign"> virtual int </td><td class="memItemRight bottomAlign"><b><a href="testqdoc-testderived.html#id" translate="no">id</a></b>() override</td></tr>
+<tr><td class="memItemLeft rightAlign topAlign"> virtual void </td><td class="memItemRight bottomAlign"><b><a href="testqdoc-testderived.html#virtualFun" translate="no">virtualFun</a></b>() override</td></tr>
+</table></div>
+<h2 id="public-slots">Public Slots</h2>
+<div class="table"><table class="alignedsummary" translate="no">
+<tr><td class="memItemLeft rightAlign topAlign"> void </td><td class="memItemRight bottomAlign"><b><a href="testqdoc-testderived.html#boolProp-prop" translate="no">resetBoolProp</a></b>()</td></tr>
+<tr><td class="memItemLeft rightAlign topAlign"> void </td><td class="memItemRight bottomAlign"><b><a href="testqdoc-testderived.html#bindableProp-prop" translate="no">setBindableProp</a></b>(const QString &amp;<i>s</i>)</td></tr>
+<tr><td class="memItemLeft rightAlign topAlign"> void </td><td class="memItemRight bottomAlign"><b><a href="testqdoc-testderived.html#boolProp-prop" translate="no">setBoolProp</a></b>(bool <i>b</i>)</td></tr>
+</table></div>
+<h2 id="signals">Signals</h2>
+<div class="table"><table class="alignedsummary" translate="no">
+<tr><td class="memItemLeft rightAlign topAlign"> void </td><td class="memItemRight bottomAlign"><b><a href="testqdoc-testderived.html#bindableProp-prop" translate="no">bindablePropChanged</a></b>()</td></tr>
+<tr><td class="memItemLeft rightAlign topAlign"> void </td><td class="memItemRight bottomAlign"><b><a href="testqdoc-testderived.html#boolProp-prop" translate="no">boolPropChanged</a></b>()</td></tr>
+<tr><td class="memItemLeft rightAlign topAlign"> void </td><td class="memItemRight bottomAlign"><b><a href="testqdoc-testderived.html#emitSomething" translate="no">emitSomething</a></b>()</td></tr>
+</table></div>
+<!-- $$$TestDerived-description -->
+<div class="descr">
+<h2 id="details">Detailed Description</h2>
+</div>
+<!-- @@@TestDerived -->
+<div class="types">
+<h2>Member Type Documentation</h2>
+<!-- $$$DerivedType -->
+<h3 class="fn" translate="no" id="DerivedType-typedef"><code class="details extra" translate="no">[alias]</code> TestDerived::<span class="name">DerivedType</span></h3>
+<p>An aliased typedef.</p>
+<!-- @@@DerivedType -->
+<!-- $$$NotTypedef -->
+<h3 class="fn" translate="no" id="NotTypedef-typedef"><code class="details extra" translate="no">[alias]</code> TestDerived::<span class="name">NotTypedef</span></h3>
+<p>I'm an alias, not a typedef.</p>
+<!-- @@@NotTypedef -->
+</div>
+<div class="prop">
+<h2>Property Documentation</h2>
+<!-- $$$bindableProp-prop$$$bindableProp$$$setBindablePropconstQString&$$$bindablePropChanged -->
+<h3 class="fn" translate="no" id="bindableProp-prop"><code class="details extra" translate="no">[bindable]</code> <span class="name">bindableProp</span> : <span class="type">QString</span></h3>
+<div class="admonition note"><p><b>Note: </b>This property supports <a href="https://wiki.qt.io/QProperty">QProperty</a> bindings.</p>
+</div><p>Some property.</p>
+<p><b>See also </b><a href="testqdoc-testderived.html#someProp-prop" translate="no">someProp</a>.</p>
+<!-- @@@bindableProp -->
+<!-- $$$boolProp-prop$$$boolProp$$$setBoolPropbool$$$resetBoolProp$$$boolPropChanged -->
+<h3 class="fn" translate="no" id="boolProp-prop"><span class="name">boolProp</span> : <span class="type">bool</span></h3>
+<p>A boolean property.</p>
+<p><b>Access functions:</b></p>
+<div class="table"><table class="alignedsummary" translate="no">
+<tr><td class="memItemLeft topAlign rightAlign"> bool </td><td class="memItemRight bottomAlign"><span class="name"><b>boolProp</b></span>()</td></tr>
+<tr><td class="memItemLeft topAlign rightAlign"> void </td><td class="memItemRight bottomAlign"><span class="name"><b>setBoolProp</b></span>(bool <i>b</i>)</td></tr>
+<tr><td class="memItemLeft topAlign rightAlign"> void </td><td class="memItemRight bottomAlign"><span class="name"><b>resetBoolProp</b></span>()</td></tr>
+</table></div>
+<p><b>Notifier signal:</b></p>
+<div class="table"><table class="alignedsummary" translate="no">
+<tr><td class="memItemLeft topAlign rightAlign"> void </td><td class="memItemRight bottomAlign"><span class="name"><b>boolPropChanged</b></span>()</td></tr>
+</table></div>
+<!-- @@@boolProp -->
+<!-- $$$intProp-prop$$$getInt -->
+<h3 class="fn" translate="no" id="intProp-prop"><code class="details extra" translate="no">[read-only]</code> <span class="name">intProp</span> : <span class="type">int</span>* const</h3>
+<p>An integer property.</p>
+<p><b>Access functions:</b></p>
+<div class="table"><table class="alignedsummary" translate="no">
+<tr><td class="memItemLeft topAlign rightAlign"> int *</td><td class="memItemRight bottomAlign"><span class="name"><b>getInt</b></span>()</td></tr>
+</table></div>
+<!-- @@@intProp -->
+<!-- $$$name-prop$$$name -->
+<h3 class="fn" translate="no" id="name-prop"><code class="details extra" translate="no">[read-only]</code> <span class="name">name</span> : const <span class="type">QString</span>*</h3>
+<p>This property holds a name.</p>
+<p><b>Access functions:</b></p>
+<div class="table"><table class="alignedsummary" translate="no">
+<tr><td class="memItemLeft topAlign rightAlign"> const QString *</td><td class="memItemRight bottomAlign"><span class="name"><b>name</b></span>() const</td></tr>
+</table></div>
+<!-- @@@name -->
+<!-- $$$someProp-prop$$$someProp -->
+<h3 class="fn" translate="no" id="someProp-prop"><code class="details extra" translate="no">[bindable read-only]</code> <span class="name">someProp</span> : <span class="type">QString</span></h3>
+<div class="admonition note"><p><b>Note: </b>This property supports <a href="https://wiki.qt.io/QProperty">QProperty</a> bindings.</p>
+</div><p>Another property.</p>
+<!-- @@@someProp -->
+</div>
+<div class="func">
+<h2>Member Function Documentation</h2>
+<!-- $$$emitSomething[overload1]$$$emitSomething -->
+<h3 class="fn" translate="no" id="emitSomething"><code class="details extra" translate="no">[private signal]</code> <span class="type">void</span> TestDerived::<span class="name">emitSomething</span>()</h3>
+<p>Emitted when things happen.</p>
+<div class="admonition note"><p><b>Note: </b>This is a private signal. It can be used in signal connections but cannot be emitted by the user.</p>
+</div><!-- @@@emitSomething -->
+<!-- $$$id[overload1]$$$id -->
+<h3 class="fn" translate="no" id="id"><code class="details extra" translate="no">[override virtual]</code> <span class="type">int</span> TestDerived::<span class="name">id</span>()</h3>
+<!-- @@@id -->
+<!-- $$$invokeMe[overload1]$$$invokeMe -->
+<h3 class="fn" translate="no" id="invokeMe"><code class="details extra" translate="no">[invokable]</code> <span class="type">void</span> TestDerived::<span class="name">invokeMe</span>() const</h3>
+<p>Something invokable.</p>
+<div class="admonition note"><p><b>Note: </b>This function can be invoked via the meta-object system and from QML. See Q_INVOKABLE.</p>
+</div><!-- @@@invokeMe -->
+<!-- $$$someValue[overload1]$$$someValue -->
+<h3 class="fn" translate="no" id="someValue"><span class="type"><a href="testqdoc-testderived.html#NotTypedef-typedef" translate="no">TestQDoc::TestDerived::NotTypedef</a></span> TestDerived::<span class="name">someValue</span>()</h3>
+<p>Returns a value using an aliases type.</p>
+<!-- @@@someValue -->
+<!-- $$$virtualFun[overload1]$$$virtualFun -->
+<h3 class="fn" translate="no" id="virtualFun"><code class="details extra" translate="no">[override virtual]</code> <span class="type">void</span> TestDerived::<span class="name">virtualFun</span>()</h3>
+<p>Reimplements: <a href="testqdoc-test.html#virtualFun" translate="no">Test::virtualFun</a>().</p>
+<!-- @@@virtualFun -->
+</div>
+</body>
+</html>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/testqdoc.html b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/testqdoc.html
new file mode 100644
index 000000000..7ccc7f4dd
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/testqdoc.html
@@ -0,0 +1,65 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+<!-- testcpp.cpp -->
+ <meta name="description" content="A namespace.">
+ <title>TestQDoc Namespace | Test 6.2.11</title>
+</head>
+<body>
+<div class="sidebar">
+<div class="toc">
+<h3 id="toc">Contents</h3>
+<ul>
+<li class="level1"><a href="#classes">Classes</a></li>
+<li class="level1"><a href="#macros">Macros</a></li>
+<li class="level1"><a href="#details">Detailed Description</a></li>
+<li class="level2"><a href="#usage">Usage</a></li>
+</ul>
+</div>
+<div class="sidebar-content" id="sidebar-content"></div></div>
+<h1 class="title" translate="no">TestQDoc Namespace</h1>
+<!-- $$$TestQDoc-brief -->
+<p>A namespace. <a href="#details">More...</a></p>
+<!-- @@@TestQDoc -->
+<div class="table"><table class="alignedsummary" translate="no">
+<tr><td class="memItemLeft rightAlign topAlign"> Header:</td><td class="memItemRight bottomAlign"> <span class="preprocessor">#include &lt;TestCPP&gt;</span></td></tr>
+<tr><td class="memItemLeft rightAlign topAlign"> CMake:</td><td class="memItemRight bottomAlign"> find_package(Qt6 REQUIRED COMPONENTS QDocTest) <br/>
+target_link_libraries(mytarget PRIVATE Qt6::QDocTest)</td></tr>
+<tr><td class="memItemLeft rightAlign topAlign"> qmake:</td><td class="memItemRight bottomAlign"> QT += testcpp</td></tr>
+<tr><td class="memItemLeft rightAlign topAlign"> Since:</td><td class="memItemRight bottomAlign"> Qt 2.0</td></tr>
+</table></div>
+<h2 id="classes">Classes</h2>
+<div class="table"><table class="alignedsummary" translate="no">
+<tr><td class="memItemLeft rightAlign topAlign"> <code class="summary extra" translate="no">(since 1.1)</code> class </td><td class="memItemRight bottomAlign"><b><a href="testqdoc-test.html" translate="no">Test</a></b></td></tr>
+<tr><td class="memItemLeft rightAlign topAlign"> <code class="summary extra" translate="no">(since 2.0)</code> class </td><td class="memItemRight bottomAlign"><b><a href="testqdoc-testderived.html" translate="no">TestDerived</a></b></td></tr>
+</table></div>
+<h2 id="macros">Macros</h2>
+<div class="table"><table class="alignedsummary" translate="no">
+<tr><td class="memItemLeft rightAlign topAlign"> <code class="summary extra" translate="no">(since Test 0.9)</code> </td><td class="memItemRight bottomAlign"><b><a href="testqdoc.html#QDOCTEST_MACRO" translate="no">QDOCTEST_MACRO</a></b></td></tr>
+</table></div>
+<!-- $$$TestQDoc-description -->
+<div class="descr">
+<h2 id="details">Detailed Description</h2>
+<h3 id="usage">Usage</h3>
+<p>This namespace is for testing QDoc output.</p>
+</div>
+<!-- @@@TestQDoc -->
+<div class="classes">
+<h2>Classes</h2>
+<h3> class <a href="testqdoc-test.html">Test</a></h3><!-- $$$Test-brief -->
+<p>A class in a namespace. <a href="testqdoc-test.html#details">More...</a></p>
+<!-- @@@Test -->
+<h3> class <a href="testqdoc-testderived.html">TestDerived</a></h3><!-- $$$TestDerived-brief -->
+<p>A class in a namespace, derived from <a href="testqdoc-test.html" translate="no">Test</a>. <a href="testqdoc-testderived.html#details">More...</a></p>
+<!-- @@@TestDerived -->
+</div>
+<div class="macros">
+<h2>Macro Documentation</h2>
+<!-- $$$QDOCTEST_MACRO[overload1]$$$QDOCTEST_MACRO -->
+<h3 class="fn" translate="no" id="QDOCTEST_MACRO"><code class="details extra" translate="no">[since Test 0.9]</code> <span class="name">QDOCTEST_MACRO</span></h3>
+<p>This macro was introduced in Test 0.9.</p>
+<!-- @@@QDOCTEST_MACRO -->
+</div>
+</body>
+</html>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/testtagfile.tags b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/testtagfile.tags
new file mode 100644
index 000000000..f68dffed6
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/testtagfile.tags
@@ -0,0 +1,500 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<tagfile>
+ <compound kind="class">
+ <name>QDoc.Test.AbstractParent</name>
+ <filename>qml-qdoc-test-abstractparent.html</filename>
+ <member kind="function" protection="public" virtualness="non" static="no">
+ <type>void</type>
+ <name>name</name>
+ <anchorfile>qml-qdoc-test-abstractparent.html</anchorfile>
+ <anchor>name-method</anchor>
+ <arglist>()</arglist>
+ </member>
+ <member kind="function" protection="public" virtualness="non" static="no">
+ <type>void</type>
+ <name>name</name>
+ <anchorfile>qml-qdoc-test-abstractparent.html</anchorfile>
+ <anchor>name-method</anchor>
+ <arglist>(Child child, name)</arglist>
+ </member>
+ <member kind="function" protection="public" virtualness="non" static="no">
+ <type>void</type>
+ <name>rear</name>
+ <anchorfile>qml-qdoc-test-abstractparent.html</anchorfile>
+ <anchor>rear-method</anchor>
+ <arglist>(Child child, var method)</arglist>
+ </member>
+ </compound>
+ <compound kind="class">
+ <name>QDoc.Test.Child</name>
+ <filename>qml-qdoc-test-child.html</filename>
+ <member kind="function" protection="public" virtualness="non" static="no">
+ <type>void</type>
+ <name>name</name>
+ <anchorfile>qml-qdoc-test-child.html</anchorfile>
+ <anchor>name-method</anchor>
+ <arglist>(Child child, name)</arglist>
+ </member>
+ </compound>
+ <compound kind="namespace">
+ <name>CrossModuleRef</name>
+ <filename>crossmoduleref.html</filename>
+ <member kind="function" protection="public" virtualness="non" static="no">
+ <type>void</type>
+ <name>documentMe</name>
+ <anchorfile>crossmoduleref.html</anchorfile>
+ <anchor>documentMe</anchor>
+ <arglist>()</arglist>
+ </member>
+ </compound>
+ <compound kind="class">
+ <name>QDoc.Test.DocTest</name>
+ <filename>qml-qdoc-test-doctest.html</filename>
+ <member kind="function" protection="public" virtualness="non" static="no">
+ <type></type>
+ <name>completed</name>
+ <anchorfile>qml-qdoc-test-doctest.html</anchorfile>
+ <anchor>completed-signal</anchor>
+ <arglist>()</arglist>
+ </member>
+ <member kind="function" protection="public" virtualness="non" static="no">
+ <type></type>
+ <name>fail</name>
+ <anchorfile>qml-qdoc-test-doctest.html</anchorfile>
+ <anchor>fail-method</anchor>
+ <arglist>(message)</arglist>
+ </member>
+ <member kind="function" protection="public" virtualness="non" static="no">
+ <type></type>
+ <name>fail_hard</name>
+ <anchorfile>qml-qdoc-test-doctest.html</anchorfile>
+ <anchor>fail_hard-method</anchor>
+ <arglist>(msg, option)</arglist>
+ </member>
+ <member kind="function" protection="public" virtualness="non" static="no">
+ <type></type>
+ <name>foo</name>
+ <anchorfile>qml-qdoc-test-doctest.html</anchorfile>
+ <anchor>foo-signal</anchor>
+ <arglist>(var bar)</arglist>
+ </member>
+ <member kind="function" protection="public" virtualness="non" static="no">
+ <type></type>
+ <name>itsHappening</name>
+ <anchorfile>qml-qdoc-test-doctest.html</anchorfile>
+ <anchor>itsHappening-signal</anchor>
+ <arglist>(bool really)</arglist>
+ </member>
+ </compound>
+ <compound kind="class">
+ <name>Test.NoVer.DocTest</name>
+ <filename>qml-test-nover-doctest.html</filename>
+ </compound>
+ <compound kind="class">
+ <name>QDoc.Test.OldType</name>
+ <filename>qml-qdoc-test-oldtype.html</filename>
+ </compound>
+ <compound kind="class">
+ <name>UIComponents.ProgressBar</name>
+ <filename>qml-uicomponents-progressbar.html</filename>
+ </compound>
+ <compound kind="class">
+ <name>SeenClass</name>
+ <filename>seenclass.html</filename>
+ </compound>
+ <compound kind="class">
+ <name>UIComponents.Switch</name>
+ <filename>qml-uicomponents-switch.html</filename>
+ <member kind="function" protection="public" virtualness="non" static="no">
+ <type></type>
+ <name>toggle</name>
+ <anchorfile>qml-uicomponents-switch.html</anchorfile>
+ <anchor>toggle-method</anchor>
+ <arglist>()</arglist>
+ </member>
+ </compound>
+ <compound kind="class">
+ <name>UIComponents.TabWidget</name>
+ <filename>qml-uicomponents-tabwidget.html</filename>
+ </compound>
+ <compound kind="namespace">
+ <name>TestQDoc</name>
+ <filename>testqdoc.html</filename>
+ <class>TestQDoc::Test</class>
+ <class>TestQDoc::TestDerived</class>
+ <member kind="function" protection="public" virtualness="non" static="no">
+ <type></type>
+ <name>QDOCTEST_MACRO</name>
+ <anchorfile>testqdoc.html</anchorfile>
+ <anchor>QDOCTEST_MACRO</anchor>
+ <arglist>QDOCTEST_MACRO</arglist>
+ </member>
+ </compound>
+ <compound kind="class">
+ <name>TestQDoc::Test</name>
+ <filename>testqdoc-test.html</filename>
+ <member kind="function" protection="public" virtualness="non" static="no">
+ <type></type>
+ <name>QDOCTEST_MACRO2</name>
+ <anchorfile>testqdoc-test.html</anchorfile>
+ <anchor>QDOCTEST_MACRO2</anchor>
+ <arglist>(int &amp;x)</arglist>
+ </member>
+ <member kind="function" protection="public" virtualness="non" static="no">
+ <type></type>
+ <name>Test</name>
+ <anchorfile>testqdoc-test.html</anchorfile>
+ <anchor>Test</anchor>
+ <arglist>()</arglist>
+ </member>
+ <member kind="function" protection="public" virtualness="non" static="no">
+ <type>TestQDoc::Test &amp;</type>
+ <name>operator++</name>
+ <anchorfile>testqdoc-test-obsolete.html</anchorfile>
+ <anchor>operator-2b-2b</anchor>
+ <arglist>()</arglist>
+ </member>
+ <member kind="function" protection="public" virtualness="non" static="no">
+ <type>TestQDoc::Test &amp;</type>
+ <name>operator--</name>
+ <anchorfile>testqdoc-test-obsolete.html</anchorfile>
+ <anchor>operator--</anchor>
+ <arglist>()</arglist>
+ </member>
+ <member kind="function" protection="public" virtualness="non" static="no">
+ <type>TestQDoc::Test &amp;</type>
+ <name>operator=</name>
+ <anchorfile>testqdoc-test.html</anchorfile>
+ <anchor>operator-eq</anchor>
+ <arglist>(TestQDoc::Test &amp;&amp;other)</arglist>
+ </member>
+ <member kind="function" protection="public" virtualness="non" static="no">
+ <type>bool</type>
+ <name>operator==</name>
+ <anchorfile>testqdoc-test.html</anchorfile>
+ <anchor>operator-eq-eq</anchor>
+ <arglist>(const TestQDoc::Test &amp;lhs, const TestQDoc::Test &amp;rhs)</arglist>
+ </member>
+ <member kind="function" protection="public" virtualness="non" static="no">
+ <type>int</type>
+ <name>someFunction</name>
+ <anchorfile>testqdoc-test.html</anchorfile>
+ <anchor>someFunction</anchor>
+ <arglist>(int, int v)</arglist>
+ </member>
+ <member kind="function" protection="public" virtualness="non" static="no">
+ <type>void (*)(bool)</type>
+ <name>funcPtr</name>
+ <anchorfile>testqdoc-test.html</anchorfile>
+ <anchor>funcPtr</anchor>
+ <arglist>(*)(bool) funcPtr(bool b, const char *s)</arglist>
+ </member>
+ <member kind="function" protection="public" virtualness="non" static="no">
+ <type>void</type>
+ <name>anotherObsoleteMember</name>
+ <anchorfile>testqdoc-test-obsolete.html</anchorfile>
+ <anchor>anotherObsoleteMember</anchor>
+ <arglist>()</arglist>
+ </member>
+ <member kind="function" protection="public" virtualness="non" static="no">
+ <type>void</type>
+ <name>deprecatedMember</name>
+ <anchorfile>testqdoc-test-obsolete.html</anchorfile>
+ <anchor>deprecatedMember</anchor>
+ <arglist>()</arglist>
+ </member>
+ <member kind="function" protection="public" virtualness="non" static="no">
+ <type>void</type>
+ <name>inlineFunction</name>
+ <anchorfile>testqdoc-test.html</anchorfile>
+ <anchor>inlineFunction</anchor>
+ <arglist>()</arglist>
+ </member>
+ <member kind="function" protection="public" virtualness="non" static="no">
+ <type>void</type>
+ <name>methodWithEmDashInItsDocs</name>
+ <anchorfile>testqdoc-test.html</anchorfile>
+ <anchor>methodWithEmDashInItsDocs</anchor>
+ <arglist>()</arglist>
+ </member>
+ <member kind="function" protection="public" virtualness="non" static="no">
+ <type>void</type>
+ <name>methodWithEnDashInItsDocs</name>
+ <anchorfile>testqdoc-test.html</anchorfile>
+ <anchor>methodWithEnDashInItsDocs</anchor>
+ <arglist>()</arglist>
+ </member>
+ <member kind="function" protection="public" virtualness="non" static="no">
+ <type>void</type>
+ <name>obsoleteMember</name>
+ <anchorfile>testqdoc-test-obsolete.html</anchorfile>
+ <anchor>obsoleteMember</anchor>
+ <arglist>()</arglist>
+ </member>
+ <member kind="function" protection="protected" virtualness="non" static="no">
+ <type>void</type>
+ <name>overload</name>
+ <anchorfile>testqdoc-test.html</anchorfile>
+ <anchor>overload</anchor>
+ <arglist>()</arglist>
+ </member>
+ <member kind="function" protection="protected" virtualness="non" static="no">
+ <type>void</type>
+ <name>overload</name>
+ <anchorfile>testqdoc-test.html</anchorfile>
+ <anchor>overload-1</anchor>
+ <arglist>(bool b)</arglist>
+ </member>
+ <member kind="function" protection="public" virtualness="virtual" static="no">
+ <type>virtual void</type>
+ <name>virtualFun</name>
+ <anchorfile>testqdoc-test.html</anchorfile>
+ <anchor>virtualFun</anchor>
+ <arglist>()</arglist>
+ </member>
+ <member kind="function" protection="public" virtualness="non" static="no">
+ <type>void</type>
+ <name>someFunctionDefaultArg</name>
+ <anchorfile>testqdoc-test.html</anchorfile>
+ <anchor>someFunctionDefaultArg</anchor>
+ <arglist>(int i, bool b) const const</arglist>
+ </member>
+ <member kind="typedef" type="">
+ <name>SomeType</name>
+ <anchorfile>testqdoc-test.html</anchorfile>
+ <anchor>SomeType-typedef</anchor>
+ <arglist></arglist>
+ </member>
+ </compound>
+ <compound kind="class">
+ <name>TestQDoc::TestDerived</name>
+ <filename>testqdoc-testderived.html</filename>
+ <base>Test</base>
+ <member kind="function" protection="public" virtualness="non" static="no">
+ <type>QBindable&lt;QString&gt;</type>
+ <name>bindableProp</name>
+ <anchorfile>testqdoc-testderived.html</anchorfile>
+ <anchor>bindableProp-prop</anchor>
+ <arglist>()</arglist>
+ </member>
+ <member kind="function" protection="public" virtualness="non" static="no">
+ <type>TestQDoc::TestDerived::NotTypedef</type>
+ <name>someValue</name>
+ <anchorfile>testqdoc-testderived.html</anchorfile>
+ <anchor>someValue</anchor>
+ <arglist>()</arglist>
+ </member>
+ <member kind="function" protection="public" virtualness="non" static="no">
+ <type>bool</type>
+ <name>boolProp</name>
+ <anchorfile>testqdoc-testderived.html</anchorfile>
+ <anchor>boolProp-prop</anchor>
+ <arglist>()</arglist>
+ </member>
+ <member kind="function" protection="public" virtualness="non" static="no">
+ <type>const QString &amp;</type>
+ <name>someProp</name>
+ <anchorfile>testqdoc-testderived.html</anchorfile>
+ <anchor>someProp-prop</anchor>
+ <arglist>()</arglist>
+ </member>
+ <member kind="function" protection="public" virtualness="non" static="no">
+ <type>int *</type>
+ <name>getInt</name>
+ <anchorfile>testqdoc-testderived.html</anchorfile>
+ <anchor>intProp-prop</anchor>
+ <arglist>()</arglist>
+ </member>
+ <member kind="function" protection="public" virtualness="virtual" static="no">
+ <type>virtual int</type>
+ <name>id</name>
+ <anchorfile>testqdoc-testderived.html</anchorfile>
+ <anchor>id</anchor>
+ <arglist>() override</arglist>
+ </member>
+ <member kind="function" protection="public" virtualness="non" static="no">
+ <type>void</type>
+ <name>bindablePropChanged</name>
+ <anchorfile>testqdoc-testderived.html</anchorfile>
+ <anchor>bindableProp-prop</anchor>
+ <arglist>()</arglist>
+ </member>
+ <member kind="function" protection="public" virtualness="non" static="no">
+ <type>void</type>
+ <name>boolPropChanged</name>
+ <anchorfile>testqdoc-testderived.html</anchorfile>
+ <anchor>boolProp-prop</anchor>
+ <arglist>()</arglist>
+ </member>
+ <member kind="function" protection="public" virtualness="non" static="no">
+ <type>void</type>
+ <name>emitSomething</name>
+ <anchorfile>testqdoc-testderived.html</anchorfile>
+ <anchor>emitSomething</anchor>
+ <arglist>()</arglist>
+ </member>
+ <member kind="function" protection="public" virtualness="non" static="no">
+ <type>void</type>
+ <name>resetBoolProp</name>
+ <anchorfile>testqdoc-testderived.html</anchorfile>
+ <anchor>boolProp-prop</anchor>
+ <arglist>()</arglist>
+ </member>
+ <member kind="function" protection="public" virtualness="non" static="no">
+ <type>void</type>
+ <name>setBindableProp</name>
+ <anchorfile>testqdoc-testderived.html</anchorfile>
+ <anchor>bindableProp-prop</anchor>
+ <arglist>(const QString &amp;s)</arglist>
+ </member>
+ <member kind="function" protection="public" virtualness="non" static="no">
+ <type>void</type>
+ <name>setBoolProp</name>
+ <anchorfile>testqdoc-testderived.html</anchorfile>
+ <anchor>boolProp-prop</anchor>
+ <arglist>(bool b)</arglist>
+ </member>
+ <member kind="function" protection="public" virtualness="non" static="yes">
+ <type>void</type>
+ <name>staticObsoleteMember</name>
+ <anchorfile>testqdoc-testderived-obsolete.html</anchorfile>
+ <anchor>staticObsoleteMember</anchor>
+ <arglist>()</arglist>
+ </member>
+ <member kind="function" protection="public" virtualness="virtual" static="no">
+ <type>virtual void</type>
+ <name>virtualFun</name>
+ <anchorfile>testqdoc-testderived.html</anchorfile>
+ <anchor>virtualFun</anchor>
+ <arglist>() override</arglist>
+ </member>
+ <member kind="function" protection="public" virtualness="non" static="no">
+ <type>const QString *</type>
+ <name>name</name>
+ <anchorfile>testqdoc-testderived.html</anchorfile>
+ <anchor>name-prop</anchor>
+ <arglist>() const const</arglist>
+ </member>
+ <member kind="function" protection="public" virtualness="non" static="no">
+ <type>void</type>
+ <name>invokeMe</name>
+ <anchorfile>testqdoc-testderived.html</anchorfile>
+ <anchor>invokeMe</anchor>
+ <arglist>() const const</arglist>
+ </member>
+ <member kind="typedef" type="">
+ <name>DerivedType</name>
+ <anchorfile>testqdoc-testderived.html</anchorfile>
+ <anchor>DerivedType-typedef</anchor>
+ <arglist></arglist>
+ </member>
+ <member kind="typedef" type="">
+ <name>NotTypedef</name>
+ <anchorfile>testqdoc-testderived.html</anchorfile>
+ <anchor>NotTypedef-typedef</anchor>
+ <arglist></arglist>
+ </member>
+ <member kind="property" type="QString">
+ <name>bindableProp</name>
+ <anchorfile>testqdoc-testderived.html</anchorfile>
+ <anchor>bindableProp-prop</anchor>
+ <arglist></arglist>
+ </member>
+ <member kind="property" type="bool">
+ <name>boolProp</name>
+ <anchorfile>testqdoc-testderived.html</anchorfile>
+ <anchor>boolProp-prop</anchor>
+ <arglist></arglist>
+ </member>
+ <member kind="property" type="int*">
+ <name>intProp</name>
+ <anchorfile>testqdoc-testderived.html</anchorfile>
+ <anchor>intProp-prop</anchor>
+ <arglist></arglist>
+ </member>
+ <member kind="property" type="const QString*">
+ <name>name</name>
+ <anchorfile>testqdoc-testderived.html</anchorfile>
+ <anchor>name-prop</anchor>
+ <arglist></arglist>
+ </member>
+ <member kind="property" type="QString">
+ <name>someProp</name>
+ <anchorfile>testqdoc-testderived.html</anchorfile>
+ <anchor>someProp-prop</anchor>
+ <arglist></arglist>
+ </member>
+ </compound>
+ <compound kind="class">
+ <name>TheModule.TheType</name>
+ <filename>qml-themodule-thetype.html</filename>
+ </compound>
+ <compound kind="class">
+ <name>QDoc.Test.Type</name>
+ <filename>qml-qdoc-test-type.html</filename>
+ <member kind="function" protection="public" virtualness="non" static="no">
+ <type>Type</type>
+ <name>copy</name>
+ <anchorfile>qml-qdoc-test-type.html</anchorfile>
+ <anchor>copy-method</anchor>
+ <arglist>(a)</arglist>
+ </member>
+ <member kind="function" protection="public" virtualness="non" static="no">
+ <type></type>
+ <name>completed</name>
+ <anchorfile>qml-qdoc-test-type.html</anchorfile>
+ <anchor>completed-signal</anchor>
+ <arglist>(int status)</arglist>
+ </member>
+ <member kind="function" protection="public" virtualness="non" static="no">
+ <type></type>
+ <name>configured</name>
+ <anchorfile>qml-qdoc-test-type.html</anchorfile>
+ <anchor>configured-signal</anchor>
+ <arglist>()</arglist>
+ </member>
+ <member kind="function" protection="public" virtualness="non" static="no">
+ <type></type>
+ <name>deprecatedMethod</name>
+ <anchorfile>qml-qdoc-test-type-obsolete.html</anchorfile>
+ <anchor>deprecatedMethod-method</anchor>
+ <arglist>()</arglist>
+ </member>
+ <member kind="function" protection="public" virtualness="non" static="no">
+ <type></type>
+ <name>disable</name>
+ <anchorfile>qml-qdoc-test-type.html</anchorfile>
+ <anchor>disable-method</anchor>
+ <arglist>()</arglist>
+ </member>
+ <member kind="function" protection="public" virtualness="non" static="no">
+ <type></type>
+ <name>enable</name>
+ <anchorfile>qml-qdoc-test-type.html</anchorfile>
+ <anchor>enable-method</anchor>
+ <arglist>()</arglist>
+ </member>
+ <member kind="function" protection="public" virtualness="non" static="no">
+ <type></type>
+ <name>futureDeprecated</name>
+ <anchorfile>qml-qdoc-test-type.html</anchorfile>
+ <anchor>futureDeprecated-method</anchor>
+ <arglist>()</arglist>
+ </member>
+ <member kind="function" protection="public" virtualness="non" static="no">
+ <type></type>
+ <name>group.created</name>
+ <anchorfile>qml-qdoc-test-type.html</anchorfile>
+ <anchor>group.created-signal</anchor>
+ <arglist>()</arglist>
+ </member>
+ </compound>
+ <compound kind="class">
+ <name>Test.NoVer.TypeNoVersion</name>
+ <filename>qml-test-nover-typenoversion.html</filename>
+ </compound>
+ <compound kind="class">
+ <name>QDoc.Test.YetAnotherChild</name>
+ <filename>qml-qdoc-test-yetanotherchild.html</filename>
+ </compound>
+</tagfile>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/themodule-qmlmodule.html b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/themodule-qmlmodule.html
new file mode 100644
index 000000000..e3d28561d
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/themodule-qmlmodule.html
@@ -0,0 +1,18 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+<!-- properties.qdoc -->
+ <title>Test 6.2.11</title>
+</head>
+<body>
+<div class="sidebar"><div class="sidebar-content" id="sidebar-content"></div></div>
+<!-- $$$TheModule-description -->
+<div class="descr" id="details">
+</div>
+<!-- @@@TheModule -->
+<div class="table"><table class="annotated">
+<tr class="odd topAlign"><td class="tblName" translate="no"><p><a href="qml-themodule-thetype.html">TheType</a></p></td></tr>
+</table></div>
+</body>
+</html>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/uicomponents-qmlmodule.html b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/uicomponents-qmlmodule.html
new file mode 100644
index 000000000..e45470a92
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/html/uicomponents-qmlmodule.html
@@ -0,0 +1,24 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+<!-- examples.qdoc -->
+ <meta name="description" content="Basic set of UI components.">
+ <title>UI Components | Test 6.2.11</title>
+</head>
+<body>
+<div class="sidebar"><div class="sidebar-content" id="sidebar-content"></div></div>
+<h1 class="title" translate="no">UI Components</h1>
+<!-- $$$UIComponents-description -->
+<div class="descr" id="details">
+<p>This is a listing of a list of UI components implemented by QML types. These files are available for general import and they are based on the Qt Quick Code Samples.</p>
+<p>This module is part of the <a href="test-componentset-example.html">UIComponents</a> example.</p>
+</div>
+<!-- @@@UIComponents -->
+<div class="table"><table class="annotated">
+<tr class="odd topAlign"><td class="tblName" translate="no"><p><a href="qml-uicomponents-progressbar.html">ProgressBar</a></p></td><td class="tblDescr"><p>A component that shows the progress of an event</p></td></tr>
+<tr class="even topAlign"><td class="tblName" translate="no"><p><a href="qml-uicomponents-switch.html">Switch</a></p></td><td class="tblDescr"><p>A component that can be turned on or off</p></td></tr>
+<tr class="odd topAlign"><td class="tblName" translate="no"><p><a href="qml-uicomponents-tabwidget.html">TabWidget</a></p></td><td class="tblDescr"><p>A widget that places its children as tabs</p></td></tr>
+</table></div>
+</body>
+</html>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/webxml/autolinking.webxml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/webxml/autolinking.webxml
new file mode 100644
index 000000000..91f35d5d7
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/webxml/autolinking.webxml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<WebXML>
+ <document>
+ <page name="autolinking.html" href="autolinking.html" status="active" location="classlists.qdoc" documented="true" subtype="page" title="Autolinking" fulltitle="Autolinking" subtitle="">
+ <contents name="testqdoc" title="TestQDoc" level="1"/>
+ <contents name="someprop" title="someProp" level="1"/>
+ <description>
+ <section id="testqdoc">
+ <heading level="1">TestQDoc</heading>
+ <para>The string <link raw="TestQDoc" href="testqdoc.html" type="namespace">TestQDoc</link> links to the C++ namespace unless linking explicitly, <link raw="#TestQDoc" href="autolinking.html#testqdoc" type="page" page="Autolinking">like this</link>, or <link raw="TestQDoc" href="testqdoc.html" type="namespace">this</link>. Also,</para>
+ <para>Autolinks:</para>
+ <list type="bullet">
+ <item>
+ <para>
+ <link raw="TestQDoc::TestDerived" href="testqdoc-testderived.html" type="class">TestQDoc::TestDerived</link></para>
+ </item>
+ </list>
+ <para>Explicit links:</para>
+ <list type="bullet">
+ <item>
+ <para>
+ <link raw="TestQDoc::TestDerived" href="testqdoc-testderived.html" type="class">TestQDoc::TestDerived</link></para>
+ </item>
+ <item>
+ <para>
+ <link raw="Obsolete Classes#TestQDoc" href="obsolete-classes.html#testqdoc" type="page" page="Obsolete Classes">Obsolete Classes#TestQDoc</link></para>
+ </item>
+ </list>
+ </section>
+ <section id="someprop">
+ <heading level="1">someProp</heading>
+ </section>
+ </description>
+ </page>
+ </document>
+</WebXML>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/webxml/classes.webxml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/webxml/classes.webxml
new file mode 100644
index 000000000..55faed6d6
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/webxml/classes.webxml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<WebXML>
+ <document>
+ <page name="classes.html" href="classes.html" status="active" location="unseenclass.qdoc" documented="true" subtype="page" title="Classes" fulltitle="Classes" subtitle="">
+ <description>
+ <generatedlist contents="annotatedclasses"/>
+ </description>
+ </page>
+ </document>
+</WebXML>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/webxml/cpptypes.webxml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/webxml/cpptypes.webxml
new file mode 100644
index 000000000..df7cd7024
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/webxml/cpptypes.webxml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<WebXML>
+ <document>
+ <group name="cpptypes" href="cpptypes.html" status="active" location="classlists.qdoc" documented="true" seen="true" title="Test C++ Types">
+ <description>
+ <generatedlist contents="testgroup"/>
+ <table width="100%">
+ <row>
+ <item>
+ <para>
+ <link raw="TestQDoc::Test" href="testqdoc-test.html" type="class"/>
+ </para>
+ </item>
+ <item>
+ <para>A class in a namespace.</para>
+ </item>
+ </row>
+ </table>
+ </description>
+ </group>
+ </document>
+</WebXML>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/webxml/crossmoduleref.webxml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/webxml/crossmoduleref.webxml
new file mode 100644
index 000000000..682799bd8
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/webxml/crossmoduleref.webxml
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<WebXML>
+ <document>
+ <namespace name="CrossModuleRef" href="crossmoduleref.html" status="active" access="public" location="testcpp.h" since="3.0" documented="true" module="TestCPP" brief="Namespace that has documented functions in multiple modules">
+ <description>
+ <brief>Namespace that has documented functions in multiple modules.</brief>
+ </description>
+ <function name="documentMe" fullname="CrossModuleRef::documentMe" href="crossmoduleref.html#documentMe" status="active" access="public" location="testcpp.h" documented="true" meta="plain" type="void" signature="void documentMe()">
+ <description>
+ <para>Document me!</para>
+ </description>
+ </function>
+ </namespace>
+ </document>
+</WebXML>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/webxml/images/leonardo-da-vinci.png b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/webxml/images/leonardo-da-vinci.png
new file mode 100644
index 000000000..854acb4ca
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/webxml/images/leonardo-da-vinci.png
Binary files differ
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/webxml/obsolete-classes.webxml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/webxml/obsolete-classes.webxml
new file mode 100644
index 000000000..dda841458
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/webxml/obsolete-classes.webxml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<WebXML>
+ <document>
+ <page name="obsolete-classes.html" href="obsolete-classes.html" status="active" location="classlists.qdoc" documented="true" subtype="page" title="Obsolete Classes" fulltitle="Obsolete Classes" subtitle="">
+ <contents name="classes-with-obsolete-members" title="Classes with obsolete members" level="1"/>
+ <contents name="testqdoc" title="TestQDoc" level="2"/>
+ <description>
+ <section id="classes-with-obsolete-members">
+ <heading level="1">Classes with obsolete members</heading>
+ <generatedlist contents="obsoletecppmembers"/>
+ </section>
+ <section id="testqdoc">
+ <heading level="2">TestQDoc</heading>
+ </section>
+ </description>
+ </page>
+ </document>
+</WebXML>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/webxml/qdoc-test-qmlmodule.webxml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/webxml/qdoc-test-qmlmodule.webxml
new file mode 100644
index 000000000..5d24b3077
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/webxml/qdoc-test-qmlmodule.webxml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<WebXML>
+ <document/>
+</WebXML>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/webxml/qmlmodules.webxml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/webxml/qmlmodules.webxml
new file mode 100644
index 000000000..dffd151d2
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/webxml/qmlmodules.webxml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<WebXML>
+ <document>
+ <page name="qmlmodules.html" href="qmlmodules.html" status="active" location="modules.qdoc" documented="true" subtype="page" title="QML Modules" fulltitle="QML Modules" subtitle="">
+ <contents name="qml-types" title="QML types" level="1"/>
+ <contents name="qml-value-types" title="QML value types" level="1"/>
+ <description>
+ <generatedlist contents="qml-modules"/>
+ <section id="qml-types">
+ <heading level="1">QML types</heading>
+ <generatedlist contents="qmltypesbymodule QDoc.Test"/>
+ </section>
+ <section id="qml-value-types">
+ <heading level="1">QML value types</heading>
+ <generatedlist contents="qmlvaluetypes"/>
+ <generatedlist contents="qmlvaluetypesbymodule QDoc.Test"/>
+ </section>
+ </description>
+ </page>
+ </document>
+</WebXML>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/webxml/seenclass.webxml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/webxml/seenclass.webxml
new file mode 100644
index 000000000..92c2ad8f0
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/webxml/seenclass.webxml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<WebXML>
+ <document>
+ <class name="SeenClass" href="seenclass.html" status="active" access="public" location="dont.h" since="2.0" documented="true" module="TestCPP" brief="A public but undocumented class">
+ <description>
+ <brief>A public but undocumented class.</brief>
+ </description>
+ </class>
+ </document>
+</WebXML>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/webxml/test-cmaketest-cmakelists-txt.webxml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/webxml/test-cmaketest-cmakelists-txt.webxml
new file mode 100644
index 000000000..7b4bf2ce6
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/webxml/test-cmaketest-cmakelists-txt.webxml
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<WebXML>
+ <document>
+ <page name="cmaketest/CMakeLists.txt" href="test-cmaketest-cmakelists-txt.html" title="CMakeLists.txt Example File" fulltitle="CMakeLists.txt Example File" subtitle="cmaketest/CMakeLists.txt">
+ <description>
+ <code>cmake_minimum_required(VERSION 3.16)
+project (QDOCTEST)</code>
+ </description>
+ </page>
+ </document>
+</WebXML>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/webxml/test-cmaketest-example.webxml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/webxml/test-cmaketest-example.webxml
new file mode 100644
index 000000000..b0cd15b27
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/webxml/test-cmaketest-example.webxml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<WebXML>
+ <document>
+ <page name="cmaketest" href="test-cmaketest-example.html" status="active" location="cmaketest.qdoc" documented="true" subtype="example" title="CMake Example Project" fulltitle="CMake Example Project" subtitle="">
+ <description>
+ <image href="images/leonardo-da-vinci.png"/>
+ <para>Files:</para>
+ <list type="bullet">
+ <item>
+ <para>
+ <link raw="cmaketest/CMakeLists.txt" href="test-cmaketest-cmakelists-txt.html" type="page" page="CMakeLists.txt Example File">cmaketest/CMakeLists.txt</link>
+ </para>
+ </item>
+ <item>
+ <para>
+ <link raw="cmaketest/main.cpp" href="test-cmaketest-main-cpp.html" type="page" page="main.cpp Example File">cmaketest/main.cpp</link>
+ </para>
+ </item>
+ </list>
+ </description>
+ </page>
+ </document>
+</WebXML>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/webxml/test-cmaketest-main-cpp.webxml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/webxml/test-cmaketest-main-cpp.webxml
new file mode 100644
index 000000000..7d22ced82
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/webxml/test-cmaketest-main-cpp.webxml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<WebXML>
+ <document>
+ <page name="cmaketest/main.cpp" href="test-cmaketest-main-cpp.html" title="main.cpp Example File" fulltitle="main.cpp Example File" subtitle="cmaketest/main.cpp">
+ <description>
+ <code>&lt;@type&gt;void&lt;/@type&gt; main(){}</code>
+ </description>
+ </page>
+ </document>
+</WebXML>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/webxml/test-componentset-componentset-pro.webxml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/webxml/test-componentset-componentset-pro.webxml
new file mode 100644
index 000000000..08be3fa28
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/webxml/test-componentset-componentset-pro.webxml
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<WebXML>
+ <document>
+ <page name="componentset/componentset.pro" href="test-componentset-componentset-pro.html" title="componentset.pro Example File" fulltitle="componentset.pro Example File" subtitle="componentset/componentset.pro">
+ <description>
+ <code>SOURCES = componentset.pro \
+ ProgressBar.qml \
+ Switch.qml \
+ TabWidget.qml \
+ uicomponents.qdoc</code>
+ </description>
+ </page>
+ </document>
+</WebXML>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/webxml/test-componentset-componentset-qml.webxml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/webxml/test-componentset-componentset-qml.webxml
new file mode 100644
index 000000000..0a4297da0
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/webxml/test-componentset-componentset-qml.webxml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<WebXML>
+ <document>
+ <page name="componentset/componentset.qml" href="test-componentset-componentset-qml.html" title="componentset.qml Example File" fulltitle="componentset.qml Example File" subtitle="componentset/componentset.qml">
+ <description>
+ <code>&lt;@comment&gt;// Copyright (C) 2023 The Qt Company Ltd.&lt;/@comment&gt;
+&lt;@comment&gt;// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0&lt;/@comment&gt;
+
+import QtQuick 2.0
+
+&lt;@type&gt;Item&lt;/@type&gt; {
+}</code>
+ </description>
+ </page>
+ </document>
+</WebXML>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/webxml/test-componentset-example.webxml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/webxml/test-componentset-example.webxml
new file mode 100644
index 000000000..600f2f3ed
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/webxml/test-componentset-example.webxml
@@ -0,0 +1,67 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<WebXML>
+ <document>
+ <page name="componentset" href="test-componentset-example.html" status="active" location="examples.qdoc" documented="true" subtype="example" title="QML Documentation Example" fulltitle="QML Documentation Example" subtitle="" brief="Example for documenting QML types">
+ <contents name="qml-class" title="QML Class" level="1"/>
+ <contents name="properties-signals-handlers-and-methods" title="Properties, Signals, Handlers, and Methods" level="1"/>
+ <contents name="internal-documentation" title="Internal Documentation" level="2"/>
+ <contents name="qml-types-with-c-implementation" title="QML Types with C++ Implementation" level="1"/>
+ <description>
+ <brief>Example for documenting QML types.</brief>
+ <para>This example demonstrates one of the ways to document QML types. It also generates a warning about a missing example image, on purpose.</para>
+ <para>In particular, there are sample types that are documented with QDoc commands comments. There are documentation comments for the QML types and their public interfaces. The types are grouped into a module, the <link raw="UI Components" href="uicomponents-qmlmodule.html" type="">UI Components</link> module.</para>
+ <para>The uicomponents.qdoc file generates the overview page for the <link raw="UI Components" href="uicomponents-qmlmodule.html" type="">UI Components</link> module page.</para>
+ <para>The generated documentation is available in the <link raw="UI Components" href="uicomponents-qmlmodule.html" type="">UI Components</link> module.</para>
+ <section id="qml-class">
+ <heading level="1">QML Class</heading>
+ <para>The QML types use the \qmltype to document the type. In addition, they have the \inmodule command in order for QDoc to associate them to the <teletype type="highlighted">UIComponents</teletype> module.</para>
+ <para>QDoc uses the \brief command to place a basic description when listing the types.</para>
+ </section>
+ <section id="properties-signals-handlers-and-methods">
+ <heading level="1">Properties, Signals, Handlers, and Methods</heading>
+ <para>The types have their properties, signals, handlers, and methods defined in their respective QML files. QDoc associates the properties and methods to the types, therefore, you only need to place the documentation above the property, method, or signal.</para>
+ <para>To document the type of a <italic>property alias</italic>, you must use the \qmlproperty command to specify the data type.</para>
+ <code>\qmlproperty int anAliasedProperty
+An aliased property of type int.</code>
+ </section>
+ <section id="internal-documentation">
+ <heading level="2">Internal Documentation</heading>
+ <para>You may declare that a documentation is for internal use by placing the \internal command after the beginning QDoc comment <teletype type="highlighted">/*</teletype>. QDoc will prevent the internal documentation from appearing in the public API.</para>
+ <para>If you wish to omit certain parts of the documentation, you may use the \omit and \endomit command.</para>
+ </section>
+ <section id="qml-types-with-c-implementation">
+ <heading level="1">QML Types with C++ Implementation</heading>
+ <para>This example only demonstrates the documentation for types in QML files, but the regular QML commands may be placed inside C++ classes to define the public API of the QML type.</para>
+ </section>
+ <para>Files:</para>
+ <list type="bullet">
+ <item>
+ <para>
+ <link raw="componentset/ProgressBar.qml" href="test-componentset-progressbar-qml.html" type="page" page="ProgressBar.qml Example File">componentset/ProgressBar.qml</link>
+ </para>
+ </item>
+ <item>
+ <para>
+ <link raw="componentset/Switch.qml" href="test-componentset-switch-qml.html" type="page" page="Switch.qml Example File">componentset/Switch.qml</link>
+ </para>
+ </item>
+ <item>
+ <para>
+ <link raw="componentset/TabWidget.qml" href="test-componentset-tabwidget-qml.html" type="page" page="TabWidget.qml Example File">componentset/TabWidget.qml</link>
+ </para>
+ </item>
+ <item>
+ <para>
+ <link raw="componentset/componentset.pro" href="test-componentset-componentset-pro.html" type="page" page="componentset.pro Example File">componentset/componentset.pro</link>
+ </para>
+ </item>
+ <item>
+ <para>
+ <link raw="componentset/componentset.qml" href="test-componentset-componentset-qml.html" type="page" page="componentset.qml Example File">componentset/componentset.qml</link>
+ </para>
+ </item>
+ </list>
+ </description>
+ </page>
+ </document>
+</WebXML>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/webxml/test-componentset-progressbar-qml.webxml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/webxml/test-componentset-progressbar-qml.webxml
new file mode 100644
index 000000000..6b5d2f8ac
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/webxml/test-componentset-progressbar-qml.webxml
@@ -0,0 +1,107 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<WebXML>
+ <document>
+ <page name="componentset/ProgressBar.qml" href="test-componentset-progressbar-qml.html" title="ProgressBar.qml Example File" fulltitle="ProgressBar.qml Example File" subtitle="componentset/ProgressBar.qml">
+ <description>
+ <code>&lt;@comment&gt;// Copyright (C) 2016 The Qt Company Ltd.&lt;/@comment&gt;
+&lt;@comment&gt;// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause&lt;/@comment&gt;
+
+import QtQuick 1.0
+
+&lt;@comment&gt;/*!
+ \qmltype ProgressBar
+ \inqmlmodule UIComponents
+ \brief A component that shows the progress of an event.
+
+ A ProgressBar shows the linear progress of an event as its \l value.
+ The range is specified using the \l {minimum} and the \l{maximum} values.
+
+ The ProgressBar component is part of the \l {UI Components} module.
+
+ This documentation is part of the \l{componentset}{UIComponents} example.
+*/&lt;/@comment&gt;
+&lt;@type&gt;Item&lt;/@type&gt; {
+ &lt;@name&gt;id&lt;/@name&gt;: &lt;@name&gt;progressbar&lt;/@name&gt;
+
+ &lt;@comment&gt;/*!
+ The minimum value of the ProgressBar range.
+ The \l value must not be less than this value.
+ */&lt;/@comment&gt;
+ property &lt;@type&gt;int&lt;/@type&gt; &lt;@name&gt;minimum&lt;/@name&gt;: &lt;@number&gt;0&lt;/@number&gt;
+
+ &lt;@comment&gt;/*!
+ The maximum value of the ProgressBar range.
+ The \l value must not be more than this value.
+ */&lt;/@comment&gt;
+ property &lt;@type&gt;int&lt;/@type&gt; &lt;@name&gt;maximum&lt;/@name&gt;: &lt;@number&gt;100&lt;/@number&gt;
+
+ &lt;@comment&gt;/*!
+ The value of the progress.
+ */&lt;/@comment&gt;
+ property &lt;@type&gt;int&lt;/@type&gt; &lt;@name&gt;value&lt;/@name&gt;: &lt;@number&gt;0&lt;/@number&gt;
+
+ &lt;@comment&gt;/*!
+ \qmlproperty color ProgressBar::color
+ The color of the ProgressBar's gradient. Must bind to a color type.
+
+ \omit
+ The &amp;quot;\qmlproperty &amp;lt;type&amp;gt; &amp;lt;property name&amp;gt;&amp;quot; is needed because
+ property alias need to have their types manually entered.
+
+ QDoc will not publish the documentation within omit and endomit.
+ \endomit
+
+ \sa secondColor
+ */&lt;/@comment&gt;
+ property &lt;@type&gt;alias&lt;/@type&gt; &lt;@name&gt;color&lt;/@name&gt;: &lt;@name&gt;gradient1&lt;/@name&gt;.&lt;@name&gt;color&lt;/@name&gt;
+
+ &lt;@comment&gt;/*!
+ \qmlproperty color ProgressBar::secondColor
+ The second color of the ProgressBar's gradient.
+ Must bind to a color type.
+
+ \omit
+ The &amp;quot;\qmlproperty &amp;lt;type&amp;gt; &amp;lt;property name&amp;gt;&amp;quot; is needed because
+ property alias need to have their types manually entered.
+
+ QDoc will not publish the documentation within omit and endomit.
+ \endomit
+
+ \sa color
+ */&lt;/@comment&gt;
+ property &lt;@type&gt;alias&lt;/@type&gt; &lt;@name&gt;secondColor&lt;/@name&gt;: &lt;@name&gt;gradient2&lt;/@name&gt;.&lt;@name&gt;color&lt;/@name&gt;
+
+ &lt;@name&gt;width&lt;/@name&gt;: &lt;@number&gt;250&lt;/@number&gt;; &lt;@name&gt;height&lt;/@name&gt;: &lt;@number&gt;23&lt;/@number&gt;
+ &lt;@name&gt;clip&lt;/@name&gt;: &lt;@number&gt;true&lt;/@number&gt;
+
+ &lt;@type&gt;Rectangle&lt;/@type&gt; {
+ &lt;@name&gt;id&lt;/@name&gt;: &lt;@name&gt;highlight&lt;/@name&gt;
+
+ &lt;@comment&gt;/*!
+ An internal documentation comment. The widthDest property is not
+ a public API and therefore will not be exposed.
+ */&lt;/@comment&gt;
+ property &lt;@type&gt;int&lt;/@type&gt; &lt;@name&gt;widthDest&lt;/@name&gt;: ((&lt;@name&gt;progressbar&lt;/@name&gt;.&lt;@name&gt;width&lt;/@name&gt; &lt;@op&gt;*&lt;/@op&gt; (&lt;@name&gt;value&lt;/@name&gt; &lt;@op&gt;-&lt;/@op&gt; &lt;@name&gt;minimum&lt;/@name&gt;)) &lt;@op&gt;/&lt;/@op&gt; (&lt;@name&gt;maximum&lt;/@name&gt; &lt;@op&gt;-&lt;/@op&gt; &lt;@name&gt;minimum&lt;/@name&gt;) &lt;@op&gt;-&lt;/@op&gt; &lt;@number&gt;6&lt;/@number&gt;)
+
+ &lt;@name&gt;width&lt;/@name&gt;: &lt;@name&gt;highlight&lt;/@name&gt;.&lt;@name&gt;widthDest&lt;/@name&gt;
+ Behavior on &lt;@name&gt;width&lt;/@name&gt; { &lt;@type&gt;SmoothedAnimation&lt;/@type&gt; { &lt;@name&gt;velocity&lt;/@name&gt;: &lt;@number&gt;1200&lt;/@number&gt; } }
+
+ &lt;@type&gt;anchors&lt;/@type&gt; { &lt;@name&gt;left&lt;/@name&gt;: &lt;@name&gt;parent&lt;/@name&gt;.&lt;@name&gt;left&lt;/@name&gt;; &lt;@name&gt;top&lt;/@name&gt;: &lt;@name&gt;parent&lt;/@name&gt;.&lt;@name&gt;top&lt;/@name&gt;; &lt;@name&gt;bottom&lt;/@name&gt;: &lt;@name&gt;parent&lt;/@name&gt;.&lt;@name&gt;bottom&lt;/@name&gt;; &lt;@name&gt;margins&lt;/@name&gt;: &lt;@number&gt;3&lt;/@number&gt; }
+ &lt;@name&gt;radius&lt;/@name&gt;: &lt;@number&gt;1&lt;/@number&gt;
+ &lt;@name&gt;gradient&lt;/@name&gt;: &lt;@name&gt;Gradient&lt;/@name&gt; {
+ &lt;@type&gt;GradientStop&lt;/@type&gt; { &lt;@name&gt;id&lt;/@name&gt;: &lt;@name&gt;gradient1&lt;/@name&gt;; &lt;@name&gt;position&lt;/@name&gt;: &lt;@number&gt;0.0&lt;/@number&gt; }
+ &lt;@type&gt;GradientStop&lt;/@type&gt; { &lt;@name&gt;id&lt;/@name&gt;: &lt;@name&gt;gradient2&lt;/@name&gt;; &lt;@name&gt;position&lt;/@name&gt;: &lt;@number&gt;1.0&lt;/@number&gt; }
+ }
+
+ }
+ &lt;@type&gt;Text&lt;/@type&gt; {
+ &lt;@type&gt;anchors&lt;/@type&gt; { &lt;@name&gt;right&lt;/@name&gt;: &lt;@name&gt;highlight&lt;/@name&gt;.&lt;@name&gt;right&lt;/@name&gt;; &lt;@name&gt;rightMargin&lt;/@name&gt;: &lt;@number&gt;6&lt;/@number&gt;; &lt;@name&gt;verticalCenter&lt;/@name&gt;: &lt;@name&gt;parent&lt;/@name&gt;.&lt;@name&gt;verticalCenter&lt;/@name&gt; }
+ &lt;@name&gt;color&lt;/@name&gt;: &lt;@string&gt;&amp;quot;white&amp;quot;&lt;/@string&gt;
+ &lt;@name&gt;font&lt;/@name&gt;.bold: &lt;@number&gt;true&lt;/@number&gt;
+ &lt;@name&gt;text&lt;/@name&gt;: &lt;@name&gt;Math&lt;/@name&gt;.&lt;@name&gt;floor&lt;/@name&gt;((&lt;@name&gt;value&lt;/@name&gt; &lt;@op&gt;-&lt;/@op&gt; &lt;@name&gt;minimum&lt;/@name&gt;) &lt;@op&gt;/&lt;/@op&gt; (&lt;@name&gt;maximum&lt;/@name&gt; &lt;@op&gt;-&lt;/@op&gt; &lt;@name&gt;minimum&lt;/@name&gt;) &lt;@op&gt;*&lt;/@op&gt; &lt;@number&gt;100&lt;/@number&gt;) &lt;@op&gt;+&lt;/@op&gt; &lt;@string&gt;'%'&lt;/@string&gt;
+ }
+}</code>
+ </description>
+ </page>
+ </document>
+</WebXML>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/webxml/test-componentset-switch-qml.webxml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/webxml/test-componentset-switch-qml.webxml
new file mode 100644
index 000000000..4e09d9f25
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/webxml/test-componentset-switch-qml.webxml
@@ -0,0 +1,112 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<WebXML>
+ <document>
+ <page name="componentset/Switch.qml" href="test-componentset-switch-qml.html" title="Switch.qml Example File" fulltitle="Switch.qml Example File" subtitle="componentset/Switch.qml">
+ <description>
+ <code>&lt;@comment&gt;// Copyright (C) 2016 The Qt Company Ltd.&lt;/@comment&gt;
+&lt;@comment&gt;// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause&lt;/@comment&gt;
+
+import QtQuick 1.0
+
+&lt;@comment&gt;/*!
+ \qmltype ToggleSwitch
+ \inqmlmodule UIComponents
+ \brief A component that can be turned on or off.
+
+ A toggle switch has two states: an \c on and an \c off state. The \c off
+ state is when the \l on property is set to \c false.
+
+ The ToggleSwitch component is part of the \l {UI Components} module.
+
+ This documentation is part of the \l{componentset}{UIComponents} example.
+
+*/&lt;/@comment&gt;
+&lt;@type&gt;Item&lt;/@type&gt; {
+ &lt;@name&gt;id&lt;/@name&gt;: &lt;@name&gt;toggleswitch&lt;/@name&gt;
+ &lt;@name&gt;width&lt;/@name&gt;: &lt;@name&gt;background&lt;/@name&gt;.&lt;@name&gt;width&lt;/@name&gt;; &lt;@name&gt;height&lt;/@name&gt;: &lt;@name&gt;background&lt;/@name&gt;.&lt;@name&gt;height&lt;/@name&gt;
+
+ &lt;@comment&gt;/*!
+ Indicates the state of the switch. If \c false, then the switch is in
+ the \c off state.
+
+ \omit
+ The \qmlproperty &amp;lt;type&amp;gt; &amp;lt;propertyname&amp;gt; is not necessary as QDoc
+ will associate this property to the ToggleSwitch
+
+ QDoc will not publish the documentation within omit and endomit.
+ \endomit
+ */&lt;/@comment&gt;
+ property &lt;@type&gt;bool&lt;/@type&gt; &lt;@name&gt;on&lt;/@name&gt;: &lt;@number&gt;false&lt;/@number&gt;
+
+ &lt;@comment&gt;/*!
+ A method to toggle the switch. If the switch is \c on, the toggling it
+ will turn it \c off. Toggling a switch in the \c off position will
+ turn it \c on.
+ */&lt;/@comment&gt;
+ &lt;@keyword&gt;function &lt;/@keyword&gt;&lt;@name&gt;toggle&lt;/@name&gt;() {
+ &lt;@keyword&gt;if&lt;/@keyword&gt; (&lt;@name&gt;toggleswitch&lt;/@name&gt;.&lt;@name&gt;state&lt;/@name&gt; &lt;@op&gt;==&lt;/@op&gt; &lt;@string&gt;&amp;quot;on&amp;quot;&lt;/@string&gt;)
+ &lt;@name&gt;toggleswitch&lt;/@name&gt;.&lt;@name&gt;state&lt;/@name&gt; &lt;@op&gt;=&lt;/@op&gt; &lt;@string&gt;&amp;quot;off&amp;quot;&lt;/@string&gt;;
+ &lt;@keyword&gt;else&lt;/@keyword&gt;
+ &lt;@name&gt;toggleswitch&lt;/@name&gt;.&lt;@name&gt;state&lt;/@name&gt; &lt;@op&gt;=&lt;/@op&gt; &lt;@string&gt;&amp;quot;on&amp;quot;&lt;/@string&gt;;
+ }
+
+ &lt;@comment&gt;/*!
+ \internal
+
+ An internal function to synchronize the switch's internals. This
+ function is not for public access. The \internal command will
+ prevent QDoc from publishing this comment in the public API.
+ */&lt;/@comment&gt;
+ &lt;@keyword&gt;function &lt;/@keyword&gt;&lt;@name&gt;releaseSwitch&lt;/@name&gt;() {
+ &lt;@keyword&gt;if&lt;/@keyword&gt; (&lt;@name&gt;knob&lt;/@name&gt;.&lt;@name&gt;x&lt;/@name&gt; &lt;@op&gt;==&lt;/@op&gt; &lt;@number&gt;1&lt;/@number&gt;) {
+ &lt;@keyword&gt;if&lt;/@keyword&gt; (&lt;@name&gt;toggleswitch&lt;/@name&gt;.&lt;@name&gt;state&lt;/@name&gt; &lt;@op&gt;==&lt;/@op&gt; &lt;@string&gt;&amp;quot;off&amp;quot;&lt;/@string&gt;) &lt;@keyword&gt;return&lt;/@keyword&gt;;
+ }
+ &lt;@keyword&gt;if&lt;/@keyword&gt; (&lt;@name&gt;knob&lt;/@name&gt;.&lt;@name&gt;x&lt;/@name&gt; &lt;@op&gt;==&lt;/@op&gt; &lt;@number&gt;78&lt;/@number&gt;) {
+ &lt;@keyword&gt;if&lt;/@keyword&gt; (&lt;@name&gt;toggleswitch&lt;/@name&gt;.&lt;@name&gt;state&lt;/@name&gt; &lt;@op&gt;==&lt;/@op&gt; &lt;@string&gt;&amp;quot;on&amp;quot;&lt;/@string&gt;) &lt;@keyword&gt;return&lt;/@keyword&gt;;
+ }
+ &lt;@name&gt;toggle&lt;/@name&gt;();
+ }
+
+ &lt;@type&gt;Rectangle&lt;/@type&gt; {
+ &lt;@name&gt;id&lt;/@name&gt;: &lt;@name&gt;background&lt;/@name&gt;
+ &lt;@name&gt;width&lt;/@name&gt;: &lt;@number&gt;130&lt;/@number&gt;; &lt;@name&gt;height&lt;/@name&gt;: &lt;@number&gt;48&lt;/@number&gt;
+ &lt;@name&gt;radius&lt;/@name&gt;: &lt;@number&gt;48&lt;/@number&gt;
+ &lt;@name&gt;color&lt;/@name&gt;: &lt;@string&gt;&amp;quot;lightsteelblue&amp;quot;&lt;/@string&gt;
+ &lt;@type&gt;MouseArea&lt;/@type&gt; { &lt;@name&gt;anchors&lt;/@name&gt;.fill: &lt;@name&gt;parent&lt;/@name&gt;; &lt;@name&gt;onClicked&lt;/@name&gt;: &lt;@name&gt;toggle&lt;/@name&gt;() }
+ }
+
+ &lt;@type&gt;Rectangle&lt;/@type&gt; {
+ &lt;@name&gt;id&lt;/@name&gt;: &lt;@name&gt;knob&lt;/@name&gt;
+ &lt;@name&gt;width&lt;/@name&gt;: &lt;@number&gt;48&lt;/@number&gt;; &lt;@name&gt;height&lt;/@name&gt;: &lt;@number&gt;48&lt;/@number&gt;
+ &lt;@name&gt;radius&lt;/@name&gt;: &lt;@name&gt;width&lt;/@name&gt;
+ &lt;@name&gt;color&lt;/@name&gt;: &lt;@string&gt;&amp;quot;lightblue&amp;quot;&lt;/@string&gt;
+
+ &lt;@type&gt;MouseArea&lt;/@type&gt; {
+ &lt;@name&gt;anchors&lt;/@name&gt;.fill: &lt;@name&gt;parent&lt;/@name&gt;
+ &lt;@name&gt;drag&lt;/@name&gt;.target: &lt;@name&gt;knob&lt;/@name&gt;; &lt;@name&gt;drag&lt;/@name&gt;.axis: &lt;@name&gt;Drag&lt;/@name&gt;.&lt;@name&gt;XAxis&lt;/@name&gt;; &lt;@name&gt;drag&lt;/@name&gt;.minimumX: &lt;@number&gt;1&lt;/@number&gt;; &lt;@name&gt;drag&lt;/@name&gt;.maximumX: &lt;@number&gt;78&lt;/@number&gt;
+ &lt;@name&gt;onClicked&lt;/@name&gt;: &lt;@name&gt;toggle&lt;/@name&gt;()
+ &lt;@name&gt;onReleased&lt;/@name&gt;: &lt;@name&gt;releaseSwitch&lt;/@name&gt;()
+ }
+ }
+
+ &lt;@name&gt;states&lt;/@name&gt;: [
+ &lt;@type&gt;State&lt;/@type&gt; {
+ &lt;@name&gt;name&lt;/@name&gt;: &lt;@string&gt;&amp;quot;on&amp;quot;&lt;/@string&gt;
+ &lt;@type&gt;PropertyChanges&lt;/@type&gt; { &lt;@name&gt;target&lt;/@name&gt;: &lt;@name&gt;knob&lt;/@name&gt;; &lt;@name&gt;x&lt;/@name&gt;: &lt;@number&gt;78&lt;/@number&gt; }
+ &lt;@type&gt;PropertyChanges&lt;/@type&gt; { &lt;@name&gt;target&lt;/@name&gt;: &lt;@name&gt;toggleswitch&lt;/@name&gt;; &lt;@name&gt;on&lt;/@name&gt;: &lt;@number&gt;true&lt;/@number&gt; }
+ },
+ &lt;@type&gt;State&lt;/@type&gt; {
+ &lt;@name&gt;name&lt;/@name&gt;: &lt;@string&gt;&amp;quot;off&amp;quot;&lt;/@string&gt;
+ &lt;@type&gt;PropertyChanges&lt;/@type&gt; { &lt;@name&gt;target&lt;/@name&gt;: &lt;@name&gt;knob&lt;/@name&gt;; &lt;@name&gt;x&lt;/@name&gt;: &lt;@number&gt;1&lt;/@number&gt; }
+ &lt;@type&gt;PropertyChanges&lt;/@type&gt; { &lt;@name&gt;target&lt;/@name&gt;: &lt;@name&gt;toggleswitch&lt;/@name&gt;; &lt;@name&gt;on&lt;/@name&gt;: &lt;@number&gt;false&lt;/@number&gt; }
+ }
+ ]
+
+ &lt;@name&gt;transitions&lt;/@name&gt;: &lt;@name&gt;Transition&lt;/@name&gt; {
+ &lt;@type&gt;NumberAnimation&lt;/@type&gt; { &lt;@name&gt;properties&lt;/@name&gt;: &lt;@string&gt;&amp;quot;x&amp;quot;&lt;/@string&gt;; &lt;@name&gt;easing&lt;/@name&gt;.type: &lt;@name&gt;Easing&lt;/@name&gt;.&lt;@name&gt;InOutQuad&lt;/@name&gt;; &lt;@name&gt;duration&lt;/@name&gt;: &lt;@number&gt;200&lt;/@number&gt; }
+ }
+}</code>
+ </description>
+ </page>
+ </document>
+</WebXML>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/webxml/test-componentset-tabwidget-qml.webxml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/webxml/test-componentset-tabwidget-qml.webxml
new file mode 100644
index 000000000..6ee3ce5dc
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/webxml/test-componentset-tabwidget-qml.webxml
@@ -0,0 +1,154 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<WebXML>
+ <document>
+ <page name="componentset/TabWidget.qml" href="test-componentset-tabwidget-qml.html" title="TabWidget.qml Example File" fulltitle="TabWidget.qml Example File" subtitle="componentset/TabWidget.qml">
+ <description>
+ <code>&lt;@comment&gt;// Copyright (C) 2016 The Qt Company Ltd.&lt;/@comment&gt;
+&lt;@comment&gt;// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause&lt;/@comment&gt;
+
+import QtQuick 1.0
+
+&lt;@comment&gt;/*!
+ \qmltype TabWidget
+ \inqmlmodule UIComponents
+ \brief A widget that places its children as tabs.
+
+ A TabWidget places its children as tabs in a view. Selecting
+ a tab involves selecting the tab at the top.
+
+ The TabWidget component is part of the \l {UI Components} module.
+
+ This documentation is part of the \l{componentset}{UIComponents} example.
+
+ \section1 Adding Tabs
+
+ To add a tab, declare the tab as a child of the TabWidget.
+
+ \code
+ TabWidget {
+ id: tabwidget
+
+ Rectangle {
+ id: tab1
+ color: &amp;quot;red&amp;quot;
+ //... omitted
+ }
+ Rectangle {
+ id: tab2
+ color: &amp;quot;blue&amp;quot;
+ //... omitted
+ }
+
+ }
+ \endcode
+
+*/&lt;/@comment&gt;
+&lt;@type&gt;Item&lt;/@type&gt; {
+ &lt;@name&gt;id&lt;/@name&gt;: &lt;@name&gt;tabWidget&lt;/@name&gt;
+
+ &lt;@comment&gt;/*!
+ \internal
+
+ Setting the default property to stack.children means any child items
+ of the TabWidget are actually added to the 'stack' item's children.
+
+ See the \l{&amp;quot;Property Binding in QML&amp;quot;}
+ documentation for details on default properties.
+
+ This is an implementation detail, not meant for public knowledge. Putting
+ the \internal command at the beginning will cause QDoc to not publish this
+ documentation in the public API page.
+
+ Normally, a property alias needs to have a
+ &amp;quot;\qmlproperty &amp;lt;type&amp;gt; &amp;lt;propertyname&amp;gt;&amp;quot; to assign the alias a type.
+
+ */&lt;/@comment&gt;
+ default property &lt;@type&gt;alias&lt;/@type&gt; &lt;@name&gt;content&lt;/@name&gt;: &lt;@name&gt;stack&lt;/@name&gt;.&lt;@name&gt;children&lt;/@name&gt;
+
+ &lt;@comment&gt;/*!
+ The currently active tab in the TabWidget.
+ */&lt;/@comment&gt;
+ property &lt;@type&gt;int&lt;/@type&gt; &lt;@name&gt;current&lt;/@name&gt;: &lt;@number&gt;0&lt;/@number&gt;
+
+ &lt;@comment&gt;/*!
+ A sample \c{read-only} property.
+ A contrived property to demonstrate QDoc's ability to detect
+ read-only properties.
+
+ The signature is:
+ \code
+ readonly property int sampleReadOnlyProperty: 0
+ \endcode
+
+ Note that the property must be initialized to a value.
+
+ */&lt;/@comment&gt;
+ readonly property &lt;@type&gt;int&lt;/@type&gt; &lt;@name&gt;sampleReadOnlyProperty&lt;/@name&gt;: &lt;@number&gt;0&lt;/@number&gt;
+
+ &lt;@comment&gt;/*!
+ \internal
+
+ This handler is an implementation
+ detail. The \c{\internal} command will prevent QDoc from publishing this
+ documentation on the public API.
+ */&lt;/@comment&gt;
+ &lt;@name&gt;onCurrentChanged&lt;/@name&gt;: &lt;@name&gt;setOpacities&lt;/@name&gt;()
+ &lt;@name&gt;Component&lt;/@name&gt;.onCompleted: &lt;@name&gt;setOpacities&lt;/@name&gt;()
+
+ &lt;@comment&gt;/*!
+ \internal
+
+ An internal function to set the opacity.
+ The \internal command will prevent QDoc from publishing this
+ documentation on the public API.
+ */&lt;/@comment&gt;
+ &lt;@keyword&gt;function &lt;/@keyword&gt;&lt;@name&gt;setOpacities&lt;/@name&gt;() {
+ &lt;@keyword&gt;for&lt;/@keyword&gt; (var i = 0; &lt;@name&gt;i&lt;/@name&gt; &lt;@op&gt;&amp;lt;&lt;/@op&gt; &lt;@name&gt;stack&lt;/@name&gt;.&lt;@name&gt;children&lt;/@name&gt;.&lt;@name&gt;length&lt;/@name&gt;; ++&lt;@name&gt;i&lt;/@name&gt;) {
+ &lt;@name&gt;stack&lt;/@name&gt;.&lt;@name&gt;children&lt;/@name&gt;[&lt;@name&gt;i&lt;/@name&gt;].&lt;@name&gt;opacity&lt;/@name&gt; &lt;@op&gt;=&lt;/@op&gt; (&lt;@name&gt;i&lt;/@name&gt; &lt;@op&gt;==&lt;/@op&gt; &lt;@name&gt;current&lt;/@name&gt; ? &lt;@number&gt;1&lt;/@number&gt; : &lt;@number&gt;0&lt;/@number&gt;)
+ }
+ }
+
+ &lt;@type&gt;Row&lt;/@type&gt; {
+ &lt;@name&gt;id&lt;/@name&gt;: &lt;@name&gt;header&lt;/@name&gt;
+
+ &lt;@type&gt;Repeater&lt;/@type&gt; {
+ &lt;@name&gt;model&lt;/@name&gt;: &lt;@name&gt;stack&lt;/@name&gt;.&lt;@name&gt;children&lt;/@name&gt;.&lt;@name&gt;length&lt;/@name&gt;
+ &lt;@name&gt;delegate&lt;/@name&gt;: &lt;@name&gt;Rectangle&lt;/@name&gt; {
+ &lt;@name&gt;width&lt;/@name&gt;: &lt;@name&gt;tabWidget&lt;/@name&gt;.&lt;@name&gt;width&lt;/@name&gt; &lt;@op&gt;/&lt;/@op&gt; &lt;@name&gt;stack&lt;/@name&gt;.&lt;@name&gt;children&lt;/@name&gt;.&lt;@name&gt;length&lt;/@name&gt;; &lt;@name&gt;height&lt;/@name&gt;: &lt;@number&gt;36&lt;/@number&gt;
+
+ &lt;@type&gt;Rectangle&lt;/@type&gt; {
+ &lt;@name&gt;width&lt;/@name&gt;: &lt;@name&gt;parent&lt;/@name&gt;.&lt;@name&gt;width&lt;/@name&gt;; &lt;@name&gt;height&lt;/@name&gt;: &lt;@number&gt;1&lt;/@number&gt;
+ &lt;@type&gt;anchors&lt;/@type&gt; { &lt;@name&gt;bottom&lt;/@name&gt;: &lt;@name&gt;parent&lt;/@name&gt;.&lt;@name&gt;bottom&lt;/@name&gt;; &lt;@name&gt;bottomMargin&lt;/@name&gt;: &lt;@number&gt;1&lt;/@number&gt; }
+ &lt;@name&gt;color&lt;/@name&gt;: &lt;@string&gt;&amp;quot;#acb2c2&amp;quot;&lt;/@string&gt;
+ }
+ &lt;@type&gt;BorderImage&lt;/@type&gt; {
+ &lt;@type&gt;anchors&lt;/@type&gt; { &lt;@name&gt;fill&lt;/@name&gt;: &lt;@name&gt;parent&lt;/@name&gt;; &lt;@name&gt;leftMargin&lt;/@name&gt;: &lt;@number&gt;2&lt;/@number&gt;; &lt;@name&gt;topMargin&lt;/@name&gt;: &lt;@number&gt;5&lt;/@number&gt;; &lt;@name&gt;rightMargin&lt;/@name&gt;: &lt;@number&gt;1&lt;/@number&gt; }
+ &lt;@type&gt;border&lt;/@type&gt; { &lt;@name&gt;left&lt;/@name&gt;: &lt;@number&gt;7&lt;/@number&gt;; &lt;@name&gt;right&lt;/@name&gt;: &lt;@number&gt;7&lt;/@number&gt; }
+ &lt;@name&gt;source&lt;/@name&gt;: &lt;@string&gt;&amp;quot;tab.png&amp;quot;&lt;/@string&gt;
+ &lt;@name&gt;visible&lt;/@name&gt;: &lt;@name&gt;tabWidget&lt;/@name&gt;.&lt;@name&gt;current&lt;/@name&gt; &lt;@op&gt;==&lt;/@op&gt; &lt;@name&gt;index&lt;/@name&gt;
+ }
+ &lt;@type&gt;Text&lt;/@type&gt; {
+ &lt;@name&gt;horizontalAlignment&lt;/@name&gt;: &lt;@name&gt;Qt&lt;/@name&gt;.&lt;@name&gt;AlignHCenter&lt;/@name&gt;; &lt;@name&gt;verticalAlignment&lt;/@name&gt;: &lt;@name&gt;Qt&lt;/@name&gt;.&lt;@name&gt;AlignVCenter&lt;/@name&gt;
+ &lt;@name&gt;anchors&lt;/@name&gt;.fill: &lt;@name&gt;parent&lt;/@name&gt;
+ &lt;@name&gt;text&lt;/@name&gt;: &lt;@name&gt;stack&lt;/@name&gt;.&lt;@name&gt;children&lt;/@name&gt;[&lt;@name&gt;index&lt;/@name&gt;].&lt;@name&gt;title&lt;/@name&gt;
+ &lt;@name&gt;elide&lt;/@name&gt;: &lt;@name&gt;Text&lt;/@name&gt;.&lt;@name&gt;ElideRight&lt;/@name&gt;
+ &lt;@name&gt;font&lt;/@name&gt;.bold: &lt;@name&gt;tabWidget&lt;/@name&gt;.&lt;@name&gt;current&lt;/@name&gt; &lt;@op&gt;==&lt;/@op&gt; &lt;@name&gt;index&lt;/@name&gt;
+ }
+ &lt;@type&gt;MouseArea&lt;/@type&gt; {
+ &lt;@name&gt;anchors&lt;/@name&gt;.fill: &lt;@name&gt;parent&lt;/@name&gt;
+ &lt;@name&gt;onClicked&lt;/@name&gt;: &lt;@name&gt;tabWidget&lt;/@name&gt;.&lt;@name&gt;current&lt;/@name&gt; &lt;@op&gt;=&lt;/@op&gt; &lt;@name&gt;index&lt;/@name&gt;
+ }
+ }
+ }
+ }
+
+ &lt;@type&gt;Item&lt;/@type&gt; {
+ &lt;@name&gt;id&lt;/@name&gt;: &lt;@name&gt;stack&lt;/@name&gt;
+ &lt;@name&gt;width&lt;/@name&gt;: &lt;@name&gt;tabWidget&lt;/@name&gt;.&lt;@name&gt;width&lt;/@name&gt;
+ &lt;@name&gt;anchors&lt;/@name&gt;.top: &lt;@name&gt;header&lt;/@name&gt;.&lt;@name&gt;bottom&lt;/@name&gt;; &lt;@name&gt;anchors&lt;/@name&gt;.bottom: &lt;@name&gt;tabWidget&lt;/@name&gt;.&lt;@name&gt;bottom&lt;/@name&gt;
+ }
+}</code>
+ </description>
+ </page>
+ </document>
+</WebXML>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/webxml/test-demos-demo-demo-cpp.webxml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/webxml/test-demos-demo-demo-cpp.webxml
new file mode 100644
index 000000000..fd0268d30
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/webxml/test-demos-demo-demo-cpp.webxml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<WebXML>
+ <document>
+ <page name="demos/demo/demo.cpp" href="test-demos-demo-demo-cpp.html" title="demo.cpp Example File" fulltitle="demo.cpp Example File" subtitle="demos/demo/demo.cpp">
+ <description>
+ <code>&lt;@comment&gt;// Copyright (C) 2023 The Qt Company Ltd.&lt;/@comment&gt;
+&lt;@comment&gt;// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0&lt;/@comment&gt;
+
+&lt;@type&gt;bool&lt;/@type&gt; isOverThousand(&lt;@type&gt;int&lt;/@type&gt; n)
+{
+ &lt;@keyword&gt;if&lt;/@keyword&gt; (n &lt;@op&gt;&amp;gt;&lt;/@op&gt; &lt;@number&gt;1'000&lt;/@number&gt;)
+ &lt;@keyword&gt;return&lt;/@keyword&gt; &lt;@keyword&gt;true&lt;/@keyword&gt;;
+ &lt;@keyword&gt;return&lt;/@keyword&gt; &lt;@keyword&gt;false&lt;/@keyword&gt;;
+}</code>
+ </description>
+ </page>
+ </document>
+</WebXML>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/webxml/test-demos-demo-demo-pro.webxml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/webxml/test-demos-demo-demo-pro.webxml
new file mode 100644
index 000000000..45f8457e9
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/webxml/test-demos-demo-demo-pro.webxml
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<WebXML>
+ <document>
+ <page name="demos/demo/demo.pro" href="test-demos-demo-demo-pro.html" title="demo.pro Example File" fulltitle="demo.pro Example File" subtitle="demos/demo/demo.pro">
+ <description>
+ <code>TEMPLATE = aux
+message(&amp;quot;Nothing to see here.&amp;quot;)</code>
+ </description>
+ </page>
+ </document>
+</WebXML>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/webxml/test-demos-demo-dontxclude-cmakelists-txt.webxml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/webxml/test-demos-demo-dontxclude-cmakelists-txt.webxml
new file mode 100644
index 000000000..effb3cfb9
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/webxml/test-demos-demo-dontxclude-cmakelists-txt.webxml
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<WebXML>
+ <document>
+ <page name="demos/demo/dontxclude/CMakeLists.txt" href="test-demos-demo-dontxclude-cmakelists-txt.html" title="CMakeLists.txt Example File" fulltitle="CMakeLists.txt Example File" subtitle="demos/demo/dontxclude/CMakeLists.txt">
+ <description>
+ <code>cmake_minimum_required(VERSION 3.16)
+project (DONTXCLUDEDIRS_QDOCTEST)</code>
+ </description>
+ </page>
+ </document>
+</WebXML>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/webxml/test-demos-demo-example.webxml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/webxml/test-demos-demo-example.webxml
new file mode 100644
index 000000000..71472790d
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/webxml/test-demos-demo-example.webxml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<WebXML>
+ <document>
+ <page name="demos/demo" href="test-demos-demo-example.html" status="active" location="demo.qdoc" documented="true" subtype="example" title="Demo" fulltitle="Demo" subtitle="">
+ <description>
+ <image href="images/leonardo-da-vinci.png"/>
+ <code> if (n &gt; 1'000)
+ return true;</code>
+ <para>Files:</para>
+ <list type="bullet">
+ <item>
+ <para>
+ <link raw="demos/demo/demo.cpp" href="test-demos-demo-demo-cpp.html" type="page" page="demo.cpp Example File">demos/demo/demo.cpp</link>
+ </para>
+ </item>
+ <item>
+ <para>
+ <link raw="demos/demo/demo.pro" href="test-demos-demo-demo-pro.html" type="page" page="demo.pro Example File">demos/demo/demo.pro</link>
+ </para>
+ </item>
+ <item>
+ <para>
+ <link raw="demos/demo/dontxclude/CMakeLists.txt" href="test-demos-demo-dontxclude-cmakelists-txt.html" type="page" page="CMakeLists.txt Example File">demos/demo/dontxclude/CMakeLists.txt</link>
+ </para>
+ </item>
+ </list>
+ </description>
+ </page>
+ </document>
+</WebXML>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/webxml/test-demos-hidden-example.webxml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/webxml/test-demos-hidden-example.webxml
new file mode 100644
index 000000000..4f87887ed
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/webxml/test-demos-hidden-example.webxml
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<WebXML>
+ <document>
+ <page name="demos/hidden" href="test-demos-hidden-example.html" status="active" location="hidden.qdoc" documented="true" subtype="example" title="Hidden Demo" fulltitle="Hidden Demo" subtitle="" brief="Tagged 'broken', does not appear in examples-manifest.xml">
+ <description>
+ <brief>Tagged 'broken', does not appear in examples-manifest.xml.</brief>
+ <para>Also missing an image, but that's OK as it's broken anyway.</para>
+ </description>
+ </page>
+ </document>
+</WebXML>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/webxml/test-empty-qmlmodule.webxml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/webxml/test-empty-qmlmodule.webxml
new file mode 100644
index 000000000..5d24b3077
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/webxml/test-empty-qmlmodule.webxml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<WebXML>
+ <document/>
+</WebXML>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/webxml/test-nover-qmlmodule.webxml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/webxml/test-nover-qmlmodule.webxml
new file mode 100644
index 000000000..5d24b3077
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/webxml/test-nover-qmlmodule.webxml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<WebXML>
+ <document/>
+</WebXML>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/webxml/test.index b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/webxml/test.index
new file mode 100644
index 000000000..8f26aa523
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/webxml/test.index
@@ -0,0 +1,226 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE QDOCINDEX>
+<INDEX url="" title="A test project for QDoc build artifacts" version="6.2.11" project="Test">
+ <namespace name="" status="active" access="public" module="test">
+ <function name="QDOCTEST_MACRO" href="testqdoc.html#QDOCTEST_MACRO" status="active" access="public" documented="true" related="0" since="Test 0.9" meta="macrowithoutparams" signature="QDOCTEST_MACRO"/>
+ <function name="QDOCTEST_MACRO2" href="testqdoc-test.html#QDOCTEST_MACRO2" status="active" access="public" documented="true" related="1" since="Test 1.1" meta="macrowithparams" brief="A macro with argument x" signature="QDOCTEST_MACRO2(int &amp;x)" groups="testgroup">
+ <parameter type="int &amp;" name="x" default=""/>
+ </function>
+ <qmlclass name="AbstractParent" qml-module-name="QDoc.Test" fullname="QDoc.Test.AbstractParent" href="qml-qdoc-test-abstractparent.html" status="active" access="public" abstract="true" location="parent.qdoc" since="1.1" documented="true" title="AbstractParent" fulltitle="AbstractParent" subtitle="" brief="Abstract base QML type">
+ <function name="name" fullname="QDoc.Test.AbstractParent.name" href="qml-qdoc-test-abstractparent.html#name-method" status="active" access="public" location="parent.qdoc" documented="true" meta="qmlmethod" type="void"/>
+ <function name="name" fullname="QDoc.Test.AbstractParent.name" href="qml-qdoc-test-abstractparent.html#name-method" status="active" access="public" location="parent.qdoc" documented="true" meta="qmlmethod" type="void">
+ <parameter type="Child" name="child" default=""/>
+ <parameter type="" name="name" default=""/>
+ </function>
+ <function name="rear" fullname="QDoc.Test.AbstractParent.rear" href="qml-qdoc-test-abstractparent.html#rear-method" status="active" access="public" location="parent.qdoc" documented="true" meta="qmlmethod" type="void">
+ <parameter type="Child" name="child" default=""/>
+ <parameter type="var" name="method" default="Strict"/>
+ </function>
+ <qmlproperty name="children" fullname="QDoc.Test.AbstractParent.children" status="active" access="public" location="parent.qdoc" documented="true" type="list&lt;Child&gt;" attached="false" writable="true" brief="Children of the type"/>
+ <qmlproperty name="name" fullname="QDoc.Test.AbstractParent.name" status="active" access="public" location="parent.qdoc" documented="true" type="string" attached="false" writable="true" brief="Name of this parent"/>
+ </qmlclass>
+ <page name="autolinking.html" href="autolinking.html" status="active" location="classlists.qdoc" documented="true" subtype="page" title="Autolinking" fulltitle="Autolinking" subtitle="">
+ <contents name="testqdoc" title="TestQDoc" level="1"/>
+ <contents name="someprop" title="someProp" level="1"/>
+ </page>
+ <page name="cmaketest" href="test-cmaketest-example.html" status="active" location="cmaketest.qdoc" documented="true" subtype="example" title="CMake Example Project" fulltitle="CMake Example Project" subtitle=""/>
+ <qmlclass name="Child" qml-module-name="QDoc.Test" qml-base-type="QDoc.Test::AbstractParent" fullname="QDoc.Test.Child" href="qml-qdoc-test-child.html" status="active" access="public" location="parent.qdoc" since="1.1" documented="true" title="Child" fulltitle="Child" subtitle="" brief="A Child inheriting its parent">
+ <function name="name" fullname="QDoc.Test.Child.name" href="qml-qdoc-test-child.html#name-method" status="active" access="public" location="parent.qdoc" documented="true" meta="qmlmethod" type="void">
+ <parameter type="Child" name="child" default=""/>
+ <parameter type="" name="name" default=""/>
+ </function>
+ <qmlproperty name="name" fullname="QDoc.Test.Child.name" href="qml-qdoc-test-child.html#name-prop" status="active" access="public" location="parent.qdoc" documented="true" type="string" attached="false" writable="true" brief="Name of this child"/>
+ </qmlclass>
+ <page name="classes.html" href="classes.html" status="active" location="unseenclass.qdoc" documented="true" subtype="page" title="Classes" fulltitle="Classes" subtitle=""/>
+ <namespace name="CrossModuleRef" href="crossmoduleref.html" status="active" access="public" location="testcpp.h" since="3.0" documented="true" module="TestCPP" brief="Namespace that has documented functions in multiple modules">
+ <function name="documentMe" fullname="CrossModuleRef::documentMe" href="crossmoduleref.html#documentMe" status="active" access="public" location="testcpp.h" documented="true" meta="plain" type="void" signature="void documentMe()"/>
+ </namespace>
+ <page name="demos/demo" href="test-demos-demo-example.html" status="active" location="demo.qdoc" documented="true" subtype="example" title="Demo" fulltitle="Demo" subtitle=""/>
+ <qmlclass name="DocTest" qml-module-name="QDoc.Test" fullname="QDoc.Test.DocTest" href="qml-qdoc-test-doctest.html" status="active" access="public" location="DocTest.qml" since="QDoc.Test 0.9" documented="true" title="DocTest" fulltitle="DocTest" subtitle="" brief="Represents a doc test case">
+ <contents name="introduction" title="Introduction" level="1"/>
+ <function name="completed" fullname="QDoc.Test.DocTest.completed" href="qml-qdoc-test-doctest.html#completed-signal" status="active" access="public" documented="true" meta="qmlsignal"/>
+ <function name="fail" fullname="QDoc.Test.DocTest.fail" href="qml-qdoc-test-doctest.html#fail-method" status="active" access="public" location="DocTest.qml" documented="true" since="QDoc.Test 1.0" meta="qmlmethod">
+ <parameter type="" name="message" default="&quot;oops&quot;"/>
+ </function>
+ <function name="fail_hard" fullname="QDoc.Test.DocTest.fail_hard" href="qml-qdoc-test-doctest.html#fail_hard-method" status="active" access="public" documented="true" meta="qmlmethod">
+ <parameter type="" name="msg" default="&quot;facepalm&quot;"/>
+ <parameter type="" name="option" default="123"/>
+ </function>
+ <function name="foo" fullname="QDoc.Test.DocTest.foo" href="qml-qdoc-test-doctest.html#foo-signal" status="active" access="public" location="DocTest.qml" documented="true" meta="qmlsignal">
+ <parameter type="var" name="bar" default=""/>
+ </function>
+ <function name="itsHappening" fullname="QDoc.Test.DocTest.itsHappening" href="qml-qdoc-test-doctest.html#itsHappening-signal" status="active" access="public" documented="true" meta="qmlsignal">
+ <parameter type="bool" name="really" default=""/>
+ </function>
+ <qmlproperty name="active" fullname="QDoc.Test.DocTest.active" href="qml-qdoc-test-doctest.html#active-prop" status="active" access="public" documented="true" type="bool" attached="false" writable="true"/>
+ <qmlproperty name="name" fullname="QDoc.Test.DocTest.name" href="qml-qdoc-test-doctest.html#name-prop" status="active" access="public" documented="true" type="string" attached="false" writable="true" required="true"/>
+ </qmlclass>
+ <qmlclass name="DocTest" qml-module-name="Test.NoVer" fullname="Test.NoVer.DocTest" href="qml-test-nover-doctest.html" status="active" access="public" location="DocTest.qml" since="1.1" documented="true" title="DocTest" fulltitle="DocTest" subtitle="" brief="Shadows the type name in QDoc.Test module"/>
+ <class name="DontLinkToMe" href="dontlinktome.html" status="ignored" access="public" location="testcpp.h" since="2.0" documented="true" module="TestCPP" brief="Class that does not generate documentation"/>
+ <page name="demos/hidden" href="test-demos-hidden-example.html" status="active" location="hidden.qdoc" documented="true" subtype="example" title="Hidden Demo" fulltitle="Hidden Demo" subtitle="" brief="Tagged 'broken', does not appear in examples-manifest.xml"/>
+ <page name="obsolete-classes.html" href="obsolete-classes.html" status="active" location="classlists.qdoc" documented="true" subtype="page" title="Obsolete Classes" fulltitle="Obsolete Classes" subtitle="">
+ <contents name="classes-with-obsolete-members" title="Classes with obsolete members" level="1"/>
+ <contents name="testqdoc" title="TestQDoc" level="2"/>
+ </page>
+ <qmlclass name="OldType" qml-module-name="QDoc.Test" fullname="QDoc.Test.OldType" href="qml-qdoc-test-oldtype.html" status="deprecated" access="public" since="1.1" documented="true" title="OldType" fulltitle="OldType" subtitle="" brief="Deprecated old type"/>
+ <qmlclass name="ProgressBar" qml-module-name="UIComponents" fullname="UIComponents.ProgressBar" href="qml-uicomponents-progressbar.html" status="active" access="public" location="ProgressBar.qml" documented="true" title="ProgressBar" fulltitle="ProgressBar" subtitle="" brief="A component that shows the progress of an event">
+ <qmlproperty name="color" fullname="UIComponents.ProgressBar.color" href="qml-uicomponents-progressbar.html#color-prop" status="active" access="public" documented="true" type="color" attached="false" writable="true"/>
+ <qmlproperty name="maximum" fullname="UIComponents.ProgressBar.maximum" href="qml-uicomponents-progressbar.html#maximum-prop" status="active" access="public" documented="true" type="int" attached="false" writable="true"/>
+ <qmlproperty name="minimum" fullname="UIComponents.ProgressBar.minimum" href="qml-uicomponents-progressbar.html#minimum-prop" status="active" access="public" documented="true" type="int" attached="false" writable="true"/>
+ <qmlproperty name="secondColor" fullname="UIComponents.ProgressBar.secondColor" href="qml-uicomponents-progressbar.html#secondColor-prop" status="active" access="public" documented="true" type="color" attached="false" writable="true"/>
+ <qmlproperty name="value" fullname="UIComponents.ProgressBar.value" href="qml-uicomponents-progressbar.html#value-prop" status="active" access="public" documented="true" type="int" attached="false" writable="true"/>
+ </qmlclass>
+ <page name="componentset" href="test-componentset-example.html" status="active" location="examples.qdoc" documented="true" subtype="example" title="QML Documentation Example" fulltitle="QML Documentation Example" subtitle="" brief="Example for documenting QML types">
+ <contents name="qml-class" title="QML Class" level="1"/>
+ <contents name="properties-signals-handlers-and-methods" title="Properties, Signals, Handlers, and Methods" level="1"/>
+ <contents name="internal-documentation" title="Internal Documentation" level="2"/>
+ <contents name="qml-types-with-c-implementation" title="QML Types with C++ Implementation" level="1"/>
+ </page>
+ <page name="qmlmodules.html" href="qmlmodules.html" status="active" location="modules.qdoc" documented="true" subtype="page" title="QML Modules" fulltitle="QML Modules" subtitle="">
+ <contents name="qml-types" title="QML types" level="1"/>
+ <contents name="qml-value-types" title="QML value types" level="1"/>
+ </page>
+ <page name="https://wiki.qt.io/QProperty" href="https://wiki.qt.io/QProperty" status="active" location="properties.qdoc" documented="true" subtype="externalpage" title="QProperty" fulltitle="QProperty" subtitle=""/>
+ <class name="SeenClass" href="seenclass.html" status="active" access="public" location="dont.h" since="2.0" documented="true" module="TestCPP" brief="A public but undocumented class"/>
+ <qmlclass name="Switch" qml-module-name="UIComponents" fullname="UIComponents.Switch" href="qml-uicomponents-switch.html" status="active" access="public" location="Switch.qml" documented="true" title="Switch" fulltitle="Switch" subtitle="" brief="A component that can be turned on or off">
+ <function name="toggle" fullname="UIComponents.Switch.toggle" href="qml-uicomponents-switch.html#toggle-method" status="active" access="public" documented="true" meta="qmlmethod"/>
+ <qmlproperty name="on" fullname="UIComponents.Switch.on" href="qml-uicomponents-switch.html#on-prop" status="active" access="public" documented="true" type="bool" attached="false" writable="true"/>
+ </qmlclass>
+ <qmlclass name="TabWidget" qml-module-name="UIComponents" fullname="UIComponents.TabWidget" href="qml-uicomponents-tabwidget.html" status="active" access="public" location="TabWidget.qml" documented="true" title="TabWidget" fulltitle="TabWidget" subtitle="" brief="A widget that places its children as tabs">
+ <contents name="adding-tabs" title="Adding Tabs" level="1"/>
+ <qmlproperty name="current" fullname="UIComponents.TabWidget.current" href="qml-uicomponents-tabwidget.html#current-prop" status="active" access="public" documented="true" type="int" attached="false" writable="true"/>
+ <qmlproperty name="sampleReadOnlyProperty" fullname="UIComponents.TabWidget.sampleReadOnlyProperty" href="qml-uicomponents-tabwidget.html#sampleReadOnlyProperty-prop" status="active" access="public" documented="true" type="int" attached="false" writable="false"/>
+ </qmlclass>
+ <namespace name="TestQDoc" href="testqdoc.html" status="active" access="public" location="testcpp.h" since="2.0" documented="true" module="TestCPP" brief="A namespace">
+ <contents name="usage" title="Usage" level="1"/>
+ <function name="QDOCTEST_MACRO" href="testqdoc.html#QDOCTEST_MACRO" status="active" access="public" documented="true" related="0" since="Test 0.9" meta="macrowithoutparams" signature="QDOCTEST_MACRO"/>
+ <class threadsafety="reentrant" name="Test" fullname="TestQDoc::Test" href="testqdoc-test.html" status="active" access="public" location="testcpp.h" since="1.1" documented="true" groups="cpptypes,testgroup" module="TestCPP" brief="A class in a namespace">
+ <function name="QDOCTEST_MACRO2" href="testqdoc-test.html#QDOCTEST_MACRO2" status="active" access="public" documented="true" related="1" since="Test 1.1" meta="macrowithparams" brief="A macro with argument x" signature="QDOCTEST_MACRO2(int &amp;x)" groups="testgroup">
+ <parameter type="int &amp;" name="x" default=""/>
+ </function>
+ <function name="Test" fullname="TestQDoc::Test::Test" href="testqdoc-test.html#Test" status="active" access="public" location="testcpp.h" documented="true" meta="constructor" signature="Test()"/>
+ <function name="anotherObsoleteMember" fullname="TestQDoc::Test::anotherObsoleteMember" href="testqdoc-test-obsolete.html#anotherObsoleteMember" status="deprecated" access="public" location="testcpp.h" documented="true" meta="plain" type="void" signature="void anotherObsoleteMember()"/>
+ <function name="deprecatedMember" fullname="TestQDoc::Test::deprecatedMember" href="testqdoc-test-obsolete.html#deprecatedMember" status="deprecated" access="public" location="testcpp.h" documented="true" meta="plain" type="void" signature="void deprecatedMember()"/>
+ <function name="funcPtr" fullname="TestQDoc::Test::funcPtr" href="testqdoc-test.html#funcPtr" status="active" access="public" location="testcpp.h" documented="true" meta="plain" type="void (*)(bool)" signature="void (*)(bool) funcPtr(bool b, const char *s)">
+ <parameter type="bool" name="b" default=""/>
+ <parameter type="const char *" name="s" default=""/>
+ </function>
+ <function name="inlineFunction" fullname="TestQDoc::Test::inlineFunction" href="testqdoc-test.html#inlineFunction" status="active" access="public" location="testcpp.h" documented="true" meta="plain" type="void" brief="An inline function, documented using the \fn QDoc command" signature="void inlineFunction()"/>
+ <function name="methodWithEmDashInItsDocs" fullname="TestQDoc::Test::methodWithEmDashInItsDocs" href="testqdoc-test.html#methodWithEmDashInItsDocs" status="active" access="public" location="testcpp.h" documented="true" meta="plain" type="void" signature="void methodWithEmDashInItsDocs()"/>
+ <function name="methodWithEnDashInItsDocs" fullname="TestQDoc::Test::methodWithEnDashInItsDocs" href="testqdoc-test.html#methodWithEnDashInItsDocs" status="active" access="public" location="testcpp.h" documented="true" meta="plain" type="void" signature="void methodWithEnDashInItsDocs()"/>
+ <function name="obsoleteMember" fullname="TestQDoc::Test::obsoleteMember" href="testqdoc-test-obsolete.html#obsoleteMember" status="deprecated" access="public" location="testcpp.h" documented="true" meta="plain" type="void" signature="void obsoleteMember()"/>
+ <function name="operator++" fullname="TestQDoc::Test::operator++" href="testqdoc-test-obsolete.html#operator-2b-2b" status="deprecated" access="public" location="testcpp.h" documented="true" meta="plain" type="TestQDoc::Test &amp;" signature="TestQDoc::Test &amp; operator++()"/>
+ <function name="operator--" fullname="TestQDoc::Test::operator--" href="testqdoc-test-obsolete.html#operator--" status="deprecated" access="public" location="testcpp.h" documented="true" meta="plain" type="TestQDoc::Test &amp;" signature="TestQDoc::Test &amp; operator--()"/>
+ <function name="operator=" fullname="TestQDoc::Test::operator=" href="testqdoc-test.html#operator-eq" status="active" access="public" location="testcpp.h" documented="true" meta="move-assign" type="TestQDoc::Test &amp;" signature="TestQDoc::Test &amp; operator=(TestQDoc::Test &amp;&amp;other)" groups="testgroup">
+ <parameter type="TestQDoc::Test &amp;&amp;" name="other" default=""/>
+ </function>
+ <function name="operator==" href="testqdoc-test.html#operator-eq-eq" status="active" access="public" location="testcpp.h" documented="true" related="2" meta="plain" type="bool" signature="bool operator==(const TestQDoc::Test &amp;lhs, const TestQDoc::Test &amp;rhs)">
+ <parameter type="const TestQDoc::Test &amp;" name="lhs" default=""/>
+ <parameter type="const TestQDoc::Test &amp;" name="rhs" default=""/>
+ </function>
+ <function name="overload" fullname="TestQDoc::Test::overload" href="testqdoc-test.html#overload" status="active" access="protected" location="testcpp.h" documented="true" meta="plain" type="void" signature="void overload()"/>
+ <function name="overload" fullname="TestQDoc::Test::overload" href="testqdoc-test.html#overload-1" status="active" access="protected" location="testcpp.h" documented="true" since="Test 1.2" meta="plain" overload="true" overload-number="1" type="void" signature="void overload(bool b)">
+ <parameter type="bool" name="b" default=""/>
+ </function>
+ <function name="someFunction" fullname="TestQDoc::Test::someFunction" href="testqdoc-test.html#someFunction" status="active" access="public" location="testcpp.h" documented="true" since="Test 1.0" meta="plain" type="int" signature="int someFunction(int, int v)">
+ <parameter type="int" name="" default=""/>
+ <parameter type="int" name="v" default="0"/>
+ </function>
+ <function name="someFunctionDefaultArg" fullname="TestQDoc::Test::someFunctionDefaultArg" href="testqdoc-test.html#someFunctionDefaultArg" threadsafety="non-reentrant" status="active" access="public" location="testcpp.h" documented="true" since="2.0" meta="plain" const="true" type="void" signature="void someFunctionDefaultArg(int i, bool b) const" groups="testgroup">
+ <parameter type="int" name="i" default=""/>
+ <parameter type="bool" name="b" default="false"/>
+ </function>
+ <function name="virtualFun" fullname="TestQDoc::Test::virtualFun" href="testqdoc-test.html#virtualFun" status="active" access="public" location="testcpp.h" documented="true" meta="plain" virtual="virtual" type="void" signature="void virtualFun()"/>
+ <typedef name="SomeType" fullname="TestQDoc::Test::SomeType" href="testqdoc-test.html#SomeType-typedef" status="active" access="public" location="testcpp.h" documented="true"/>
+ </class>
+ <class name="TestDerived" fullname="TestQDoc::TestDerived" href="testqdoc-testderived.html" status="active" access="public" location="testcpp.h" since="2.0" documented="true" bases="TestQDoc::Test" module="TestCPP" brief="A class in a namespace, derived from Test">
+ <function name="bindableProp" fullname="TestQDoc::TestDerived::bindableProp" href="testqdoc-testderived.html#bindableProp-prop" status="active" access="public" location="testcpp.h" meta="plain" associated-property="bindableProp" type="QBindable&lt;QString&gt;" signature="QBindable&lt;QString&gt; bindableProp()"/>
+ <function name="bindablePropChanged" fullname="TestQDoc::TestDerived::bindablePropChanged" href="testqdoc-testderived.html#bindableProp-prop" status="active" access="public" location="testcpp.h" meta="signal" associated-property="bindableProp" type="void" signature="void bindablePropChanged()"/>
+ <function name="boolProp" fullname="TestQDoc::TestDerived::boolProp" href="testqdoc-testderived.html#boolProp-prop" status="active" access="public" location="testcpp.h" meta="plain" associated-property="boolProp" type="bool" signature="bool boolProp()"/>
+ <function name="boolPropChanged" fullname="TestQDoc::TestDerived::boolPropChanged" href="testqdoc-testderived.html#boolProp-prop" status="active" access="public" location="testcpp.h" meta="signal" associated-property="boolProp" type="void" signature="void boolPropChanged()"/>
+ <function name="emitSomething" fullname="TestQDoc::TestDerived::emitSomething" href="testqdoc-testderived.html#emitSomething" status="active" access="public" location="testcpp.h" documented="true" meta="signal" type="void" signature="void emitSomething()"/>
+ <function name="getInt" fullname="TestQDoc::TestDerived::getInt" href="testqdoc-testderived.html#intProp-prop" status="active" access="public" location="testcpp.h" meta="plain" associated-property="intProp" type="int *" signature="int * getInt()"/>
+ <function name="id" fullname="TestQDoc::TestDerived::id" href="testqdoc-testderived.html#id" status="active" access="public" location="testcpp.h" documented="true" meta="plain" virtual="virtual" override="true" type="int" signature="int id() override"/>
+ <function name="invokeMe" fullname="TestQDoc::TestDerived::invokeMe" href="testqdoc-testderived.html#invokeMe" status="active" access="public" location="testcpp.h" documented="true" meta="plain" const="true" type="void" brief="Something invokable" signature="void invokeMe() const"/>
+ <function name="name" fullname="TestQDoc::TestDerived::name" href="testqdoc-testderived.html#name-prop" status="active" access="public" location="testcpp.h" meta="plain" const="true" associated-property="name" type="const QString *" signature="const QString * name() const"/>
+ <function name="resetBoolProp" fullname="TestQDoc::TestDerived::resetBoolProp" href="testqdoc-testderived.html#boolProp-prop" status="active" access="public" location="testcpp.h" meta="slot" associated-property="boolProp" type="void" signature="void resetBoolProp()"/>
+ <function name="setBindableProp" fullname="TestQDoc::TestDerived::setBindableProp" href="testqdoc-testderived.html#bindableProp-prop" status="active" access="public" location="testcpp.h" meta="slot" associated-property="bindableProp" type="void" signature="void setBindableProp(const QString &amp;s)">
+ <parameter type="const QString &amp;" name="s" default=""/>
+ </function>
+ <function name="setBoolProp" fullname="TestQDoc::TestDerived::setBoolProp" href="testqdoc-testderived.html#boolProp-prop" status="active" access="public" location="testcpp.h" meta="slot" associated-property="boolProp" type="void" signature="void setBoolProp(bool b)">
+ <parameter type="bool" name="b" default=""/>
+ </function>
+ <function name="someProp" fullname="TestQDoc::TestDerived::someProp" href="testqdoc-testderived.html#someProp-prop" status="active" access="public" location="testcpp.h" meta="plain" associated-property="someProp" type="const QString &amp;" signature="const QString &amp; someProp()"/>
+ <function name="someValue" fullname="TestQDoc::TestDerived::someValue" href="testqdoc-testderived.html#someValue" status="active" access="public" location="testcpp.h" documented="true" meta="plain" type="TestQDoc::TestDerived::NotTypedef" signature="TestQDoc::TestDerived::NotTypedef someValue()"/>
+ <function name="staticObsoleteMember" fullname="TestQDoc::TestDerived::staticObsoleteMember" href="testqdoc-testderived-obsolete.html#staticObsoleteMember" status="deprecated" access="public" location="testcpp.h" documented="true" meta="plain" static="true" type="void" signature="void staticObsoleteMember()"/>
+ <function name="virtualFun" fullname="TestQDoc::TestDerived::virtualFun" href="testqdoc-testderived.html#virtualFun" status="active" access="public" location="testcpp.h" documented="true" meta="plain" virtual="virtual" override="true" type="void" signature="void virtualFun() override"/>
+ <typedef name="DerivedType" fullname="TestQDoc::TestDerived::DerivedType" href="testqdoc-testderived.html#DerivedType-typedef" status="active" access="public" location="testcpp.h" documented="true" aliasedtype="Test::SomeType"/>
+ <typedef name="NotTypedef" fullname="TestQDoc::TestDerived::NotTypedef" href="testqdoc-testderived.html#NotTypedef-typedef" status="active" access="public" location="testcpp.h" documented="true" aliasedtype="int"/>
+ <property name="bindableProp" fullname="TestQDoc::TestDerived::bindableProp" href="testqdoc-testderived.html#bindableProp-prop" status="active" access="public" location="testcpp.h" documented="true" bindable="true">
+ <getter name="bindableProp"/>
+ <setter name="setBindableProp"/>
+ <notifier name="bindablePropChanged"/>
+ </property>
+ <property name="boolProp" fullname="TestQDoc::TestDerived::boolProp" href="testqdoc-testderived.html#boolProp-prop" status="active" access="public" location="testcpp.h" documented="true">
+ <getter name="boolProp"/>
+ <setter name="setBoolProp"/>
+ <resetter name="resetBoolProp"/>
+ <notifier name="boolPropChanged"/>
+ </property>
+ <property name="intProp" fullname="TestQDoc::TestDerived::intProp" href="testqdoc-testderived.html#intProp-prop" status="active" access="public" location="testcpp.h" documented="true" writable="false">
+ <getter name="getInt"/>
+ </property>
+ <property name="name" fullname="TestQDoc::TestDerived::name" href="testqdoc-testderived.html#name-prop" status="active" access="public" location="testcpp.h" documented="true" writable="false" brief="Name">
+ <getter name="name"/>
+ </property>
+ <property name="someProp" fullname="TestQDoc::TestDerived::someProp" href="testqdoc-testderived.html#someProp-prop" status="active" access="public" location="testcpp.h" documented="true" bindable="true" writable="false">
+ <getter name="someProp"/>
+ </property>
+ </class>
+ </namespace>
+ <qmlclass name="TheType" qml-module-name="TheModule" fullname="TheModule.TheType" href="qml-themodule-thetype.html" status="active" access="public" location="properties.qdoc" documented="true" title="TheType" fulltitle="TheType" subtitle="">
+ <qmlproperty name="name" fullname="TheModule.TheType.name" href="qml-themodule-thetype.html#name-prop" status="active" access="public" location="properties.qdoc" documented="true" type="string" attached="false" writable="false" brief="Read-only status of this property is resolved from Q_PROPERTY"/>
+ </qmlclass>
+ <qmlclass name="Type" qml-module-name="QDoc.Test" fullname="QDoc.Test.Type" href="qml-qdoc-test-type.html" status="active" access="public" since="1.1" documented="true" title="Type" fulltitle="Type" subtitle="" brief="A QML type documented in a .cpp file">
+ <function name="completed" fullname="QDoc.Test.Type.completed" href="qml-qdoc-test-type.html#completed-signal" status="active" access="public" documented="true" meta="qmlsignal">
+ <parameter type="int" name="status" default=""/>
+ </function>
+ <function name="configured" fullname="QDoc.Test.Type.configured" href="qml-qdoc-test-type.html#configured-signal" status="active" access="public" documented="true" meta="qmlsignal"/>
+ <function name="copy" fullname="QDoc.Test.Type.copy" href="qml-qdoc-test-type.html#copy-method" status="active" access="public" documented="true" meta="qmlmethod" type="Type">
+ <parameter type="" name="a" default=""/>
+ </function>
+ <function name="deprecatedMethod" fullname="QDoc.Test.Type.deprecatedMethod" href="qml-qdoc-test-type-obsolete.html#deprecatedMethod-method" status="deprecated" access="public" documented="true" meta="qmlmethod"/>
+ <function name="disable" fullname="QDoc.Test.Type.disable" href="qml-qdoc-test-type.html#disable-method" status="active" access="public" documented="true" meta="qmlmethod"/>
+ <function name="enable" fullname="QDoc.Test.Type.enable" href="qml-qdoc-test-type.html#enable-method" status="active" access="public" documented="true" meta="qmlmethod"/>
+ <function name="futureDeprecated" fullname="QDoc.Test.Type.futureDeprecated" href="qml-qdoc-test-type.html#futureDeprecated-method" status="active" access="public" documented="true" meta="qmlmethod"/>
+ <function name="group.created" fullname="QDoc.Test.Type.group.created" href="qml-qdoc-test-type.html#group.created-signal" status="active" access="public" documented="true" meta="qmlsignal"/>
+ <qmlproperty name="fifth" fullname="QDoc.Test.Type.fifth" href="qml-qdoc-test-type.html#fifth-prop" status="active" access="public" documented="true" type="int" attached="false" writable="true" brief="A group of properties sharing a documentation comment"/>
+ <qmlproperty name="fourth" fullname="QDoc.Test.Type.fourth" href="qml-qdoc-test-type.html#fourth-prop" status="active" access="public" documented="true" type="int" attached="false" writable="true" brief="A group of properties sharing a documentation comment"/>
+ <qmlproperty name="group.first" fullname="QDoc.Test.Type.group.first" href="qml-qdoc-test-type.html#group.first-prop" status="active" access="public" documented="true" type="int" attached="false" writable="true" brief="A property group"/>
+ <qmlproperty name="group.second" fullname="QDoc.Test.Type.group.second" href="qml-qdoc-test-type.html#group.second-prop" status="active" access="public" documented="true" type="int" attached="false" writable="true" brief="A property group"/>
+ <qmlproperty name="group.third" fullname="QDoc.Test.Type.group.third" href="qml-qdoc-test-type.html#group.third-prop" status="active" access="public" documented="true" type="int" attached="false" writable="true" brief="A property group"/>
+ <qmlproperty name="id" fullname="QDoc.Test.Type.id" href="qml-qdoc-test-type.html#id-prop" status="active" access="public" documented="true" type="int" attached="false" writable="false" brief="A read-only property"/>
+ <qmlproperty name="name" fullname="QDoc.Test.Type.name" href="qml-qdoc-test-type.html#name-prop" status="active" access="public" documented="true" type="string" attached="false" writable="true" required="true" brief="Name of the Test"/>
+ <qmlproperty name="type" fullname="QDoc.Test.Type.type" href="qml-qdoc-test-type.html#type-attached-prop" status="active" access="public" documented="true" type="enumeration" attached="true" writable="true"/>
+ <qmlproperty name="group" fullname="QDoc.Test.Type.group" href="qml-qdoc-test-type.html#group-prop" status="active" access="public" documented="true"/>
+ </qmlclass>
+ <qmlclass name="TypeNoVersion" qml-module-name="Test.NoVer" fullname="Test.NoVer.TypeNoVersion" href="qml-test-nover-typenoversion.html" status="active" access="public" since="1.1" documented="true" title="TypeNoVersion" fulltitle="TypeNoVersion" subtitle="" brief="Another QML type documented in a .cpp file"/>
+ <class name="UnseenClass" href="unseenclass.html" status="ignored" access="public" location="dont.h" since="2.0" documented="true" module="TestCPP" brief="A public but undocumented class"/>
+ <qmlclass name="YetAnotherChild" qml-module-name="QDoc.Test" qml-base-type="QDoc.Test::InternParent" fullname="QDoc.Test.YetAnotherChild" href="qml-qdoc-test-yetanotherchild.html" status="active" access="public" location="parent.qdoc" since="1.1" documented="true" title="YetAnotherChild" fulltitle="YetAnotherChild" subtitle="" brief="A type inheriting from internal abstract parent"/>
+ <qmlvaluetype name="int" qml-module-name="QDoc.Test" fullname="QDoc.Test.int" href="qml-int.html" status="active" access="public" location="parent.qdoc" since="1.1" documented="true" title="int" fulltitle="int" subtitle="" brief="An integer value type">
+ <function name="abs" fullname="QDoc.Test.int.abs" href="qml-int.html#abs-method" status="active" access="public" location="parent.qdoc" documented="true" meta="qmlmethod" type="int"/>
+ </qmlvaluetype>
+ <page name="https://doc.qt.io/qt/17-qdoc-commands-thread.html#reentrant-command" href="https://doc.qt.io/qt/17-qdoc-commands-thread.html#reentrant-command" status="active" location="classlists.qdoc" documented="true" subtype="externalpage" title="reentrant" fulltitle="reentrant" subtitle=""/>
+ <group name="cpptypes" href="cpptypes.html" status="active" location="classlists.qdoc" documented="true" seen="true" title="Test C++ Types"/>
+ <group name="testgroup" href="testgroup.html" status="internal" seen="false" title=""/>
+ <module name="TestCPP" href="testcpp-module.html" status="active" since="2.0" documented="true" seen="true" title="QDoc Test C++ Classes" brief="A test module page">
+ <contents name="linking-to-function-like-things" title="Linking to function-like things" level="1"/>
+ <contents name="section" title="section()" level="2"/>
+ </module>
+ <qmlmodule name="QDoc.Test" qml-module-name="QDoc.Test" qml-module-version="1.1" href="qdoc-test-qmlmodule.html" status="preliminary" since="1.1" documented="true" seen="true" title="QDoc.Test QML Module" brief="QML Types for the Test module"/>
+ <qmlmodule name="Test.Empty" qml-module-name="Test.Empty" qml-module-version="1.0" href="test-empty-qmlmodule.html" status="active" documented="true" seen="true" title="No QML Types Here" brief="A QML module with no member types"/>
+ <qmlmodule name="Test.NoVer" qml-module-name="Test.NoVer" href="test-nover-qmlmodule.html" status="active" since="1.1" documented="true" seen="true" title="Versionless QML Module" brief="QML Types for the Test module without version"/>
+ <qmlmodule name="TheModule" qml-module-name="TheModule" href="themodule-qmlmodule.html" status="active" location="properties.qdoc" documented="true" seen="true" title=""/>
+ <qmlmodule name="UIComponents" qml-module-name="UIComponents" qml-module-version="1.0" href="uicomponents-qmlmodule.html" status="active" location="examples.qdoc" documented="true" seen="true" title="UI Components" brief="Basic set of UI components"/>
+ </namespace>
+</INDEX>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/webxml/testcpp-module.webxml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/webxml/testcpp-module.webxml
new file mode 100644
index 000000000..5d24b3077
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/webxml/testcpp-module.webxml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<WebXML>
+ <document/>
+</WebXML>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/webxml/testqdoc-test.webxml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/webxml/testqdoc-test.webxml
new file mode 100644
index 000000000..9c9766dc0
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/webxml/testqdoc-test.webxml
@@ -0,0 +1,121 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<WebXML>
+ <document>
+ <class threadsafety="reentrant" name="Test" fullname="TestQDoc::Test" href="testqdoc-test.html" status="active" access="public" location="testcpp.h" since="1.1" documented="true" groups="cpptypes,testgroup" module="TestCPP" brief="A class in a namespace">
+ <description>
+ <brief>A class in a namespace.</brief>
+ </description>
+ <function name="QDOCTEST_MACRO2" href="testqdoc-test.html#QDOCTEST_MACRO2" status="active" access="public" documented="true" related="1" since="Test 1.1" meta="macrowithparams" brief="A macro with argument x" signature="QDOCTEST_MACRO2(int &amp;x)" groups="testgroup">
+ <parameter type="int &amp;" name="x" default=""/>
+ <description>
+ <brief>A macro with argument <argument>x</argument>.</brief>
+ </description>
+ </function>
+ <function name="Test" fullname="TestQDoc::Test::Test" href="testqdoc-test.html#Test" status="active" access="public" location="testcpp.h" documented="true" meta="constructor" signature="Test()">
+ <description>
+ <para>The constructor is deleted.</para>
+ </description>
+ </function>
+ <function name="anotherObsoleteMember" fullname="TestQDoc::Test::anotherObsoleteMember" href="testqdoc-test-obsolete.html#anotherObsoleteMember" status="deprecated" access="public" location="testcpp.h" documented="true" meta="plain" type="void" signature="void anotherObsoleteMember()">
+ <description>
+ <para>Use <link raw="obsoleteMember()" href="testqdoc-test.html#obsoleteMember" type="function">obsoleteMember()</link> instead.</para>
+ </description>
+ </function>
+ <function name="deprecatedMember" fullname="TestQDoc::Test::deprecatedMember" href="testqdoc-test-obsolete.html#deprecatedMember" status="deprecated" access="public" location="testcpp.h" documented="true" meta="plain" type="void" signature="void deprecatedMember()">
+ <description>
+ <para>Use <link raw="someFunction()" href="testqdoc-test.html#someFunction" type="function">someFunction()</link> instead.</para>
+ </description>
+ </function>
+ <function name="funcPtr" fullname="TestQDoc::Test::funcPtr" href="testqdoc-test.html#funcPtr" status="active" access="public" location="testcpp.h" documented="true" meta="plain" type="void (*)(bool)" signature="void (*)(bool) funcPtr(bool b, const char *s)">
+ <parameter type="bool" name="b" default=""/>
+ <parameter type="const char *" name="s" default=""/>
+ <description>
+ <para>Returns a pointer to a function that takes a boolean. Uses <argument>b</argument> and <argument>s</argument>.</para>
+ </description>
+ </function>
+ <function name="inlineFunction" fullname="TestQDoc::Test::inlineFunction" href="testqdoc-test.html#inlineFunction" status="active" access="public" location="testcpp.h" documented="true" meta="plain" type="void" brief="An inline function, documented using the \fn QDoc command" signature="void inlineFunction()">
+ <description>
+ <brief>An inline function, documented using the \fn QDoc command.</brief>
+ </description>
+ </function>
+ <function name="methodWithEmDashInItsDocs" fullname="TestQDoc::Test::methodWithEmDashInItsDocs" href="testqdoc-test.html#methodWithEmDashInItsDocs" status="active" access="public" location="testcpp.h" documented="true" meta="plain" type="void" signature="void methodWithEmDashInItsDocs()">
+ <description>
+ <para>This method has em dashes in its documentation—as you'll find represented by <teletype type="highlighted">---</teletype> in the sources—here and there. The important bit to note is that when passed e.g. to the \c command, the three hyphens are processed as input to the command and not replaced by an em dash.</para>
+ <para>-----------------------------------------------------------------------</para>
+ <para>People can still add a bunch of dashes, though, without QDoc replacing them all with a series of em dashes.</para>
+ <para>—You can also start a new paragraph with an em dash, if you want to.</para>
+ <see-also>
+ <link raw="methodWithEnDashInItsDocs" href="testqdoc-test.html#methodWithEnDashInItsDocs" type="function">methodWithEnDashInItsDocs</link>
+ </see-also>
+ </description>
+ </function>
+ <function name="methodWithEnDashInItsDocs" fullname="TestQDoc::Test::methodWithEnDashInItsDocs" href="testqdoc-test.html#methodWithEnDashInItsDocs" status="active" access="public" location="testcpp.h" documented="true" meta="plain" type="void" signature="void methodWithEnDashInItsDocs()">
+ <description>
+ <para>This method has en dashes in its documentation – as you'll find represented by <teletype type="highlighted">--</teletype> in the sources – here and there. The important bit to note is that when passed e.g. to the \c command, the two hyphens are processed as input to the command and not replaced by an en dash. This also applies to code blocks, where otherwise, the decrement operator would get completely borked:</para>
+ <code>for (int i = 42; i &gt; 0; --i)
+ // Do something cool during countdown.</code>
+ <para>...as it would be silly if this would output –i instead of <teletype type="highlighted">--i</teletype>.</para>
+ <para>-----------------------------------------------------------------------</para>
+ <para>It still allows people to add a bunch of dashes, though, without replacing them all with a series of en dashes. Of course, they might want to use the \hr command instead, like this:</para>
+ <para>– You can also start a new paragraph with an en dash, if you want to.</para>
+ <see-also>methodWithEnDashInItsDocs</see-also>
+ </description>
+ </function>
+ <function name="obsoleteMember" fullname="TestQDoc::Test::obsoleteMember" href="testqdoc-test-obsolete.html#obsoleteMember" status="deprecated" access="public" location="testcpp.h" documented="true" meta="plain" type="void" signature="void obsoleteMember()">
+ <description>
+ <para>Use <link raw="someFunction()" href="testqdoc-test.html#someFunction" type="function">someFunction()</link> instead.</para>
+ </description>
+ </function>
+ <function name="operator++" fullname="TestQDoc::Test::operator++" href="testqdoc-test-obsolete.html#operator-2b-2b" status="deprecated" access="public" location="testcpp.h" documented="true" meta="plain" type="TestQDoc::Test &amp;" signature="TestQDoc::Test &amp; operator++()">
+ <description/>
+ </function>
+ <function name="operator--" fullname="TestQDoc::Test::operator--" href="testqdoc-test-obsolete.html#operator--" status="deprecated" access="public" location="testcpp.h" documented="true" meta="plain" type="TestQDoc::Test &amp;" signature="TestQDoc::Test &amp; operator--()">
+ <description/>
+ </function>
+ <function name="operator=" fullname="TestQDoc::Test::operator=" href="testqdoc-test.html#operator-eq" status="active" access="public" location="testcpp.h" documented="true" meta="move-assign" type="TestQDoc::Test &amp;" signature="TestQDoc::Test &amp; operator=(TestQDoc::Test &amp;&amp;other)" groups="testgroup">
+ <parameter type="TestQDoc::Test &amp;&amp;" name="other" default=""/>
+ <description>
+ <para>The move assignment operator is deleted. <argument>other</argument> cannot be moved from.</para>
+ </description>
+ </function>
+ <function name="operator==" href="testqdoc-test.html#operator-eq-eq" status="active" access="public" location="testcpp.h" documented="true" related="2" meta="plain" type="bool" signature="bool operator==(const TestQDoc::Test &amp;lhs, const TestQDoc::Test &amp;rhs)">
+ <parameter type="const TestQDoc::Test &amp;" name="lhs" default=""/>
+ <parameter type="const TestQDoc::Test &amp;" name="rhs" default=""/>
+ <description>
+ <para>Returns true if <argument>lhs</argument> and <argument>rhs</argument> are equal.</para>
+ </description>
+ </function>
+ <function name="overload" fullname="TestQDoc::Test::overload" href="testqdoc-test.html#overload" status="active" access="protected" location="testcpp.h" documented="true" meta="plain" type="void" signature="void overload()">
+ <description/>
+ </function>
+ <function name="overload" fullname="TestQDoc::Test::overload" href="testqdoc-test.html#overload-1" status="active" access="protected" location="testcpp.h" documented="true" since="Test 1.2" meta="plain" overload="true" overload-number="1" type="void" signature="void overload(bool b)">
+ <parameter type="bool" name="b" default=""/>
+ <description/>
+ </function>
+ <function name="someFunction" fullname="TestQDoc::Test::someFunction" href="testqdoc-test.html#someFunction" status="active" access="public" location="testcpp.h" documented="true" since="Test 1.0" meta="plain" type="int" signature="int someFunction(int, int v)">
+ <parameter type="int" name="" default=""/>
+ <parameter type="int" name="v" default="0"/>
+ <description>
+ <para>Function that takes a parameter <argument>v</argument>. Also returns the value of <argument>v</argument>.</para>
+ </description>
+ </function>
+ <function name="someFunctionDefaultArg" fullname="TestQDoc::Test::someFunctionDefaultArg" href="testqdoc-test.html#someFunctionDefaultArg" threadsafety="non-reentrant" status="active" access="public" location="testcpp.h" documented="true" since="2.0" meta="plain" const="true" type="void" signature="void someFunctionDefaultArg(int i, bool b) const" groups="testgroup">
+ <parameter type="int" name="i" default=""/>
+ <parameter type="bool" name="b" default="false"/>
+ <description>
+ <para>Function that takes a parameter <argument>i</argument> and <argument>b</argument>.</para>
+ </description>
+ </function>
+ <function name="virtualFun" fullname="TestQDoc::Test::virtualFun" href="testqdoc-test.html#virtualFun" status="active" access="public" location="testcpp.h" documented="true" meta="plain" virtual="virtual" type="void" signature="void virtualFun()">
+ <description>
+ <para>Function that must be reimplemented.</para>
+ </description>
+ </function>
+ <typedef name="SomeType" fullname="TestQDoc::Test::SomeType" href="testqdoc-test.html#SomeType-typedef" status="active" access="public" location="testcpp.h" documented="true">
+ <description>
+ <brief>A typedef.</brief>
+ </description>
+ </typedef>
+ </class>
+ </document>
+</WebXML>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/webxml/testqdoc-testderived.webxml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/webxml/testqdoc-testderived.webxml
new file mode 100644
index 000000000..197cacd1d
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/webxml/testqdoc-testderived.webxml
@@ -0,0 +1,124 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<WebXML>
+ <document>
+ <class name="TestDerived" fullname="TestQDoc::TestDerived" href="testqdoc-testderived.html" status="active" access="public" location="testcpp.h" since="2.0" documented="true" bases="TestQDoc::Test" module="TestCPP" brief="A class in a namespace, derived from Test">
+ <description>
+ <brief>A class in a namespace, derived from <link raw="Test" href="testqdoc-test.html" type="class">Test</link>.</brief>
+ </description>
+ <function name="bindableProp" fullname="TestQDoc::TestDerived::bindableProp" href="testqdoc-testderived.html#bindableProp-prop" status="active" access="public" location="testcpp.h" meta="plain" associated-property="bindableProp" type="QBindable&lt;QString&gt;" signature="QBindable&lt;QString&gt; bindableProp()">
+ <description>
+ <see-also>setBindableProp()</see-also>
+ </description>
+ </function>
+ <function name="bindablePropChanged" fullname="TestQDoc::TestDerived::bindablePropChanged" href="testqdoc-testderived.html#bindableProp-prop" status="active" access="public" location="testcpp.h" meta="signal" associated-property="bindableProp" type="void" signature="void bindablePropChanged()">
+ <description/>
+ </function>
+ <function name="boolProp" fullname="TestQDoc::TestDerived::boolProp" href="testqdoc-testderived.html#boolProp-prop" status="active" access="public" location="testcpp.h" meta="plain" associated-property="boolProp" type="bool" signature="bool boolProp()">
+ <description>
+ <see-also>setBoolProp()</see-also>
+ </description>
+ </function>
+ <function name="boolPropChanged" fullname="TestQDoc::TestDerived::boolPropChanged" href="testqdoc-testderived.html#boolProp-prop" status="active" access="public" location="testcpp.h" meta="signal" associated-property="boolProp" type="void" signature="void boolPropChanged()">
+ <description/>
+ </function>
+ <function name="emitSomething" fullname="TestQDoc::TestDerived::emitSomething" href="testqdoc-testderived.html#emitSomething" status="active" access="public" location="testcpp.h" documented="true" meta="signal" type="void" signature="void emitSomething()">
+ <description>
+ <para>Emitted when things happen.</para>
+ </description>
+ </function>
+ <function name="getInt" fullname="TestQDoc::TestDerived::getInt" href="testqdoc-testderived.html#intProp-prop" status="active" access="public" location="testcpp.h" meta="plain" associated-property="intProp" type="int *" signature="int * getInt()">
+ <description/>
+ </function>
+ <function name="id" fullname="TestQDoc::TestDerived::id" href="testqdoc-testderived.html#id" status="active" access="public" location="testcpp.h" documented="true" meta="plain" virtual="virtual" override="true" type="int" signature="int id() override">
+ <description/>
+ </function>
+ <function name="invokeMe" fullname="TestQDoc::TestDerived::invokeMe" href="testqdoc-testderived.html#invokeMe" status="active" access="public" location="testcpp.h" documented="true" meta="plain" const="true" type="void" brief="Something invokable" signature="void invokeMe() const">
+ <description>
+ <brief>Something invokable.</brief>
+ </description>
+ </function>
+ <function name="name" fullname="TestQDoc::TestDerived::name" href="testqdoc-testderived.html#name-prop" status="active" access="public" location="testcpp.h" meta="plain" const="true" associated-property="name" type="const QString *" signature="const QString * name() const">
+ <description/>
+ </function>
+ <function name="resetBoolProp" fullname="TestQDoc::TestDerived::resetBoolProp" href="testqdoc-testderived.html#boolProp-prop" status="active" access="public" location="testcpp.h" meta="slot" associated-property="boolProp" type="void" signature="void resetBoolProp()">
+ <description/>
+ </function>
+ <function name="setBindableProp" fullname="TestQDoc::TestDerived::setBindableProp" href="testqdoc-testderived.html#bindableProp-prop" status="active" access="public" location="testcpp.h" meta="slot" associated-property="bindableProp" type="void" signature="void setBindableProp(const QString &amp;s)">
+ <parameter type="const QString &amp;" name="s" default=""/>
+ <description>
+ <see-also>bindableProp()</see-also>
+ </description>
+ </function>
+ <function name="setBoolProp" fullname="TestQDoc::TestDerived::setBoolProp" href="testqdoc-testderived.html#boolProp-prop" status="active" access="public" location="testcpp.h" meta="slot" associated-property="boolProp" type="void" signature="void setBoolProp(bool b)">
+ <parameter type="bool" name="b" default=""/>
+ <description>
+ <see-also>boolProp()</see-also>
+ </description>
+ </function>
+ <function name="someProp" fullname="TestQDoc::TestDerived::someProp" href="testqdoc-testderived.html#someProp-prop" status="active" access="public" location="testcpp.h" meta="plain" associated-property="someProp" type="const QString &amp;" signature="const QString &amp; someProp()">
+ <description/>
+ </function>
+ <function name="someValue" fullname="TestQDoc::TestDerived::someValue" href="testqdoc-testderived.html#someValue" status="active" access="public" location="testcpp.h" documented="true" meta="plain" type="TestQDoc::TestDerived::NotTypedef" signature="TestQDoc::TestDerived::NotTypedef someValue()">
+ <description>
+ <para>Returns a value using an aliases type.</para>
+ </description>
+ </function>
+ <function name="staticObsoleteMember" fullname="TestQDoc::TestDerived::staticObsoleteMember" href="testqdoc-testderived-obsolete.html#staticObsoleteMember" status="deprecated" access="public" location="testcpp.h" documented="true" meta="plain" static="true" type="void" signature="void staticObsoleteMember()">
+ <description>
+ <para>Static obsolete method.</para>
+ </description>
+ </function>
+ <function name="virtualFun" fullname="TestQDoc::TestDerived::virtualFun" href="testqdoc-testderived.html#virtualFun" status="active" access="public" location="testcpp.h" documented="true" meta="plain" virtual="virtual" override="true" type="void" signature="void virtualFun() override">
+ <description/>
+ </function>
+ <typedef name="DerivedType" fullname="TestQDoc::TestDerived::DerivedType" href="testqdoc-testderived.html#DerivedType-typedef" status="active" access="public" location="testcpp.h" documented="true" aliasedtype="Test::SomeType">
+ <description>
+ <para>An aliased typedef.</para>
+ </description>
+ </typedef>
+ <typedef name="NotTypedef" fullname="TestQDoc::TestDerived::NotTypedef" href="testqdoc-testderived.html#NotTypedef-typedef" status="active" access="public" location="testcpp.h" documented="true" aliasedtype="int">
+ <description>
+ <para>I'm an alias, not a typedef.</para>
+ </description>
+ </typedef>
+ <property name="bindableProp" fullname="TestQDoc::TestDerived::bindableProp" href="testqdoc-testderived.html#bindableProp-prop" status="active" access="public" location="testcpp.h" documented="true" bindable="true">
+ <getter name="bindableProp"/>
+ <setter name="setBindableProp"/>
+ <notifier name="bindablePropChanged"/>
+ <description>
+ <para>Some property.</para>
+ <see-also>
+ <link raw="someProp" href="testqdoc-testderived.html#someProp-prop" type="function">someProp</link>
+ </see-also>
+ </description>
+ </property>
+ <property name="boolProp" fullname="TestQDoc::TestDerived::boolProp" href="testqdoc-testderived.html#boolProp-prop" status="active" access="public" location="testcpp.h" documented="true">
+ <getter name="boolProp"/>
+ <setter name="setBoolProp"/>
+ <resetter name="resetBoolProp"/>
+ <notifier name="boolPropChanged"/>
+ <description>
+ <para>A boolean property.</para>
+ </description>
+ </property>
+ <property name="intProp" fullname="TestQDoc::TestDerived::intProp" href="testqdoc-testderived.html#intProp-prop" status="active" access="public" location="testcpp.h" documented="true" writable="false">
+ <getter name="getInt"/>
+ <description>
+ <para>An integer property.</para>
+ </description>
+ </property>
+ <property name="name" fullname="TestQDoc::TestDerived::name" href="testqdoc-testderived.html#name-prop" status="active" access="public" location="testcpp.h" documented="true" writable="false" brief="Name">
+ <getter name="name"/>
+ <description>
+ <brief>This property holds a name..</brief>
+ </description>
+ </property>
+ <property name="someProp" fullname="TestQDoc::TestDerived::someProp" href="testqdoc-testderived.html#someProp-prop" status="active" access="public" location="testcpp.h" documented="true" bindable="true" writable="false">
+ <getter name="someProp"/>
+ <description>
+ <para>Another property.</para>
+ </description>
+ </property>
+ </class>
+ </document>
+</WebXML>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/webxml/testqdoc.webxml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/webxml/testqdoc.webxml
new file mode 100644
index 000000000..dfd9eeb9d
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/webxml/testqdoc.webxml
@@ -0,0 +1,253 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<WebXML>
+ <document>
+ <namespace name="TestQDoc" href="testqdoc.html" status="active" access="public" location="testcpp.h" since="2.0" documented="true" module="TestCPP" brief="A namespace">
+ <contents name="usage" title="Usage" level="1"/>
+ <description>
+ <brief>A namespace.</brief>
+ <section id="usage">
+ <heading level="1">Usage</heading>
+ <para>This namespace is for testing QDoc output.</para>
+ </section>
+ </description>
+ <function name="QDOCTEST_MACRO" href="testqdoc.html#QDOCTEST_MACRO" status="active" access="public" documented="true" related="0" since="Test 0.9" meta="macrowithoutparams" signature="QDOCTEST_MACRO">
+ <description/>
+ </function>
+ <class threadsafety="reentrant" name="Test" fullname="TestQDoc::Test" href="testqdoc-test.html" status="active" access="public" location="testcpp.h" since="1.1" documented="true" groups="cpptypes,testgroup" module="TestCPP" brief="A class in a namespace">
+ <description>
+ <brief>A class in a namespace.</brief>
+ </description>
+ <function name="QDOCTEST_MACRO2" href="testqdoc-test.html#QDOCTEST_MACRO2" status="active" access="public" documented="true" related="1" since="Test 1.1" meta="macrowithparams" brief="A macro with argument x" signature="QDOCTEST_MACRO2(int &amp;x)" groups="testgroup">
+ <parameter type="int &amp;" name="x" default=""/>
+ <description>
+ <brief>A macro with argument <argument>x</argument>.</brief>
+ </description>
+ </function>
+ <function name="Test" fullname="TestQDoc::Test::Test" href="testqdoc-test.html#Test" status="active" access="public" location="testcpp.h" documented="true" meta="constructor" signature="Test()">
+ <description>
+ <para>The constructor is deleted.</para>
+ </description>
+ </function>
+ <function name="anotherObsoleteMember" fullname="TestQDoc::Test::anotherObsoleteMember" href="testqdoc-test-obsolete.html#anotherObsoleteMember" status="deprecated" access="public" location="testcpp.h" documented="true" meta="plain" type="void" signature="void anotherObsoleteMember()">
+ <description>
+ <para>Use <link raw="obsoleteMember()" href="testqdoc-test.html#obsoleteMember" type="function">obsoleteMember()</link> instead.</para>
+ </description>
+ </function>
+ <function name="deprecatedMember" fullname="TestQDoc::Test::deprecatedMember" href="testqdoc-test-obsolete.html#deprecatedMember" status="deprecated" access="public" location="testcpp.h" documented="true" meta="plain" type="void" signature="void deprecatedMember()">
+ <description>
+ <para>Use <link raw="someFunction()" href="testqdoc-test.html#someFunction" type="function">someFunction()</link> instead.</para>
+ </description>
+ </function>
+ <function name="funcPtr" fullname="TestQDoc::Test::funcPtr" href="testqdoc-test.html#funcPtr" status="active" access="public" location="testcpp.h" documented="true" meta="plain" type="void (*)(bool)" signature="void (*)(bool) funcPtr(bool b, const char *s)">
+ <parameter type="bool" name="b" default=""/>
+ <parameter type="const char *" name="s" default=""/>
+ <description>
+ <para>Returns a pointer to a function that takes a boolean. Uses <argument>b</argument> and <argument>s</argument>.</para>
+ </description>
+ </function>
+ <function name="inlineFunction" fullname="TestQDoc::Test::inlineFunction" href="testqdoc-test.html#inlineFunction" status="active" access="public" location="testcpp.h" documented="true" meta="plain" type="void" brief="An inline function, documented using the \fn QDoc command" signature="void inlineFunction()">
+ <description>
+ <brief>An inline function, documented using the \fn QDoc command.</brief>
+ </description>
+ </function>
+ <function name="methodWithEmDashInItsDocs" fullname="TestQDoc::Test::methodWithEmDashInItsDocs" href="testqdoc-test.html#methodWithEmDashInItsDocs" status="active" access="public" location="testcpp.h" documented="true" meta="plain" type="void" signature="void methodWithEmDashInItsDocs()">
+ <description>
+ <para>This method has em dashes in its documentation—as you'll find represented by <teletype type="highlighted">---</teletype> in the sources—here and there. The important bit to note is that when passed e.g. to the \c command, the three hyphens are processed as input to the command and not replaced by an em dash.</para>
+ <para>-----------------------------------------------------------------------</para>
+ <para>People can still add a bunch of dashes, though, without QDoc replacing them all with a series of em dashes.</para>
+ <para>—You can also start a new paragraph with an em dash, if you want to.</para>
+ <see-also>
+ <link raw="methodWithEnDashInItsDocs" href="testqdoc-test.html#methodWithEnDashInItsDocs" type="function">methodWithEnDashInItsDocs</link>
+ </see-also>
+ </description>
+ </function>
+ <function name="methodWithEnDashInItsDocs" fullname="TestQDoc::Test::methodWithEnDashInItsDocs" href="testqdoc-test.html#methodWithEnDashInItsDocs" status="active" access="public" location="testcpp.h" documented="true" meta="plain" type="void" signature="void methodWithEnDashInItsDocs()">
+ <description>
+ <para>This method has en dashes in its documentation – as you'll find represented by <teletype type="highlighted">--</teletype> in the sources – here and there. The important bit to note is that when passed e.g. to the \c command, the two hyphens are processed as input to the command and not replaced by an en dash. This also applies to code blocks, where otherwise, the decrement operator would get completely borked:</para>
+ <code>for (int i = 42; i &gt; 0; --i)
+ // Do something cool during countdown.</code>
+ <para>...as it would be silly if this would output –i instead of <teletype type="highlighted">--i</teletype>.</para>
+ <para>-----------------------------------------------------------------------</para>
+ <para>It still allows people to add a bunch of dashes, though, without replacing them all with a series of en dashes. Of course, they might want to use the \hr command instead, like this:</para>
+ <para>– You can also start a new paragraph with an en dash, if you want to.</para>
+ <see-also>methodWithEnDashInItsDocs</see-also>
+ </description>
+ </function>
+ <function name="obsoleteMember" fullname="TestQDoc::Test::obsoleteMember" href="testqdoc-test-obsolete.html#obsoleteMember" status="deprecated" access="public" location="testcpp.h" documented="true" meta="plain" type="void" signature="void obsoleteMember()">
+ <description>
+ <para>Use <link raw="someFunction()" href="testqdoc-test.html#someFunction" type="function">someFunction()</link> instead.</para>
+ </description>
+ </function>
+ <function name="operator++" fullname="TestQDoc::Test::operator++" href="testqdoc-test-obsolete.html#operator-2b-2b" status="deprecated" access="public" location="testcpp.h" documented="true" meta="plain" type="TestQDoc::Test &amp;" signature="TestQDoc::Test &amp; operator++()">
+ <description/>
+ </function>
+ <function name="operator--" fullname="TestQDoc::Test::operator--" href="testqdoc-test-obsolete.html#operator--" status="deprecated" access="public" location="testcpp.h" documented="true" meta="plain" type="TestQDoc::Test &amp;" signature="TestQDoc::Test &amp; operator--()">
+ <description/>
+ </function>
+ <function name="operator=" fullname="TestQDoc::Test::operator=" href="testqdoc-test.html#operator-eq" status="active" access="public" location="testcpp.h" documented="true" meta="move-assign" type="TestQDoc::Test &amp;" signature="TestQDoc::Test &amp; operator=(TestQDoc::Test &amp;&amp;other)" groups="testgroup">
+ <parameter type="TestQDoc::Test &amp;&amp;" name="other" default=""/>
+ <description>
+ <para>The move assignment operator is deleted. <argument>other</argument> cannot be moved from.</para>
+ </description>
+ </function>
+ <function name="operator==" href="testqdoc-test.html#operator-eq-eq" status="active" access="public" location="testcpp.h" documented="true" related="2" meta="plain" type="bool" signature="bool operator==(const TestQDoc::Test &amp;lhs, const TestQDoc::Test &amp;rhs)">
+ <parameter type="const TestQDoc::Test &amp;" name="lhs" default=""/>
+ <parameter type="const TestQDoc::Test &amp;" name="rhs" default=""/>
+ <description>
+ <para>Returns true if <argument>lhs</argument> and <argument>rhs</argument> are equal.</para>
+ </description>
+ </function>
+ <function name="overload" fullname="TestQDoc::Test::overload" href="testqdoc-test.html#overload" status="active" access="protected" location="testcpp.h" documented="true" meta="plain" type="void" signature="void overload()">
+ <description/>
+ </function>
+ <function name="overload" fullname="TestQDoc::Test::overload" href="testqdoc-test.html#overload-1" status="active" access="protected" location="testcpp.h" documented="true" since="Test 1.2" meta="plain" overload="true" overload-number="1" type="void" signature="void overload(bool b)">
+ <parameter type="bool" name="b" default=""/>
+ <description/>
+ </function>
+ <function name="someFunction" fullname="TestQDoc::Test::someFunction" href="testqdoc-test.html#someFunction" status="active" access="public" location="testcpp.h" documented="true" since="Test 1.0" meta="plain" type="int" signature="int someFunction(int, int v)">
+ <parameter type="int" name="" default=""/>
+ <parameter type="int" name="v" default="0"/>
+ <description>
+ <para>Function that takes a parameter <argument>v</argument>. Also returns the value of <argument>v</argument>.</para>
+ </description>
+ </function>
+ <function name="someFunctionDefaultArg" fullname="TestQDoc::Test::someFunctionDefaultArg" href="testqdoc-test.html#someFunctionDefaultArg" threadsafety="non-reentrant" status="active" access="public" location="testcpp.h" documented="true" since="2.0" meta="plain" const="true" type="void" signature="void someFunctionDefaultArg(int i, bool b) const" groups="testgroup">
+ <parameter type="int" name="i" default=""/>
+ <parameter type="bool" name="b" default="false"/>
+ <description>
+ <para>Function that takes a parameter <argument>i</argument> and <argument>b</argument>.</para>
+ </description>
+ </function>
+ <function name="virtualFun" fullname="TestQDoc::Test::virtualFun" href="testqdoc-test.html#virtualFun" status="active" access="public" location="testcpp.h" documented="true" meta="plain" virtual="virtual" type="void" signature="void virtualFun()">
+ <description>
+ <para>Function that must be reimplemented.</para>
+ </description>
+ </function>
+ <typedef name="SomeType" fullname="TestQDoc::Test::SomeType" href="testqdoc-test.html#SomeType-typedef" status="active" access="public" location="testcpp.h" documented="true">
+ <description>
+ <brief>A typedef.</brief>
+ </description>
+ </typedef>
+ </class>
+ <class name="TestDerived" fullname="TestQDoc::TestDerived" href="testqdoc-testderived.html" status="active" access="public" location="testcpp.h" since="2.0" documented="true" bases="TestQDoc::Test" module="TestCPP" brief="A class in a namespace, derived from Test">
+ <description>
+ <brief>A class in a namespace, derived from <link raw="Test" href="testqdoc-test.html" type="class">Test</link>.</brief>
+ </description>
+ <function name="bindableProp" fullname="TestQDoc::TestDerived::bindableProp" href="testqdoc-testderived.html#bindableProp-prop" status="active" access="public" location="testcpp.h" meta="plain" associated-property="bindableProp" type="QBindable&lt;QString&gt;" signature="QBindable&lt;QString&gt; bindableProp()">
+ <description>
+ <see-also>setBindableProp()</see-also>
+ </description>
+ </function>
+ <function name="bindablePropChanged" fullname="TestQDoc::TestDerived::bindablePropChanged" href="testqdoc-testderived.html#bindableProp-prop" status="active" access="public" location="testcpp.h" meta="signal" associated-property="bindableProp" type="void" signature="void bindablePropChanged()">
+ <description/>
+ </function>
+ <function name="boolProp" fullname="TestQDoc::TestDerived::boolProp" href="testqdoc-testderived.html#boolProp-prop" status="active" access="public" location="testcpp.h" meta="plain" associated-property="boolProp" type="bool" signature="bool boolProp()">
+ <description>
+ <see-also>setBoolProp()</see-also>
+ </description>
+ </function>
+ <function name="boolPropChanged" fullname="TestQDoc::TestDerived::boolPropChanged" href="testqdoc-testderived.html#boolProp-prop" status="active" access="public" location="testcpp.h" meta="signal" associated-property="boolProp" type="void" signature="void boolPropChanged()">
+ <description/>
+ </function>
+ <function name="emitSomething" fullname="TestQDoc::TestDerived::emitSomething" href="testqdoc-testderived.html#emitSomething" status="active" access="public" location="testcpp.h" documented="true" meta="signal" type="void" signature="void emitSomething()">
+ <description>
+ <para>Emitted when things happen.</para>
+ </description>
+ </function>
+ <function name="getInt" fullname="TestQDoc::TestDerived::getInt" href="testqdoc-testderived.html#intProp-prop" status="active" access="public" location="testcpp.h" meta="plain" associated-property="intProp" type="int *" signature="int * getInt()">
+ <description/>
+ </function>
+ <function name="id" fullname="TestQDoc::TestDerived::id" href="testqdoc-testderived.html#id" status="active" access="public" location="testcpp.h" documented="true" meta="plain" virtual="virtual" override="true" type="int" signature="int id() override">
+ <description/>
+ </function>
+ <function name="invokeMe" fullname="TestQDoc::TestDerived::invokeMe" href="testqdoc-testderived.html#invokeMe" status="active" access="public" location="testcpp.h" documented="true" meta="plain" const="true" type="void" brief="Something invokable" signature="void invokeMe() const">
+ <description>
+ <brief>Something invokable.</brief>
+ </description>
+ </function>
+ <function name="name" fullname="TestQDoc::TestDerived::name" href="testqdoc-testderived.html#name-prop" status="active" access="public" location="testcpp.h" meta="plain" const="true" associated-property="name" type="const QString *" signature="const QString * name() const">
+ <description/>
+ </function>
+ <function name="resetBoolProp" fullname="TestQDoc::TestDerived::resetBoolProp" href="testqdoc-testderived.html#boolProp-prop" status="active" access="public" location="testcpp.h" meta="slot" associated-property="boolProp" type="void" signature="void resetBoolProp()">
+ <description/>
+ </function>
+ <function name="setBindableProp" fullname="TestQDoc::TestDerived::setBindableProp" href="testqdoc-testderived.html#bindableProp-prop" status="active" access="public" location="testcpp.h" meta="slot" associated-property="bindableProp" type="void" signature="void setBindableProp(const QString &amp;s)">
+ <parameter type="const QString &amp;" name="s" default=""/>
+ <description>
+ <see-also>bindableProp()</see-also>
+ </description>
+ </function>
+ <function name="setBoolProp" fullname="TestQDoc::TestDerived::setBoolProp" href="testqdoc-testderived.html#boolProp-prop" status="active" access="public" location="testcpp.h" meta="slot" associated-property="boolProp" type="void" signature="void setBoolProp(bool b)">
+ <parameter type="bool" name="b" default=""/>
+ <description>
+ <see-also>boolProp()</see-also>
+ </description>
+ </function>
+ <function name="someProp" fullname="TestQDoc::TestDerived::someProp" href="testqdoc-testderived.html#someProp-prop" status="active" access="public" location="testcpp.h" meta="plain" associated-property="someProp" type="const QString &amp;" signature="const QString &amp; someProp()">
+ <description/>
+ </function>
+ <function name="someValue" fullname="TestQDoc::TestDerived::someValue" href="testqdoc-testderived.html#someValue" status="active" access="public" location="testcpp.h" documented="true" meta="plain" type="TestQDoc::TestDerived::NotTypedef" signature="TestQDoc::TestDerived::NotTypedef someValue()">
+ <description>
+ <para>Returns a value using an aliases type.</para>
+ </description>
+ </function>
+ <function name="staticObsoleteMember" fullname="TestQDoc::TestDerived::staticObsoleteMember" href="testqdoc-testderived-obsolete.html#staticObsoleteMember" status="deprecated" access="public" location="testcpp.h" documented="true" meta="plain" static="true" type="void" signature="void staticObsoleteMember()">
+ <description>
+ <para>Static obsolete method.</para>
+ </description>
+ </function>
+ <function name="virtualFun" fullname="TestQDoc::TestDerived::virtualFun" href="testqdoc-testderived.html#virtualFun" status="active" access="public" location="testcpp.h" documented="true" meta="plain" virtual="virtual" override="true" type="void" signature="void virtualFun() override">
+ <description/>
+ </function>
+ <typedef name="DerivedType" fullname="TestQDoc::TestDerived::DerivedType" href="testqdoc-testderived.html#DerivedType-typedef" status="active" access="public" location="testcpp.h" documented="true" aliasedtype="Test::SomeType">
+ <description>
+ <para>An aliased typedef.</para>
+ </description>
+ </typedef>
+ <typedef name="NotTypedef" fullname="TestQDoc::TestDerived::NotTypedef" href="testqdoc-testderived.html#NotTypedef-typedef" status="active" access="public" location="testcpp.h" documented="true" aliasedtype="int">
+ <description>
+ <para>I'm an alias, not a typedef.</para>
+ </description>
+ </typedef>
+ <property name="bindableProp" fullname="TestQDoc::TestDerived::bindableProp" href="testqdoc-testderived.html#bindableProp-prop" status="active" access="public" location="testcpp.h" documented="true" bindable="true">
+ <getter name="bindableProp"/>
+ <setter name="setBindableProp"/>
+ <notifier name="bindablePropChanged"/>
+ <description>
+ <para>Some property.</para>
+ <see-also>
+ <link raw="someProp" href="testqdoc-testderived.html#someProp-prop" type="function">someProp</link>
+ </see-also>
+ </description>
+ </property>
+ <property name="boolProp" fullname="TestQDoc::TestDerived::boolProp" href="testqdoc-testderived.html#boolProp-prop" status="active" access="public" location="testcpp.h" documented="true">
+ <getter name="boolProp"/>
+ <setter name="setBoolProp"/>
+ <resetter name="resetBoolProp"/>
+ <notifier name="boolPropChanged"/>
+ <description>
+ <para>A boolean property.</para>
+ </description>
+ </property>
+ <property name="intProp" fullname="TestQDoc::TestDerived::intProp" href="testqdoc-testderived.html#intProp-prop" status="active" access="public" location="testcpp.h" documented="true" writable="false">
+ <getter name="getInt"/>
+ <description>
+ <para>An integer property.</para>
+ </description>
+ </property>
+ <property name="name" fullname="TestQDoc::TestDerived::name" href="testqdoc-testderived.html#name-prop" status="active" access="public" location="testcpp.h" documented="true" writable="false" brief="Name">
+ <getter name="name"/>
+ <description>
+ <brief>This property holds a name..</brief>
+ </description>
+ </property>
+ <property name="someProp" fullname="TestQDoc::TestDerived::someProp" href="testqdoc-testderived.html#someProp-prop" status="active" access="public" location="testcpp.h" documented="true" bindable="true" writable="false">
+ <getter name="someProp"/>
+ <description>
+ <para>Another property.</para>
+ </description>
+ </property>
+ </class>
+ </namespace>
+ </document>
+</WebXML>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/webxml/testtagfile.tags b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/webxml/testtagfile.tags
new file mode 100644
index 000000000..f68dffed6
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/webxml/testtagfile.tags
@@ -0,0 +1,500 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<tagfile>
+ <compound kind="class">
+ <name>QDoc.Test.AbstractParent</name>
+ <filename>qml-qdoc-test-abstractparent.html</filename>
+ <member kind="function" protection="public" virtualness="non" static="no">
+ <type>void</type>
+ <name>name</name>
+ <anchorfile>qml-qdoc-test-abstractparent.html</anchorfile>
+ <anchor>name-method</anchor>
+ <arglist>()</arglist>
+ </member>
+ <member kind="function" protection="public" virtualness="non" static="no">
+ <type>void</type>
+ <name>name</name>
+ <anchorfile>qml-qdoc-test-abstractparent.html</anchorfile>
+ <anchor>name-method</anchor>
+ <arglist>(Child child, name)</arglist>
+ </member>
+ <member kind="function" protection="public" virtualness="non" static="no">
+ <type>void</type>
+ <name>rear</name>
+ <anchorfile>qml-qdoc-test-abstractparent.html</anchorfile>
+ <anchor>rear-method</anchor>
+ <arglist>(Child child, var method)</arglist>
+ </member>
+ </compound>
+ <compound kind="class">
+ <name>QDoc.Test.Child</name>
+ <filename>qml-qdoc-test-child.html</filename>
+ <member kind="function" protection="public" virtualness="non" static="no">
+ <type>void</type>
+ <name>name</name>
+ <anchorfile>qml-qdoc-test-child.html</anchorfile>
+ <anchor>name-method</anchor>
+ <arglist>(Child child, name)</arglist>
+ </member>
+ </compound>
+ <compound kind="namespace">
+ <name>CrossModuleRef</name>
+ <filename>crossmoduleref.html</filename>
+ <member kind="function" protection="public" virtualness="non" static="no">
+ <type>void</type>
+ <name>documentMe</name>
+ <anchorfile>crossmoduleref.html</anchorfile>
+ <anchor>documentMe</anchor>
+ <arglist>()</arglist>
+ </member>
+ </compound>
+ <compound kind="class">
+ <name>QDoc.Test.DocTest</name>
+ <filename>qml-qdoc-test-doctest.html</filename>
+ <member kind="function" protection="public" virtualness="non" static="no">
+ <type></type>
+ <name>completed</name>
+ <anchorfile>qml-qdoc-test-doctest.html</anchorfile>
+ <anchor>completed-signal</anchor>
+ <arglist>()</arglist>
+ </member>
+ <member kind="function" protection="public" virtualness="non" static="no">
+ <type></type>
+ <name>fail</name>
+ <anchorfile>qml-qdoc-test-doctest.html</anchorfile>
+ <anchor>fail-method</anchor>
+ <arglist>(message)</arglist>
+ </member>
+ <member kind="function" protection="public" virtualness="non" static="no">
+ <type></type>
+ <name>fail_hard</name>
+ <anchorfile>qml-qdoc-test-doctest.html</anchorfile>
+ <anchor>fail_hard-method</anchor>
+ <arglist>(msg, option)</arglist>
+ </member>
+ <member kind="function" protection="public" virtualness="non" static="no">
+ <type></type>
+ <name>foo</name>
+ <anchorfile>qml-qdoc-test-doctest.html</anchorfile>
+ <anchor>foo-signal</anchor>
+ <arglist>(var bar)</arglist>
+ </member>
+ <member kind="function" protection="public" virtualness="non" static="no">
+ <type></type>
+ <name>itsHappening</name>
+ <anchorfile>qml-qdoc-test-doctest.html</anchorfile>
+ <anchor>itsHappening-signal</anchor>
+ <arglist>(bool really)</arglist>
+ </member>
+ </compound>
+ <compound kind="class">
+ <name>Test.NoVer.DocTest</name>
+ <filename>qml-test-nover-doctest.html</filename>
+ </compound>
+ <compound kind="class">
+ <name>QDoc.Test.OldType</name>
+ <filename>qml-qdoc-test-oldtype.html</filename>
+ </compound>
+ <compound kind="class">
+ <name>UIComponents.ProgressBar</name>
+ <filename>qml-uicomponents-progressbar.html</filename>
+ </compound>
+ <compound kind="class">
+ <name>SeenClass</name>
+ <filename>seenclass.html</filename>
+ </compound>
+ <compound kind="class">
+ <name>UIComponents.Switch</name>
+ <filename>qml-uicomponents-switch.html</filename>
+ <member kind="function" protection="public" virtualness="non" static="no">
+ <type></type>
+ <name>toggle</name>
+ <anchorfile>qml-uicomponents-switch.html</anchorfile>
+ <anchor>toggle-method</anchor>
+ <arglist>()</arglist>
+ </member>
+ </compound>
+ <compound kind="class">
+ <name>UIComponents.TabWidget</name>
+ <filename>qml-uicomponents-tabwidget.html</filename>
+ </compound>
+ <compound kind="namespace">
+ <name>TestQDoc</name>
+ <filename>testqdoc.html</filename>
+ <class>TestQDoc::Test</class>
+ <class>TestQDoc::TestDerived</class>
+ <member kind="function" protection="public" virtualness="non" static="no">
+ <type></type>
+ <name>QDOCTEST_MACRO</name>
+ <anchorfile>testqdoc.html</anchorfile>
+ <anchor>QDOCTEST_MACRO</anchor>
+ <arglist>QDOCTEST_MACRO</arglist>
+ </member>
+ </compound>
+ <compound kind="class">
+ <name>TestQDoc::Test</name>
+ <filename>testqdoc-test.html</filename>
+ <member kind="function" protection="public" virtualness="non" static="no">
+ <type></type>
+ <name>QDOCTEST_MACRO2</name>
+ <anchorfile>testqdoc-test.html</anchorfile>
+ <anchor>QDOCTEST_MACRO2</anchor>
+ <arglist>(int &amp;x)</arglist>
+ </member>
+ <member kind="function" protection="public" virtualness="non" static="no">
+ <type></type>
+ <name>Test</name>
+ <anchorfile>testqdoc-test.html</anchorfile>
+ <anchor>Test</anchor>
+ <arglist>()</arglist>
+ </member>
+ <member kind="function" protection="public" virtualness="non" static="no">
+ <type>TestQDoc::Test &amp;</type>
+ <name>operator++</name>
+ <anchorfile>testqdoc-test-obsolete.html</anchorfile>
+ <anchor>operator-2b-2b</anchor>
+ <arglist>()</arglist>
+ </member>
+ <member kind="function" protection="public" virtualness="non" static="no">
+ <type>TestQDoc::Test &amp;</type>
+ <name>operator--</name>
+ <anchorfile>testqdoc-test-obsolete.html</anchorfile>
+ <anchor>operator--</anchor>
+ <arglist>()</arglist>
+ </member>
+ <member kind="function" protection="public" virtualness="non" static="no">
+ <type>TestQDoc::Test &amp;</type>
+ <name>operator=</name>
+ <anchorfile>testqdoc-test.html</anchorfile>
+ <anchor>operator-eq</anchor>
+ <arglist>(TestQDoc::Test &amp;&amp;other)</arglist>
+ </member>
+ <member kind="function" protection="public" virtualness="non" static="no">
+ <type>bool</type>
+ <name>operator==</name>
+ <anchorfile>testqdoc-test.html</anchorfile>
+ <anchor>operator-eq-eq</anchor>
+ <arglist>(const TestQDoc::Test &amp;lhs, const TestQDoc::Test &amp;rhs)</arglist>
+ </member>
+ <member kind="function" protection="public" virtualness="non" static="no">
+ <type>int</type>
+ <name>someFunction</name>
+ <anchorfile>testqdoc-test.html</anchorfile>
+ <anchor>someFunction</anchor>
+ <arglist>(int, int v)</arglist>
+ </member>
+ <member kind="function" protection="public" virtualness="non" static="no">
+ <type>void (*)(bool)</type>
+ <name>funcPtr</name>
+ <anchorfile>testqdoc-test.html</anchorfile>
+ <anchor>funcPtr</anchor>
+ <arglist>(*)(bool) funcPtr(bool b, const char *s)</arglist>
+ </member>
+ <member kind="function" protection="public" virtualness="non" static="no">
+ <type>void</type>
+ <name>anotherObsoleteMember</name>
+ <anchorfile>testqdoc-test-obsolete.html</anchorfile>
+ <anchor>anotherObsoleteMember</anchor>
+ <arglist>()</arglist>
+ </member>
+ <member kind="function" protection="public" virtualness="non" static="no">
+ <type>void</type>
+ <name>deprecatedMember</name>
+ <anchorfile>testqdoc-test-obsolete.html</anchorfile>
+ <anchor>deprecatedMember</anchor>
+ <arglist>()</arglist>
+ </member>
+ <member kind="function" protection="public" virtualness="non" static="no">
+ <type>void</type>
+ <name>inlineFunction</name>
+ <anchorfile>testqdoc-test.html</anchorfile>
+ <anchor>inlineFunction</anchor>
+ <arglist>()</arglist>
+ </member>
+ <member kind="function" protection="public" virtualness="non" static="no">
+ <type>void</type>
+ <name>methodWithEmDashInItsDocs</name>
+ <anchorfile>testqdoc-test.html</anchorfile>
+ <anchor>methodWithEmDashInItsDocs</anchor>
+ <arglist>()</arglist>
+ </member>
+ <member kind="function" protection="public" virtualness="non" static="no">
+ <type>void</type>
+ <name>methodWithEnDashInItsDocs</name>
+ <anchorfile>testqdoc-test.html</anchorfile>
+ <anchor>methodWithEnDashInItsDocs</anchor>
+ <arglist>()</arglist>
+ </member>
+ <member kind="function" protection="public" virtualness="non" static="no">
+ <type>void</type>
+ <name>obsoleteMember</name>
+ <anchorfile>testqdoc-test-obsolete.html</anchorfile>
+ <anchor>obsoleteMember</anchor>
+ <arglist>()</arglist>
+ </member>
+ <member kind="function" protection="protected" virtualness="non" static="no">
+ <type>void</type>
+ <name>overload</name>
+ <anchorfile>testqdoc-test.html</anchorfile>
+ <anchor>overload</anchor>
+ <arglist>()</arglist>
+ </member>
+ <member kind="function" protection="protected" virtualness="non" static="no">
+ <type>void</type>
+ <name>overload</name>
+ <anchorfile>testqdoc-test.html</anchorfile>
+ <anchor>overload-1</anchor>
+ <arglist>(bool b)</arglist>
+ </member>
+ <member kind="function" protection="public" virtualness="virtual" static="no">
+ <type>virtual void</type>
+ <name>virtualFun</name>
+ <anchorfile>testqdoc-test.html</anchorfile>
+ <anchor>virtualFun</anchor>
+ <arglist>()</arglist>
+ </member>
+ <member kind="function" protection="public" virtualness="non" static="no">
+ <type>void</type>
+ <name>someFunctionDefaultArg</name>
+ <anchorfile>testqdoc-test.html</anchorfile>
+ <anchor>someFunctionDefaultArg</anchor>
+ <arglist>(int i, bool b) const const</arglist>
+ </member>
+ <member kind="typedef" type="">
+ <name>SomeType</name>
+ <anchorfile>testqdoc-test.html</anchorfile>
+ <anchor>SomeType-typedef</anchor>
+ <arglist></arglist>
+ </member>
+ </compound>
+ <compound kind="class">
+ <name>TestQDoc::TestDerived</name>
+ <filename>testqdoc-testderived.html</filename>
+ <base>Test</base>
+ <member kind="function" protection="public" virtualness="non" static="no">
+ <type>QBindable&lt;QString&gt;</type>
+ <name>bindableProp</name>
+ <anchorfile>testqdoc-testderived.html</anchorfile>
+ <anchor>bindableProp-prop</anchor>
+ <arglist>()</arglist>
+ </member>
+ <member kind="function" protection="public" virtualness="non" static="no">
+ <type>TestQDoc::TestDerived::NotTypedef</type>
+ <name>someValue</name>
+ <anchorfile>testqdoc-testderived.html</anchorfile>
+ <anchor>someValue</anchor>
+ <arglist>()</arglist>
+ </member>
+ <member kind="function" protection="public" virtualness="non" static="no">
+ <type>bool</type>
+ <name>boolProp</name>
+ <anchorfile>testqdoc-testderived.html</anchorfile>
+ <anchor>boolProp-prop</anchor>
+ <arglist>()</arglist>
+ </member>
+ <member kind="function" protection="public" virtualness="non" static="no">
+ <type>const QString &amp;</type>
+ <name>someProp</name>
+ <anchorfile>testqdoc-testderived.html</anchorfile>
+ <anchor>someProp-prop</anchor>
+ <arglist>()</arglist>
+ </member>
+ <member kind="function" protection="public" virtualness="non" static="no">
+ <type>int *</type>
+ <name>getInt</name>
+ <anchorfile>testqdoc-testderived.html</anchorfile>
+ <anchor>intProp-prop</anchor>
+ <arglist>()</arglist>
+ </member>
+ <member kind="function" protection="public" virtualness="virtual" static="no">
+ <type>virtual int</type>
+ <name>id</name>
+ <anchorfile>testqdoc-testderived.html</anchorfile>
+ <anchor>id</anchor>
+ <arglist>() override</arglist>
+ </member>
+ <member kind="function" protection="public" virtualness="non" static="no">
+ <type>void</type>
+ <name>bindablePropChanged</name>
+ <anchorfile>testqdoc-testderived.html</anchorfile>
+ <anchor>bindableProp-prop</anchor>
+ <arglist>()</arglist>
+ </member>
+ <member kind="function" protection="public" virtualness="non" static="no">
+ <type>void</type>
+ <name>boolPropChanged</name>
+ <anchorfile>testqdoc-testderived.html</anchorfile>
+ <anchor>boolProp-prop</anchor>
+ <arglist>()</arglist>
+ </member>
+ <member kind="function" protection="public" virtualness="non" static="no">
+ <type>void</type>
+ <name>emitSomething</name>
+ <anchorfile>testqdoc-testderived.html</anchorfile>
+ <anchor>emitSomething</anchor>
+ <arglist>()</arglist>
+ </member>
+ <member kind="function" protection="public" virtualness="non" static="no">
+ <type>void</type>
+ <name>resetBoolProp</name>
+ <anchorfile>testqdoc-testderived.html</anchorfile>
+ <anchor>boolProp-prop</anchor>
+ <arglist>()</arglist>
+ </member>
+ <member kind="function" protection="public" virtualness="non" static="no">
+ <type>void</type>
+ <name>setBindableProp</name>
+ <anchorfile>testqdoc-testderived.html</anchorfile>
+ <anchor>bindableProp-prop</anchor>
+ <arglist>(const QString &amp;s)</arglist>
+ </member>
+ <member kind="function" protection="public" virtualness="non" static="no">
+ <type>void</type>
+ <name>setBoolProp</name>
+ <anchorfile>testqdoc-testderived.html</anchorfile>
+ <anchor>boolProp-prop</anchor>
+ <arglist>(bool b)</arglist>
+ </member>
+ <member kind="function" protection="public" virtualness="non" static="yes">
+ <type>void</type>
+ <name>staticObsoleteMember</name>
+ <anchorfile>testqdoc-testderived-obsolete.html</anchorfile>
+ <anchor>staticObsoleteMember</anchor>
+ <arglist>()</arglist>
+ </member>
+ <member kind="function" protection="public" virtualness="virtual" static="no">
+ <type>virtual void</type>
+ <name>virtualFun</name>
+ <anchorfile>testqdoc-testderived.html</anchorfile>
+ <anchor>virtualFun</anchor>
+ <arglist>() override</arglist>
+ </member>
+ <member kind="function" protection="public" virtualness="non" static="no">
+ <type>const QString *</type>
+ <name>name</name>
+ <anchorfile>testqdoc-testderived.html</anchorfile>
+ <anchor>name-prop</anchor>
+ <arglist>() const const</arglist>
+ </member>
+ <member kind="function" protection="public" virtualness="non" static="no">
+ <type>void</type>
+ <name>invokeMe</name>
+ <anchorfile>testqdoc-testderived.html</anchorfile>
+ <anchor>invokeMe</anchor>
+ <arglist>() const const</arglist>
+ </member>
+ <member kind="typedef" type="">
+ <name>DerivedType</name>
+ <anchorfile>testqdoc-testderived.html</anchorfile>
+ <anchor>DerivedType-typedef</anchor>
+ <arglist></arglist>
+ </member>
+ <member kind="typedef" type="">
+ <name>NotTypedef</name>
+ <anchorfile>testqdoc-testderived.html</anchorfile>
+ <anchor>NotTypedef-typedef</anchor>
+ <arglist></arglist>
+ </member>
+ <member kind="property" type="QString">
+ <name>bindableProp</name>
+ <anchorfile>testqdoc-testderived.html</anchorfile>
+ <anchor>bindableProp-prop</anchor>
+ <arglist></arglist>
+ </member>
+ <member kind="property" type="bool">
+ <name>boolProp</name>
+ <anchorfile>testqdoc-testderived.html</anchorfile>
+ <anchor>boolProp-prop</anchor>
+ <arglist></arglist>
+ </member>
+ <member kind="property" type="int*">
+ <name>intProp</name>
+ <anchorfile>testqdoc-testderived.html</anchorfile>
+ <anchor>intProp-prop</anchor>
+ <arglist></arglist>
+ </member>
+ <member kind="property" type="const QString*">
+ <name>name</name>
+ <anchorfile>testqdoc-testderived.html</anchorfile>
+ <anchor>name-prop</anchor>
+ <arglist></arglist>
+ </member>
+ <member kind="property" type="QString">
+ <name>someProp</name>
+ <anchorfile>testqdoc-testderived.html</anchorfile>
+ <anchor>someProp-prop</anchor>
+ <arglist></arglist>
+ </member>
+ </compound>
+ <compound kind="class">
+ <name>TheModule.TheType</name>
+ <filename>qml-themodule-thetype.html</filename>
+ </compound>
+ <compound kind="class">
+ <name>QDoc.Test.Type</name>
+ <filename>qml-qdoc-test-type.html</filename>
+ <member kind="function" protection="public" virtualness="non" static="no">
+ <type>Type</type>
+ <name>copy</name>
+ <anchorfile>qml-qdoc-test-type.html</anchorfile>
+ <anchor>copy-method</anchor>
+ <arglist>(a)</arglist>
+ </member>
+ <member kind="function" protection="public" virtualness="non" static="no">
+ <type></type>
+ <name>completed</name>
+ <anchorfile>qml-qdoc-test-type.html</anchorfile>
+ <anchor>completed-signal</anchor>
+ <arglist>(int status)</arglist>
+ </member>
+ <member kind="function" protection="public" virtualness="non" static="no">
+ <type></type>
+ <name>configured</name>
+ <anchorfile>qml-qdoc-test-type.html</anchorfile>
+ <anchor>configured-signal</anchor>
+ <arglist>()</arglist>
+ </member>
+ <member kind="function" protection="public" virtualness="non" static="no">
+ <type></type>
+ <name>deprecatedMethod</name>
+ <anchorfile>qml-qdoc-test-type-obsolete.html</anchorfile>
+ <anchor>deprecatedMethod-method</anchor>
+ <arglist>()</arglist>
+ </member>
+ <member kind="function" protection="public" virtualness="non" static="no">
+ <type></type>
+ <name>disable</name>
+ <anchorfile>qml-qdoc-test-type.html</anchorfile>
+ <anchor>disable-method</anchor>
+ <arglist>()</arglist>
+ </member>
+ <member kind="function" protection="public" virtualness="non" static="no">
+ <type></type>
+ <name>enable</name>
+ <anchorfile>qml-qdoc-test-type.html</anchorfile>
+ <anchor>enable-method</anchor>
+ <arglist>()</arglist>
+ </member>
+ <member kind="function" protection="public" virtualness="non" static="no">
+ <type></type>
+ <name>futureDeprecated</name>
+ <anchorfile>qml-qdoc-test-type.html</anchorfile>
+ <anchor>futureDeprecated-method</anchor>
+ <arglist>()</arglist>
+ </member>
+ <member kind="function" protection="public" virtualness="non" static="no">
+ <type></type>
+ <name>group.created</name>
+ <anchorfile>qml-qdoc-test-type.html</anchorfile>
+ <anchor>group.created-signal</anchor>
+ <arglist>()</arglist>
+ </member>
+ </compound>
+ <compound kind="class">
+ <name>Test.NoVer.TypeNoVersion</name>
+ <filename>qml-test-nover-typenoversion.html</filename>
+ </compound>
+ <compound kind="class">
+ <name>QDoc.Test.YetAnotherChild</name>
+ <filename>qml-qdoc-test-yetanotherchild.html</filename>
+ </compound>
+</tagfile>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/webxml/themodule-qmlmodule.webxml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/webxml/themodule-qmlmodule.webxml
new file mode 100644
index 000000000..5d24b3077
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/webxml/themodule-qmlmodule.webxml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<WebXML>
+ <document/>
+</WebXML>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/webxml/uicomponents-qmlmodule.webxml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/webxml/uicomponents-qmlmodule.webxml
new file mode 100644
index 000000000..5d24b3077
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/expected/webxml/uicomponents-qmlmodule.webxml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<WebXML>
+ <document/>
+</WebXML>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/src/TestCPP b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/src/TestCPP
new file mode 100644
index 000000000..4ed786108
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/src/TestCPP
@@ -0,0 +1,5 @@
+#include "testcpp.h"
+
+#ifdef test_template
+# include "testtemplate.h"
+#endif
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/src/classlists.qdoc b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/src/classlists.qdoc
new file mode 100644
index 000000000..2954e5beb
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/src/classlists.qdoc
@@ -0,0 +1,51 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+/*!
+ \page obsolete-classes.html
+ \title Obsolete Classes
+
+ \section1 Classes with obsolete members
+ \generatelist obsoletecppmembers
+
+ \section2 TestQDoc
+*/
+
+/*!
+ \page autolinking.html
+ \title Autolinking
+
+ //! a section title that qualifies for autolinking
+ \section1 TestQDoc
+
+ The string TestQDoc links to the C++ namespace unless linking explicitly,
+ \l {#TestQDoc}{like this}, or \l {TestQDoc}{this}. Also,
+
+ Autolinks:
+
+ \list
+ \li TestQDoc::TestDerived
+ \endlist
+
+ Explicit links:
+
+ \list
+ \li \l [CPP] {TestQDoc::TestDerived}
+ \li \l {Obsolete Classes#TestQDoc}
+ \endlist
+
+ //! a section title shadowing a known property name
+ \section1 someProp
+*/
+
+/*!
+ \group cpptypes
+ \title Test C++ Types
+
+ \generatelist testgroup
+*/
+
+/*!
+ \externalpage https://doc.qt.io/qt/17-qdoc-commands-thread.html#reentrant-command
+ \title reentrant
+*/
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/src/dont.cpp b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/src/dont.cpp
new file mode 100644
index 000000000..39be5bbbf
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/src/dont.cpp
@@ -0,0 +1,22 @@
+// Copyright (C) 2020 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#include "dont.h"
+
+/*!
+ \class UnseenClass
+ \inmodule TestCPP
+ \brief A public but undocumented class.
+*/
+UnseenClass::UnseenClass()
+{
+}
+
+/*!
+ \class SeenClass
+ \inmodule TestCPP
+ \brief A public but undocumented class.
+*/
+SeenClass::SeenClass()
+{
+}
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/src/dont.h b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/src/dont.h
new file mode 100644
index 000000000..0614c713e
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/src/dont.h
@@ -0,0 +1,16 @@
+// Copyright (C) 2020 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#pragma once
+
+class UnseenClass
+{
+public:
+ UnseenClass();
+};
+
+class SeenClass : public UnseenClass
+{
+public:
+ SeenClass();
+};
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/src/examples/demos/demo/demo.cpp b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/src/examples/demos/demo/demo.cpp
new file mode 100644
index 000000000..906c6292f
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/src/examples/demos/demo/demo.cpp
@@ -0,0 +1,11 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+bool isOverThousand(int n)
+{
+//! [integer literal with separator]
+ if (n > 1'000)
+ return true;
+//! [integer literal with separator]
+ return false;
+}
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/src/examples/demos/demo/demo.pro b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/src/examples/demos/demo/demo.pro
new file mode 100644
index 000000000..dbe8ff3c0
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/src/examples/demos/demo/demo.pro
@@ -0,0 +1,2 @@
+TEMPLATE = aux
+message("Nothing to see here.")
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/src/examples/demos/demo/doc/src/demo.qdoc b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/src/examples/demos/demo/doc/src/demo.qdoc
new file mode 100644
index 000000000..21ebc5588
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/src/examples/demos/demo/doc/src/demo.qdoc
@@ -0,0 +1,11 @@
+// Copyright (C) 2020 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+/*!
+ \example demos/demo
+ \title Demo
+ \image leonardo-da-vinci.png
+ //! Icon made by Smashicons from www.flaticon.com
+
+ \snippet demos/demo/demo.cpp integer literal with separator
+*/
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/src/examples/demos/demo/dontxclude/CMakeLists.txt b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/src/examples/demos/demo/dontxclude/CMakeLists.txt
new file mode 100644
index 000000000..d29157aad
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/src/examples/demos/demo/dontxclude/CMakeLists.txt
@@ -0,0 +1,2 @@
+cmake_minimum_required(VERSION 3.16)
+project (DONTXCLUDEDIRS_QDOCTEST)
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/src/examples/demos/demo/excludes/CMakeLists.txt b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/src/examples/demos/demo/excludes/CMakeLists.txt
new file mode 100644
index 000000000..09b447642
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/src/examples/demos/demo/excludes/CMakeLists.txt
@@ -0,0 +1,2 @@
+cmake_minimum_required(VERSION 3.16)
+project (EXCLUDEDIR_QDOCTEST)
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/src/examples/demos/hidden/doc/src/hidden.qdoc b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/src/examples/demos/hidden/doc/src/hidden.qdoc
new file mode 100644
index 000000000..dddcbc074
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/src/examples/demos/hidden/doc/src/hidden.qdoc
@@ -0,0 +1,11 @@
+// Copyright (C) 2020 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+/*!
+ \example demos/hidden
+ \title Hidden Demo
+ \meta tag broken
+ \brief Tagged 'broken', does not appear in examples-manifest.xml.
+
+ Also missing an image, but that's OK as it's broken anyway.
+*/
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/src/examples/demos/hidden/hidden.pro b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/src/examples/demos/hidden/hidden.pro
new file mode 100644
index 000000000..dbe8ff3c0
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/src/examples/demos/hidden/hidden.pro
@@ -0,0 +1,2 @@
+TEMPLATE = aux
+message("Nothing to see here.")
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/src/images/01.png b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/src/images/01.png
new file mode 100644
index 000000000..d73ab969b
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/src/images/01.png
Binary files differ
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/src/images/leonardo-da-vinci.png b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/src/images/leonardo-da-vinci.png
new file mode 100644
index 000000000..854acb4ca
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/src/images/leonardo-da-vinci.png
Binary files differ
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/src/properties.qdoc b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/src/properties.qdoc
new file mode 100644
index 000000000..1370f813b
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/src/properties.qdoc
@@ -0,0 +1,55 @@
+// Copyright (C) 2020 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+/*!
+ \property TestQDoc::TestDerived::bindableProp
+ Some property.
+
+ \sa someProp
+*/
+
+/*!
+ \property TestQDoc::TestDerived::someProp
+ Another property.
+*/
+
+/*!
+ \property TestQDoc::TestDerived::name
+ \brief a name.
+*/
+
+/*!
+ \property TestQDoc::TestDerived::intProp
+ An integer property.
+*/
+
+/*!
+ \property TestQDoc::TestDerived::boolProp
+ A boolean property.
+*/
+
+/*!
+ \fn TestQDoc::TestDerived::invokeMe() const
+ \brief Something invokable.
+*/
+
+/*!
+ \qmlmodule TheModule
+*/
+
+/*!
+ \qmltype TheType
+ \nativetype TestQDoc::TestDerived
+ \inqmlmodule TheModule
+*/
+
+/*!
+ \qmlproperty string TheType::name
+ \brief Read-only status of this property is resolved from Q_PROPERTY.
+*/
+
+/*!
+ //! avoid link warnings for auto-generated links to QProperty
+ \externalpage https://wiki.qt.io/QProperty
+ \title QProperty
+*/
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/src/qml/DocTest.qml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/src/qml/DocTest.qml
new file mode 100644
index 000000000..dfae6f13d
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/src/qml/DocTest.qml
@@ -0,0 +1,86 @@
+// Copyright (C) 2019 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+import QtQuick 2.0
+
+/*!
+ \qmltype DocTest
+ \inherits Test
+ \inqmlmodule QDoc.Test
+ \brief Represents a doc test case.
+ \since QDoc.Test 0.9
+
+ \section1 Introduction
+
+ A documentation test case, itself documented inline in \DocTest.qml.
+*/
+Item {
+ id: testCase
+
+ /*!
+ \qmlsignal QDocTest::completed
+ */
+ signal completed
+
+ /*!
+ \qmlsignal DocTest::test(var bar)
+ Signal with parameter \a bar.
+ */
+ signal foo(var bar)
+
+ /*!
+ Signals that something is \a really happening.
+ */
+ signal itsHappening(bool really)
+
+ /*!
+ \qmlproperty string DocTest::name
+
+ Name of the test.
+ \qml
+ DocTest {
+ name: "test"
+ // ...
+ }
+ \endqml
+ */
+ required property string name
+
+ /*!
+ Whether the test is active.
+ \default true
+
+ \sa name
+ */
+ property bool active: true
+
+ /*! \internal */
+ property int doctest_internal: -1
+
+ /*!
+ \qmlmethod DocTest::fail(message = "oops")
+ \since QDoc.Test 1.0
+
+ Fails the current test case, with the optional \a message.
+ */
+ function fail(msg) {
+ if (msg === undefined)
+ msg = "oops";
+ }
+
+ /*! \internal */
+ function doctest_fail(msg) {
+ if (msg === undefined)
+ msg = "";
+ }
+
+ /*!
+ \brief Fails the current test case, hard.
+ \list
+ \li Prints out \a msg.
+ \li Accepts a random \a option.
+ \endlist
+ */
+ function fail_hard(msg = "facepalm", option = 123) {
+ }
+}
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/src/qml/cmaketest/CMakeLists.txt b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/src/qml/cmaketest/CMakeLists.txt
new file mode 100644
index 000000000..89eafa300
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/src/qml/cmaketest/CMakeLists.txt
@@ -0,0 +1,2 @@
+cmake_minimum_required(VERSION 3.16)
+project (QDOCTEST)
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/src/qml/cmaketest/doc/src/cmaketest.qdoc b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/src/qml/cmaketest/doc/src/cmaketest.qdoc
new file mode 100644
index 000000000..0e8a2ab3c
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/src/qml/cmaketest/doc/src/cmaketest.qdoc
@@ -0,0 +1,9 @@
+// Copyright (C) 2020 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+/*!
+ \example cmaketest
+ \title CMake Example Project
+ \image leonardo-da-vinci.png
+ //! Icon made by Smashicons from www.flaticon.com
+*/
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/src/qml/cmaketest/main.cpp b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/src/qml/cmaketest/main.cpp
new file mode 100644
index 000000000..68d71eb71
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/src/qml/cmaketest/main.cpp
@@ -0,0 +1 @@
+void main(){}
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/src/qml/componentset/ProgressBar.qml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/src/qml/componentset/ProgressBar.qml
new file mode 100644
index 000000000..633a3ff5d
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/src/qml/componentset/ProgressBar.qml
@@ -0,0 +1,98 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick 1.0
+
+/*!
+ \qmltype ProgressBar
+ \inqmlmodule UIComponents
+ \brief A component that shows the progress of an event.
+
+ A ProgressBar shows the linear progress of an event as its \l value.
+ The range is specified using the \l {minimum} and the \l{maximum} values.
+
+ The ProgressBar component is part of the \l {UI Components} module.
+
+ This documentation is part of the \l{componentset}{UIComponents} example.
+*/
+Item {
+ id: progressbar
+
+ /*!
+ The minimum value of the ProgressBar range.
+ The \l value must not be less than this value.
+ */
+ property int minimum: 0
+
+ /*!
+ The maximum value of the ProgressBar range.
+ The \l value must not be more than this value.
+ */
+ property int maximum: 100
+
+ /*!
+ The value of the progress.
+ */
+ property int value: 0
+
+ /*!
+ \qmlproperty color ProgressBar::color
+ The color of the ProgressBar's gradient. Must bind to a color type.
+
+ \omit
+ The "\qmlproperty <type> <property name>" is needed because
+ property alias need to have their types manually entered.
+
+ QDoc will not publish the documentation within omit and endomit.
+ \endomit
+
+ \sa secondColor
+ */
+ property alias color: gradient1.color
+
+ /*!
+ \qmlproperty color ProgressBar::secondColor
+ The second color of the ProgressBar's gradient.
+ Must bind to a color type.
+
+ \omit
+ The "\qmlproperty <type> <property name>" is needed because
+ property alias need to have their types manually entered.
+
+ QDoc will not publish the documentation within omit and endomit.
+ \endomit
+
+ \sa color
+ */
+ property alias secondColor: gradient2.color
+
+ width: 250; height: 23
+ clip: true
+
+ Rectangle {
+ id: highlight
+
+ /*!
+ An internal documentation comment. The widthDest property is not
+ a public API and therefore will not be exposed.
+ */
+ property int widthDest: ((progressbar.width * (value - minimum)) / (maximum - minimum) - 6)
+
+ width: highlight.widthDest
+ Behavior on width { SmoothedAnimation { velocity: 1200 } }
+
+ anchors { left: parent.left; top: parent.top; bottom: parent.bottom; margins: 3 }
+ radius: 1
+ gradient: Gradient {
+ GradientStop { id: gradient1; position: 0.0 }
+ GradientStop { id: gradient2; position: 1.0 }
+ }
+
+ }
+ Text {
+ anchors { right: highlight.right; rightMargin: 6; verticalCenter: parent.verticalCenter }
+ color: "white"
+ font.bold: true
+ text: Math.floor((value - minimum) / (maximum - minimum) * 100) + '%'
+ }
+}
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/src/qml/componentset/Switch.qml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/src/qml/componentset/Switch.qml
new file mode 100644
index 000000000..123e86468
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/src/qml/componentset/Switch.qml
@@ -0,0 +1,105 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick 1.0
+
+/*!
+ \qmltype ToggleSwitch
+ \inqmlmodule UIComponents
+ \brief A component that can be turned on or off.
+
+ A toggle switch has two states: an \c on and an \c off state. The \c off
+ state is when the \l on property is set to \c false.
+
+ The ToggleSwitch component is part of the \l {UI Components} module.
+
+ This documentation is part of the \l{componentset}{UIComponents} example.
+
+*/
+Item {
+ id: toggleswitch
+ width: background.width; height: background.height
+
+ /*!
+ Indicates the state of the switch. If \c false, then the switch is in
+ the \c off state.
+
+ \omit
+ The \qmlproperty <type> <propertyname> is not necessary as QDoc
+ will associate this property to the ToggleSwitch
+
+ QDoc will not publish the documentation within omit and endomit.
+ \endomit
+ */
+ property bool on: false
+
+
+ /*!
+ A method to toggle the switch. If the switch is \c on, the toggling it
+ will turn it \c off. Toggling a switch in the \c off position will
+ turn it \c on.
+ */
+ function toggle() {
+ if (toggleswitch.state == "on")
+ toggleswitch.state = "off";
+ else
+ toggleswitch.state = "on";
+ }
+
+
+ /*!
+ \internal
+
+ An internal function to synchronize the switch's internals. This
+ function is not for public access. The \internal command will
+ prevent QDoc from publishing this comment in the public API.
+ */
+ function releaseSwitch() {
+ if (knob.x == 1) {
+ if (toggleswitch.state == "off") return;
+ }
+ if (knob.x == 78) {
+ if (toggleswitch.state == "on") return;
+ }
+ toggle();
+ }
+
+ Rectangle {
+ id: background
+ width: 130; height: 48
+ radius: 48
+ color: "lightsteelblue"
+ MouseArea { anchors.fill: parent; onClicked: toggle() }
+ }
+
+ Rectangle {
+ id: knob
+ width: 48; height: 48
+ radius: width
+ color: "lightblue"
+
+ MouseArea {
+ anchors.fill: parent
+ drag.target: knob; drag.axis: Drag.XAxis; drag.minimumX: 1; drag.maximumX: 78
+ onClicked: toggle()
+ onReleased: releaseSwitch()
+ }
+ }
+
+ states: [
+ State {
+ name: "on"
+ PropertyChanges { target: knob; x: 78 }
+ PropertyChanges { target: toggleswitch; on: true }
+ },
+ State {
+ name: "off"
+ PropertyChanges { target: knob; x: 1 }
+ PropertyChanges { target: toggleswitch; on: false }
+ }
+ ]
+
+ transitions: Transition {
+ NumberAnimation { properties: "x"; easing.type: Easing.InOutQuad; duration: 200 }
+ }
+}
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/src/qml/componentset/TabWidget.qml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/src/qml/componentset/TabWidget.qml
new file mode 100644
index 000000000..57808c1a5
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/src/qml/componentset/TabWidget.qml
@@ -0,0 +1,146 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick 1.0
+
+/*!
+ \qmltype TabWidget
+ \inqmlmodule UIComponents
+ \brief A widget that places its children as tabs.
+
+ A TabWidget places its children as tabs in a view. Selecting
+ a tab involves selecting the tab at the top.
+
+ The TabWidget component is part of the \l {UI Components} module.
+
+ This documentation is part of the \l{componentset}{UIComponents} example.
+
+ \section1 Adding Tabs
+
+ To add a tab, declare the tab as a child of the TabWidget.
+
+ \code
+ TabWidget {
+ id: tabwidget
+
+ Rectangle {
+ id: tab1
+ color: "red"
+ //... omitted
+ }
+ Rectangle {
+ id: tab2
+ color: "blue"
+ //... omitted
+ }
+
+ }
+ \endcode
+
+*/
+Item {
+ id: tabWidget
+
+ /*!
+ \internal
+
+ Setting the default property to stack.children means any child items
+ of the TabWidget are actually added to the 'stack' item's children.
+
+ See the \l{"Property Binding in QML"}
+ documentation for details on default properties.
+
+ This is an implementation detail, not meant for public knowledge. Putting
+ the \internal command at the beginning will cause QDoc to not publish this
+ documentation in the public API page.
+
+ Normally, a property alias needs to have a
+ "\qmlproperty <type> <propertyname>" to assign the alias a type.
+
+ */
+ default property alias content: stack.children
+
+
+ /*!
+ The currently active tab in the TabWidget.
+ */
+ property int current: 0
+
+ /*!
+ A sample \c{read-only} property.
+ A contrived property to demonstrate QDoc's ability to detect
+ read-only properties.
+
+ The signature is:
+ \code
+ readonly property int sampleReadOnlyProperty: 0
+ \endcode
+
+ Note that the property must be initialized to a value.
+
+ */
+ readonly property int sampleReadOnlyProperty: 0
+
+ /*!
+ \internal
+
+ This handler is an implementation
+ detail. The \c{\internal} command will prevent QDoc from publishing this
+ documentation on the public API.
+ */
+ onCurrentChanged: setOpacities()
+ Component.onCompleted: setOpacities()
+
+ /*!
+ \internal
+
+ An internal function to set the opacity.
+ The \internal command will prevent QDoc from publishing this
+ documentation on the public API.
+ */
+ function setOpacities() {
+ for (var i = 0; i < stack.children.length; ++i) {
+ stack.children[i].opacity = (i == current ? 1 : 0)
+ }
+ }
+
+ Row {
+ id: header
+
+ Repeater {
+ model: stack.children.length
+ delegate: Rectangle {
+ width: tabWidget.width / stack.children.length; height: 36
+
+ Rectangle {
+ width: parent.width; height: 1
+ anchors { bottom: parent.bottom; bottomMargin: 1 }
+ color: "#acb2c2"
+ }
+ BorderImage {
+ anchors { fill: parent; leftMargin: 2; topMargin: 5; rightMargin: 1 }
+ border { left: 7; right: 7 }
+ source: "tab.png"
+ visible: tabWidget.current == index
+ }
+ Text {
+ horizontalAlignment: Qt.AlignHCenter; verticalAlignment: Qt.AlignVCenter
+ anchors.fill: parent
+ text: stack.children[index].title
+ elide: Text.ElideRight
+ font.bold: tabWidget.current == index
+ }
+ MouseArea {
+ anchors.fill: parent
+ onClicked: tabWidget.current = index
+ }
+ }
+ }
+ }
+
+ Item {
+ id: stack
+ width: tabWidget.width
+ anchors.top: header.bottom; anchors.bottom: tabWidget.bottom
+ }
+}
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/src/qml/componentset/componentset.pro b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/src/qml/componentset/componentset.pro
new file mode 100644
index 000000000..5b44737c2
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/src/qml/componentset/componentset.pro
@@ -0,0 +1,5 @@
+SOURCES = componentset.pro \
+ ProgressBar.qml \
+ Switch.qml \
+ TabWidget.qml \
+ uicomponents.qdoc
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/src/qml/componentset/componentset.qml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/src/qml/componentset/componentset.qml
new file mode 100644
index 000000000..18b25884a
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/src/qml/componentset/componentset.qml
@@ -0,0 +1,7 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+import QtQuick 2.0
+
+Item {
+}
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/src/qml/componentset/examples.qdoc b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/src/qml/componentset/examples.qdoc
new file mode 100644
index 000000000..1069c55fd
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/src/qml/componentset/examples.qdoc
@@ -0,0 +1,82 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+
+/*!
+ \example componentset
+ \title QML Documentation Example
+ \brief Example for documenting QML types.
+
+ \testnoautolist
+
+ \meta tags {test,sample}
+ \meta tag {application}
+ \meta category {Application Example}
+ \meta installpath tutorials
+
+ This example demonstrates one of the ways to document QML types. It also
+ generates a warning about a missing example image, on purpose.
+
+ In particular, there are sample types that are documented with QDoc
+ commands comments. There are documentation comments for the QML types
+ and their public interfaces. The types are grouped into a module, the
+ \l{UI Components} module.
+
+ The uicomponents.qdoc file generates
+ the overview page for the \l{UI Components} module page.
+
+ The generated documentation is available in the \l{UI Components} module.
+
+ \section1 QML Class
+
+ The QML types use the \\qmltype to document the
+ type. In addition, they have the \\inmodule
+ command in order for QDoc to associate them to the \c UIComponents module.
+
+ QDoc uses the \\brief command to place a basic
+ description when listing the types.
+
+ \section1 Properties, Signals, Handlers, and Methods
+
+ The types have their properties, signals, handlers, and methods
+ defined in their respective QML files. QDoc associates the properties and
+ methods to the types, therefore, you only need to place the
+ documentation above the property, method, or signal.
+
+ To document the type of a \e {property alias}, you must use the
+ \\qmlproperty command to specify the data type.
+
+ \code
+ \qmlproperty int anAliasedProperty
+ An aliased property of type int.
+ \endcode
+
+ \section2 Internal Documentation
+
+ You may declare that a documentation is for internal use by placing the
+ \\internal command after the beginning QDoc comment
+ \begincomment. QDoc will prevent the internal documentation from appearing
+ in the public API.
+
+ If you wish to omit certain parts of the documentation, you may use the
+ \\omit and \\endomit command.
+
+ \section1 QML Types with C++ Implementation
+
+ This example only demonstrates the documentation for types in QML
+ files, but the regular QML commands may be placed
+ inside C++ classes to define the public API of the QML type.
+
+*/
+
+
+/*!
+ \qmlmodule UIComponents 1.0
+ \title UI Components
+ \brief Basic set of UI components.
+
+ This is a listing of a list of UI components implemented by QML types. These
+ files are available for general import and they are based on the
+ Qt Quick Code Samples.
+
+ This module is part of the \l{componentset}{UIComponents} example.
+*/
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/src/qml/componentset/uicomponents.qdoc.sample b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/src/qml/componentset/uicomponents.qdoc.sample
new file mode 100644
index 000000000..efbcd9511
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/src/qml/componentset/uicomponents.qdoc.sample
@@ -0,0 +1,14 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+
+/*!
+ \qmlmodule UIComponents 1.0
+ \title UI Components
+ \brief Basic set of UI components
+
+ This is a listing of a list of UI components implemented by QML types. These
+ files are available for general import and they are based off the \l{Qt
+ Quick Code Samples}.
+
+ This module is part of the \l{componentset}{UIComponents} example.
+*/
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/src/qml/doctest/DocTest.qml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/src/qml/doctest/DocTest.qml
new file mode 100644
index 000000000..07201403f
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/src/qml/doctest/DocTest.qml
@@ -0,0 +1,11 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+import QtQuick
+
+/*!
+ //! omit the \qmltype command, file name (base) is the type name.
+ \inqmlmodule Test.NoVer
+ \brief Shadows the type name in QDoc.Test module.
+*/
+Item {}
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/src/qml/modules.qdoc b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/src/qml/modules.qdoc
new file mode 100644
index 000000000..3b17b5d92
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/src/qml/modules.qdoc
@@ -0,0 +1,19 @@
+// Copyright (C) 2020 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+/*!
+ \page qmlmodules.html
+ \title QML Modules
+
+ \generatelist qml-modules
+
+ \section1 QML types
+
+ \generatelist qmltypesbymodule QDoc.Test
+
+ \section1 QML value types
+
+ \generatelist qmlvaluetypes
+
+ \generatelist qmlvaluetypesbymodule QDoc.Test
+*/
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/src/qml/parent.qdoc b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/src/qml/parent.qdoc
new file mode 100644
index 000000000..d6bbb5738
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/src/qml/parent.qdoc
@@ -0,0 +1,87 @@
+// Copyright (C) 2019 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+/*!
+ \qmltype AbstractParent
+ \inqmlmodule QDoc.Test
+ \qmlabstract
+ \brief Abstract base QML type.
+*/
+
+/*!
+ \qmlproperty list<Child> AbstractParent::children
+ \qmldefault
+ \brief Children of the type.
+*/
+
+/*!
+ \qmlmethod void AbstractParent::rear(Child child, var method = Strict)
+ \brief Do some abstract parenting on \a child using a specific \a method.
+*/
+
+/*!
+ \qmlproperty string AbstractParent::name
+ \brief Name of this parent.
+*/
+
+/*!
+ \qmlmethod void AbstractParent::name(Child child, name)
+ \brief Name a \a child using \a name.
+
+*/
+
+/*!
+ \qmlmethod void AbstractParent::name()
+ \brief Name all children with random names.
+*/
+
+/*!
+ \qmltype Child
+ \inqmlmodule QDoc.Test
+ \inherits AbstractParent
+ \brief A Child inheriting its parent.
+*/
+
+/*!
+ //! override from abstract base
+ \qmlproperty string Child::name
+ \brief Name of this child.
+*/
+
+/*!
+ //! override from abstract base
+ \qmlmethod void Child::name(Child child, name)
+ \brief Name a \a child of this child using \a name.
+*/
+
+/*!
+ \qmlvaluetype int
+ \inqmlmodule QDoc.Test
+
+ \brief An integer value type.
+*/
+
+/*!
+ \qmlmethod int int::abs()
+ Returns the absolute value of this integer.
+*/
+
+/*!
+ \qmltype InternParent
+ \inqmlmodule QDoc.Test
+ \internal
+ \qmlabstract
+ \brief Internal abstract base QML type.
+*/
+
+/*!
+ \qmlproperty int InternParent::prop
+ \brief Propagated to inheriting type docs.
+*/
+
+/*!
+ \qmltype YetAnotherChild
+ \inherits InternParent
+ \inqmlmodule QDoc.Test
+ \brief A type inheriting from internal abstract parent.
+*/
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/src/qml/type.cpp b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/src/qml/type.cpp
new file mode 100644
index 000000000..956616ed7
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/src/qml/type.cpp
@@ -0,0 +1,133 @@
+// Copyright (C) 2019 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "testcpp.h"
+
+/*!
+ \qmlmodule QDoc.Test \QDocTestVer
+ \title QDoc.Test QML Module
+ \brief QML Types for the Test module.
+ \since 1.1
+ \preliminary
+
+ \testnoautolist
+*/
+
+/*!
+ \qmlmodule Test.Empty 1.0
+ \title No QML Types Here
+ \brief A QML module with no member types.
+*/
+
+/*!
+ \qmlmodule Test.NoVer
+ \title Versionless QML Module
+ \brief QML Types for the Test module without version.
+ \since 1.1
+ \modulestate Tech Preview
+*/
+
+/*!
+ \qmltype Type
+ \nativetype TestQDoc::Test
+ \inqmlmodule QDoc.Test
+ \brief A QML type documented in a .cpp file.
+ \meta status { <Work In Progress> }
+*/
+
+/*!
+ \qmltype TypeNoVersion
+ \inqmlmodule Test.NoVer
+ \brief Another QML type documented in a .cpp file.
+*/
+
+/*!
+ \qmltype OldType
+ \inqmlmodule QDoc.Test
+ \brief Deprecated old type.
+ \deprecated [1.0]
+*/
+
+/*!
+ \qmlproperty int Type::id
+ \readonly
+ \brief A read-only property.
+*/
+
+/*!
+ \qmlproperty string QDoc.Test::Type::name
+ \required
+ \brief Name of the Test.
+*/
+
+/*!
+ \qmlattachedproperty enumeration Type::type
+ \default Type.NoType
+
+ \value Type.NoType
+ Nothing
+ \value Type.SomeType
+ Something
+*/
+
+/*!
+ \qmlproperty int Type::group.first
+ \qmlproperty int Type::group.second
+ \qmlproperty int Type::group.third
+
+ \brief A property group.
+*/
+
+/*!
+ \qmlsignal Type::group.created
+
+ This signal is prefixed with \e group.
+*/
+
+/*!
+ \qmlproperty int Type::fourth
+ \qmlproperty int Type::fifth
+
+ \brief A group of properties sharing a documentation comment.
+*/
+
+/*!
+ \qmlmethod Type Type::copy(a)
+
+ Returns another Type based on \a a.
+*/
+
+/*!
+ \qmlmethod Type::enable()
+ \qmlmethod Type::disable()
+
+ Enables or disables this type.
+*/
+
+/*!
+ \qmlsignal Type::completed(int status)
+
+ This signal is emitted when the operation completed with \a status.
+*/
+
+/*!
+ \qmlattachedsignal Type::configured()
+
+ This attached signal is emitted when the type was configured.
+*/
+
+/*!
+ \qmlmethod Type::deprecatedMethod()
+
+ \deprecated [6.2] This method has no replacement.
+
+ This is a method that should include information about being deprecated
+ and that it has been so since 6.2 in its docs.
+*/
+
+/*!
+ \qmlmethod Type::futureDeprecated()
+ \deprecated [6.3] Use something else instead.
+
+ This is a method that's marked for deprecation in a future version.
+*/
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/src/snippets/snippet_testcpp.cpp b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/src/snippets/snippet_testcpp.cpp
new file mode 100644
index 000000000..1660fbc2b
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/src/snippets/snippet_testcpp.cpp
@@ -0,0 +1,3 @@
+//! [random tag]
+You're not supposed to see this.
+//! [random tag]
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/src/testcpp.cpp b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/src/testcpp.cpp
new file mode 100644
index 000000000..b14270e45
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/src/testcpp.cpp
@@ -0,0 +1,418 @@
+// Copyright (C) 2019 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+#include "testcpp.h"
+
+namespace TestQDoc {
+
+/*
+//! [random tag]
+\note This is just a test.
+//! [random tag]
+
+//! [args]
+\1\2 \3 \2\1
+//! [args]
+*/
+
+/*!
+ \module TestCPP
+ \qtvariable testcpp
+ \qtcmakepackage QDocTest
+ \title QDoc Test C++ Classes
+ \brief A test module page.
+ \since 2.0
+
+ \testnoautolist
+
+ \include testcpp.cpp random tag
+ \include testcpp.cpp {args} {/} {*} {Look, Ma! {I'm made of arguments!}}
+
+\if defined(test_nestedmacro)
+ \versionnote {module} {\ver 5.15.0}
+ \ver 1.0.0
+\endif
+
+ \section1 Linking to function-like things
+
+ \list
+ \li \l {TestQDoc::Test::someFunctionDefaultArg}
+ {someFunctionDefaultArg()}
+ \li QDOCTEST_MACRO2()
+ \li \l {TestQDoc::Test::}{QDOCTEST_MACRO2(int &x)}
+ \li \l {section()}
+ \li \l {section()} {section() is a section title}
+ \li \l {TestQDoc::Test::Test()} {open( parenthesis}
+ \li \l {https://en.cppreference.com/w/cpp/utility/move}
+ {C++11 added std::move(T&& t)}
+ \endlist
+
+ \section2 section()
+*/
+
+/*!
+ \namespace TestQDoc
+ \inheaderfile TestCPP
+ \inmodule TestCPP
+ \brief A namespace.
+
+ \section1 Usage
+ This namespace is for testing QDoc output.
+*/
+
+/*!
+ \class TestQDoc::Test
+ \inmodule TestCPP
+ \brief A class in a namespace.
+
+\if defined(test_ignoresince)
+ //! omitted by ignoresince
+ \since 1.1
+\endif
+ \ingroup testgroup
+ \ingroup cpptypes
+ \reentrant
+*/
+
+/*!
+ \fn TestQDoc::Test::Test()
+
+ The constructor is deleted.
+*/
+
+/*!
+ \fn Test &Test::operator=(Test &&other)
+ \ingroup testgroup
+
+ The move assignment operator is deleted. \a other cannot be moved from.
+*/
+
+/*!
+ \class TestQDoc::TestDerived
+ \inmodule TestCPP
+ \brief A class in a namespace, derived from \l [CPP] Test.
+*/
+
+/*!
+ \macro QDOCTEST_MACRO
+ \relates TestQDoc
+\if defined(test_ignoresince)
+ //! omitted by ignoresince.Test
+ \since Test 0.9
+\endif
+*/
+
+/*!
+ \macro QDOCTEST_MACRO2(int &x)
+ \relates TestQDoc::Test
+ \since Test 1.1
+ \brief A macro with argument \a x.
+ \ingroup testgroup
+*/
+
+/*!
+\if defined(test_properties)
+ \property Test::id
+\else
+ \nothing
+\endif
+*/
+
+/*!
+ \deprecated [6.0] Use someFunction() instead.
+*/
+void Test::deprecatedMember()
+{
+ return;
+}
+
+/*!
+ \obsolete
+
+ Use someFunction() instead.
+*/
+void Test::obsoleteMember()
+{
+ return;
+}
+
+/*!
+ \obsolete Use obsoleteMember() instead.
+*/
+void Test::anotherObsoleteMember()
+{
+ return;
+}
+
+/*!
+ \nonreentrant
+ Function that takes a parameter \a i and \a b.
+\if defined(test_ignoresince)
+ \since 2.0
+\endif
+ \ingroup testgroup
+*/
+void Test::someFunctionDefaultArg(int i, bool b = false) const
+{
+ return;
+}
+
+/*!
+ \fn void Test::func(bool)
+ \internal
+*/
+
+/*!
+ \fn [funcPtr] void (*funcPtr(bool b, const char *s))(bool)
+
+ Returns a pointer to a function that takes a boolean. Uses \a b and \a s.
+*/
+
+/*!
+ \fn [op-inc] Test::operator++()
+ \fn [op-dec] Test::operator--()
+ \deprecated
+*/
+
+/*!
+ This method has en dashes in its documentation -- as you'll find
+ represented by \c{--} in the sources -- here and there. The important bit
+ to note is that when passed e.g. to the \\c command, the two hyphens are
+ processed as input to the command and not replaced by an en dash. This also
+ applies to code blocks, where otherwise, the decrement operator would get
+ completely borked:
+
+ \code
+ for (int i = 42; i > 0; --i)
+ // Do something cool during countdown.
+ \endcode
+
+ ...as it would be silly if this would output --i instead of \c {--i}.
+
+ -----------------------------------------------------------------------
+
+ It still allows people to add a bunch of dashes, though, without replacing
+ them all with a series of en dashes. Of course, they might want to use the
+ \\hr command instead, like this:
+ \hr
+
+ -- You can also start a new paragraph with an en dash, if you want to.
+
+ //! Self-referencing \sa-command for tests.
+ \sa methodWithEnDashInItsDocs
+*/
+void Test::methodWithEnDashInItsDocs()
+{
+ // Nothing to see here.
+}
+
+/*!
+ This method has em dashes in its documentation---as you'll find
+ represented by \c{---} in the sources---here and there. The important bit
+ to note is that when passed e.g. to the \\c command, the three hyphens are
+ processed as input to the command and not replaced by an em dash.
+
+ -----------------------------------------------------------------------
+
+ People can still add a bunch of dashes, though, without QDoc replacing
+ them all with a series of em dashes.
+
+ ---You can also start a new paragraph with an em dash, if you want to.
+
+ \sa methodWithEnDashInItsDocs
+
+*/
+void Test::methodWithEmDashInItsDocs()
+{
+ // Woah! Look at that!
+}
+
+// Documented below with an \fn command. Unnecessary but we support it, and it's used.
+int Test::someFunction(int, int v)
+{
+ return v;
+}
+
+/*!
+ \fn void TestQDoc::Test::inlineFunction()
+
+ \brief An inline function, documented using the \CMDFN QDoc command.
+*/
+
+/*!
+ \fn int Test::someFunction(int, int v = 0)
+
+ Function that takes a parameter \a v.
+ Also returns the value of \a v.
+\if defined(test_ignoresince)
+ \since Test 1.0
+\endif
+*/
+
+/*!
+ Function that must be reimplemented.
+*/
+void Test::virtualFun()
+{
+ return;
+}
+
+/*!
+ \fn bool Test::operator==(const Test &lhs, const Test &rhs)
+
+ Returns true if \a lhs and \a rhs are equal.
+*/
+
+/*!
+ \typedef TestQDoc::Test::SomeType
+ \brief A typedef.
+*/
+
+/*!
+ \reimp
+*/
+void TestDerived::virtualFun()
+{
+ return;
+}
+
+/*!
+ \fn TestQDoc::Test::overload()
+ \fn Test::overload(bool b)
+ //! The second overload should match even without the fully qualified path
+
+ Overloads that share a documentation comment, optionally taking
+ a parameter \a b.
+*/
+
+/*!
+ \fn Test::overload(bool b)
+ \since Test 1.2
+*/
+
+/*!
+ \typealias TestQDoc::TestDerived::DerivedType
+ An aliased typedef.
+*/
+
+/*!
+ \typedef TestQDoc::TestDerived::NotTypedef
+ I'm an alias, not a typedef.
+*/
+
+/*!
+ \obsolete
+
+ Static obsolete method.
+*/
+void TestDerived::staticObsoleteMember()
+{
+ return;
+}
+
+/*!
+\if defined(test_properties)
+ \fn void TestDerived::emitSomething()
+ Emitted when things happen.
+\else
+ \nothing
+\endif
+*/
+
+/*!
+\if defined(test_properties)
+ \reimp
+\else
+ \nothing
+\endif
+*/
+int TestDerived::id()
+{
+ return 1;
+}
+
+/*!
+ Returns a value using an aliases type.
+*/
+TestDerived::NotTypedef TestDerived::someValue()
+{
+ return 0;
+}
+
+/*!
+\if defined(test_template)
+ \fn template <typename T1, typename T2> void TestQDoc::Test::funcTemplate(T1 a, T2 b)
+ \brief Function template with two parameters, \a a and \a b.
+\else
+ \nothing
+\endif
+*/
+
+/*!
+\if defined(test_template)
+ \struct TestQDoc::Test::Struct
+ \inmodule TestCPP
+ \brief Templated struct.
+\else
+ \nothing
+\endif
+*/
+
+/*!
+\if defined(test_template)
+ \typealias TestQDoc::Test::Specialized
+\else
+ \nothing
+\endif
+*/
+
+/*!
+\if defined(test_template)
+ \class TestQDoc::Vec
+ \inmodule TestCPP
+ \brief Type alias that has its own reference.
+\else
+ \nothing
+\endif
+*/
+
+/*!
+\if defined(test_template)
+ \macro Q_INVOKABLE
+ \relates TestQDoc::Test
+
+ This is a mock Q_INVOKABLE for the purpose of ensuring QDoc autolink to it
+ as expected.
+\else
+ \nothing
+\endif
+*/
+
+} // namespace TestQDoc
+
+
+/*!
+ \namespace CrossModuleRef
+ \inmodule TestCPP
+ \brief Namespace that has documented functions in multiple modules.
+ \since 3.0
+*/
+namespace CrossModuleRef {
+
+/*!
+ Document me!
+*/
+void documentMe()
+{
+}
+
+} // namespace CrossModuleRef
+
+/*!
+ \class DontLinkToMe
+ \inmodule TestCPP
+ \brief Class that does not generate documentation.
+*/
+
+/*!
+ \dontdocument (DontLinkToMe)
+*/
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/src/testcpp.h b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/src/testcpp.h
new file mode 100644
index 000000000..a72367b24
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/src/testcpp.h
@@ -0,0 +1,140 @@
+// Copyright (C) 2019 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+#pragma once
+
+#ifdef test_properties
+#include <QtCore/qmetaobject.h>
+#include <QtCore/qproperty.h>
+#include <QtCore/qstring.h>
+#endif
+
+#define QDOCTEST_MACRO test
+#define QDOCTEST_MACRO2(x) (x) < 0 ? 0 : (x)
+
+namespace TestQDoc {
+
+class Test {
+#ifdef test_properties
+ Q_OBJECT
+ Q_PROPERTY(int id READ id)
+#endif
+public:
+
+#ifdef test_template
+template<typename D, typename T> struct Struct {};
+template<typename T>
+using Specialized = Struct<int, T>;
+#endif
+
+#ifdef test_template
+# define Q_INVOKABLE void foo() {};
+#endif
+
+#ifdef test_scopedenum
+ enum ClassicEnum { Yee, Haw, Howdy, Partner };
+
+ enum class ScopedEnum : unsigned char {
+ This = 0x01,
+ That = 0x02,
+ All = This | That,
+ OmittedValue = 99,
+ UselessValue,
+ VeryLastValue
+ };
+#endif
+ typedef struct {
+ int data;
+ } SomeType;
+ int someFunction(int, int v = 0);
+ void someFunctionDefaultArg(int i, bool b) const;
+ void obsoleteMember();
+ void anotherObsoleteMember();
+ void deprecatedMember();
+ void methodWithEnDashInItsDocs();
+ void methodWithEmDashInItsDocs();
+ void func(bool) {};
+ //! [funcPtr]
+ void (*funcPtr(bool b, const char *s))(bool) {
+ return func;
+ }
+ //! [op-inc]
+ Test &operator++() { return *this; }
+ //! [op-dec]
+ Test &operator--() { return *this; }
+
+ void anotherFunc() {};
+ inline void inlineFunction() {};
+ virtual void virtualFun();
+
+ friend bool operator==(const Test &lhs, const Test &rhs) { return false; }
+
+ Test() = delete;
+ Test &operator=(Test &&other) = delete;
+
+protected:
+ void overload() {}
+ void overload(bool b) { if (!b) return; }
+#ifdef test_template
+ template <typename T1, typename T2> void funcTemplate(T1 a, T2 b) {
+ a = b;
+ }
+#endif
+#ifdef test_properties
+ virtual int id() { return 0; }
+#endif
+};
+
+class TestDerived : public Test {
+#ifdef test_properties
+ Q_OBJECT
+
+ Q_PROPERTY(QString bindableProp READ bindableProp WRITE setBindableProp NOTIFY bindablePropChanged BINDABLE bindableProp)
+ Q_PROPERTY(QString someProp READ someProp BINDABLE somBindableProp)
+ Q_PROPERTY(int *intProp READ getInt STORED false CONSTANT FINAL)
+ Q_PROPERTY(const QString *name READ name)
+ QDOC_PROPERTY(bool boolProp READ boolProp WRITE setBoolProp NOTIFY boolPropChanged RESET resetBoolProp REVISION 1)
+#endif
+
+public:
+ using DerivedType = Test::SomeType;
+ using NotTypedef = int;
+ void virtualFun() override;
+ static void staticObsoleteMember();
+ NotTypedef someValue();
+#ifdef test_properties
+ QBindable<QString> bindableProp();
+ QBindable<QString> someBindableProp();
+ const QString &someProp();
+ int *getInt();
+ bool boolProp();
+ const QString *name() const;
+
+ Q_INVOKABLE void invokeMe() const {}
+ int id() override;
+
+Q_SIGNALS:
+ void emitSomething(QPrivateSignal);
+ void bindablePropChanged();
+ Q_REVISION(1) void boolPropChanged();
+
+public Q_SLOTS:
+ void setBindableProp(const QString &s);
+ void setBoolProp(bool b);
+ void resetBoolProp();
+#endif
+};
+
+#ifdef test_template
+template <typename T>
+struct BaseVec {};
+template <typename T>
+using Vec = BaseVec<T>;
+#endif
+
+} // namespace TestQDoc
+
+namespace CrossModuleRef {
+ void documentMe();
+}
+
+class DontLinkToMe {};
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/src/unseenclass.qdoc b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/src/unseenclass.qdoc
new file mode 100644
index 000000000..8798e3147
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/comprehensiveproject/src/unseenclass.qdoc
@@ -0,0 +1,11 @@
+// Copyright (C) 2020 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+/*!
+ \dontdocument (UnseenClass)
+*/
+
+/*! \page classes.html
+ \title Classes
+ \generatelist annotatedclasses
+*/
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/cxx20/cxx20.qdocconf b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/cxx20/cxx20.qdocconf
new file mode 100644
index 000000000..f8943e417
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/cxx20/cxx20.qdocconf
@@ -0,0 +1,32 @@
+project = cxx20
+
+headerdirs = ./src
+sourcedirs = ./src
+exampledirs = ./src
+
+macro.equalitycomparesto = "\\compareswith equality \1\n\\endcompareswith"
+
+outputformats = WebXML HTML DocBook
+WebXML.quotinginformation = true
+WebXML.nosubdirs = true
+WebXML.outputsubdir = webxml
+
+HTML.nosubdirs = true
+HTML.outputsubdir = html
+
+DocBook.nosubdirs = true
+DocBook.outputsubdir = docbook
+
+sources.fileextensions = "*.qml *.cpp *.qdoc"
+headers.fileextensions = "*.h"
+
+# images
+imagedirs = ./src/images
+
+# zero warning policy
+warninglimit = 0
+warninglimit.enabled = true
+
+# don't write host system-specific paths to index files
+locationinfo = false
+
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/cxx20/expected/docbook/bar.xml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/cxx20/expected/docbook/bar.xml
new file mode 100644
index 000000000..5a6754700
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/cxx20/expected/docbook/bar.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<db:article xmlns:db="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" version="5.2" xml:lang="en">
+<db:info>
+<db:title>Bar Class</db:title>
+<db:productname>cxx20</db:productname>
+<db:titleabbrev>cxx20 Reference Documentation</db:titleabbrev>
+<db:abstract>
+<db:para>cxx20 Reference Documentation.</db:para></db:abstract>
+</db:info>
+<db:variablelist>
+<db:varlistentry>
+<db:term>Header</db:term>
+<db:listitem>
+<db:para>Bar</db:para>
+</db:listitem>
+</db:varlistentry>
+</db:variablelist>
+<db:section xml:id="details">
+<db:title>Detailed Description</db:title>
+</db:section>
+</db:article>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/cxx20/expected/docbook/baz.xml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/cxx20/expected/docbook/baz.xml
new file mode 100644
index 000000000..799e31128
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/cxx20/expected/docbook/baz.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<db:article xmlns:db="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" version="5.2" xml:lang="en">
+<db:info>
+<db:title>Baz Class</db:title>
+<db:productname>cxx20</db:productname>
+<db:titleabbrev>cxx20 Reference Documentation</db:titleabbrev>
+<db:abstract>
+<db:para>cxx20 Reference Documentation.</db:para></db:abstract>
+</db:info>
+<db:variablelist>
+<db:varlistentry>
+<db:term>Header</db:term>
+<db:listitem>
+<db:para>Baz</db:para>
+</db:listitem>
+</db:varlistentry>
+</db:variablelist>
+<db:section xml:id="details">
+<db:title>Detailed Description</db:title>
+</db:section>
+</db:article>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/cxx20/expected/docbook/comparesstronglywithoneclassandpartiallywithanother.xml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/cxx20/expected/docbook/comparesstronglywithoneclassandpartiallywithanother.xml
new file mode 100644
index 000000000..bbfadf732
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/cxx20/expected/docbook/comparesstronglywithoneclassandpartiallywithanother.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<db:article xmlns:db="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" version="5.2" xml:lang="en">
+<db:info>
+<db:title>ComparesStronglyWithOneClassAndPartiallyWithAnother Class</db:title>
+<db:productname>cxx20</db:productname>
+<db:titleabbrev>cxx20 Reference Documentation</db:titleabbrev>
+<db:abstract>
+<db:para>Class with various comparison relationships.</db:para>
+<db:para>This class is <db:emphasis role="bold">strongly comparable</db:emphasis> with <db:link xlink:href="foo.xml">Foo</db:link>.</db:para>
+<db:para>This class is <db:emphasis role="bold">partially comparable</db:emphasis> with <db:link xlink:href="bar.xml">Bar</db:link>.</db:para>
+<db:para>Here we describe partial comparison with Bar.</db:para>
+<db:para>This class is <db:emphasis role="bold">equality-comparable</db:emphasis> with <db:link xlink:href="foo.xml">Foo</db:link>.</db:para>
+</db:abstract>
+</db:info>
+<db:variablelist>
+<db:varlistentry>
+<db:term>Header</db:term>
+<db:listitem>
+<db:para>ComparesStronglyWithOneClassAndPartiallyWithAnother</db:para>
+</db:listitem>
+</db:varlistentry>
+</db:variablelist>
+<db:section xml:id="details">
+<db:title>Detailed Description</db:title>
+<db:para>This class compares strongly with one type, and partially with another.</db:para>
+</db:section>
+</db:article>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/cxx20/expected/docbook/comparesstronglywiththreeclasses.xml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/cxx20/expected/docbook/comparesstronglywiththreeclasses.xml
new file mode 100644
index 000000000..8c39c4fa7
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/cxx20/expected/docbook/comparesstronglywiththreeclasses.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<db:article xmlns:db="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" version="5.2" xml:lang="en">
+<db:info>
+<db:title>ComparesStronglyWithThreeClasses Class</db:title>
+<db:productname>cxx20</db:productname>
+<db:titleabbrev>cxx20 Reference Documentation</db:titleabbrev>
+<db:abstract>
+<db:para>This class is <db:emphasis role="bold">strongly comparable</db:emphasis> with <db:link xlink:href="foo.xml">Foo</db:link>, Bar Bar Jinks, and <db:link xlink:href="baz.xml">Baz</db:link>.</db:para>
+</db:abstract>
+</db:info>
+<db:variablelist>
+<db:varlistentry>
+<db:term>Header</db:term>
+<db:listitem>
+<db:para>ComparesStronglyWithThreeClasses</db:para>
+</db:listitem>
+</db:varlistentry>
+</db:variablelist>
+<db:section xml:id="details">
+<db:title>Detailed Description</db:title>
+</db:section>
+</db:article>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/cxx20/expected/docbook/comparesstronglywiththreeclassesacrossmultiplelines.xml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/cxx20/expected/docbook/comparesstronglywiththreeclassesacrossmultiplelines.xml
new file mode 100644
index 000000000..b216d2bbc
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/cxx20/expected/docbook/comparesstronglywiththreeclassesacrossmultiplelines.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<db:article xmlns:db="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" version="5.2" xml:lang="en">
+<db:info>
+<db:title>ComparesStronglyWithThreeClassesAcrossMultipleLines Class</db:title>
+<db:productname>cxx20</db:productname>
+<db:titleabbrev>cxx20 Reference Documentation</db:titleabbrev>
+<db:abstract>
+<db:para>This class is <db:emphasis role="bold">strongly comparable</db:emphasis> with <db:link xlink:href="foo.xml">Foo</db:link>, <db:link xlink:href="bar.xml">Bar</db:link>, and <db:link xlink:href="baz.xml">Baz</db:link>.</db:para>
+</db:abstract>
+</db:info>
+<db:variablelist>
+<db:varlistentry>
+<db:term>Header</db:term>
+<db:listitem>
+<db:para>ComparesStronglyWithThreeClassesAcrossMultipleLines</db:para>
+</db:listitem>
+</db:varlistentry>
+</db:variablelist>
+<db:section xml:id="details">
+<db:title>Detailed Description</db:title>
+</db:section>
+</db:article>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/cxx20/expected/docbook/comparesstronglywithtwoclasses.xml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/cxx20/expected/docbook/comparesstronglywithtwoclasses.xml
new file mode 100644
index 000000000..0c72faed6
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/cxx20/expected/docbook/comparesstronglywithtwoclasses.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<db:article xmlns:db="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" version="5.2" xml:lang="en">
+<db:info>
+<db:title>ComparesStronglyWithTwoClasses Class</db:title>
+<db:productname>cxx20</db:productname>
+<db:titleabbrev>cxx20 Reference Documentation</db:titleabbrev>
+<db:abstract>
+<db:para>This class is <db:emphasis role="bold">strongly comparable</db:emphasis> with <db:link xlink:href="foo.xml">Foo</db:link>.</db:para>
+<db:para>This class is <db:emphasis role="bold">strongly comparable</db:emphasis> with <db:link xlink:href="bar.xml">Bar</db:link>.</db:para>
+<db:warning>
+<db:para>Always compare twice!</db:para>
+</db:warning>
+</db:abstract>
+</db:info>
+<db:variablelist>
+<db:varlistentry>
+<db:term>Header</db:term>
+<db:listitem>
+<db:para>ComparesStronglyWithTwoClasses</db:para>
+</db:listitem>
+</db:varlistentry>
+</db:variablelist>
+<db:section xml:id="details">
+<db:title>Detailed Description</db:title>
+</db:section>
+</db:article>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/cxx20/expected/docbook/equalitycomparableclass.xml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/cxx20/expected/docbook/equalitycomparableclass.xml
new file mode 100644
index 000000000..ed91bb03b
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/cxx20/expected/docbook/equalitycomparableclass.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<db:article xmlns:db="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" version="5.2" xml:lang="en">
+<db:info>
+<db:title>EqualityComparableClass Class</db:title>
+<db:productname>cxx20</db:productname>
+<db:titleabbrev>cxx20 Reference Documentation</db:titleabbrev>
+<db:abstract>
+<db:para>This class is <db:emphasis>equality-comparable</db:emphasis>.</db:para>
+</db:abstract>
+</db:info>
+<db:variablelist>
+<db:varlistentry>
+<db:term>Header</db:term>
+<db:listitem>
+<db:para>EqualityComparableClass</db:para>
+</db:listitem>
+</db:varlistentry>
+</db:variablelist>
+<db:section xml:id="details">
+<db:title>Detailed Description</db:title>
+</db:section>
+</db:article>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/cxx20/expected/docbook/foo.xml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/cxx20/expected/docbook/foo.xml
new file mode 100644
index 000000000..6eee2e060
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/cxx20/expected/docbook/foo.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<db:article xmlns:db="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" version="5.2" xml:lang="en">
+<db:info>
+<db:title>Foo Class</db:title>
+<db:productname>cxx20</db:productname>
+<db:titleabbrev>cxx20 Reference Documentation</db:titleabbrev>
+<db:abstract>
+<db:para>cxx20 Reference Documentation.</db:para></db:abstract>
+</db:info>
+<db:variablelist>
+<db:varlistentry>
+<db:term>Header</db:term>
+<db:listitem>
+<db:para>Foo</db:para>
+</db:listitem>
+</db:varlistentry>
+</db:variablelist>
+<db:section xml:id="details">
+<db:title>Detailed Description</db:title>
+</db:section>
+</db:article>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/cxx20/expected/docbook/partiallyorderedclass.xml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/cxx20/expected/docbook/partiallyorderedclass.xml
new file mode 100644
index 000000000..1be6bcba6
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/cxx20/expected/docbook/partiallyorderedclass.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<db:article xmlns:db="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" version="5.2" xml:lang="en">
+<db:info>
+<db:title>PartiallyOrderedClass Class</db:title>
+<db:productname>cxx20</db:productname>
+<db:titleabbrev>cxx20 Reference Documentation</db:titleabbrev>
+<db:abstract>
+<db:para>This class is <db:emphasis>partially comparable</db:emphasis>.</db:para>
+</db:abstract>
+</db:info>
+<db:variablelist>
+<db:varlistentry>
+<db:term>Header</db:term>
+<db:listitem>
+<db:para>PartiallyOrderedClass</db:para>
+</db:listitem>
+</db:varlistentry>
+</db:variablelist>
+<db:section xml:id="details">
+<db:title>Detailed Description</db:title>
+</db:section>
+</db:article>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/cxx20/expected/docbook/stronglyorderedclass.xml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/cxx20/expected/docbook/stronglyorderedclass.xml
new file mode 100644
index 000000000..146860d7b
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/cxx20/expected/docbook/stronglyorderedclass.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<db:article xmlns:db="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" version="5.2" xml:lang="en">
+<db:info>
+<db:title>StronglyOrderedClass Class</db:title>
+<db:productname>cxx20</db:productname>
+<db:titleabbrev>cxx20 Reference Documentation</db:titleabbrev>
+<db:abstract>
+<db:para>This class is <db:emphasis>strongly comparable</db:emphasis>.</db:para>
+</db:abstract>
+</db:info>
+<db:variablelist>
+<db:varlistentry>
+<db:term>Header</db:term>
+<db:listitem>
+<db:para>StronglyOrderedClass</db:para>
+</db:listitem>
+</db:varlistentry>
+</db:variablelist>
+<db:section xml:id="details">
+<db:title>Detailed Description</db:title>
+</db:section>
+</db:article>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/cxx20/expected/docbook/weaklyorderedclass.xml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/cxx20/expected/docbook/weaklyorderedclass.xml
new file mode 100644
index 000000000..39bbdeeb9
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/cxx20/expected/docbook/weaklyorderedclass.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<db:article xmlns:db="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" version="5.2" xml:lang="en">
+<db:info>
+<db:title>WeaklyOrderedClass Class</db:title>
+<db:productname>cxx20</db:productname>
+<db:titleabbrev>cxx20 Reference Documentation</db:titleabbrev>
+<db:abstract>
+<db:para>This class is <db:emphasis>weakly comparable</db:emphasis>.</db:para>
+</db:abstract>
+</db:info>
+<db:variablelist>
+<db:varlistentry>
+<db:term>Header</db:term>
+<db:listitem>
+<db:para>WeaklyOrderedClass</db:para>
+</db:listitem>
+</db:varlistentry>
+</db:variablelist>
+<db:section xml:id="details">
+<db:title>Detailed Description</db:title>
+</db:section>
+</db:article>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/cxx20/expected/html/bar.html b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/cxx20/expected/html/bar.html
new file mode 100644
index 000000000..a21a45105
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/cxx20/expected/html/bar.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+<!-- classes_with_various_ordering.cpp -->
+ <title>Bar Class | cxx20</title>
+</head>
+<body>
+<li>Bar</li>
+<div class="sidebar">
+<div class="toc">
+<h3 id="toc">Contents</h3>
+<ul>
+<li class="level1"><a href="#details">Detailed Description</a></li>
+</ul>
+</div>
+<div class="sidebar-content" id="sidebar-content"></div></div>
+<h1 class="title" translate="no">Bar Class</h1>
+<div class="table"><table class="alignedsummary" translate="no">
+<tr><td class="memItemLeft rightAlign topAlign"> Header:</td><td class="memItemRight bottomAlign"> <span class="preprocessor">#include &lt;Bar&gt;</span></td></tr>
+</table></div>
+<!-- $$$Bar-description -->
+<div class="descr">
+<h2 id="details">Detailed Description</h2>
+</div>
+<!-- @@@Bar -->
+</body>
+</html>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/cxx20/expected/html/baz.html b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/cxx20/expected/html/baz.html
new file mode 100644
index 000000000..9044101aa
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/cxx20/expected/html/baz.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+<!-- classes_with_various_ordering.cpp -->
+ <title>Baz Class | cxx20</title>
+</head>
+<body>
+<li>Baz</li>
+<div class="sidebar">
+<div class="toc">
+<h3 id="toc">Contents</h3>
+<ul>
+<li class="level1"><a href="#details">Detailed Description</a></li>
+</ul>
+</div>
+<div class="sidebar-content" id="sidebar-content"></div></div>
+<h1 class="title" translate="no">Baz Class</h1>
+<div class="table"><table class="alignedsummary" translate="no">
+<tr><td class="memItemLeft rightAlign topAlign"> Header:</td><td class="memItemRight bottomAlign"> <span class="preprocessor">#include &lt;Baz&gt;</span></td></tr>
+</table></div>
+<!-- $$$Baz-description -->
+<div class="descr">
+<h2 id="details">Detailed Description</h2>
+</div>
+<!-- @@@Baz -->
+</body>
+</html>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/cxx20/expected/html/comparesstronglywithoneclassandpartiallywithanother.html b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/cxx20/expected/html/comparesstronglywithoneclassandpartiallywithanother.html
new file mode 100644
index 000000000..42847f896
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/cxx20/expected/html/comparesstronglywithoneclassandpartiallywithanother.html
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+<!-- classes_with_various_ordering.cpp -->
+ <meta name="description" content="Class with various comparison relationships.">
+ <title>ComparesStronglyWithOneClassAndPartiallyWithAnother Class | cxx20</title>
+</head>
+<body>
+<li>ComparesStronglyWithOneClassAndPartiallyWithAnother</li>
+<div class="sidebar">
+<div class="toc">
+<h3 id="toc">Contents</h3>
+<ul>
+<li class="level1"><a href="#details">Detailed Description</a></li>
+</ul>
+</div>
+<div class="sidebar-content" id="sidebar-content"></div></div>
+<h1 class="title" translate="no">ComparesStronglyWithOneClassAndPartiallyWithAnother Class</h1>
+<!-- $$$ComparesStronglyWithOneClassAndPartiallyWithAnother-brief -->
+<p>Class with various comparison relationships. <a href="#details">More...</a></p>
+<!-- @@@ComparesStronglyWithOneClassAndPartiallyWithAnother -->
+<div class="table"><table class="alignedsummary" translate="no">
+<tr><td class="memItemLeft rightAlign topAlign"> Header:</td><td class="memItemRight bottomAlign"> <span class="preprocessor">#include &lt;ComparesStronglyWithOneClassAndPartiallyWithAnother&gt;</span></td></tr>
+</table></div>
+<p>This class is <b>strongly comparable</b> with <a href="foo.html" translate="no">Foo</a>.</p>
+<p>This class is <b>partially comparable</b> with <a href="bar.html" translate="no">Bar</a>.</p>
+<p>Here we describe partial comparison with Bar.</p>
+<p>This class is <b>equality-comparable</b> with <a href="foo.html" translate="no">Foo</a>.</p>
+<!-- $$$ComparesStronglyWithOneClassAndPartiallyWithAnother-description -->
+<div class="descr">
+<h2 id="details">Detailed Description</h2>
+<p>This class compares strongly with one type, and partially with another.</p>
+</div>
+<!-- @@@ComparesStronglyWithOneClassAndPartiallyWithAnother -->
+</body>
+</html>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/cxx20/expected/html/comparesstronglywiththreeclasses.html b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/cxx20/expected/html/comparesstronglywiththreeclasses.html
new file mode 100644
index 000000000..a92815465
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/cxx20/expected/html/comparesstronglywiththreeclasses.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+<!-- classes_with_various_ordering.cpp -->
+ <title>ComparesStronglyWithThreeClasses Class | cxx20</title>
+</head>
+<body>
+<li>ComparesStronglyWithThreeClasses</li>
+<div class="sidebar">
+<div class="toc">
+<h3 id="toc">Contents</h3>
+<ul>
+<li class="level1"><a href="#details">Detailed Description</a></li>
+</ul>
+</div>
+<div class="sidebar-content" id="sidebar-content"></div></div>
+<h1 class="title" translate="no">ComparesStronglyWithThreeClasses Class</h1>
+<div class="table"><table class="alignedsummary" translate="no">
+<tr><td class="memItemLeft rightAlign topAlign"> Header:</td><td class="memItemRight bottomAlign"> <span class="preprocessor">#include &lt;ComparesStronglyWithThreeClasses&gt;</span></td></tr>
+</table></div>
+<p>This class is <b>strongly comparable</b> with <a href="foo.html" translate="no">Foo</a>, Bar Bar Jinks, and <a href="baz.html" translate="no">Baz</a>.</p>
+<!-- $$$ComparesStronglyWithThreeClasses-description -->
+<div class="descr">
+<h2 id="details">Detailed Description</h2>
+</div>
+<!-- @@@ComparesStronglyWithThreeClasses -->
+</body>
+</html>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/cxx20/expected/html/comparesstronglywiththreeclassesacrossmultiplelines.html b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/cxx20/expected/html/comparesstronglywiththreeclassesacrossmultiplelines.html
new file mode 100644
index 000000000..736e433fd
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/cxx20/expected/html/comparesstronglywiththreeclassesacrossmultiplelines.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+<!-- classes_with_various_ordering.cpp -->
+ <title>ComparesStronglyWithThreeClassesAcrossMultipleLines Class | cxx20</title>
+</head>
+<body>
+<li>ComparesStronglyWithThreeClassesAcrossMultipleLines</li>
+<div class="sidebar">
+<div class="toc">
+<h3 id="toc">Contents</h3>
+<ul>
+<li class="level1"><a href="#details">Detailed Description</a></li>
+</ul>
+</div>
+<div class="sidebar-content" id="sidebar-content"></div></div>
+<h1 class="title" translate="no">ComparesStronglyWithThreeClassesAcrossMultipleLines Class</h1>
+<div class="table"><table class="alignedsummary" translate="no">
+<tr><td class="memItemLeft rightAlign topAlign"> Header:</td><td class="memItemRight bottomAlign"> <span class="preprocessor">#include &lt;ComparesStronglyWithThreeClassesAcrossMultipleLines&gt;</span></td></tr>
+</table></div>
+<p>This class is <b>strongly comparable</b> with <a href="foo.html" translate="no">Foo</a>, <a href="bar.html" translate="no">Bar</a>, and <a href="baz.html" translate="no">Baz</a>.</p>
+<!-- $$$ComparesStronglyWithThreeClassesAcrossMultipleLines-description -->
+<div class="descr">
+<h2 id="details">Detailed Description</h2>
+</div>
+<!-- @@@ComparesStronglyWithThreeClassesAcrossMultipleLines -->
+</body>
+</html>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/cxx20/expected/html/comparesstronglywithtwoclasses.html b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/cxx20/expected/html/comparesstronglywithtwoclasses.html
new file mode 100644
index 000000000..bd86314ae
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/cxx20/expected/html/comparesstronglywithtwoclasses.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+<!-- classes_with_various_ordering.cpp -->
+ <title>ComparesStronglyWithTwoClasses Class | cxx20</title>
+</head>
+<body>
+<li>ComparesStronglyWithTwoClasses</li>
+<div class="sidebar">
+<div class="toc">
+<h3 id="toc">Contents</h3>
+<ul>
+<li class="level1"><a href="#details">Detailed Description</a></li>
+</ul>
+</div>
+<div class="sidebar-content" id="sidebar-content"></div></div>
+<h1 class="title" translate="no">ComparesStronglyWithTwoClasses Class</h1>
+<div class="table"><table class="alignedsummary" translate="no">
+<tr><td class="memItemLeft rightAlign topAlign"> Header:</td><td class="memItemRight bottomAlign"> <span class="preprocessor">#include &lt;ComparesStronglyWithTwoClasses&gt;</span></td></tr>
+</table></div>
+<p>This class is <b>strongly comparable</b> with <a href="foo.html" translate="no">Foo</a>.</p>
+<p>This class is <b>strongly comparable</b> with <a href="bar.html" translate="no">Bar</a>.</p>
+<div class="admonition warning">
+<p><b>Warning: </b>Always compare twice!</p>
+</div>
+<!-- $$$ComparesStronglyWithTwoClasses-description -->
+<div class="descr">
+<h2 id="details">Detailed Description</h2>
+</div>
+<!-- @@@ComparesStronglyWithTwoClasses -->
+</body>
+</html>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/cxx20/expected/html/cxx20.index b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/cxx20/expected/html/cxx20.index
new file mode 100644
index 000000000..ab2c3e24f
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/cxx20/expected/html/cxx20.index
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE QDOCINDEX>
+<INDEX url="" title="cxx20 Reference Documentation" version="" project="cxx20">
+ <namespace name="" status="active" access="public" module="cxx20">
+ <class name="Bar" href="bar.html" status="active" access="public" location="classes_with_various_ordering.h" documented="true" module="TestQDoc"/>
+ <class name="Baz" href="baz.html" status="active" access="public" location="classes_with_various_ordering.h" documented="true" module="TestQDoc"/>
+ <class name="ComparesStronglyWithOneClassAndPartiallyWithAnother" href="comparesstronglywithoneclassandpartiallywithanother.html" status="active" access="public" location="classes_with_various_ordering.h" documented="true" module="TestQDoc" brief="Class with various comparison relationships"/>
+ <class name="ComparesStronglyWithThreeClasses" href="comparesstronglywiththreeclasses.html" status="active" access="public" location="classes_with_various_ordering.h" documented="true" module="TestQDoc"/>
+ <class name="ComparesStronglyWithThreeClassesAcrossMultipleLines" href="comparesstronglywiththreeclassesacrossmultiplelines.html" status="active" access="public" location="classes_with_various_ordering.h" documented="true" module="TestQDoc"/>
+ <class name="ComparesStronglyWithTwoClasses" href="comparesstronglywithtwoclasses.html" status="active" access="public" location="classes_with_various_ordering.h" documented="true" module="TestQDoc"/>
+ <class name="EqualityComparableClass" href="equalitycomparableclass.html" status="active" access="public" location="classes_with_various_ordering.h" documented="true" module="TestQDoc" comparison_category="equality"/>
+ <class name="Foo" href="foo.html" status="active" access="public" location="classes_with_various_ordering.h" documented="true" module="TestQDoc"/>
+ <class name="PartiallyOrderedClass" href="partiallyorderedclass.html" status="active" access="public" location="classes_with_various_ordering.h" documented="true" module="TestQDoc" comparison_category="partial"/>
+ <class name="StronglyOrderedClass" href="stronglyorderedclass.html" status="active" access="public" location="classes_with_various_ordering.h" documented="true" module="TestQDoc" comparison_category="strong"/>
+ <class name="WeaklyOrderedClass" href="weaklyorderedclass.html" status="active" access="public" location="classes_with_various_ordering.h" documented="true" module="TestQDoc" comparison_category="weak"/>
+ <module name="TestQDoc" href="testqdoc-module.html" status="internal" seen="false" title=""/>
+ </namespace>
+</INDEX>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/cxx20/expected/html/equalitycomparableclass.html b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/cxx20/expected/html/equalitycomparableclass.html
new file mode 100644
index 000000000..24303bb97
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/cxx20/expected/html/equalitycomparableclass.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+<!-- classes_with_various_ordering.cpp -->
+ <title>EqualityComparableClass Class | cxx20</title>
+</head>
+<body>
+<li>EqualityComparableClass</li>
+<div class="sidebar">
+<div class="toc">
+<h3 id="toc">Contents</h3>
+<ul>
+<li class="level1"><a href="#details">Detailed Description</a></li>
+</ul>
+</div>
+<div class="sidebar-content" id="sidebar-content"></div></div>
+<h1 class="title" translate="no">EqualityComparableClass Class</h1>
+<div class="table"><table class="alignedsummary" translate="no">
+<tr><td class="memItemLeft rightAlign topAlign"> Header:</td><td class="memItemRight bottomAlign"> <span class="preprocessor">#include &lt;EqualityComparableClass&gt;</span></td></tr>
+</table></div>
+<p>This class is <i>equality-comparable</i>.</p>
+<!-- $$$EqualityComparableClass-description -->
+<div class="descr">
+<h2 id="details">Detailed Description</h2>
+</div>
+<!-- @@@EqualityComparableClass -->
+</body>
+</html>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/cxx20/expected/html/foo.html b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/cxx20/expected/html/foo.html
new file mode 100644
index 000000000..ba40175c6
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/cxx20/expected/html/foo.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+<!-- classes_with_various_ordering.cpp -->
+ <title>Foo Class | cxx20</title>
+</head>
+<body>
+<li>Foo</li>
+<div class="sidebar">
+<div class="toc">
+<h3 id="toc">Contents</h3>
+<ul>
+<li class="level1"><a href="#details">Detailed Description</a></li>
+</ul>
+</div>
+<div class="sidebar-content" id="sidebar-content"></div></div>
+<h1 class="title" translate="no">Foo Class</h1>
+<div class="table"><table class="alignedsummary" translate="no">
+<tr><td class="memItemLeft rightAlign topAlign"> Header:</td><td class="memItemRight bottomAlign"> <span class="preprocessor">#include &lt;Foo&gt;</span></td></tr>
+</table></div>
+<!-- $$$Foo-description -->
+<div class="descr">
+<h2 id="details">Detailed Description</h2>
+</div>
+<!-- @@@Foo -->
+</body>
+</html>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/cxx20/expected/html/partiallyorderedclass.html b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/cxx20/expected/html/partiallyorderedclass.html
new file mode 100644
index 000000000..359742532
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/cxx20/expected/html/partiallyorderedclass.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+<!-- classes_with_various_ordering.cpp -->
+ <title>PartiallyOrderedClass Class | cxx20</title>
+</head>
+<body>
+<li>PartiallyOrderedClass</li>
+<div class="sidebar">
+<div class="toc">
+<h3 id="toc">Contents</h3>
+<ul>
+<li class="level1"><a href="#details">Detailed Description</a></li>
+</ul>
+</div>
+<div class="sidebar-content" id="sidebar-content"></div></div>
+<h1 class="title" translate="no">PartiallyOrderedClass Class</h1>
+<div class="table"><table class="alignedsummary" translate="no">
+<tr><td class="memItemLeft rightAlign topAlign"> Header:</td><td class="memItemRight bottomAlign"> <span class="preprocessor">#include &lt;PartiallyOrderedClass&gt;</span></td></tr>
+</table></div>
+<p>This class is <i>partially comparable</i>.</p>
+<!-- $$$PartiallyOrderedClass-description -->
+<div class="descr">
+<h2 id="details">Detailed Description</h2>
+</div>
+<!-- @@@PartiallyOrderedClass -->
+</body>
+</html>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/cxx20/expected/html/stronglyorderedclass.html b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/cxx20/expected/html/stronglyorderedclass.html
new file mode 100644
index 000000000..d4972aaa6
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/cxx20/expected/html/stronglyorderedclass.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+<!-- classes_with_various_ordering.cpp -->
+ <title>StronglyOrderedClass Class | cxx20</title>
+</head>
+<body>
+<li>StronglyOrderedClass</li>
+<div class="sidebar">
+<div class="toc">
+<h3 id="toc">Contents</h3>
+<ul>
+<li class="level1"><a href="#details">Detailed Description</a></li>
+</ul>
+</div>
+<div class="sidebar-content" id="sidebar-content"></div></div>
+<h1 class="title" translate="no">StronglyOrderedClass Class</h1>
+<div class="table"><table class="alignedsummary" translate="no">
+<tr><td class="memItemLeft rightAlign topAlign"> Header:</td><td class="memItemRight bottomAlign"> <span class="preprocessor">#include &lt;StronglyOrderedClass&gt;</span></td></tr>
+</table></div>
+<p>This class is <i>strongly comparable</i>.</p>
+<!-- $$$StronglyOrderedClass-description -->
+<div class="descr">
+<h2 id="details">Detailed Description</h2>
+</div>
+<!-- @@@StronglyOrderedClass -->
+</body>
+</html>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/cxx20/expected/html/weaklyorderedclass.html b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/cxx20/expected/html/weaklyorderedclass.html
new file mode 100644
index 000000000..0849b2a1b
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/cxx20/expected/html/weaklyorderedclass.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+<!-- classes_with_various_ordering.cpp -->
+ <title>WeaklyOrderedClass Class | cxx20</title>
+</head>
+<body>
+<li>WeaklyOrderedClass</li>
+<div class="sidebar">
+<div class="toc">
+<h3 id="toc">Contents</h3>
+<ul>
+<li class="level1"><a href="#details">Detailed Description</a></li>
+</ul>
+</div>
+<div class="sidebar-content" id="sidebar-content"></div></div>
+<h1 class="title" translate="no">WeaklyOrderedClass Class</h1>
+<div class="table"><table class="alignedsummary" translate="no">
+<tr><td class="memItemLeft rightAlign topAlign"> Header:</td><td class="memItemRight bottomAlign"> <span class="preprocessor">#include &lt;WeaklyOrderedClass&gt;</span></td></tr>
+</table></div>
+<p>This class is <i>weakly comparable</i>.</p>
+<!-- $$$WeaklyOrderedClass-description -->
+<div class="descr">
+<h2 id="details">Detailed Description</h2>
+</div>
+<!-- @@@WeaklyOrderedClass -->
+</body>
+</html>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/cxx20/expected/webxml/bar.webxml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/cxx20/expected/webxml/bar.webxml
new file mode 100644
index 000000000..a4e70b190
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/cxx20/expected/webxml/bar.webxml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<WebXML>
+ <document>
+ <class name="Bar" href="bar.html" status="active" access="public" location="classes_with_various_ordering.h" documented="true" module="TestQDoc">
+ <description/>
+ </class>
+ </document>
+</WebXML>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/cxx20/expected/webxml/baz.webxml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/cxx20/expected/webxml/baz.webxml
new file mode 100644
index 000000000..18eff7e9c
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/cxx20/expected/webxml/baz.webxml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<WebXML>
+ <document>
+ <class name="Baz" href="baz.html" status="active" access="public" location="classes_with_various_ordering.h" documented="true" module="TestQDoc">
+ <description/>
+ </class>
+ </document>
+</WebXML>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/cxx20/expected/webxml/comparesstronglywithoneclassandpartiallywithanother.webxml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/cxx20/expected/webxml/comparesstronglywithoneclassandpartiallywithanother.webxml
new file mode 100644
index 000000000..a296ae73f
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/cxx20/expected/webxml/comparesstronglywithoneclassandpartiallywithanother.webxml
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<WebXML>
+ <document>
+ <class name="ComparesStronglyWithOneClassAndPartiallyWithAnother" href="comparesstronglywithoneclassandpartiallywithanother.html" status="active" access="public" location="classes_with_various_ordering.h" documented="true" module="TestQDoc" brief="Class with various comparison relationships">
+ <description>
+ <brief>Class with various comparison relationships.</brief>
+ <para>This class compares strongly with one type, and partially with another.</para>
+ </description>
+ </class>
+ </document>
+</WebXML>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/cxx20/expected/webxml/comparesstronglywiththreeclasses.webxml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/cxx20/expected/webxml/comparesstronglywiththreeclasses.webxml
new file mode 100644
index 000000000..c58fd61a5
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/cxx20/expected/webxml/comparesstronglywiththreeclasses.webxml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<WebXML>
+ <document>
+ <class name="ComparesStronglyWithThreeClasses" href="comparesstronglywiththreeclasses.html" status="active" access="public" location="classes_with_various_ordering.h" documented="true" module="TestQDoc">
+ <description/>
+ </class>
+ </document>
+</WebXML>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/cxx20/expected/webxml/comparesstronglywiththreeclassesacrossmultiplelines.webxml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/cxx20/expected/webxml/comparesstronglywiththreeclassesacrossmultiplelines.webxml
new file mode 100644
index 000000000..971a554ba
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/cxx20/expected/webxml/comparesstronglywiththreeclassesacrossmultiplelines.webxml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<WebXML>
+ <document>
+ <class name="ComparesStronglyWithThreeClassesAcrossMultipleLines" href="comparesstronglywiththreeclassesacrossmultiplelines.html" status="active" access="public" location="classes_with_various_ordering.h" documented="true" module="TestQDoc">
+ <description/>
+ </class>
+ </document>
+</WebXML>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/cxx20/expected/webxml/comparesstronglywithtwoclasses.webxml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/cxx20/expected/webxml/comparesstronglywithtwoclasses.webxml
new file mode 100644
index 000000000..524689e0a
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/cxx20/expected/webxml/comparesstronglywithtwoclasses.webxml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<WebXML>
+ <document>
+ <class name="ComparesStronglyWithTwoClasses" href="comparesstronglywithtwoclasses.html" status="active" access="public" location="classes_with_various_ordering.h" documented="true" module="TestQDoc">
+ <description/>
+ </class>
+ </document>
+</WebXML>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/cxx20/expected/webxml/cxx20.index b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/cxx20/expected/webxml/cxx20.index
new file mode 100644
index 000000000..ab2c3e24f
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/cxx20/expected/webxml/cxx20.index
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE QDOCINDEX>
+<INDEX url="" title="cxx20 Reference Documentation" version="" project="cxx20">
+ <namespace name="" status="active" access="public" module="cxx20">
+ <class name="Bar" href="bar.html" status="active" access="public" location="classes_with_various_ordering.h" documented="true" module="TestQDoc"/>
+ <class name="Baz" href="baz.html" status="active" access="public" location="classes_with_various_ordering.h" documented="true" module="TestQDoc"/>
+ <class name="ComparesStronglyWithOneClassAndPartiallyWithAnother" href="comparesstronglywithoneclassandpartiallywithanother.html" status="active" access="public" location="classes_with_various_ordering.h" documented="true" module="TestQDoc" brief="Class with various comparison relationships"/>
+ <class name="ComparesStronglyWithThreeClasses" href="comparesstronglywiththreeclasses.html" status="active" access="public" location="classes_with_various_ordering.h" documented="true" module="TestQDoc"/>
+ <class name="ComparesStronglyWithThreeClassesAcrossMultipleLines" href="comparesstronglywiththreeclassesacrossmultiplelines.html" status="active" access="public" location="classes_with_various_ordering.h" documented="true" module="TestQDoc"/>
+ <class name="ComparesStronglyWithTwoClasses" href="comparesstronglywithtwoclasses.html" status="active" access="public" location="classes_with_various_ordering.h" documented="true" module="TestQDoc"/>
+ <class name="EqualityComparableClass" href="equalitycomparableclass.html" status="active" access="public" location="classes_with_various_ordering.h" documented="true" module="TestQDoc" comparison_category="equality"/>
+ <class name="Foo" href="foo.html" status="active" access="public" location="classes_with_various_ordering.h" documented="true" module="TestQDoc"/>
+ <class name="PartiallyOrderedClass" href="partiallyorderedclass.html" status="active" access="public" location="classes_with_various_ordering.h" documented="true" module="TestQDoc" comparison_category="partial"/>
+ <class name="StronglyOrderedClass" href="stronglyorderedclass.html" status="active" access="public" location="classes_with_various_ordering.h" documented="true" module="TestQDoc" comparison_category="strong"/>
+ <class name="WeaklyOrderedClass" href="weaklyorderedclass.html" status="active" access="public" location="classes_with_various_ordering.h" documented="true" module="TestQDoc" comparison_category="weak"/>
+ <module name="TestQDoc" href="testqdoc-module.html" status="internal" seen="false" title=""/>
+ </namespace>
+</INDEX>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/cxx20/expected/webxml/equalitycomparableclass.webxml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/cxx20/expected/webxml/equalitycomparableclass.webxml
new file mode 100644
index 000000000..32d48bd41
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/cxx20/expected/webxml/equalitycomparableclass.webxml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<WebXML>
+ <document>
+ <class name="EqualityComparableClass" href="equalitycomparableclass.html" status="active" access="public" location="classes_with_various_ordering.h" documented="true" module="TestQDoc" comparison_category="equality">
+ <description/>
+ </class>
+ </document>
+</WebXML>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/cxx20/expected/webxml/foo.webxml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/cxx20/expected/webxml/foo.webxml
new file mode 100644
index 000000000..e36896ca9
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/cxx20/expected/webxml/foo.webxml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<WebXML>
+ <document>
+ <class name="Foo" href="foo.html" status="active" access="public" location="classes_with_various_ordering.h" documented="true" module="TestQDoc">
+ <description/>
+ </class>
+ </document>
+</WebXML>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/cxx20/expected/webxml/partiallyorderedclass.webxml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/cxx20/expected/webxml/partiallyorderedclass.webxml
new file mode 100644
index 000000000..fd41aa9a8
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/cxx20/expected/webxml/partiallyorderedclass.webxml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<WebXML>
+ <document>
+ <class name="PartiallyOrderedClass" href="partiallyorderedclass.html" status="active" access="public" location="classes_with_various_ordering.h" documented="true" module="TestQDoc" comparison_category="partial">
+ <description/>
+ </class>
+ </document>
+</WebXML>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/cxx20/expected/webxml/stronglyorderedclass.webxml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/cxx20/expected/webxml/stronglyorderedclass.webxml
new file mode 100644
index 000000000..7f8440a60
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/cxx20/expected/webxml/stronglyorderedclass.webxml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<WebXML>
+ <document>
+ <class name="StronglyOrderedClass" href="stronglyorderedclass.html" status="active" access="public" location="classes_with_various_ordering.h" documented="true" module="TestQDoc" comparison_category="strong">
+ <description/>
+ </class>
+ </document>
+</WebXML>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/cxx20/expected/webxml/weaklyorderedclass.webxml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/cxx20/expected/webxml/weaklyorderedclass.webxml
new file mode 100644
index 000000000..d8580e92b
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/cxx20/expected/webxml/weaklyorderedclass.webxml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<WebXML>
+ <document>
+ <class name="WeaklyOrderedClass" href="weaklyorderedclass.html" status="active" access="public" location="classes_with_various_ordering.h" documented="true" module="TestQDoc" comparison_category="weak">
+ <description/>
+ </class>
+ </document>
+</WebXML>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/cxx20/src/classes_with_various_ordering.cpp b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/cxx20/src/classes_with_various_ordering.cpp
new file mode 100644
index 000000000..c9029e446
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/cxx20/src/classes_with_various_ordering.cpp
@@ -0,0 +1,89 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+/*!
+ \class StronglyOrderedClass
+ \inmodule TestQDoc
+ \compares strong
+*/
+
+/*!
+ \class PartiallyOrderedClass
+ \inmodule TestQDoc
+ \compares partial
+*/
+
+/*!
+ \class WeaklyOrderedClass
+ \inmodule TestQDoc
+ \compares weak
+*/
+
+/*!
+ \class EqualityComparableClass
+ \inmodule TestQDoc
+ \compares equality
+*/
+
+/*!
+ \class ComparesStronglyWithTwoClasses
+ \inmodule TestQDoc
+
+ \compareswith strong Foo
+ \endcompareswith
+
+ \compareswith strong Bar
+ \warning Always compare twice!
+ \endcompareswith
+*/
+
+/*!
+ \class ComparesStronglyWithThreeClasses
+ \inmodule TestQDoc
+
+ \compareswith strong Foo {Bar Bar Jinks} Baz
+ \endcompareswith
+*/
+
+/*!
+ \class ComparesStronglyWithThreeClassesAcrossMultipleLines
+ \inmodule TestQDoc
+
+ \compareswith strong Foo \
+ Bar Baz
+ \endcompareswith
+*/
+
+/*!
+ \class ComparesStronglyWithOneClassAndPartiallyWithAnother
+ \inmodule TestQDoc
+ \brief Class with various comparison relationships.
+
+ \compareswith strong Foo
+ \endcompareswith
+
+ \compareswith partial Bar
+ Here we describe partial comparison with Bar.
+ \endcompareswith
+
+ //! using a macro
+ \equalitycomparesto {Foo}
+
+ This class compares strongly with one type, and partially with another.
+*/
+
+
+/*!
+ \class Foo
+ \inmodule TestQDoc
+*/
+
+/*!
+ \class Bar
+ \inmodule TestQDoc
+*/
+
+/*!
+ \class Baz
+ \inmodule TestQDoc
+*/
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/cxx20/src/classes_with_various_ordering.h b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/cxx20/src/classes_with_various_ordering.h
new file mode 100644
index 000000000..4c9f6970e
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/cxx20/src/classes_with_various_ordering.h
@@ -0,0 +1,41 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#ifndef CLASSES_WITH_VARIOUS_ORDERING_H
+#define CLASSES_WITH_VARIOUS_ORDERING_H
+
+class StronglyOrderedClass
+{
+};
+class PartiallyOrderedClass
+{
+};
+class WeaklyOrderedClass
+{
+};
+class EqualityComparableClass
+{
+};
+class ComparesStronglyWithTwoClasses
+{
+};
+class ComparesStronglyWithThreeClasses
+{
+};
+class ComparesStronglyWithThreeClassesAcrossMultipleLines
+{
+};
+class ComparesStronglyWithOneClassAndPartiallyWithAnother
+{
+};
+class Foo
+{
+};
+class Bar
+{
+};
+class Baz
+{
+};
+
+#endif // CLASSES_WITH_VARIOUS_ORDERING_H
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/duplicate_section_titles_have_unique_anchors/duplicate_section_titles_have_unique_anchors.qdocconf b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/duplicate_section_titles_have_unique_anchors/duplicate_section_titles_have_unique_anchors.qdocconf
new file mode 100644
index 000000000..d1ebe89d0
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/duplicate_section_titles_have_unique_anchors/duplicate_section_titles_have_unique_anchors.qdocconf
@@ -0,0 +1,25 @@
+project = Duplicate section titles have unique anchors
+
+locationinfo = false
+
+headers.fileextensions = "*.h *.hpp"
+sources.fileextensions = "*.cpp *.qml *.qdoc"
+
+headerdirs = .
+sourcedirs = ./src
+
+warninglimit = 0
+warninglimit.enabled = true
+
+outputformats = WebXML HTML DocBook
+WebXML.quotinginformation = true
+
+HTML.nosubdirs = true
+HTML.outputsubdir = html
+
+WebXML.nosubdirs = true
+WebXML.outputsubdir = webxml
+
+DocBook.nosubdirs = true
+DocBook.outputsubdir = docbook
+
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/duplicate_section_titles_have_unique_anchors/expected/docbook/page-one.xml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/duplicate_section_titles_have_unique_anchors/expected/docbook/page-one.xml
new file mode 100644
index 000000000..350130f15
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/duplicate_section_titles_have_unique_anchors/expected/docbook/page-one.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<db:article xmlns:db="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" version="5.2" xml:lang="en">
+<db:info>
+<db:title>Page One</db:title>
+<db:productname>Duplicate section titles have unique anchors</db:productname>
+<db:titleabbrev>Duplicate section titles have unique anchors Reference Documentation</db:titleabbrev>
+<db:abstract>
+<db:para>Duplicate section titles have unique anchors Reference Documentation.</db:para></db:abstract>
+</db:info>
+<db:para>This test is for a documentation project with multiple section titles whose names collide.</db:para>
+<db:section xml:id="section-one">
+<db:title>Section One</db:title>
+<db:para>This is the first section.</db:para>
+<db:section xml:id="section-two">
+<db:title>Section Two</db:title>
+<db:para>This is the first sub-section.</db:para>
+</db:section>
+</db:section>
+<db:section xml:id="section-one-2">
+<db:title>Section One</db:title>
+<db:para>This is the second section.</db:para>
+<db:section xml:id="section-one-2-section-two-3">
+<db:title>Section Two</db:title>
+<db:para>This is the second sub-section.</db:para>
+</db:section>
+</db:section>
+</db:article>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/duplicate_section_titles_have_unique_anchors/expected/html/duplicate-section-titles-have-unique-anchors.index b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/duplicate_section_titles_have_unique_anchors/expected/html/duplicate-section-titles-have-unique-anchors.index
new file mode 100644
index 000000000..3f5d2a9af
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/duplicate_section_titles_have_unique_anchors/expected/html/duplicate-section-titles-have-unique-anchors.index
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE QDOCINDEX>
+<INDEX url="" title="Duplicate section titles have unique anchors Reference Documentation" version="" project="Duplicate section titles have unique anchors">
+ <namespace name="" status="active" access="public" module="duplicate section titles have unique anchors">
+ <page name="page_one.html" href="page-one.html" status="active" location="page_one.qdoc" documented="true" subtype="page" title="Page One" fulltitle="Page One" subtitle="">
+ <contents name="section-one" title="Section One" level="1"/>
+ <contents name="section-two" title="Section Two" level="2"/>
+ <contents name="section-one-2" title="Section One" level="1"/>
+ <contents name="section-one-2-section-two-3" title="Section Two" level="2"/>
+ </page>
+ </namespace>
+</INDEX>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/duplicate_section_titles_have_unique_anchors/expected/html/page-one.html b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/duplicate_section_titles_have_unique_anchors/expected/html/page-one.html
new file mode 100644
index 000000000..5f6501bfa
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/duplicate_section_titles_have_unique_anchors/expected/html/page-one.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+<!-- page_one.qdoc -->
+ <title>Page One | Duplicate section titles have unique anchors</title>
+</head>
+<body>
+<div class="sidebar">
+<div class="toc">
+<h3 id="toc">Contents</h3>
+<ul>
+<li class="level1"><a href="#section-one">Section One</a></li>
+<li class="level2"><a href="#section-two">Section Two</a></li>
+<li class="level1"><a href="#section-one-2">Section One</a></li>
+<li class="level2"><a href="#section-one-2-section-two-3">Section Two</a></li>
+</ul>
+</div>
+<div class="sidebar-content" id="sidebar-content"></div></div>
+<h1 class="title">Page One</h1>
+<!-- $$$page_one.html-description -->
+<div class="descr" id="details">
+<p>This test is for a documentation project with multiple section titles whose names collide.</p>
+<h2 id="section-one">Section One</h2>
+<p>This is the first section.</p>
+<h3 id="section-two">Section Two</h3>
+<p>This is the first sub-section.</p>
+<h2 id="section-one-2">Section One</h2>
+<p>This is the second section.</p>
+<h3 id="section-one-2-section-two-3">Section Two</h3>
+<p>This is the second sub-section.</p>
+</div>
+<!-- @@@page_one.html -->
+</body>
+</html>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/duplicate_section_titles_have_unique_anchors/expected/webxml/duplicate-section-titles-have-unique-anchors.index b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/duplicate_section_titles_have_unique_anchors/expected/webxml/duplicate-section-titles-have-unique-anchors.index
new file mode 100644
index 000000000..3f5d2a9af
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/duplicate_section_titles_have_unique_anchors/expected/webxml/duplicate-section-titles-have-unique-anchors.index
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE QDOCINDEX>
+<INDEX url="" title="Duplicate section titles have unique anchors Reference Documentation" version="" project="Duplicate section titles have unique anchors">
+ <namespace name="" status="active" access="public" module="duplicate section titles have unique anchors">
+ <page name="page_one.html" href="page-one.html" status="active" location="page_one.qdoc" documented="true" subtype="page" title="Page One" fulltitle="Page One" subtitle="">
+ <contents name="section-one" title="Section One" level="1"/>
+ <contents name="section-two" title="Section Two" level="2"/>
+ <contents name="section-one-2" title="Section One" level="1"/>
+ <contents name="section-one-2-section-two-3" title="Section Two" level="2"/>
+ </page>
+ </namespace>
+</INDEX>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/duplicate_section_titles_have_unique_anchors/expected/webxml/page-one.webxml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/duplicate_section_titles_have_unique_anchors/expected/webxml/page-one.webxml
new file mode 100644
index 000000000..98c92b784
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/duplicate_section_titles_have_unique_anchors/expected/webxml/page-one.webxml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<WebXML>
+ <document>
+ <page name="page_one.html" href="page-one.html" status="active" location="page_one.qdoc" documented="true" subtype="page" title="Page One" fulltitle="Page One" subtitle="">
+ <contents name="section-one" title="Section One" level="1"/>
+ <contents name="section-two" title="Section Two" level="2"/>
+ <contents name="section-one-2" title="Section One" level="1"/>
+ <contents name="section-one-2-section-two-3" title="Section Two" level="2"/>
+ <description>
+ <para>This test is for a documentation project with multiple section titles whose names collide.</para>
+ <section id="section-one">
+ <heading level="1">Section One</heading>
+ <para>This is the first section.</para>
+ </section>
+ <section id="section-two">
+ <heading level="2">Section Two</heading>
+ <para>This is the first sub-section.</para>
+ </section>
+ <section id="section-one">
+ <heading level="1">Section One</heading>
+ <para>This is the second section.</para>
+ </section>
+ <section id="section-two">
+ <heading level="2">Section Two</heading>
+ <para>This is the second sub-section.</para>
+ </section>
+ </description>
+ </page>
+ </document>
+</WebXML>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/duplicate_section_titles_have_unique_anchors/src/page_one.qdoc b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/duplicate_section_titles_have_unique_anchors/src/page_one.qdoc
new file mode 100644
index 000000000..4596ffccd
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/duplicate_section_titles_have_unique_anchors/src/page_one.qdoc
@@ -0,0 +1,21 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+
+/*!
+ \page page_one.html
+ \title Page One
+
+ This test is for a documentation project with multiple section titles whose names collide.
+
+ \section1 Section One
+ This is the first section.
+
+ \section2 Section Two
+ This is the first sub-section.
+
+ \section1 Section One
+ This is the second section.
+
+ \section2 Section Two
+ This is the second sub-section.
+*/
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/globalfunc/expected/html/globals.html b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/globalfunc/expected/html/globals.html
new file mode 100644
index 000000000..92f6883ee
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/globalfunc/expected/html/globals.html
@@ -0,0 +1,45 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+<!-- global.qdoc -->
+ <title>Globals Class | TestGlobals</title>
+</head>
+<body>
+<li>Globals</li>
+<div class="sidebar">
+<div class="toc">
+<h3 id="toc">Contents</h3>
+<ul>
+<li class="level1"><a href="#related-non-members">Related Non-Members</a></li>
+<li class="level1"><a href="#details">Detailed Description</a></li>
+</ul>
+</div>
+<div class="sidebar-content" id="sidebar-content"></div></div>
+<h1 class="title" translate="no">Globals Class</h1>
+<div class="table"><table class="alignedsummary" translate="no">
+<tr><td class="memItemLeft rightAlign topAlign"> Header:</td><td class="memItemRight bottomAlign"> <span class="preprocessor">#include &lt;Globals&gt;</span></td></tr>
+</table></div>
+<h2 id="related-non-members">Related Non-Members</h2>
+<div class="table"><table class="alignedsummary" translate="no">
+<tr><td class="memItemLeft rightAlign topAlign"> int </td><td class="memItemRight bottomAlign"><b><a href="globals.html#foo" translate="no">foo</a></b>(int <i>a</i>)</td></tr>
+<tr><td class="memItemLeft rightAlign topAlign"> int </td><td class="memItemRight bottomAlign"><b><a href="globals.html#foo-1" translate="no">foo</a></b>(int <i>a</i>, bool <i>b</i>)</td></tr>
+</table></div>
+<!-- $$$Globals-description -->
+<div class="descr">
+<h2 id="details">Detailed Description</h2>
+</div>
+<!-- @@@Globals -->
+<div class="relnonmem">
+<h2>Related Non-Members</h2>
+<!-- $$$foo[overload1]$$$fooint -->
+<h3 class="fn" translate="no" id="foo"><span class="type">int</span> <span class="name">foo</span>(<span class="type">int</span> <i>a</i>)</h3>
+<p>Params: <i translate="no">a</i></p>
+<!-- @@@foo -->
+<!-- $$$foo$$$foointbool -->
+<h3 class="fn" translate="no" id="foo-1"><span class="type">int</span> <span class="name">foo</span>(<span class="type">int</span> <i>a</i>, <span class="type">bool</span> <i>b</i>)</h3>
+<p>Params: <i translate="no">b</i>, <i translate="no">b</i></p>
+<!-- @@@foo -->
+</div>
+</body>
+</html>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/globalfunc/expected/html/testglobals-module.html b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/globalfunc/expected/html/testglobals-module.html
new file mode 100644
index 000000000..23f59510c
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/globalfunc/expected/html/testglobals-module.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+<!-- global.qdoc -->
+ <title>TestGlobals</title>
+</head>
+<body>
+<div class="sidebar">
+<div class="toc">
+<h3 id="toc">Contents</h3>
+<ul>
+<li class="level1"><a href="#namespaces">Namespaces</a></li>
+<li class="level1"><a href="#classes">Classes</a></li>
+<li class="level1"><a href="#details">Detailed Description</a></li>
+</ul>
+</div>
+<div class="sidebar-content" id="sidebar-content"></div></div>
+<h2 id="classes">Classes</h2>
+<div class="table"><table class="annotated">
+<tr class="odd topAlign"><td class="tblName" translate="no"><p><a href="globals.html">Globals</a></p></td></tr>
+</table></div>
+<!-- $$$TestGlobals-description -->
+<div class="descr" id="details">
+<p>A collection of <a href="globals.html#foo" translate="no">foo</a>() functions.</p>
+</div>
+<!-- @@@TestGlobals -->
+</body>
+</html>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/globalfunc/expected/html/testglobals.index b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/globalfunc/expected/html/testglobals.index
new file mode 100644
index 000000000..f1f0871d3
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/globalfunc/expected/html/testglobals.index
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE QDOCINDEX>
+<INDEX url="" title="TestGlobals Reference Documentation" version="" project="TestGlobals">
+ <namespace name="" status="active" access="public" module="testglobals">
+ <function name="foo" href="globals.html#foo" status="active" access="public" location="global.h" documented="true" related="0" meta="plain" type="int" signature="int foo(int a)">
+ <parameter type="int" name="a" default=""/>
+ </function>
+ <function name="foo" href="globals.html#foo-1" status="active" access="public" location="global.h" documented="true" related="1" meta="plain" overload="true" overload-number="1" type="int" signature="int foo(int a, bool b)">
+ <parameter type="int" name="a" default=""/>
+ <parameter type="bool" name="b" default=""/>
+ </function>
+ <class name="Globals" href="globals.html" status="active" access="public" location="global.h" documented="true" module="TestGlobals">
+ <function name="foo" href="globals.html#foo" status="active" access="public" location="global.h" documented="true" related="0" meta="plain" type="int" signature="int foo(int a)">
+ <parameter type="int" name="a" default=""/>
+ </function>
+ <function name="foo" href="globals.html#foo-1" status="active" access="public" location="global.h" documented="true" related="1" meta="plain" overload="true" overload-number="1" type="int" signature="int foo(int a, bool b)">
+ <parameter type="int" name="a" default=""/>
+ <parameter type="bool" name="b" default=""/>
+ </function>
+ </class>
+ <module name="TestGlobals" href="testglobals-module.html" status="active" location="global.qdoc" documented="true" seen="true" title=""/>
+ </namespace>
+</INDEX>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/globalfunc/globalfunc.qdocconf b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/globalfunc/globalfunc.qdocconf
new file mode 100644
index 000000000..fb28478bf
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/globalfunc/globalfunc.qdocconf
@@ -0,0 +1,23 @@
+sources.fileextensions = "*.qml *.cpp *.qdoc"
+headers.fileextensions = "*.h"
+
+# images
+imagedirs = ./src/images
+
+# zero warning policy
+warninglimit = 0
+warninglimit.enabled = true
+
+# don't write host system-specific paths to index files
+locationinfo = false
+
+# By default, use -outputdir directly, no subdir.
+# outputsubdir '.' matches the root of expected_output/
+HTML.nosubdirs = true
+HTML.outputsubdir = html
+
+project = TestGlobals
+includepaths += -I./src/globalfunc
+
+headers = ./src/global.h
+sources = ./src/global.qdoc
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/globalfunc/src/TestGlobals b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/globalfunc/src/TestGlobals
new file mode 100644
index 000000000..dc4f98a6e
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/globalfunc/src/TestGlobals
@@ -0,0 +1 @@
+#include "global.h"
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/globalfunc/src/global.h b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/globalfunc/src/global.h
new file mode 100644
index 000000000..8b5290072
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/globalfunc/src/global.h
@@ -0,0 +1,8 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+class Globals {};
+inline int foo(int a) { return a; }
+inline int foo(int a, bool b) { return b ? a : -a; }
+// Undocumented overload
+inline int foo(int a, bool b, bool c) { return b || c ? a : -a; }
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/globalfunc/src/global.qdoc b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/globalfunc/src/global.qdoc
new file mode 100644
index 000000000..f61ed5f58
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/globalfunc/src/global.qdoc
@@ -0,0 +1,25 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+/*!
+ \module TestGlobals
+
+ A collection of \l foo() functions.
+*/
+
+/*!
+ \class Globals
+ \inmodule TestGlobals
+*/
+
+/*!
+ \fn int foo(int a)
+ \relates Globals
+ Params: \a a
+*/
+
+/*!
+ \fn int foo(int a, bool b)
+ \relates Globals
+ Params: \a b, \a b
+*/
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/headerfile/expected/docbook/headers.xml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/headerfile/expected/docbook/headers.xml
new file mode 100644
index 000000000..052108b2a
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/headerfile/expected/docbook/headers.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<db:article xmlns:db="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" version="5.2" xml:lang="en">
+<db:info>
+<db:title>Headers</db:title>
+<db:productname>HeaderFile</db:productname>
+<db:titleabbrev>HeaderFile Reference Documentation</db:titleabbrev>
+<db:abstract>
+<db:para>HeaderFile Reference Documentation.</db:para></db:abstract>
+</db:info>
+<db:anchor xml:id="details"/>
+<db:variablelist role="members">
+<db:varlistentry>
+<db:term><db:link xlink:href="testheader.xml" xlink:role="">&lt;TestHeader&gt;</db:link></db:term>
+<db:listitem>
+<db:para>A header file.</db:para>
+</db:listitem>
+</db:varlistentry>
+</db:variablelist>
+</db:article>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/headerfile/expected/docbook/testheader.xml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/headerfile/expected/docbook/testheader.xml
new file mode 100644
index 000000000..03a49cb67
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/headerfile/expected/docbook/testheader.xml
@@ -0,0 +1,68 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<db:article xmlns:db="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" version="5.2" xml:lang="en">
+<db:info>
+<db:title>&lt;TestHeader&gt; - Test Header</db:title>
+<db:productname>HeaderFile</db:productname>
+<db:titleabbrev>HeaderFile Reference Documentation</db:titleabbrev>
+<db:abstract>
+<db:para>A header file.</db:para>
+</db:abstract>
+</db:info>
+<db:variablelist>
+<db:varlistentry>
+<db:term>Header</db:term>
+<db:listitem>
+<db:para>TestHeader</db:para>
+</db:listitem>
+</db:varlistentry>
+<db:varlistentry>
+<db:term>Group</db:term>
+<db:listitem>
+<db:para>&lt;TestHeader&gt; is part of <db:simplelist><db:member><db:link xlink:href="headers.xml">Headers</db:link></db:member><db:member><db:link xlink:href="tests.xml">Tests</db:link></db:member></db:simplelist>
+</db:para>
+</db:listitem>
+</db:varlistentry>
+</db:variablelist>
+<db:section xml:id="details">
+<db:title>Detailed Description</db:title>
+</db:section>
+<db:section xml:id="type-documentation">
+<db:title>Type Documentation</db:title>
+<db:section xml:id="Globals-enum">
+<db:title>enum Globals</db:title>
+<db:informaltable>
+<db:thead>
+<db:tr>
+<db:th>Constant</db:th>
+</db:tr>
+</db:thead>
+<db:tr>
+<db:td>
+<db:para><db:code>Glo</db:code></db:para>
+</db:td>
+<db:td><db:code>0</db:code></db:td>
+</db:tr>
+<db:tr>
+<db:td>
+<db:para><db:code>Bal</db:code></db:para>
+</db:td>
+<db:td><db:code>1</db:code></db:td>
+</db:tr>
+</db:informaltable>
+</db:section>
+</db:section>
+<db:section xml:id="variable-documentation">
+<db:title>Variable Documentation</db:title>
+<db:section xml:id="globalVar-var">
+<db:title>const int globalVar</db:title>
+<db:para>Global variable.</db:para>
+</db:section>
+</db:section>
+<db:section xml:id="function-documentation">
+<db:title>Function Documentation</db:title>
+<db:section xml:id="globalFunc">
+<db:title>void globalFunc()</db:title>
+<db:para>Global function.</db:para>
+</db:section>
+</db:section>
+</db:article>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/headerfile/expected/docbook/tests.xml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/headerfile/expected/docbook/tests.xml
new file mode 100644
index 000000000..25f95affd
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/headerfile/expected/docbook/tests.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<db:article xmlns:db="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" version="5.2" xml:lang="en">
+<db:info>
+<db:title>Tests</db:title>
+<db:productname>HeaderFile</db:productname>
+<db:titleabbrev>HeaderFile Reference Documentation</db:titleabbrev>
+<db:abstract>
+<db:para>HeaderFile Reference Documentation.</db:para></db:abstract>
+</db:info>
+<db:anchor xml:id="details"/>
+<db:variablelist role="members">
+<db:varlistentry>
+<db:term><db:link xlink:href="testheader.xml" xlink:role="">&lt;TestHeader&gt;</db:link></db:term>
+<db:listitem>
+<db:para>A header file.</db:para>
+</db:listitem>
+</db:varlistentry>
+</db:variablelist>
+</db:article>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/headerfile/expected/html/headerfile.index b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/headerfile/expected/html/headerfile.index
new file mode 100644
index 000000000..cf72522f9
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/headerfile/expected/html/headerfile.index
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE QDOCINDEX>
+<INDEX url="" title="HeaderFile Reference Documentation" version="" project="HeaderFile">
+ <namespace name="" status="active" access="public" module="headerfile">
+ <function name="globalFunc" href="testheader.html#globalFunc" status="active" access="public" location="testheader.h" documented="true" related="0" meta="plain" type="void" brief="Global function" signature="void globalFunc()"/>
+ <header name="&lt;TestHeader&gt;" href="testheader.html" status="active" documented="true" groups="headers,tests" module="TestCPP" brief="A header file" title="Test Header" fulltitle="&lt;TestHeader&gt; - Test Header" subtitle="">
+ <function name="globalFunc" href="testheader.html#globalFunc" status="active" access="public" location="testheader.h" documented="true" related="0" meta="plain" type="void" brief="Global function" signature="void globalFunc()"/>
+ <enum name="Globals" href="testheader.html#Globals-enum" status="active" access="public" location="testheader.h" related="1" documented="true">
+ <value name="Glo" value="0"/>
+ <value name="Bal" value="1"/>
+ </enum>
+ <variable name="globalVar" href="testheader.html#globalVar-var" status="active" access="public" location="testheader.h" related="2" documented="true" type="const int" static="false" brief="Global variable"/>
+ </header>
+ <enum name="Globals" href="testheader.html#Globals-enum" status="active" access="public" location="testheader.h" related="1" documented="true">
+ <value name="Glo" value="0"/>
+ <value name="Bal" value="1"/>
+ </enum>
+ <variable name="globalVar" href="testheader.html#globalVar-var" status="active" access="public" location="testheader.h" related="2" documented="true" type="const int" static="false" brief="Global variable"/>
+ <group name="headers" href="headers.html" status="active" documented="true" seen="true" title="Headers"/>
+ <group name="tests" href="tests.html" status="active" documented="true" seen="true" title="Tests"/>
+ <module name="TestCPP" href="testcpp-module.html" status="internal" seen="false" title=""/>
+ </namespace>
+</INDEX>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/headerfile/expected/html/headers.html b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/headerfile/expected/html/headers.html
new file mode 100644
index 000000000..1a60ec30b
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/headerfile/expected/html/headers.html
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+<!-- testheader.cpp -->
+ <title>Headers | HeaderFile</title>
+</head>
+<body>
+<div class="sidebar"><div class="sidebar-content" id="sidebar-content"></div></div>
+<h1 class="title">Headers</h1>
+<!-- $$$headers-description -->
+<div class="descr" id="details">
+</div>
+<!-- @@@headers -->
+<div class="table"><table class="annotated">
+<tr class="odd topAlign"><td class="tblName" translate="no"><p><a href="testheader.html">&lt;TestHeader&gt;</a></p></td><td class="tblDescr"><p>A header file</p></td></tr>
+</table></div>
+</body>
+</html>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/headerfile/expected/html/testheader.html b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/headerfile/expected/html/testheader.html
new file mode 100644
index 000000000..a728ddd12
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/headerfile/expected/html/testheader.html
@@ -0,0 +1,69 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+<!-- testheader.cpp -->
+ <meta name="description" content="A header file.">
+ <title>&lt;TestHeader&gt; - Test Header | HeaderFile</title>
+</head>
+<body>
+<li><a href="headers.html">Headers</a></li>
+<li>&lt;TestHeader&gt; - Test Header</li>
+<div class="sidebar">
+<div class="toc">
+<h3 id="toc">Contents</h3>
+</div>
+<div class="sidebar-content" id="sidebar-content"></div></div>
+<h1 class="title" translate="no">&lt;TestHeader&gt; - Test Header</h1>
+<!-- $$$<TestHeader>-brief -->
+<p>A header file. <a href="#details">More...</a></p>
+<!-- @@@<TestHeader> -->
+<div class="table"><table class="alignedsummary" translate="no">
+<tr><td class="memItemLeft rightAlign topAlign"> Header:</td><td class="memItemRight bottomAlign"> <span class="preprocessor">#include &lt;TestHeader&gt;</span></td></tr>
+</table></div>
+<ul>
+<li><TestHeader> is part of <a href="headers.html">Headers</a> and <a href="tests.html">Tests</a>.</li>
+</ul>
+<h2 id="types">Types</h2>
+<div class="table"><table class="alignedsummary" translate="no">
+<tr><td class="memItemLeft rightAlign topAlign"> enum </td><td class="memItemRight bottomAlign"><b><a href="testheader.html#Globals-enum" translate="no">Globals</a></b> { Glo, Bal }</td></tr>
+</table></div>
+<h2 id="variables">Variables</h2>
+<div class="table"><table class="alignedsummary" translate="no">
+<tr><td class="memItemLeft rightAlign topAlign"> const int </td><td class="memItemRight bottomAlign"><b><a href="testheader.html#globalVar-var" translate="no">globalVar</a></b></td></tr>
+</table></div>
+<h2 id="functions">Functions</h2>
+<div class="table"><table class="alignedsummary" translate="no">
+<tr><td class="memItemLeft rightAlign topAlign"> void </td><td class="memItemRight bottomAlign"><b><a href="testheader.html#globalFunc" translate="no">globalFunc</a></b>()</td></tr>
+</table></div>
+<!-- $$$<TestHeader>-description -->
+<div class="descr">
+<h2 id="details">Detailed Description</h2>
+</div>
+<!-- @@@<TestHeader> -->
+<div class="types">
+<h2>Type Documentation</h2>
+<!-- $$$Globals$$$Glo$$$Bal -->
+<h3 class="fn" translate="no" id="Globals-enum">enum <span class="name">Globals</span></h3>
+<div class="table"><table class="valuelist"><tr><th class="tblConst">Constant</th><th class="tblVal">Value</th></tr>
+<tr><td class="topAlign"><code translate="no">Glo</code></td><td class="topAlign tblval"><code translate="no">0</code></td></tr>
+<tr><td class="topAlign"><code translate="no">Bal</code></td><td class="topAlign tblval"><code translate="no">1</code></td></tr>
+</table></div>
+<!-- @@@Globals -->
+</div>
+<div class="vars">
+<h2>Variable Documentation</h2>
+<!-- $$$globalVar -->
+<h3 class="fn" translate="no" id="globalVar-var">const <span class="type">int</span> <span class="name">globalVar</span></h3>
+<p>Global variable.</p>
+<!-- @@@globalVar -->
+</div>
+<div class="func">
+<h2>Function Documentation</h2>
+<!-- $$$globalFunc[overload1]$$$globalFunc -->
+<h3 class="fn" translate="no" id="globalFunc"><span class="type">void</span> <span class="name">globalFunc</span>()</h3>
+<p>Global function.</p>
+<!-- @@@globalFunc -->
+</div>
+</body>
+</html>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/headerfile/expected/html/tests.html b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/headerfile/expected/html/tests.html
new file mode 100644
index 000000000..a4221f375
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/headerfile/expected/html/tests.html
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+<!-- testheader.cpp -->
+ <title>Tests | HeaderFile</title>
+</head>
+<body>
+<div class="sidebar"><div class="sidebar-content" id="sidebar-content"></div></div>
+<h1 class="title">Tests</h1>
+<!-- $$$tests-description -->
+<div class="descr" id="details">
+</div>
+<!-- @@@tests -->
+<div class="table"><table class="annotated">
+<tr class="odd topAlign"><td class="tblName" translate="no"><p><a href="testheader.html">&lt;TestHeader&gt;</a></p></td><td class="tblDescr"><p>A header file</p></td></tr>
+</table></div>
+</body>
+</html>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/headerfile/expected/webxml/headerfile.index b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/headerfile/expected/webxml/headerfile.index
new file mode 100644
index 000000000..cf72522f9
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/headerfile/expected/webxml/headerfile.index
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE QDOCINDEX>
+<INDEX url="" title="HeaderFile Reference Documentation" version="" project="HeaderFile">
+ <namespace name="" status="active" access="public" module="headerfile">
+ <function name="globalFunc" href="testheader.html#globalFunc" status="active" access="public" location="testheader.h" documented="true" related="0" meta="plain" type="void" brief="Global function" signature="void globalFunc()"/>
+ <header name="&lt;TestHeader&gt;" href="testheader.html" status="active" documented="true" groups="headers,tests" module="TestCPP" brief="A header file" title="Test Header" fulltitle="&lt;TestHeader&gt; - Test Header" subtitle="">
+ <function name="globalFunc" href="testheader.html#globalFunc" status="active" access="public" location="testheader.h" documented="true" related="0" meta="plain" type="void" brief="Global function" signature="void globalFunc()"/>
+ <enum name="Globals" href="testheader.html#Globals-enum" status="active" access="public" location="testheader.h" related="1" documented="true">
+ <value name="Glo" value="0"/>
+ <value name="Bal" value="1"/>
+ </enum>
+ <variable name="globalVar" href="testheader.html#globalVar-var" status="active" access="public" location="testheader.h" related="2" documented="true" type="const int" static="false" brief="Global variable"/>
+ </header>
+ <enum name="Globals" href="testheader.html#Globals-enum" status="active" access="public" location="testheader.h" related="1" documented="true">
+ <value name="Glo" value="0"/>
+ <value name="Bal" value="1"/>
+ </enum>
+ <variable name="globalVar" href="testheader.html#globalVar-var" status="active" access="public" location="testheader.h" related="2" documented="true" type="const int" static="false" brief="Global variable"/>
+ <group name="headers" href="headers.html" status="active" documented="true" seen="true" title="Headers"/>
+ <group name="tests" href="tests.html" status="active" documented="true" seen="true" title="Tests"/>
+ <module name="TestCPP" href="testcpp-module.html" status="internal" seen="false" title=""/>
+ </namespace>
+</INDEX>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/headerfile/expected/webxml/headers.webxml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/headerfile/expected/webxml/headers.webxml
new file mode 100644
index 000000000..6fdc7de0b
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/headerfile/expected/webxml/headers.webxml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<WebXML>
+ <document>
+ <group name="headers" href="headers.html" status="active" documented="true" seen="true" title="Headers">
+ <description>
+ <table width="100%">
+ <row>
+ <item>
+ <para>
+ <link raw="&lt;TestHeader&gt;" href="testheader.html" type=""/>
+ </para>
+ </item>
+ <item>
+ <para>A header file.</para>
+ </item>
+ </row>
+ </table>
+ </description>
+ </group>
+ </document>
+</WebXML>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/headerfile/expected/webxml/testheader.webxml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/headerfile/expected/webxml/testheader.webxml
new file mode 100644
index 000000000..fcc37e5ca
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/headerfile/expected/webxml/testheader.webxml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<WebXML>
+ <document>
+ <header name="&lt;TestHeader&gt;" href="testheader.html" status="active" documented="true" groups="headers,tests" module="TestCPP" brief="A header file" title="Test Header" fulltitle="&lt;TestHeader&gt; - Test Header" subtitle="">
+ <description>
+ <brief>A header file.</brief>
+ </description>
+ <function name="globalFunc" href="testheader.html#globalFunc" status="active" access="public" location="testheader.h" documented="true" related="0" meta="plain" type="void" brief="Global function" signature="void globalFunc()">
+ <description>
+ <brief>Global function.</brief>
+ </description>
+ </function>
+ <enum name="Globals" href="testheader.html#Globals-enum" status="active" access="public" location="testheader.h" related="1" documented="true">
+ <value name="Glo" value="0"/>
+ <value name="Bal" value="1"/>
+ <description>
+ <list type="enum">
+ <definition>
+ <term>Glo</term>Glo</definition>
+ <item/>
+ <definition>
+ <term>Bal</term>Bal</definition>
+ <item/>
+ </list>
+ </description>
+ </enum>
+ <variable name="globalVar" href="testheader.html#globalVar-var" status="active" access="public" location="testheader.h" related="2" documented="true" type="const int" static="false" brief="Global variable">
+ <description>
+ <brief>This variable holds Global variable..</brief>
+ </description>
+ </variable>
+ </header>
+ </document>
+</WebXML>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/headerfile/expected/webxml/tests.webxml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/headerfile/expected/webxml/tests.webxml
new file mode 100644
index 000000000..7c3deb373
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/headerfile/expected/webxml/tests.webxml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<WebXML>
+ <document>
+ <group name="tests" href="tests.html" status="active" documented="true" seen="true" title="Tests">
+ <description>
+ <table width="100%">
+ <row>
+ <item>
+ <para>
+ <link raw="&lt;TestHeader&gt;" href="testheader.html" type=""/>
+ </para>
+ </item>
+ <item>
+ <para>A header file.</para>
+ </item>
+ </row>
+ </table>
+ </description>
+ </group>
+ </document>
+</WebXML>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/headerfile/headerfile.qdocconf b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/headerfile/headerfile.qdocconf
new file mode 100644
index 000000000..b449319cc
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/headerfile/headerfile.qdocconf
@@ -0,0 +1,25 @@
+sources.fileextensions = "*.qml *.cpp *.qdoc"
+headers.fileextensions = "*.h"
+
+# images
+imagedirs = ./src/images
+
+# zero warning policy
+warninglimit = 0
+warninglimit.enabled = true
+
+# don't write host system-specific paths to index files
+locationinfo = false
+
+project = HeaderFile
+moduleheader = .src/testheader.h
+
+{includepaths,sourcedirs,headerdirs} += ./src
+
+outputformats = HTML WebXML DocBook
+HTML.nosubdirs = true
+HTML.outputsubdir = html
+WebXML.nosubdirs = true
+WebXML.outputsubdir = webxml
+DocBook.nosubdirs = true
+DocBook.outputsubdir = docbook
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/headerfile/src/testheader.cpp b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/headerfile/src/testheader.cpp
new file mode 100644
index 000000000..43c512f36
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/headerfile/src/testheader.cpp
@@ -0,0 +1,43 @@
+// Copyright (C) 2020 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "testheader.h"
+
+const int globalVar = 0;
+
+/*!
+ \headerfile <TestHeader>
+ \title Test Header
+ \inmodule TestCPP
+ \brief A header file.
+ \ingroup headers
+ \ingroup tests
+*/
+
+/*!
+ \group headers
+ \title Headers
+*/
+
+/*! \group tests
+ \title Tests
+*/
+
+/*!
+ \fn void globalFunc()
+ \brief Global function.
+ \relates <TestHeader>
+*/
+
+/*!
+ \variable globalVar
+ \brief Global variable.
+ \relates <TestHeader>
+*/
+
+/*!
+ \enum Globals
+ \relates <TestHeader>
+ \value Glo
+ \value Bal
+*/
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/headerfile/src/testheader.h b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/headerfile/src/testheader.h
new file mode 100644
index 000000000..5ea361685
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/headerfile/src/testheader.h
@@ -0,0 +1,8 @@
+// Copyright (C) 2020 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#pragma once
+
+void globalFunc() {};
+enum Globals { Glo, Bal };
+extern const int globalVar;
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/illformatted_documentation/expected/html/another-page-with-comments-in-the-brief.html b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/illformatted_documentation/expected/html/another-page-with-comments-in-the-brief.html
new file mode 100644
index 000000000..71ef2af25
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/illformatted_documentation/expected/html/another-page-with-comments-in-the-brief.html
@@ -0,0 +1,18 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+<!-- brief_adventures.qdoc -->
+ <meta name="description" content="Another test that the \brief command isn't completely borked">
+ <title>There's no end to the possibilities! | IllformattedDocumentation</title>
+</head>
+<body>
+<div class="sidebar"><div class="sidebar-content" id="sidebar-content"></div></div>
+<h1 class="title">There's no end to the possibilities!</h1>
+<!-- $$$another-page-with-comments-in-the-brief-description -->
+<div class="descr" id="details">
+<p>The brief for this page is: &quot;Another test that the \brief command isn't completely borked&quot;. Notice the lack of a trailing full stop in the brief. QDoc should generate a warning about that when generating HTML.</p>
+</div>
+<!-- @@@another-page-with-comments-in-the-brief -->
+</body>
+</html>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/illformatted_documentation/expected/html/brief-adventures.html b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/illformatted_documentation/expected/html/brief-adventures.html
new file mode 100644
index 000000000..1600b124c
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/illformatted_documentation/expected/html/brief-adventures.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+<!-- brief_adventures.qdoc -->
+ <meta name="description" content="Test that the \brief command doesn't eat content that follows it.">
+ <title>Adventures with QDoc's \brief command | IllformattedDocumentation</title>
+</head>
+<body>
+<div class="sidebar">
+<div class="toc">
+<h3 id="toc">Contents</h3>
+<ul>
+<li class="level1"><a href="#further-details">Further details</a></li>
+</ul>
+</div>
+<div class="sidebar-content" id="sidebar-content"></div></div>
+<h1 class="title">Adventures with QDoc's \brief command</h1>
+<!-- $$$brief-adventures.html-description -->
+<div class="descr" id="details">
+<p>The purpose of this test data is to provide a regression mechanism as part of QDoc's end-to-end test, tst_generatedOutput, for an issue (QTBUG-70959) that was reported against QDoc's \keyword command. The issue, as experienced by the reporter of the bug, is that if the \keyword command isn't followed by a new command, or is the last command in a paragraph, the following paragraph is &quot;eaten&quot;. That means the entire paragraph is understood by QDoc as keywords and included as html meta information instead of as part of the rendered output.</p>
+<h2 id="further-details">Further details</h2>
+<p>The bug report is at <a href="https://bugreports.qt.io/browse/QTBUG-70959">https://bugreports.qt.io/browse/QTBUG-70959</a>. It refers to a change that bypassed the issue by moving the \keyword command, at <a href="https://codereview.qt-project.org/c/qt/qtdoc/+/242033/">https://codereview.qt-project.org/c/qt/qtdoc/+/242033/</a>.</p>
+</div>
+<!-- @@@brief-adventures.html -->
+</body>
+</html>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/illformatted_documentation/expected/html/illformatted-examples.html b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/illformatted_documentation/expected/html/illformatted-examples.html
new file mode 100644
index 000000000..5253ae1d1
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/illformatted_documentation/expected/html/illformatted-examples.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+<!-- illformatted-examples.qdoc -->
+ <meta name="description" content="Demonstrate correctness for example generation.">
+ <title>Test generated output for illformatted examples | IllformattedDocumentation</title>
+</head>
+<body>
+<div class="sidebar">
+<div class="toc">
+<h3 id="toc">Contents</h3>
+<ul>
+<li class="level1"><a href="#qml-examples">QML Examples</a></li>
+<li class="level1"><a href="#c-examples">C++ Examples</a></li>
+</ul>
+</div>
+<div class="sidebar-content" id="sidebar-content"></div></div>
+<h1 class="title">Test generated output for illformatted examples</h1>
+<!-- $$$illformatted-examples.html-description -->
+<div class="descr" id="details">
+<p>This test includes the following examples:</p>
+<h2 id="qml-examples">QML Examples</h2>
+<div class="table"><table class="annotated">
+<tr class="odd topAlign"><td class="tblName" translate="no"><p><a href="illformatteddocumentation-someexample-example.html">QDoc: some example</a></p></td><td class="tblDescr"><p></p></td></tr>
+</table></div>
+<h2 id="c-examples">C++ Examples</h2>
+</div>
+<!-- @@@illformatted-examples.html -->
+</body>
+</html>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/illformatted_documentation/expected/html/illformatteddocumentation-someexample-example.html b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/illformatted_documentation/expected/html/illformatteddocumentation-someexample-example.html
new file mode 100644
index 000000000..ebeb5a5b4
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/illformatted_documentation/expected/html/illformatteddocumentation-someexample-example.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+<!-- some_example.qdoc -->
+ <title>QDoc: some example | IllformattedDocumentation</title>
+</head>
+<body>
+<div class="sidebar"><div class="sidebar-content" id="sidebar-content"></div></div>
+<h1 class="title">QDoc: some example</h1>
+<!-- $$$someexample-description -->
+<div class="descr" id="details">
+</div>
+<!-- @@@someexample -->
+</body>
+</html>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/illformatted_documentation/expected/html/illformatteddocumentation.index b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/illformatted_documentation/expected/html/illformatteddocumentation.index
new file mode 100644
index 000000000..e9898cd2e
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/illformatted_documentation/expected/html/illformatteddocumentation.index
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE QDOCINDEX>
+<INDEX url="" title="IllformattedDocumentation Reference Documentation" version="" project="IllformattedDocumentation">
+ <namespace name="" status="active" access="public" module="illformatteddocumentation">
+ <page name="brief-adventures.html" href="brief-adventures.html" status="active" location="brief_adventures.qdoc" documented="true" subtype="page" title="Adventures with QDoc's \brief command" fulltitle="Adventures with QDoc's \brief command" subtitle="" brief="Test that the \brief command doesn't eat content that follows it">
+ <keyword name="some-keyword" title="some_keyword"/>
+ <contents name="further-details" title="Further details" level="1"/>
+ </page>
+ <page name="someexample" href="illformatteddocumentation-someexample-example.html" status="active" location="some_example.qdoc" documented="true" groups="illformatted-examples-qml" subtype="example" title="QDoc: some example" fulltitle="QDoc: some example" subtitle=""/>
+ <page name="illformatted-examples.html" href="illformatted-examples.html" status="active" location="illformatted-examples.qdoc" documented="true" groups="all-examples" subtype="page" title="Test generated output for illformatted examples" fulltitle="Test generated output for illformatted examples" subtitle="" brief="Demonstrate correctness for example generation">
+ <contents name="qml-examples" title="QML Examples" level="1"/>
+ <contents name="c-examples" title="C++ Examples" level="1"/>
+ </page>
+ <page name="another-page-with-comments-in-the-brief" href="another-page-with-comments-in-the-brief.html" status="active" location="brief_adventures.qdoc" documented="true" subtype="page" title="There's no end to the possibilities!" fulltitle="There's no end to the possibilities!" subtitle="" brief="Another test that the \brief command isn't completely borked"/>
+ <page name="page-with-an-image-at-the-top.html" href="page-with-an-image-at-the-top.html" status="active" location="brief_adventures.qdoc" documented="true" subtype="page" title="This page starts with an image" fulltitle="This page starts with an image" subtitle="" brief="This page has an image right at the top"/>
+ <page name="page-with-comment-after-brief.html" href="page-with-comment-after-brief.html" status="active" location="brief_adventures.qdoc" documented="true" subtype="page" title="Yet another adventure with QDoc's \brief command" fulltitle="Yet another adventure with QDoc's \brief command" subtitle="" brief="Another test that the \brief command doesn't eat content that follows it"/>
+ <page name="page-with-comment-in-brief.html" href="page-with-comment-in-brief.html" status="active" location="brief_adventures.qdoc" documented="true" subtype="page" title="Yet, yet another adventure with QDoc's \brief command" fulltitle="Yet, yet another adventure with QDoc's \brief command" subtitle="" brief="Another test that the \brief command isn't entirely broken"/>
+ <group name="all-examples" href="all-examples.html" status="internal" seen="false" title=""/>
+ <group name="illformatted-examples-qml" href="illformatted-examples-qml.html" status="internal" seen="false" title=""/>
+ <module name="sometestgroup" href="sometestgroup-module.html" status="internal" seen="false" title=""/>
+ </namespace>
+</INDEX>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/illformatted_documentation/expected/html/page-with-an-image-at-the-top.html b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/illformatted_documentation/expected/html/page-with-an-image-at-the-top.html
new file mode 100644
index 000000000..e66e6ba51
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/illformatted_documentation/expected/html/page-with-an-image-at-the-top.html
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+<!-- brief_adventures.qdoc -->
+ <meta name="description" content="This page has an image right at the top.">
+ <title>This page starts with an image | IllformattedDocumentation</title>
+</head>
+<body>
+<div class="sidebar"><div class="sidebar-content" id="sidebar-content"></div></div>
+<h1 class="title">This page starts with an image</h1>
+<!-- $$$page-with-an-image-at-the-top.html-description -->
+<div class="descr" id="details">
+<p class="centerAlign"><font color="red">[Missing image leonardo-da-vinci.png]</font></p><p>The image should render as expected, and the alternate text should be there, too. This text is contained in its own paragraph following the image.</p>
+<p>This paragraph is a new paragraph, and doesn't contain an image.</p>
+</div>
+<!-- @@@page-with-an-image-at-the-top.html -->
+</body>
+</html>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/illformatted_documentation/expected/html/page-with-comment-after-brief.html b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/illformatted_documentation/expected/html/page-with-comment-after-brief.html
new file mode 100644
index 000000000..fc6fa1b92
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/illformatted_documentation/expected/html/page-with-comment-after-brief.html
@@ -0,0 +1,18 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+<!-- brief_adventures.qdoc -->
+ <meta name="description" content="Another test that the \brief command doesn't eat content that follows it.">
+ <title>Yet another adventure with QDoc's \brief command | IllformattedDocumentation</title>
+</head>
+<body>
+<div class="sidebar"><div class="sidebar-content" id="sidebar-content"></div></div>
+<h1 class="title">Yet another adventure with QDoc's \brief command</h1>
+<!-- $$$page-with-comment-after-brief.html-description -->
+<div class="descr" id="details">
+<p>This paragraph should render normally, but might be eaten by the \brief brief because the brief is followed by a QDoc comment (which shouldn't be rendered at all).</p>
+</div>
+<!-- @@@page-with-comment-after-brief.html -->
+</body>
+</html>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/illformatted_documentation/expected/html/page-with-comment-in-brief.html b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/illformatted_documentation/expected/html/page-with-comment-in-brief.html
new file mode 100644
index 000000000..4283ae544
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/illformatted_documentation/expected/html/page-with-comment-in-brief.html
@@ -0,0 +1,18 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+<!-- brief_adventures.qdoc -->
+ <meta name="description" content="Another test that the \brief command isn't entirely broken.">
+ <title>Yet, yet another adventure with QDoc's \brief command | IllformattedDocumentation</title>
+</head>
+<body>
+<div class="sidebar"><div class="sidebar-content" id="sidebar-content"></div></div>
+<h1 class="title">Yet, yet another adventure with QDoc's \brief command</h1>
+<!-- $$$page-with-comment-in-brief.html-description -->
+<div class="descr" id="details">
+<p>The brief for this page is: &quot;Another test that the \brief command isn't entirely broken.&quot;</p>
+</div>
+<!-- @@@page-with-comment-in-brief.html -->
+</body>
+</html>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/illformatted_documentation/expected/webxml/another-page-with-comments-in-the-brief.webxml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/illformatted_documentation/expected/webxml/another-page-with-comments-in-the-brief.webxml
new file mode 100644
index 000000000..7e82dfad6
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/illformatted_documentation/expected/webxml/another-page-with-comments-in-the-brief.webxml
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<WebXML>
+ <document>
+ <page name="another-page-with-comments-in-the-brief" href="another-page-with-comments-in-the-brief.html" status="active" location="brief_adventures.qdoc" documented="true" subtype="page" title="There's no end to the possibilities!" fulltitle="There's no end to the possibilities!" subtitle="" brief="Another test that the \brief command isn't completely borked">
+ <description>
+ <brief>Another test that the \brief command isn't completely borked</brief>
+ <para>The brief for this page is: &quot;Another test that the \brief command isn't completely borked&quot;. Notice the lack of a trailing full stop in the brief. QDoc should generate a warning about that when generating HTML.</para>
+ </description>
+ </page>
+ </document>
+</WebXML>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/illformatted_documentation/expected/webxml/brief-adventures.webxml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/illformatted_documentation/expected/webxml/brief-adventures.webxml
new file mode 100644
index 000000000..3a6f5f2d5
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/illformatted_documentation/expected/webxml/brief-adventures.webxml
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<WebXML>
+ <document>
+ <page name="brief-adventures.html" href="brief-adventures.html" status="active" location="brief_adventures.qdoc" documented="true" subtype="page" title="Adventures with QDoc's \brief command" fulltitle="Adventures with QDoc's \brief command" subtitle="" brief="Test that the \brief command doesn't eat content that follows it">
+ <keyword name="some-keyword" title="some_keyword"/>
+ <contents name="further-details" title="Further details" level="1"/>
+ <description>
+ <brief>Test that the \brief command doesn't eat content that follows it.</brief>
+ <para>The purpose of this test data is to provide a regression mechanism as part of QDoc's end-to-end test, tst_generatedOutput, for an issue (QTBUG-70959) that was reported against QDoc's \keyword command. The issue, as experienced by the reporter of the bug, is that if the \keyword command isn't followed by a new command, or is the last command in a paragraph, the following paragraph is &quot;eaten&quot;. That means the entire paragraph is understood by QDoc as keywords and included as html meta information instead of as part of the rendered output.</para>
+ <section id="further-details">
+ <heading level="1">Further details</heading>
+ <para>The bug report is at <link raw="https://bugreports.qt.io/browse/QTBUG-70959" href="https://bugreports.qt.io/browse/QTBUG-70959" type="external">https://bugreports.qt.io/browse/QTBUG-70959</link>. It refers to a change that bypassed the issue by moving the \keyword command, at <link raw="https://codereview.qt-project.org/c/qt/qtdoc/+/242033/" href="https://codereview.qt-project.org/c/qt/qtdoc/+/242033/" type="external">https://codereview.qt-project.org/c/qt/qtdoc/+/242033/</link>.</para>
+ </section>
+ </description>
+ </page>
+ </document>
+</WebXML>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/illformatted_documentation/expected/webxml/illformatted-examples.webxml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/illformatted_documentation/expected/webxml/illformatted-examples.webxml
new file mode 100644
index 000000000..b49d1b297
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/illformatted_documentation/expected/webxml/illformatted-examples.webxml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<WebXML>
+ <document>
+ <page name="illformatted-examples.html" href="illformatted-examples.html" status="active" location="illformatted-examples.qdoc" documented="true" groups="all-examples" subtype="page" title="Test generated output for illformatted examples" fulltitle="Test generated output for illformatted examples" subtitle="" brief="Demonstrate correctness for example generation">
+ <contents name="qml-examples" title="QML Examples" level="1"/>
+ <contents name="c-examples" title="C++ Examples" level="1"/>
+ <description>
+ <brief>Demonstrate correctness for example generation.</brief>
+ <para>This test includes the following examples:</para>
+ <section id="qml-examples">
+ <heading level="1">QML Examples</heading>
+ <table width="100%">
+ <row>
+ <item>
+ <para>
+ <link raw="QDoc: some example" href="illformatteddocumentation-someexample-example.html" type="page" page="QDoc: some example"/>
+ </para>
+ </item>
+ <item>
+ <para></para>
+ </item>
+ </row>
+ </table>
+ </section>
+ <section id="c-examples">
+ <heading level="1">C++ Examples</heading>
+ </section>
+ </description>
+ </page>
+ </document>
+</WebXML>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/illformatted_documentation/expected/webxml/illformatteddocumentation-someexample-example.webxml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/illformatted_documentation/expected/webxml/illformatteddocumentation-someexample-example.webxml
new file mode 100644
index 000000000..675e3d74f
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/illformatted_documentation/expected/webxml/illformatteddocumentation-someexample-example.webxml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<WebXML>
+ <document>
+ <page name="someexample" href="illformatteddocumentation-someexample-example.html" status="active" location="some_example.qdoc" documented="true" groups="illformatted-examples-qml" subtype="example" title="QDoc: some example" fulltitle="QDoc: some example" subtitle="">
+ <description/>
+ </page>
+ </document>
+</WebXML>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/illformatted_documentation/expected/webxml/illformatteddocumentation.index b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/illformatted_documentation/expected/webxml/illformatteddocumentation.index
new file mode 100644
index 000000000..e9898cd2e
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/illformatted_documentation/expected/webxml/illformatteddocumentation.index
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE QDOCINDEX>
+<INDEX url="" title="IllformattedDocumentation Reference Documentation" version="" project="IllformattedDocumentation">
+ <namespace name="" status="active" access="public" module="illformatteddocumentation">
+ <page name="brief-adventures.html" href="brief-adventures.html" status="active" location="brief_adventures.qdoc" documented="true" subtype="page" title="Adventures with QDoc's \brief command" fulltitle="Adventures with QDoc's \brief command" subtitle="" brief="Test that the \brief command doesn't eat content that follows it">
+ <keyword name="some-keyword" title="some_keyword"/>
+ <contents name="further-details" title="Further details" level="1"/>
+ </page>
+ <page name="someexample" href="illformatteddocumentation-someexample-example.html" status="active" location="some_example.qdoc" documented="true" groups="illformatted-examples-qml" subtype="example" title="QDoc: some example" fulltitle="QDoc: some example" subtitle=""/>
+ <page name="illformatted-examples.html" href="illformatted-examples.html" status="active" location="illformatted-examples.qdoc" documented="true" groups="all-examples" subtype="page" title="Test generated output for illformatted examples" fulltitle="Test generated output for illformatted examples" subtitle="" brief="Demonstrate correctness for example generation">
+ <contents name="qml-examples" title="QML Examples" level="1"/>
+ <contents name="c-examples" title="C++ Examples" level="1"/>
+ </page>
+ <page name="another-page-with-comments-in-the-brief" href="another-page-with-comments-in-the-brief.html" status="active" location="brief_adventures.qdoc" documented="true" subtype="page" title="There's no end to the possibilities!" fulltitle="There's no end to the possibilities!" subtitle="" brief="Another test that the \brief command isn't completely borked"/>
+ <page name="page-with-an-image-at-the-top.html" href="page-with-an-image-at-the-top.html" status="active" location="brief_adventures.qdoc" documented="true" subtype="page" title="This page starts with an image" fulltitle="This page starts with an image" subtitle="" brief="This page has an image right at the top"/>
+ <page name="page-with-comment-after-brief.html" href="page-with-comment-after-brief.html" status="active" location="brief_adventures.qdoc" documented="true" subtype="page" title="Yet another adventure with QDoc's \brief command" fulltitle="Yet another adventure with QDoc's \brief command" subtitle="" brief="Another test that the \brief command doesn't eat content that follows it"/>
+ <page name="page-with-comment-in-brief.html" href="page-with-comment-in-brief.html" status="active" location="brief_adventures.qdoc" documented="true" subtype="page" title="Yet, yet another adventure with QDoc's \brief command" fulltitle="Yet, yet another adventure with QDoc's \brief command" subtitle="" brief="Another test that the \brief command isn't entirely broken"/>
+ <group name="all-examples" href="all-examples.html" status="internal" seen="false" title=""/>
+ <group name="illformatted-examples-qml" href="illformatted-examples-qml.html" status="internal" seen="false" title=""/>
+ <module name="sometestgroup" href="sometestgroup-module.html" status="internal" seen="false" title=""/>
+ </namespace>
+</INDEX>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/illformatted_documentation/expected/webxml/page-with-an-image-at-the-top.webxml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/illformatted_documentation/expected/webxml/page-with-an-image-at-the-top.webxml
new file mode 100644
index 000000000..da7b4f1d9
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/illformatted_documentation/expected/webxml/page-with-an-image-at-the-top.webxml
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<WebXML>
+ <document>
+ <page name="page-with-an-image-at-the-top.html" href="page-with-an-image-at-the-top.html" status="active" location="brief_adventures.qdoc" documented="true" subtype="page" title="This page starts with an image" fulltitle="This page starts with an image" subtitle="" brief="This page has an image right at the top">
+ <description>
+ <brief>This page has an image right at the top.</brief>
+ <para>The image should render as expected, and the alternate text should be there, too. This text is contained in its own paragraph following the image.</para>
+ <para>This paragraph is a new paragraph, and doesn't contain an image.</para>
+ </description>
+ </page>
+ </document>
+</WebXML>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/illformatted_documentation/expected/webxml/page-with-comment-after-brief.webxml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/illformatted_documentation/expected/webxml/page-with-comment-after-brief.webxml
new file mode 100644
index 000000000..f5b28e302
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/illformatted_documentation/expected/webxml/page-with-comment-after-brief.webxml
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<WebXML>
+ <document>
+ <page name="page-with-comment-after-brief.html" href="page-with-comment-after-brief.html" status="active" location="brief_adventures.qdoc" documented="true" subtype="page" title="Yet another adventure with QDoc's \brief command" fulltitle="Yet another adventure with QDoc's \brief command" subtitle="" brief="Another test that the \brief command doesn't eat content that follows it">
+ <description>
+ <brief>Another test that the \brief command doesn't eat content that follows it.</brief>
+ <para>This paragraph should render normally, but might be eaten by the \brief brief because the brief is followed by a QDoc comment (which shouldn't be rendered at all).</para>
+ </description>
+ </page>
+ </document>
+</WebXML>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/illformatted_documentation/expected/webxml/page-with-comment-in-brief.webxml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/illformatted_documentation/expected/webxml/page-with-comment-in-brief.webxml
new file mode 100644
index 000000000..575cd111d
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/illformatted_documentation/expected/webxml/page-with-comment-in-brief.webxml
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<WebXML>
+ <document>
+ <page name="page-with-comment-in-brief.html" href="page-with-comment-in-brief.html" status="active" location="brief_adventures.qdoc" documented="true" subtype="page" title="Yet, yet another adventure with QDoc's \brief command" fulltitle="Yet, yet another adventure with QDoc's \brief command" subtitle="" brief="Another test that the \brief command isn't entirely broken">
+ <description>
+ <brief>Another test that the \brief command isn't entirely broken.</brief>
+ <para>The brief for this page is: &quot;Another test that the \brief command isn't entirely broken.&quot;</para>
+ </description>
+ </page>
+ </document>
+</WebXML>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/illformatted_documentation/illformatted_documentation.qdocconf b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/illformatted_documentation/illformatted_documentation.qdocconf
new file mode 100644
index 000000000..716882f41
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/illformatted_documentation/illformatted_documentation.qdocconf
@@ -0,0 +1,29 @@
+project = IllformattedDocumentation
+
+headerdirs = ./src
+sourcedirs = ./src
+exampledirs = ./src
+
+outputformats = WebXML HTML
+WebXML.quotinginformation = true
+WebXML.nosubdirs = true
+WebXML.outputsubdir = webxml
+
+warninglimit = 3 # The broken example generates three warnings
+
+sources.fileextensions = "*.qml *.cpp *.qdoc"
+headers.fileextensions = "*.h"
+
+# images
+imagedirs = ./src/images
+
+warninglimit.enabled = true
+
+# don't write host system-specific paths to index files
+locationinfo = false
+
+# By default, use -outputdir directly, no subdir.
+# outputsubdir '.' matches the root of expected_output/
+HTML.nosubdirs = true
+HTML.outputsubdir = html
+
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/illformatted_documentation/src/brief_adventures.qdoc b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/illformatted_documentation/src/brief_adventures.qdoc
new file mode 100644
index 000000000..9bad0106b
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/illformatted_documentation/src/brief_adventures.qdoc
@@ -0,0 +1,75 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+
+/*!
+ \page brief-adventures.html
+ \title Adventures with QDoc's \\brief command
+ \brief Test that the \\brief command doesn't eat content that follows it.
+ \inmodule sometestgroup
+ \keyword some_keyword
+
+ The purpose of this test data is to provide a regression mechanism as part
+ of QDoc's end-to-end test, tst_generatedOutput, for an issue (QTBUG-70959)
+ that was reported against QDoc's \\keyword command. The issue, as
+ experienced by the reporter of the bug, is that if the \\keyword command
+ isn't followed by a new command, or is the last command in a paragraph,
+ the following paragraph is "eaten". That means the entire paragraph is
+ understood by QDoc as keywords and included as html meta information
+ instead of as part of the rendered output.
+
+ \section1 Further details
+ The bug report is at \l https://bugreports.qt.io/browse/QTBUG-70959.
+ It refers to a change that bypassed the issue by moving the \\keyword
+ command, at \l https://codereview.qt-project.org/c/qt/qtdoc/+/242033/.
+*/
+
+/*!
+ \page page-with-an-image-at-the-top.html
+ \title This page starts with an image
+ \brief This page has an image right at the top.
+ \image leonardo-da-vinci.png This is the alternate text for the image
+ The image should render as expected, and the alternate text should be
+ there, too. This text is contained in its own paragraph following the
+ image.
+
+ This paragraph is a new paragraph, and doesn't contain an image.
+*/
+
+/*!
+ \page page-with-comment-after-brief.html
+ \title Yet another adventure with QDoc's \\brief command
+ \brief Another test that the \\brief command doesn't eat content that follows it.
+ //! This QDoc comment might cause an issue, let's try it!
+
+ This paragraph should render normally, but might be eaten by the \\brief
+ brief because the brief is followed by a QDoc comment (which shouldn't be
+ rendered at all).
+*/
+
+/*!
+ \page page-with-comment-in-brief.html
+ \title Yet, yet another adventure with QDoc's \\brief command
+ \brief Another test that the \\brief command //! Brief continues below
+ isn't entirely broken.
+
+ The brief for this page is:
+ "Another test that the \\brief command isn't entirely broken."
+*/
+
+/*!
+ \page another-page-with-comments-in-the-brief
+ \title There's no end to the possibilities!
+ \brief Another test that the \\brief command //! Doesn't contain a full stop.
+ //! QDoc's CMD_BRIEF should issue a warning when it doesn't end with a full
+ //! stop. However, if a \\brief is followed by a line comment (i.e. `//!`),
+ //! the warning isn't issued. This is tied to how `CMD_BRIEF` performs
+ //! macro expansion and command processing; in the case of line comments in
+ //! the argument, the last character of the brief's atom isn't a full stop.
+ isn't completely borked
+
+ The brief for this page is:
+ "Another test that the \\brief command isn't completely borked". Notice the
+ lack of a trailing full stop in the brief. QDoc should generate a warning
+ about that when generating HTML.
+
+*/
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/illformatted_documentation/src/illformatted-examples.qdoc b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/illformatted_documentation/src/illformatted-examples.qdoc
new file mode 100644
index 000000000..5bfa9e7f6
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/illformatted_documentation/src/illformatted-examples.qdoc
@@ -0,0 +1,17 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+
+/*!
+ \page illformatted-examples.html
+ \ingroup all-examples
+ \title Test generated output for illformatted examples
+ \brief Demonstrate correctness for example generation.
+
+ This test includes the following examples:
+
+ \section1 QML Examples
+ \annotatedlist illformatted-examples-qml
+
+ \section1 C++ Examples
+ \annotatedlist illformatted-examples-cpp
+*/
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/illformatted_documentation/src/some_example.qdoc b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/illformatted_documentation/src/some_example.qdoc
new file mode 100644
index 000000000..a64605b87
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/illformatted_documentation/src/some_example.qdoc
@@ -0,0 +1,8 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+
+/*!
+ \example someexample
+ \title QDoc: some example
+ \ingroup illformatted-examples-qml
+*/
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/line_comments/expected/docbook/a-page-with-a-line-comment-in-the-see-also-command.xml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/line_comments/expected/docbook/a-page-with-a-line-comment-in-the-see-also-command.xml
new file mode 100644
index 000000000..45be05ac6
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/line_comments/expected/docbook/a-page-with-a-line-comment-in-the-see-also-command.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<db:article xmlns:db="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" version="5.2" xml:lang="en">
+<db:info>
+<db:title>The source for this page has a line comment in its sa command</db:title>
+<db:productname>LineComment</db:productname>
+<db:titleabbrev>LineComment Reference Documentation</db:titleabbrev>
+<db:abstract>
+<db:para>LineComment Reference Documentation.</db:para></db:abstract>
+</db:info>
+<db:para>QDoc's \sa command doesn't respect QDoc's line comments.</db:para>
+<db:section>
+<db:title>See Also</db:title>
+<db:para><db:emphasis>See also </db:emphasis>
+<db:simplelist type="vert" role="see-also">
+<db:member><db:link xlink:href="another-page-with-an-image-at-the-top.xml">This page starts with an image</db:link></db:member>
+<db:member><db:link xlink:href="line-comment-adventures.xml">Adventures with QDoc's line comments</db:link></db:member>
+</db:simplelist>
+</db:para>
+</db:section>
+</db:article>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/line_comments/expected/docbook/another-page-with-an-image-at-the-top.xml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/line_comments/expected/docbook/another-page-with-an-image-at-the-top.xml
new file mode 100644
index 000000000..c905f9c45
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/line_comments/expected/docbook/another-page-with-an-image-at-the-top.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<db:article xmlns:db="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" version="5.2" xml:lang="en">
+<db:info>
+<db:title>This page starts with an image</db:title>
+<db:productname>LineComment</db:productname>
+<db:titleabbrev>LineComment Reference Documentation</db:titleabbrev>
+<db:abstract>
+<db:para>This page has an image right at the top.</db:para>
+</db:abstract>
+</db:info>
+<db:mediaobject>
+<db:alt>This is the alternate text for the image</db:alt>
+<db:imageobject>
+<db:imagedata fileref="images/leonardo-da-vinci.png"/>
+</db:imageobject>
+</db:mediaobject>
+<db:para>The image should render as expected, and the alternate text should be there, too. This text is contained in its own paragraph following the image.</db:para>
+<db:para>This paragraph is a new paragraph, and doesn't contain an image.</db:para>
+</db:article>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/line_comments/expected/docbook/images/leonardo-da-vinci.png b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/line_comments/expected/docbook/images/leonardo-da-vinci.png
new file mode 100644
index 000000000..854acb4ca
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/line_comments/expected/docbook/images/leonardo-da-vinci.png
Binary files differ
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/line_comments/expected/docbook/line-comment-adventures.xml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/line_comments/expected/docbook/line-comment-adventures.xml
new file mode 100644
index 000000000..e78104e3d
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/line_comments/expected/docbook/line-comment-adventures.xml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<db:article xmlns:db="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" version="5.2" xml:lang="en">
+<db:info>
+<db:title>Adventures with QDoc's line comments</db:title>
+<db:productname>LineComment</db:productname>
+<db:titleabbrev>LineComment Reference Documentation</db:titleabbrev>
+<db:abstract>
+<db:para>Test that line comments don't break documentation.</db:para>
+</db:abstract>
+</db:info>
+<db:para>The purpose of this test data is to provide a regression mechanism as part of QDoc's end-to-end test, tst_generatedOutput, for an issue (QTBUG-105754) that was reported against QDoc's \sa command. The issue, as experienced by the reporter of the bug, is that if the \sa command is followed by a line comment, QDoc generates a series of &quot;Missing comma in \sa&quot; warnings.</db:para>
+<db:section xml:id="further-details">
+<db:title>Further details</db:title>
+<db:para>The bug report is at <db:link xlink:href="https://bugreports.qt.io/browse/QTBUG-105754">https://bugreports.qt.io/browse/QTBUG-105754</db:link>.</db:para>
+</db:section>
+</db:article>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/line_comments/expected/html/a-page-with-a-line-comment-in-the-see-also-command.html b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/line_comments/expected/html/a-page-with-a-line-comment-in-the-see-also-command.html
new file mode 100644
index 000000000..a7b95776d
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/line_comments/expected/html/a-page-with-a-line-comment-in-the-see-also-command.html
@@ -0,0 +1,18 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+<!-- line_comment_adventures.qdoc -->
+ <title>The source for this page has a line comment in its sa command | LineComment</title>
+</head>
+<body>
+<div class="sidebar"><div class="sidebar-content" id="sidebar-content"></div></div>
+<h1 class="title">The source for this page has a line comment in its sa command</h1>
+<!-- $$$a-page-with-a-line-comment-in-the-see-also-command.html-description -->
+<div class="descr" id="details">
+<p>QDoc's \sa command doesn't respect QDoc's line comments.</p>
+</div>
+<p><b>See also </b><a href="another-page-with-an-image-at-the-top.html">This page starts with an image</a> and <a href="line-comment-adventures.html">Adventures with QDoc's line comments</a>.</p>
+<!-- @@@a-page-with-a-line-comment-in-the-see-also-command.html -->
+</body>
+</html>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/line_comments/expected/html/another-page-with-an-image-at-the-top.html b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/line_comments/expected/html/another-page-with-an-image-at-the-top.html
new file mode 100644
index 000000000..27c53b8ac
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/line_comments/expected/html/another-page-with-an-image-at-the-top.html
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+<!-- line_comment_adventures.qdoc -->
+ <meta name="description" content="This page has an image right at the top.">
+ <title>This page starts with an image | LineComment</title>
+</head>
+<body>
+<div class="sidebar"><div class="sidebar-content" id="sidebar-content"></div></div>
+<h1 class="title">This page starts with an image</h1>
+<!-- $$$another-page-with-an-image-at-the-top.html-description -->
+<div class="descr" id="details">
+<p class="centerAlign"><img src="images/leonardo-da-vinci.png" alt="This is the alternate text for the image" /></p><p>The image should render as expected, and the alternate text should be there, too. This text is contained in its own paragraph following the image.</p>
+<p>This paragraph is a new paragraph, and doesn't contain an image.</p>
+</div>
+<!-- @@@another-page-with-an-image-at-the-top.html -->
+</body>
+</html>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/line_comments/expected/html/images/leonardo-da-vinci.png b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/line_comments/expected/html/images/leonardo-da-vinci.png
new file mode 100644
index 000000000..854acb4ca
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/line_comments/expected/html/images/leonardo-da-vinci.png
Binary files differ
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/line_comments/expected/html/line-comment-adventures.html b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/line_comments/expected/html/line-comment-adventures.html
new file mode 100644
index 000000000..89ab9e527
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/line_comments/expected/html/line-comment-adventures.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+<!-- line_comment_adventures.qdoc -->
+ <meta name="description" content="Test that line comments don't break documentation.">
+ <title>Adventures with QDoc's line comments | LineComment</title>
+</head>
+<body>
+<div class="sidebar">
+<div class="toc">
+<h3 id="toc">Contents</h3>
+<ul>
+<li class="level1"><a href="#further-details">Further details</a></li>
+</ul>
+</div>
+<div class="sidebar-content" id="sidebar-content"></div></div>
+<h1 class="title">Adventures with QDoc's line comments</h1>
+<!-- $$$line-comment-adventures.html-description -->
+<div class="descr" id="details">
+<p>The purpose of this test data is to provide a regression mechanism as part of QDoc's end-to-end test, tst_generatedOutput, for an issue (QTBUG-105754) that was reported against QDoc's \sa command. The issue, as experienced by the reporter of the bug, is that if the \sa command is followed by a line comment, QDoc generates a series of &quot;Missing comma in \sa&quot; warnings.</p>
+<h2 id="further-details">Further details</h2>
+<p>The bug report is at <a href="https://bugreports.qt.io/browse/QTBUG-105754">https://bugreports.qt.io/browse/QTBUG-105754</a>.</p>
+</div>
+<!-- @@@line-comment-adventures.html -->
+</body>
+</html>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/line_comments/expected/html/linecomment.index b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/line_comments/expected/html/linecomment.index
new file mode 100644
index 000000000..f81bef147
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/line_comments/expected/html/linecomment.index
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE QDOCINDEX>
+<INDEX url="" title="LineComment Reference Documentation" version="" project="LineComment">
+ <namespace name="" status="active" access="public" module="linecomment">
+ <page name="line-comment-adventures.html" href="line-comment-adventures.html" status="active" location="line_comment_adventures.qdoc" documented="true" subtype="page" title="Adventures with QDoc's line comments" fulltitle="Adventures with QDoc's line comments" subtitle="" brief="Test that line comments don't break documentation">
+ <contents name="further-details" title="Further details" level="1"/>
+ </page>
+ <page name="a-page-with-a-line-comment-in-the-see-also-command.html" href="a-page-with-a-line-comment-in-the-see-also-command.html" status="active" location="line_comment_adventures.qdoc" documented="true" subtype="page" title="The source for this page has a line comment in its sa command" fulltitle="The source for this page has a line comment in its sa command" subtitle=""/>
+ <page name="another-page-with-an-image-at-the-top.html" href="another-page-with-an-image-at-the-top.html" status="active" location="line_comment_adventures.qdoc" documented="true" subtype="page" title="This page starts with an image" fulltitle="This page starts with an image" subtitle="" brief="This page has an image right at the top"/>
+ </namespace>
+</INDEX>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/line_comments/expected/webxml/a-page-with-a-line-comment-in-the-see-also-command.webxml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/line_comments/expected/webxml/a-page-with-a-line-comment-in-the-see-also-command.webxml
new file mode 100644
index 000000000..cb0caedf9
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/line_comments/expected/webxml/a-page-with-a-line-comment-in-the-see-also-command.webxml
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<WebXML>
+ <document>
+ <page name="a-page-with-a-line-comment-in-the-see-also-command.html" href="a-page-with-a-line-comment-in-the-see-also-command.html" status="active" location="line_comment_adventures.qdoc" documented="true" subtype="page" title="The source for this page has a line comment in its sa command" fulltitle="The source for this page has a line comment in its sa command" subtitle="">
+ <description>
+ <para>QDoc's \sa command doesn't respect QDoc's line comments.</para>
+ <see-also>
+ <link raw="This page starts with an image" href="another-page-with-an-image-at-the-top.html" type="page" page="This page starts with an image">This page starts with an image</link>
+ <link raw="Adventures with QDoc's line comments" href="line-comment-adventures.html" type="page" page="Adventures with QDoc's line comments">Adventures with QDoc's line comments</link>
+ </see-also>
+ </description>
+ </page>
+ </document>
+</WebXML>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/line_comments/expected/webxml/another-page-with-an-image-at-the-top.webxml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/line_comments/expected/webxml/another-page-with-an-image-at-the-top.webxml
new file mode 100644
index 000000000..b2551df18
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/line_comments/expected/webxml/another-page-with-an-image-at-the-top.webxml
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<WebXML>
+ <document>
+ <page name="another-page-with-an-image-at-the-top.html" href="another-page-with-an-image-at-the-top.html" status="active" location="line_comment_adventures.qdoc" documented="true" subtype="page" title="This page starts with an image" fulltitle="This page starts with an image" subtitle="" brief="This page has an image right at the top">
+ <description>
+ <brief>This page has an image right at the top.</brief>
+ <image href="images/leonardo-da-vinci.png"/>
+ <para>The image should render as expected, and the alternate text should be there, too. This text is contained in its own paragraph following the image.</para>
+ <para>This paragraph is a new paragraph, and doesn't contain an image.</para>
+ </description>
+ </page>
+ </document>
+</WebXML>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/line_comments/expected/webxml/images/leonardo-da-vinci.png b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/line_comments/expected/webxml/images/leonardo-da-vinci.png
new file mode 100644
index 000000000..854acb4ca
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/line_comments/expected/webxml/images/leonardo-da-vinci.png
Binary files differ
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/line_comments/expected/webxml/line-comment-adventures.webxml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/line_comments/expected/webxml/line-comment-adventures.webxml
new file mode 100644
index 000000000..b84706e96
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/line_comments/expected/webxml/line-comment-adventures.webxml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<WebXML>
+ <document>
+ <page name="line-comment-adventures.html" href="line-comment-adventures.html" status="active" location="line_comment_adventures.qdoc" documented="true" subtype="page" title="Adventures with QDoc's line comments" fulltitle="Adventures with QDoc's line comments" subtitle="" brief="Test that line comments don't break documentation">
+ <contents name="further-details" title="Further details" level="1"/>
+ <description>
+ <brief>Test that line comments don't break documentation.</brief>
+ <para>The purpose of this test data is to provide a regression mechanism as part of QDoc's end-to-end test, tst_generatedOutput, for an issue (QTBUG-105754) that was reported against QDoc's \sa command. The issue, as experienced by the reporter of the bug, is that if the \sa command is followed by a line comment, QDoc generates a series of &quot;Missing comma in \sa&quot; warnings.</para>
+ <section id="further-details">
+ <heading level="1">Further details</heading>
+ <para>The bug report is at <link raw="https://bugreports.qt.io/browse/QTBUG-105754" href="https://bugreports.qt.io/browse/QTBUG-105754" type="external">https://bugreports.qt.io/browse/QTBUG-105754</link>.</para>
+ </section>
+ </description>
+ </page>
+ </document>
+</WebXML>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/line_comments/expected/webxml/linecomment.index b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/line_comments/expected/webxml/linecomment.index
new file mode 100644
index 000000000..f81bef147
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/line_comments/expected/webxml/linecomment.index
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE QDOCINDEX>
+<INDEX url="" title="LineComment Reference Documentation" version="" project="LineComment">
+ <namespace name="" status="active" access="public" module="linecomment">
+ <page name="line-comment-adventures.html" href="line-comment-adventures.html" status="active" location="line_comment_adventures.qdoc" documented="true" subtype="page" title="Adventures with QDoc's line comments" fulltitle="Adventures with QDoc's line comments" subtitle="" brief="Test that line comments don't break documentation">
+ <contents name="further-details" title="Further details" level="1"/>
+ </page>
+ <page name="a-page-with-a-line-comment-in-the-see-also-command.html" href="a-page-with-a-line-comment-in-the-see-also-command.html" status="active" location="line_comment_adventures.qdoc" documented="true" subtype="page" title="The source for this page has a line comment in its sa command" fulltitle="The source for this page has a line comment in its sa command" subtitle=""/>
+ <page name="another-page-with-an-image-at-the-top.html" href="another-page-with-an-image-at-the-top.html" status="active" location="line_comment_adventures.qdoc" documented="true" subtype="page" title="This page starts with an image" fulltitle="This page starts with an image" subtitle="" brief="This page has an image right at the top"/>
+ </namespace>
+</INDEX>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/line_comments/line_comments.qdocconf b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/line_comments/line_comments.qdocconf
new file mode 100644
index 000000000..db5b91b55
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/line_comments/line_comments.qdocconf
@@ -0,0 +1,27 @@
+project = LineComment
+
+{headerdirs,sourcedirs,exampledirs} = ./src
+
+outputformats = WebXML HTML DocBook
+WebXML.quotinginformation = true
+
+HTML.nosubdirs = true
+HTML.outputsubdir = html
+
+WebXML.nosubdirs = true
+WebXML.outputsubdir = webxml
+
+DocBook.nosubdirs = true
+DocBook.outputsubdir = docbook
+
+sources.fileextensions = "*.qml *.cpp *.qdoc"
+headers.fileextensions = "*.h"
+
+# zero warning policy
+warninglimit = 0
+warninglimit.enabled = true
+
+# don't write host system-specific paths to index files
+locationinfo = false
+
+imagedirs = ./src/images
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/line_comments/src/images/leonardo-da-vinci.png b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/line_comments/src/images/leonardo-da-vinci.png
new file mode 100644
index 000000000..854acb4ca
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/line_comments/src/images/leonardo-da-vinci.png
Binary files differ
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/line_comments/src/line_comment_adventures.qdoc b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/line_comments/src/line_comment_adventures.qdoc
new file mode 100644
index 000000000..28dfe5c90
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/line_comments/src/line_comment_adventures.qdoc
@@ -0,0 +1,39 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+
+/*!
+ \page line-comment-adventures.html
+ \title Adventures with QDoc's line comments
+ \brief Test that line comments don't break documentation.
+
+ The purpose of this test data is to provide a regression mechanism as part
+ of QDoc's end-to-end test, tst_generatedOutput, for an issue (QTBUG-105754)
+ that was reported against QDoc's \\sa command. The issue, as experienced by
+ the reporter of the bug, is that if the \\sa command is followed by a line
+ comment, QDoc generates a series of "Missing comma in \\sa" warnings.
+
+ \section1 Further details
+ The bug report is at \l https://bugreports.qt.io/browse/QTBUG-105754.
+*/
+
+/*!
+ \page another-page-with-an-image-at-the-top.html
+ \title This page starts with an image
+ \brief This page has an image right at the top.
+ \image leonardo-da-vinci.png This is the alternate text for the image
+ The image should render as expected, and the alternate text should be
+ there, too. This text is contained in its own paragraph following the
+ image.
+
+ This paragraph is a new paragraph, and doesn't contain an image.
+*/
+
+/*!
+ \page a-page-with-a-line-comment-in-the-see-also-command.html
+ \title The source for this page has a line comment in its sa command
+
+ QDoc's \\sa command doesn't respect QDoc's line comments.
+
+ \sa {This page starts with an image}, //! let's go to bar after this
+ {Adventures with QDoc's line comments}
+*/
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/minimal_configuration/expected/a-minimal-qdoc-configuration.index b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/minimal_configuration/expected/a-minimal-qdoc-configuration.index
new file mode 100644
index 000000000..037f4bc73
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/minimal_configuration/expected/a-minimal-qdoc-configuration.index
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE QDOCINDEX>
+<INDEX url="" title="A minimal QDoc configuration Reference Documentation" version="" project="A minimal QDoc configuration">
+ <namespace name="" status="active" access="public" module="a minimal qdoc configuration">
+ <page name="readme.html" href="readme.html" status="active" location="README.qdoc" documented="true" subtype="page" title="README" fulltitle="README" subtitle=""/>
+ </namespace>
+</INDEX>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/minimal_configuration/expected/readme.html b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/minimal_configuration/expected/readme.html
new file mode 100644
index 000000000..59c54c1b5
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/minimal_configuration/expected/readme.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+<!-- README.qdoc -->
+ <title>README | A minimal QDoc configuration</title>
+</head>
+<body>
+<div class="sidebar"><div class="sidebar-content" id="sidebar-content"></div></div>
+<h1 class="title">README</h1>
+<!-- $$$readme.html-description -->
+<div class="descr" id="details">
+<p>This test is for a minimal .qdocconf file.</p>
+</div>
+<!-- @@@readme.html -->
+</body>
+</html>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/minimal_configuration/minimal_configuration.qdocconf b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/minimal_configuration/minimal_configuration.qdocconf
new file mode 100644
index 000000000..6fc9c025e
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/minimal_configuration/minimal_configuration.qdocconf
@@ -0,0 +1,12 @@
+project = A minimal QDoc configuration
+
+locationinfo = false
+
+headers.fileextensions = "*.h *.hpp"
+sources.fileextensions = "*.cpp *.qml *.qdoc"
+
+headerdirs = ./src
+sourcedirs = ./src
+
+warninglimit = 0
+warninglimit.enabled = true
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/minimal_configuration/src/README.qdoc b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/minimal_configuration/src/README.qdoc
new file mode 100644
index 000000000..879370377
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/minimal_configuration/src/README.qdoc
@@ -0,0 +1,9 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+
+/*!
+ \page readme.html
+ \title README
+
+ This test is for a minimal .qdocconf file.
+*/
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/modifiedoutputfilenames/expected/docbook/cppmodule-module-suffix.xml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/modifiedoutputfilenames/expected/docbook/cppmodule-module-suffix.xml
new file mode 100644
index 000000000..3f02a1a2c
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/modifiedoutputfilenames/expected/docbook/cppmodule-module-suffix.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<db:article xmlns:db="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" version="5.2" xml:lang="en">
+<db:info>
+<db:title></db:title>
+<db:productname>ModifiedOutputFilenames</db:productname>
+<db:titleabbrev>ModifiedOutputFilenames Reference Documentation</db:titleabbrev>
+<db:abstract>
+<db:para>ModifiedOutputFilenames Reference Documentation.</db:para></db:abstract>
+</db:info>
+<db:section xml:id="namespaces">
+<db:title>Namespaces</db:title>
+<db:itemizedlist role="namespaces">
+<db:listitem>
+<db:para><db:link xlink:href="prefix-namespace-suffix.xml" xlink:role="namespace">Namespace</db:link></db:para>
+</db:listitem>
+</db:itemizedlist>
+</db:section>
+<db:section xml:id="classes">
+<db:title>Classes</db:title>
+<db:itemizedlist role="classes">
+<db:listitem>
+<db:para><db:link xlink:href="prefix-namespace-class-suffix.xml" xlink:role="class">Namespace::Class</db:link></db:para>
+</db:listitem>
+</db:itemizedlist>
+</db:section>
+</db:article>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/modifiedoutputfilenames/expected/docbook/group.xml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/modifiedoutputfilenames/expected/docbook/group.xml
new file mode 100644
index 000000000..f3f0b072d
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/modifiedoutputfilenames/expected/docbook/group.xml
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<db:article xmlns:db="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" version="5.2" xml:lang="en">
+<db:info>
+<db:title></db:title>
+<db:productname>ModifiedOutputFilenames</db:productname>
+<db:titleabbrev>ModifiedOutputFilenames Reference Documentation</db:titleabbrev>
+<db:abstract>
+<db:para>ModifiedOutputFilenames Reference Documentation.</db:para></db:abstract>
+</db:info>
+<db:anchor xml:id="details"/>
+</db:article>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/modifiedoutputfilenames/expected/docbook/modifiedoutputfilenames-test-example.xml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/modifiedoutputfilenames/expected/docbook/modifiedoutputfilenames-test-example.xml
new file mode 100644
index 000000000..0cd81f137
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/modifiedoutputfilenames/expected/docbook/modifiedoutputfilenames-test-example.xml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<db:article xmlns:db="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" version="5.2" xml:lang="en">
+<db:info>
+<db:title></db:title>
+<db:productname>ModifiedOutputFilenames</db:productname>
+<db:titleabbrev>ModifiedOutputFilenames Reference Documentation</db:titleabbrev>
+<db:abstract>
+<db:para>ModifiedOutputFilenames Reference Documentation.</db:para></db:abstract>
+</db:info>
+</db:article>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/modifiedoutputfilenames/expected/docbook/page.xml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/modifiedoutputfilenames/expected/docbook/page.xml
new file mode 100644
index 000000000..0cd81f137
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/modifiedoutputfilenames/expected/docbook/page.xml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<db:article xmlns:db="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" version="5.2" xml:lang="en">
+<db:info>
+<db:title></db:title>
+<db:productname>ModifiedOutputFilenames</db:productname>
+<db:titleabbrev>ModifiedOutputFilenames Reference Documentation</db:titleabbrev>
+<db:abstract>
+<db:para>ModifiedOutputFilenames Reference Documentation.</db:para></db:abstract>
+</db:info>
+</db:article>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/modifiedoutputfilenames/expected/docbook/prefix-header-suffix.xml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/modifiedoutputfilenames/expected/docbook/prefix-header-suffix.xml
new file mode 100644
index 000000000..f58083323
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/modifiedoutputfilenames/expected/docbook/prefix-header-suffix.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<db:article xmlns:db="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" version="5.2" xml:lang="en">
+<db:info>
+<db:title>&lt;header&gt;</db:title>
+<db:productname>ModifiedOutputFilenames</db:productname>
+<db:titleabbrev>ModifiedOutputFilenames Reference Documentation</db:titleabbrev>
+<db:abstract>
+<db:para>ModifiedOutputFilenames Reference Documentation.</db:para></db:abstract>
+</db:info>
+<db:variablelist>
+<db:varlistentry>
+<db:term>Header</db:term>
+<db:listitem>
+<db:para>header</db:para>
+</db:listitem>
+</db:varlistentry>
+</db:variablelist>
+<db:section xml:id="details">
+<db:title>Detailed Description</db:title>
+</db:section>
+</db:article>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/modifiedoutputfilenames/expected/docbook/prefix-namespace-class-suffix.xml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/modifiedoutputfilenames/expected/docbook/prefix-namespace-class-suffix.xml
new file mode 100644
index 000000000..74bddeb56
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/modifiedoutputfilenames/expected/docbook/prefix-namespace-class-suffix.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<db:article xmlns:db="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" version="5.2" xml:lang="en">
+<db:info>
+<db:title>Class Class</db:title>
+<db:subtitle>Namespace::Class</db:subtitle>
+<db:productname>ModifiedOutputFilenames</db:productname>
+<db:titleabbrev>ModifiedOutputFilenames Reference Documentation</db:titleabbrev>
+<db:abstract>
+<db:para>ModifiedOutputFilenames Reference Documentation.</db:para></db:abstract>
+</db:info>
+<db:variablelist>
+<db:varlistentry>
+<db:term>Header</db:term>
+<db:listitem>
+<db:para>Class</db:para>
+</db:listitem>
+</db:varlistentry>
+</db:variablelist>
+<db:section xml:id="details">
+<db:title>Detailed Description</db:title>
+</db:section>
+</db:article>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/modifiedoutputfilenames/expected/docbook/prefix-namespace-suffix.xml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/modifiedoutputfilenames/expected/docbook/prefix-namespace-suffix.xml
new file mode 100644
index 000000000..7b4cc8313
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/modifiedoutputfilenames/expected/docbook/prefix-namespace-suffix.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<db:article xmlns:db="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" version="5.2" xml:lang="en">
+<db:info>
+<db:title>Namespace Namespace</db:title>
+<db:productname>ModifiedOutputFilenames</db:productname>
+<db:titleabbrev>ModifiedOutputFilenames Reference Documentation</db:titleabbrev>
+<db:abstract>
+<db:para>ModifiedOutputFilenames Reference Documentation.</db:para></db:abstract>
+</db:info>
+<db:variablelist>
+<db:varlistentry>
+<db:term>Header</db:term>
+<db:listitem>
+<db:para>Namespace</db:para>
+</db:listitem>
+</db:varlistentry>
+</db:variablelist>
+<db:section xml:id="details">
+<db:title>Detailed Description</db:title>
+</db:section>
+<db:section xml:id="classes">
+<db:title>Classes</db:title>
+<db:section>
+<db:title>class <db:link xlink:href="prefix-namespace-class-suffix.xml" xlink:role="class">Class</db:link></db:title>
+</db:section>
+</db:section>
+</db:article>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/modifiedoutputfilenames/expected/docbook/qml-qmlmodule-suffix-type.xml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/modifiedoutputfilenames/expected/docbook/qml-qmlmodule-suffix-type.xml
new file mode 100644
index 000000000..3f6ae500d
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/modifiedoutputfilenames/expected/docbook/qml-qmlmodule-suffix-type.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<db:article xmlns:db="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" version="5.2" xml:lang="en">
+<db:info>
+<db:title>Type QML Type</db:title>
+<db:productname>ModifiedOutputFilenames</db:productname>
+<db:titleabbrev>ModifiedOutputFilenames Reference Documentation</db:titleabbrev>
+<db:abstract>
+<db:para>ModifiedOutputFilenames Reference Documentation.</db:para></db:abstract>
+</db:info>
+<db:variablelist>
+<db:varlistentry>
+<db:term>Import Statement</db:term>
+<db:listitem>
+<db:para>import QmlModule</db:para>
+</db:listitem>
+</db:varlistentry>
+</db:variablelist>
+<db:section xml:id="details">
+<db:title>Detailed Description</db:title>
+</db:section>
+</db:article>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/modifiedoutputfilenames/expected/docbook/qmlmodule-qmlmodule-suffix.xml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/modifiedoutputfilenames/expected/docbook/qmlmodule-qmlmodule-suffix.xml
new file mode 100644
index 000000000..ce4324812
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/modifiedoutputfilenames/expected/docbook/qmlmodule-qmlmodule-suffix.xml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<db:article xmlns:db="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" version="5.2" xml:lang="en">
+<db:info>
+<db:title></db:title>
+<db:productname>ModifiedOutputFilenames</db:productname>
+<db:titleabbrev>ModifiedOutputFilenames Reference Documentation</db:titleabbrev>
+<db:abstract>
+<db:para>ModifiedOutputFilenames Reference Documentation.</db:para></db:abstract>
+</db:info>
+<db:anchor xml:id="details"/>
+<db:itemizedlist role="members">
+<db:listitem>
+<db:para><db:link xlink:href="qml-qmlmodule-suffix-type.xml" xlink:role="">Type</db:link></db:para>
+</db:listitem>
+</db:itemizedlist>
+</db:article>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/modifiedoutputfilenames/expected/html/cppmodule-module-suffix.html b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/modifiedoutputfilenames/expected/html/cppmodule-module-suffix.html
new file mode 100644
index 000000000..4ee779cd8
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/modifiedoutputfilenames/expected/html/cppmodule-module-suffix.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+<!-- test.cpp -->
+ <title>ModifiedOutputFilenames</title>
+</head>
+<body>
+<div class="sidebar">
+<div class="toc">
+<h3 id="toc">Contents</h3>
+<ul>
+<li class="level1"><a href="#namespaces">Namespaces</a></li>
+<li class="level1"><a href="#classes">Classes</a></li>
+<li class="level1"><a href="#details">Detailed Description</a></li>
+</ul>
+</div>
+<div class="sidebar-content" id="sidebar-content"></div></div>
+<h2 id="namespaces">Namespaces</h2>
+<div class="table"><table class="annotated">
+<tr class="odd topAlign"><td class="tblName" translate="no"><p><a href="prefix-namespace-suffix.html">Namespace</a></p></td></tr>
+</table></div>
+<h2 id="classes">Classes</h2>
+<div class="table"><table class="annotated">
+<tr class="odd topAlign"><td class="tblName" translate="no"><p><a href="prefix-namespace-class-suffix.html">Namespace::Class</a></p></td></tr>
+</table></div>
+<!-- $$$CppModule-description -->
+<div class="descr" id="details">
+</div>
+<!-- @@@CppModule -->
+</body>
+</html>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/modifiedoutputfilenames/expected/html/group.html b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/modifiedoutputfilenames/expected/html/group.html
new file mode 100644
index 000000000..ba2903f2d
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/modifiedoutputfilenames/expected/html/group.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+<!-- test.cpp -->
+ <title>ModifiedOutputFilenames</title>
+</head>
+<body>
+<div class="sidebar"><div class="sidebar-content" id="sidebar-content"></div></div>
+<!-- $$$group-description -->
+<div class="descr" id="details">
+</div>
+<!-- @@@group -->
+</body>
+</html>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/modifiedoutputfilenames/expected/html/modifiedoutputfilenames-test-example.html b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/modifiedoutputfilenames/expected/html/modifiedoutputfilenames-test-example.html
new file mode 100644
index 000000000..cfea1c3bd
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/modifiedoutputfilenames/expected/html/modifiedoutputfilenames-test-example.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+<!-- test.cpp -->
+ <title>ModifiedOutputFilenames</title>
+</head>
+<body>
+<div class="sidebar"><div class="sidebar-content" id="sidebar-content"></div></div>
+<!-- $$$test-description -->
+<div class="descr" id="details">
+</div>
+<!-- @@@test -->
+</body>
+</html>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/modifiedoutputfilenames/expected/html/modifiedoutputfilenames.index b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/modifiedoutputfilenames/expected/html/modifiedoutputfilenames.index
new file mode 100644
index 000000000..0f67183b2
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/modifiedoutputfilenames/expected/html/modifiedoutputfilenames.index
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE QDOCINDEX>
+<INDEX url="" title="ModifiedOutputFilenames Reference Documentation" version="" project="ModifiedOutputFilenames">
+ <namespace name="" href="prefix-suffix.html" status="active" access="public" module="modifiedoutputfilenames">
+ <header name="&lt;header&gt;" href="prefix-header-suffix.html" status="active" documented="true" module="CppModule" title="&lt;header&gt;" fulltitle="&lt;header&gt;" subtitle=""/>
+ <namespace name="Namespace" href="prefix-namespace-suffix.html" status="active" access="public" location="test.h" documented="true" module="CppModule">
+ <class name="Class" fullname="Namespace::Class" href="prefix-namespace-class-suffix.html" status="active" access="public" location="test.h" documented="true" module="CppModule"/>
+ </namespace>
+ <qmlclass name="Type" qml-module-name="QmlModule" fullname="QmlModule.Type" href="qml-qmlmodule-suffix-type.html" status="active" access="public" documented="true" title="Type" fulltitle="Type" subtitle=""/>
+ <page name="page.html" href="page.html" status="active" documented="true" subtype="page" title="" fulltitle="" subtitle=""/>
+ <page name="test" href="modifiedoutputfilenames-test-example.html" status="active" documented="true" subtype="example" title="" fulltitle="" subtitle=""/>
+ <group name="group" href="group.html" status="active" documented="true" seen="true" title=""/>
+ <module name="CppModule" href="cppmodule-module-suffix.html" status="active" documented="true" seen="true" title=""/>
+ <qmlmodule name="QmlModule" qml-module-name="QmlModule" href="qmlmodule-qmlmodule-suffix.html" status="active" documented="true" seen="true" title=""/>
+ </namespace>
+</INDEX>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/modifiedoutputfilenames/expected/html/page.html b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/modifiedoutputfilenames/expected/html/page.html
new file mode 100644
index 000000000..a5067395b
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/modifiedoutputfilenames/expected/html/page.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+<!-- test.cpp -->
+ <title>ModifiedOutputFilenames</title>
+</head>
+<body>
+<div class="sidebar"><div class="sidebar-content" id="sidebar-content"></div></div>
+<!-- $$$page.html-description -->
+<div class="descr" id="details">
+</div>
+<!-- @@@page.html -->
+</body>
+</html>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/modifiedoutputfilenames/expected/html/prefix-header-suffix.html b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/modifiedoutputfilenames/expected/html/prefix-header-suffix.html
new file mode 100644
index 000000000..75ce2b54b
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/modifiedoutputfilenames/expected/html/prefix-header-suffix.html
@@ -0,0 +1,24 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+<!-- test.cpp -->
+ <title>&lt;header&gt; | ModifiedOutputFilenames</title>
+</head>
+<body>
+<div class="sidebar">
+<div class="toc">
+<h3 id="toc">Contents</h3>
+</div>
+<div class="sidebar-content" id="sidebar-content"></div></div>
+<h1 class="title" translate="no">&lt;header&gt;</h1>
+<div class="table"><table class="alignedsummary" translate="no">
+<tr><td class="memItemLeft rightAlign topAlign"> Header:</td><td class="memItemRight bottomAlign"> <span class="preprocessor">#include &lt;header&gt;</span></td></tr>
+</table></div>
+<!-- $$$<header>-description -->
+<div class="descr">
+<h2 id="details">Detailed Description</h2>
+</div>
+<!-- @@@<header> -->
+</body>
+</html>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/modifiedoutputfilenames/expected/html/prefix-namespace-class-suffix.html b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/modifiedoutputfilenames/expected/html/prefix-namespace-class-suffix.html
new file mode 100644
index 000000000..757a4aed4
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/modifiedoutputfilenames/expected/html/prefix-namespace-class-suffix.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+<!-- test.cpp -->
+ <title>Class Class | ModifiedOutputFilenames</title>
+</head>
+<body>
+<li>Class</li>
+<div class="sidebar">
+<div class="toc">
+<h3 id="toc">Contents</h3>
+<ul>
+<li class="level1"><a href="#details">Detailed Description</a></li>
+</ul>
+</div>
+<div class="sidebar-content" id="sidebar-content"></div></div>
+<h1 class="title" translate="no">Class Class</h1>
+<span class="small-subtitle" translate="no">class <a href="prefix-namespace-suffix.html" translate="no">Namespace</a>::Class</span>
+<div class="table"><table class="alignedsummary" translate="no">
+<tr><td class="memItemLeft rightAlign topAlign"> Header:</td><td class="memItemRight bottomAlign"> <span class="preprocessor">#include &lt;Class&gt;</span></td></tr>
+</table></div>
+<!-- $$$Class-description -->
+<div class="descr">
+<h2 id="details">Detailed Description</h2>
+</div>
+<!-- @@@Class -->
+</body>
+</html>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/modifiedoutputfilenames/expected/html/prefix-namespace-suffix.html b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/modifiedoutputfilenames/expected/html/prefix-namespace-suffix.html
new file mode 100644
index 000000000..e3f4342d7
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/modifiedoutputfilenames/expected/html/prefix-namespace-suffix.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+<!-- test.cpp -->
+ <title>Namespace Namespace | ModifiedOutputFilenames</title>
+</head>
+<body>
+<div class="sidebar">
+<div class="toc">
+<h3 id="toc">Contents</h3>
+<ul>
+<li class="level1"><a href="#classes">Classes</a></li>
+<li class="level1"><a href="#details">Detailed Description</a></li>
+</ul>
+</div>
+<div class="sidebar-content" id="sidebar-content"></div></div>
+<h1 class="title" translate="no">Namespace Namespace</h1>
+<div class="table"><table class="alignedsummary" translate="no">
+<tr><td class="memItemLeft rightAlign topAlign"> Header:</td><td class="memItemRight bottomAlign"> <span class="preprocessor">#include &lt;Namespace&gt;</span></td></tr>
+</table></div>
+<h2 id="classes">Classes</h2>
+<div class="table"><table class="alignedsummary" translate="no">
+<tr><td class="memItemLeft rightAlign topAlign"> class </td><td class="memItemRight bottomAlign"><b><a href="prefix-namespace-class-suffix.html" translate="no">Class</a></b></td></tr>
+</table></div>
+<!-- $$$Namespace-description -->
+<div class="descr">
+<h2 id="details">Detailed Description</h2>
+</div>
+<!-- @@@Namespace -->
+<div class="classes">
+<h2>Classes</h2>
+<h3> class <a href="prefix-namespace-class-suffix.html">Class</a></h3></div>
+</body>
+</html>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/modifiedoutputfilenames/expected/html/qml-qmlmodule-suffix-type-members.html b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/modifiedoutputfilenames/expected/html/qml-qmlmodule-suffix-type-members.html
new file mode 100644
index 000000000..c3ba341de
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/modifiedoutputfilenames/expected/html/qml-qmlmodule-suffix-type-members.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+<!-- test.cpp -->
+ <title>List of All Members for Type | ModifiedOutputFilenames</title>
+</head>
+<body>
+<li>Type</li>
+<div class="sidebar"><div class="sidebar-content" id="sidebar-content"></div></div>
+<h1 class="title" translate="no">List of All Members for Type</h1>
+<p>This is the complete list of members for <a href="qml-qmlmodule-suffix-type.html">Type</a>, including inherited members.</p>
+</body>
+</html>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/modifiedoutputfilenames/expected/html/qml-qmlmodule-suffix-type.html b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/modifiedoutputfilenames/expected/html/qml-qmlmodule-suffix-type.html
new file mode 100644
index 000000000..f2324bbee
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/modifiedoutputfilenames/expected/html/qml-qmlmodule-suffix-type.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+<!-- test.cpp -->
+ <title>Type QML Type | ModifiedOutputFilenames</title>
+</head>
+<body>
+<li>Type</li>
+<div class="sidebar">
+<div class="toc">
+<h3 id="toc">Contents</h3>
+<ul>
+<li class="level1"><a href="#details">Detailed Description</a></li>
+</ul>
+</div>
+<div class="sidebar-content" id="sidebar-content"></div></div>
+<h1 class="title" translate="no">Type QML Type</h1>
+<div class="table"><table class="alignedsummary" translate="no">
+<tr><td class="memItemLeft rightAlign topAlign"> Import Statement:</td><td class="memItemRight bottomAlign"> import QmlModule</td></tr></table></div><ul>
+<li><a href="qml-qmlmodule-suffix-type-members.html">List of all members, including inherited members</a></li>
+</ul>
+<!-- $$$Type-description -->
+<h2 id="details">Detailed Description</h2>
+<!-- @@@Type -->
+</body>
+</html>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/modifiedoutputfilenames/expected/html/qmlmodule-qmlmodule-suffix.html b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/modifiedoutputfilenames/expected/html/qmlmodule-qmlmodule-suffix.html
new file mode 100644
index 000000000..2fe3ae3d6
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/modifiedoutputfilenames/expected/html/qmlmodule-qmlmodule-suffix.html
@@ -0,0 +1,18 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+<!-- test.cpp -->
+ <title>ModifiedOutputFilenames</title>
+</head>
+<body>
+<div class="sidebar"><div class="sidebar-content" id="sidebar-content"></div></div>
+<!-- $$$QmlModule-description -->
+<div class="descr" id="details">
+</div>
+<!-- @@@QmlModule -->
+<div class="table"><table class="annotated">
+<tr class="odd topAlign"><td class="tblName" translate="no"><p><a href="qml-qmlmodule-suffix-type.html">Type</a></p></td></tr>
+</table></div>
+</body>
+</html>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/modifiedoutputfilenames/expected/webxml/cppmodule-module-suffix.webxml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/modifiedoutputfilenames/expected/webxml/cppmodule-module-suffix.webxml
new file mode 100644
index 000000000..5d24b3077
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/modifiedoutputfilenames/expected/webxml/cppmodule-module-suffix.webxml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<WebXML>
+ <document/>
+</WebXML>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/modifiedoutputfilenames/expected/webxml/group.webxml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/modifiedoutputfilenames/expected/webxml/group.webxml
new file mode 100644
index 000000000..8b8147e6a
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/modifiedoutputfilenames/expected/webxml/group.webxml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<WebXML>
+ <document>
+ <group name="group" href="group.html" status="active" documented="true" seen="true" title="">
+ <description>
+ <table width="100%"/>
+ </description>
+ </group>
+ </document>
+</WebXML>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/modifiedoutputfilenames/expected/webxml/modifiedoutputfilenames-test-example.webxml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/modifiedoutputfilenames/expected/webxml/modifiedoutputfilenames-test-example.webxml
new file mode 100644
index 000000000..2f92a9490
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/modifiedoutputfilenames/expected/webxml/modifiedoutputfilenames-test-example.webxml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<WebXML>
+ <document>
+ <page name="test" href="modifiedoutputfilenames-test-example.html" status="active" documented="true" subtype="example" title="" fulltitle="" subtitle="">
+ <description/>
+ </page>
+ </document>
+</WebXML>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/modifiedoutputfilenames/expected/webxml/modifiedoutputfilenames.index b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/modifiedoutputfilenames/expected/webxml/modifiedoutputfilenames.index
new file mode 100644
index 000000000..0f67183b2
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/modifiedoutputfilenames/expected/webxml/modifiedoutputfilenames.index
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE QDOCINDEX>
+<INDEX url="" title="ModifiedOutputFilenames Reference Documentation" version="" project="ModifiedOutputFilenames">
+ <namespace name="" href="prefix-suffix.html" status="active" access="public" module="modifiedoutputfilenames">
+ <header name="&lt;header&gt;" href="prefix-header-suffix.html" status="active" documented="true" module="CppModule" title="&lt;header&gt;" fulltitle="&lt;header&gt;" subtitle=""/>
+ <namespace name="Namespace" href="prefix-namespace-suffix.html" status="active" access="public" location="test.h" documented="true" module="CppModule">
+ <class name="Class" fullname="Namespace::Class" href="prefix-namespace-class-suffix.html" status="active" access="public" location="test.h" documented="true" module="CppModule"/>
+ </namespace>
+ <qmlclass name="Type" qml-module-name="QmlModule" fullname="QmlModule.Type" href="qml-qmlmodule-suffix-type.html" status="active" access="public" documented="true" title="Type" fulltitle="Type" subtitle=""/>
+ <page name="page.html" href="page.html" status="active" documented="true" subtype="page" title="" fulltitle="" subtitle=""/>
+ <page name="test" href="modifiedoutputfilenames-test-example.html" status="active" documented="true" subtype="example" title="" fulltitle="" subtitle=""/>
+ <group name="group" href="group.html" status="active" documented="true" seen="true" title=""/>
+ <module name="CppModule" href="cppmodule-module-suffix.html" status="active" documented="true" seen="true" title=""/>
+ <qmlmodule name="QmlModule" qml-module-name="QmlModule" href="qmlmodule-qmlmodule-suffix.html" status="active" documented="true" seen="true" title=""/>
+ </namespace>
+</INDEX>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/modifiedoutputfilenames/expected/webxml/page.webxml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/modifiedoutputfilenames/expected/webxml/page.webxml
new file mode 100644
index 000000000..77523428b
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/modifiedoutputfilenames/expected/webxml/page.webxml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<WebXML>
+ <document>
+ <page name="page.html" href="page.html" status="active" documented="true" subtype="page" title="" fulltitle="" subtitle="">
+ <description/>
+ </page>
+ </document>
+</WebXML>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/modifiedoutputfilenames/expected/webxml/prefix-header-suffix.webxml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/modifiedoutputfilenames/expected/webxml/prefix-header-suffix.webxml
new file mode 100644
index 000000000..bc42d90dd
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/modifiedoutputfilenames/expected/webxml/prefix-header-suffix.webxml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<WebXML>
+ <document>
+ <header name="&lt;header&gt;" href="prefix-header-suffix.html" status="active" documented="true" module="CppModule" title="&lt;header&gt;" fulltitle="&lt;header&gt;" subtitle="">
+ <description/>
+ </header>
+ </document>
+</WebXML>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/modifiedoutputfilenames/expected/webxml/prefix-namespace-class-suffix.webxml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/modifiedoutputfilenames/expected/webxml/prefix-namespace-class-suffix.webxml
new file mode 100644
index 000000000..4ee5dee79
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/modifiedoutputfilenames/expected/webxml/prefix-namespace-class-suffix.webxml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<WebXML>
+ <document>
+ <class name="Class" fullname="Namespace::Class" href="prefix-namespace-class-suffix.html" status="active" access="public" location="test.h" documented="true" module="CppModule">
+ <description/>
+ </class>
+ </document>
+</WebXML>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/modifiedoutputfilenames/expected/webxml/prefix-namespace-suffix.webxml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/modifiedoutputfilenames/expected/webxml/prefix-namespace-suffix.webxml
new file mode 100644
index 000000000..e46a0cfb3
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/modifiedoutputfilenames/expected/webxml/prefix-namespace-suffix.webxml
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<WebXML>
+ <document>
+ <namespace name="Namespace" href="prefix-namespace-suffix.html" status="active" access="public" location="test.h" documented="true" module="CppModule">
+ <description/>
+ <class name="Class" fullname="Namespace::Class" href="prefix-namespace-class-suffix.html" status="active" access="public" location="test.h" documented="true" module="CppModule">
+ <description/>
+ </class>
+ </namespace>
+ </document>
+</WebXML>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/modifiedoutputfilenames/expected/webxml/qmlmodule-qmlmodule-suffix.webxml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/modifiedoutputfilenames/expected/webxml/qmlmodule-qmlmodule-suffix.webxml
new file mode 100644
index 000000000..5d24b3077
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/modifiedoutputfilenames/expected/webxml/qmlmodule-qmlmodule-suffix.webxml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<WebXML>
+ <document/>
+</WebXML>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/modifiedoutputfilenames/modifiedoutputfilenames.qdocconf b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/modifiedoutputfilenames/modifiedoutputfilenames.qdocconf
new file mode 100644
index 000000000..5cf3ce867
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/modifiedoutputfilenames/modifiedoutputfilenames.qdocconf
@@ -0,0 +1,22 @@
+project = ModifiedOutputFilenames
+
+{sourcedirs,headerdirs} = ./src
+exampledirs = ./src/example
+
+outputprefixes = CPP
+outputprefixes.CPP = prefix_
+
+outputsuffixes = QML CPP
+{outputsuffixes.CPP,outputsuffixes.QML} = _suffix
+
+locationinfo = false
+warninglimit = 0
+warninglimit.enabled = true
+
+outputformats = WebXML HTML DocBook
+{WebXML.nosubdirs,HTML.nosubdirs,DocBook.nosubdirs} = true
+WebXML.quotinginformation = true
+
+WebXML.outputsubdir = webxml
+HTML.outputsubdir = html
+DocBook.outputsubdir = docbook
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/modifiedoutputfilenames/src/example/test/CMakeLists.txt b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/modifiedoutputfilenames/src/example/test/CMakeLists.txt
new file mode 100644
index 000000000..b2a4ba591
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/modifiedoutputfilenames/src/example/test/CMakeLists.txt
@@ -0,0 +1 @@
+# nothing here
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/modifiedoutputfilenames/src/test.cpp b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/modifiedoutputfilenames/src/test.cpp
new file mode 100644
index 000000000..81f5b725f
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/modifiedoutputfilenames/src/test.cpp
@@ -0,0 +1,24 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+/*! \module CppModule */
+
+/*! \qmlmodule QmlModule */
+
+/*! \headerfile <header>
+ \inmodule CppModule */
+
+/*! \namespace Namespace
+ \inmodule CppModule */
+
+/*! \class Namespace::Class
+ \inmodule CppModule */
+
+/*! \qmltype Type
+ \inqmlmodule QmlModule */
+
+/*! \page page.html */
+
+/*! \group group */
+
+/*! \example test */
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/modifiedoutputfilenames/src/test.h b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/modifiedoutputfilenames/src/test.h
new file mode 100644
index 000000000..3d52569d2
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/modifiedoutputfilenames/src/test.h
@@ -0,0 +1,12 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#pragma once
+
+namespace Namespace {
+
+class Class {};
+
+} // namespace
+
+void func() {}
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/modulestate/expected/docbook/boringclass.xml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/modulestate/expected/docbook/boringclass.xml
new file mode 100644
index 000000000..df3505cdf
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/modulestate/expected/docbook/boringclass.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<db:article xmlns:db="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" version="5.2" xml:lang="en">
+<db:info>
+<db:title>BoringClass Class</db:title>
+<db:productname>ModuleState</db:productname>
+<db:titleabbrev>ModuleState Reference Documentation</db:titleabbrev>
+<db:abstract>
+<db:para>ModuleState Reference Documentation.</db:para></db:abstract>
+</db:info>
+<db:variablelist>
+<db:varlistentry>
+<db:term>Header</db:term>
+<db:listitem>
+<db:para>BoringClass</db:para>
+</db:listitem>
+</db:varlistentry>
+<db:varlistentry>
+<db:term>Status</db:term>
+<db:listitem>
+<db:para>Technical Preview</db:para>
+</db:listitem>
+</db:varlistentry>
+</db:variablelist>
+<db:section xml:id="details">
+<db:title>Detailed Description</db:title>
+<db:para>There's not much to say about <db:link xlink:href="boringclass.xml">BoringClass</db:link>, really, apart from that it is quite boring.</db:para>
+</db:section>
+</db:article>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/modulestate/expected/docbook/excitingclass.xml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/modulestate/expected/docbook/excitingclass.xml
new file mode 100644
index 000000000..29fcfa679
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/modulestate/expected/docbook/excitingclass.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<db:article xmlns:db="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" version="5.2" xml:lang="en">
+<db:info>
+<db:title>ExcitingClass Class</db:title>
+<db:productname>ModuleState</db:productname>
+<db:titleabbrev>ModuleState Reference Documentation</db:titleabbrev>
+<db:abstract>
+<db:para>ModuleState Reference Documentation.</db:para></db:abstract>
+</db:info>
+<db:variablelist>
+<db:varlistentry>
+<db:term>Header</db:term>
+<db:listitem>
+<db:para>ExcitingClass</db:para>
+</db:listitem>
+</db:varlistentry>
+<db:varlistentry>
+<db:term>Status</db:term>
+<db:listitem>
+<db:para>Technical Preview</db:para>
+</db:listitem>
+</db:varlistentry>
+</db:variablelist>
+<db:section xml:id="details">
+<db:title>Detailed Description</db:title>
+<db:para>There's a lot to say about <db:link xlink:href="excitingclass.xml">ExcitingClass</db:link>. Let's dive into the details!</db:para>
+<db:section xml:id="what-makes-excitingclass-more-exciting-than-boringclass">
+<db:title>What makes ExcitingClass more exciting than BoringClass?</db:title>
+<db:para>Well, for one, it has a \section1 heading in it's documentation! How exciting is that?!</db:para>
+</db:section>
+</db:section>
+</db:article>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/modulestate/expected/docbook/moduleinstate-module.xml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/modulestate/expected/docbook/moduleinstate-module.xml
new file mode 100644
index 000000000..776969a64
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/modulestate/expected/docbook/moduleinstate-module.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<db:article xmlns:db="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" version="5.2" xml:lang="en">
+<db:info>
+<db:title></db:title>
+<db:productname>ModuleState</db:productname>
+<db:titleabbrev>ModuleState Reference Documentation</db:titleabbrev>
+<db:abstract>
+<db:para>This module is in <db:emphasis>Technical Preview</db:emphasis> state.</db:para>
+</db:abstract>
+</db:info>
+<db:para>This module is in <db:emphasis>Technical Preview</db:emphasis> state.</db:para>
+<db:section xml:id="classes">
+<db:title>Classes</db:title>
+<db:itemizedlist role="classes">
+<db:listitem>
+<db:para><db:link xlink:href="boringclass.xml" xlink:role="class">BoringClass</db:link></db:para>
+</db:listitem>
+<db:listitem>
+<db:para><db:link xlink:href="excitingclass.xml" xlink:role="class">ExcitingClass</db:link></db:para>
+</db:listitem>
+</db:itemizedlist>
+</db:section>
+</db:article>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/modulestate/expected/html/boringclass.html b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/modulestate/expected/html/boringclass.html
new file mode 100644
index 000000000..ecee60195
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/modulestate/expected/html/boringclass.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+<!-- module_in_a_state.qdoc -->
+ <title>BoringClass Class | ModuleState</title>
+</head>
+<body>
+<li><a href="moduleinstate-module.html" translate="no">ModuleInState (Technical Preview)</a></li>
+<li>BoringClass</li>
+<div class="sidebar">
+<div class="toc">
+<h3 id="toc">Contents</h3>
+<ul>
+<li class="level1"><a href="#details">Detailed Description</a></li>
+</ul>
+</div>
+<div class="sidebar-content" id="sidebar-content"></div></div>
+<h1 class="title" translate="no">BoringClass Class</h1>
+<div class="table"><table class="alignedsummary" translate="no">
+<tr><td class="memItemLeft rightAlign topAlign"> Header:</td><td class="memItemRight bottomAlign"> <span class="preprocessor">#include &lt;BoringClass&gt;</span></td></tr>
+<tr><td class="memItemLeft rightAlign topAlign"> Status:</td><td class="memItemRight bottomAlign"> Technical Preview<span class="status technical-preview"></span></td></tr>
+</table></div>
+<!-- $$$BoringClass-description -->
+<div class="descr">
+<h2 id="details">Detailed Description</h2>
+<p>There's not much to say about BoringClass, really, apart from that it is quite boring.</p>
+</div>
+<!-- @@@BoringClass -->
+</body>
+</html>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/modulestate/expected/html/excitingclass.html b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/modulestate/expected/html/excitingclass.html
new file mode 100644
index 000000000..7d3d13943
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/modulestate/expected/html/excitingclass.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+<!-- module_in_a_state.qdoc -->
+ <title>ExcitingClass Class | ModuleState</title>
+</head>
+<body>
+<li><a href="moduleinstate-module.html" translate="no">ModuleInState (Technical Preview)</a></li>
+<li>ExcitingClass</li>
+<div class="sidebar">
+<div class="toc">
+<h3 id="toc">Contents</h3>
+<ul>
+<li class="level1"><a href="#details">Detailed Description</a></li>
+<li class="level2"><a href="#what-makes-excitingclass-more-exciting-than-boringclass">What makes ExcitingClass more exciting than BoringClass?</a></li>
+</ul>
+</div>
+<div class="sidebar-content" id="sidebar-content"></div></div>
+<h1 class="title" translate="no">ExcitingClass Class</h1>
+<div class="table"><table class="alignedsummary" translate="no">
+<tr><td class="memItemLeft rightAlign topAlign"> Header:</td><td class="memItemRight bottomAlign"> <span class="preprocessor">#include &lt;ExcitingClass&gt;</span></td></tr>
+<tr><td class="memItemLeft rightAlign topAlign"> Status:</td><td class="memItemRight bottomAlign"> Technical Preview<span class="status technical-preview"></span></td></tr>
+</table></div>
+<!-- $$$ExcitingClass-description -->
+<div class="descr">
+<h2 id="details">Detailed Description</h2>
+<p>There's a lot to say about ExcitingClass. Let's dive into the details!</p>
+<h3 id="what-makes-excitingclass-more-exciting-than-boringclass">What makes ExcitingClass more exciting than BoringClass?</h3>
+<p>Well, for one, it has a \section1 heading in it's documentation! How exciting is that?!</p>
+</div>
+<!-- @@@ExcitingClass -->
+</body>
+</html>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/modulestate/expected/html/moduleinstate-module.html b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/modulestate/expected/html/moduleinstate-module.html
new file mode 100644
index 000000000..065e85812
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/modulestate/expected/html/moduleinstate-module.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+<!-- module_in_a_state.qdoc -->
+ <title>ModuleState</title>
+</head>
+<body>
+<div class="sidebar">
+<div class="toc">
+<h3 id="toc">Contents</h3>
+<ul>
+<li class="level1"><a href="#namespaces">Namespaces</a></li>
+<li class="level1"><a href="#classes">Classes</a></li>
+<li class="level1"><a href="#details">Detailed Description</a></li>
+</ul>
+</div>
+<div class="sidebar-content" id="sidebar-content"></div></div>
+<p>This module is in <i>Technical Preview</i> state.</p>
+<h2 id="classes">Classes</h2>
+<div class="table"><table class="annotated">
+<tr class="odd topAlign"><td class="tblName" translate="no"><p><a href="boringclass.html">BoringClass</a></p></td></tr>
+<tr class="even topAlign"><td class="tblName" translate="no"><p><a href="excitingclass.html">ExcitingClass</a></p></td></tr>
+</table></div>
+<!-- $$$ModuleInState-description -->
+<div class="descr" id="details">
+</div>
+<!-- @@@ModuleInState -->
+</body>
+</html>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/modulestate/expected/html/modulestate.index b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/modulestate/expected/html/modulestate.index
new file mode 100644
index 000000000..3ab02a363
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/modulestate/expected/html/modulestate.index
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE QDOCINDEX>
+<INDEX url="" title="ModuleState Reference Documentation" version="" project="ModuleState">
+ <namespace name="" status="active" access="public" module="modulestate">
+ <class name="BoringClass" href="boringclass.html" status="active" access="public" location="classes_in_stateful_module.h" documented="true" module="ModuleInState"/>
+ <class name="ExcitingClass" href="excitingclass.html" status="active" access="public" location="classes_in_stateful_module.h" documented="true" module="ModuleInState">
+ <contents name="what-makes-excitingclass-more-exciting-than-boringclass" title="What makes ExcitingClass more exciting than BoringClass?" level="1"/>
+ </class>
+ <module name="ModuleInState" href="moduleinstate-module.html" status="active" location="module_in_a_state.qdoc" documented="true" seen="true" title=""/>
+ </namespace>
+</INDEX>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/modulestate/expected/webxml/boringclass.webxml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/modulestate/expected/webxml/boringclass.webxml
new file mode 100644
index 000000000..2e1ea77d8
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/modulestate/expected/webxml/boringclass.webxml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<WebXML>
+ <document>
+ <class name="BoringClass" href="boringclass.html" status="active" access="public" location="classes_in_stateful_module.h" documented="true" module="ModuleInState">
+ <description>
+ <para>There's not much to say about <link raw="BoringClass" href="boringclass.html" type="class">BoringClass</link>, really, apart from that it is quite boring.</para>
+ </description>
+ </class>
+ </document>
+</WebXML>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/modulestate/expected/webxml/excitingclass.webxml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/modulestate/expected/webxml/excitingclass.webxml
new file mode 100644
index 000000000..5e4c8ff8b
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/modulestate/expected/webxml/excitingclass.webxml
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<WebXML>
+ <document>
+ <class name="ExcitingClass" href="excitingclass.html" status="active" access="public" location="classes_in_stateful_module.h" documented="true" module="ModuleInState">
+ <contents name="what-makes-excitingclass-more-exciting-than-boringclass" title="What makes ExcitingClass more exciting than BoringClass?" level="1"/>
+ <description>
+ <para>There's a lot to say about <link raw="ExcitingClass" href="excitingclass.html" type="class">ExcitingClass</link>. Let's dive into the details!</para>
+ <section id="what-makes-excitingclass-more-exciting-than-boringclass">
+ <heading level="1">What makes ExcitingClass more exciting than BoringClass?</heading>
+ <para>Well, for one, it has a \section1 heading in it's documentation! How exciting is that?!</para>
+ </section>
+ </description>
+ </class>
+ </document>
+</WebXML>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/modulestate/expected/webxml/moduleinstate-module.webxml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/modulestate/expected/webxml/moduleinstate-module.webxml
new file mode 100644
index 000000000..5d24b3077
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/modulestate/expected/webxml/moduleinstate-module.webxml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<WebXML>
+ <document/>
+</WebXML>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/modulestate/expected/webxml/modulestate.index b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/modulestate/expected/webxml/modulestate.index
new file mode 100644
index 000000000..3ab02a363
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/modulestate/expected/webxml/modulestate.index
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE QDOCINDEX>
+<INDEX url="" title="ModuleState Reference Documentation" version="" project="ModuleState">
+ <namespace name="" status="active" access="public" module="modulestate">
+ <class name="BoringClass" href="boringclass.html" status="active" access="public" location="classes_in_stateful_module.h" documented="true" module="ModuleInState"/>
+ <class name="ExcitingClass" href="excitingclass.html" status="active" access="public" location="classes_in_stateful_module.h" documented="true" module="ModuleInState">
+ <contents name="what-makes-excitingclass-more-exciting-than-boringclass" title="What makes ExcitingClass more exciting than BoringClass?" level="1"/>
+ </class>
+ <module name="ModuleInState" href="moduleinstate-module.html" status="active" location="module_in_a_state.qdoc" documented="true" seen="true" title=""/>
+ </namespace>
+</INDEX>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/modulestate/modulestate.qdocconf b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/modulestate/modulestate.qdocconf
new file mode 100644
index 000000000..1f75c8036
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/modulestate/modulestate.qdocconf
@@ -0,0 +1,29 @@
+project = ModuleState
+
+headerdirs = ./src
+sourcedirs = ./src
+exampledirs = ./src
+
+outputformats = WebXML HTML DocBook
+WebXML.quotinginformation = true
+WebXML.nosubdirs = true
+WebXML.outputsubdir = webxml
+
+HTML.nosubdirs = true
+HTML.outputsubdir = html
+
+DocBook.nosubdirs = true
+DocBook.outputsubdir = docbook
+
+sources.fileextensions = "*.qml *.cpp *.qdoc"
+headers.fileextensions = "*.h"
+
+# images
+imagedirs = ./src/images
+
+# zero warning policy
+warninglimit = 0
+warninglimit.enabled = true
+
+# don't write host system-specific paths to index files
+locationinfo = false
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/modulestate/src/classes_in_stateful_module.h b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/modulestate/src/classes_in_stateful_module.h
new file mode 100644
index 000000000..21578e91b
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/modulestate/src/classes_in_stateful_module.h
@@ -0,0 +1,15 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#ifndef CLASSES_IN_STATEFUL_MODULE_H
+#define CLASSES_IN_STATEFUL_MODULE_H
+
+class BoringClass
+{
+};
+
+class ExcitingClass
+{
+};
+
+#endif // CLASSES_IN_STATEFUL_MODULE_H
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/modulestate/src/module_in_a_state.qdoc b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/modulestate/src/module_in_a_state.qdoc
new file mode 100644
index 000000000..3bdb12503
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/modulestate/src/module_in_a_state.qdoc
@@ -0,0 +1,26 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+
+/*!
+ \module ModuleInState
+ \modulestate Technical Preview
+*/
+
+/*!
+ \class BoringClass
+ \inmodule ModuleInState
+
+ There's not much to say about BoringClass, really, apart from that it is
+ quite boring.
+*/
+
+/*!
+ \class ExcitingClass
+ \inmodule ModuleInState
+
+ There's a lot to say about ExcitingClass. Let's dive into the details!
+
+ \section1 What makes ExcitingClass more exciting than BoringClass?
+ Well, for one, it has a \\section1 heading in it's documentation! How
+ exciting is that?!
+*/
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/non_ascii_character_input/expected/8b5c72eb.html b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/non_ascii_character_input/expected/8b5c72eb.html
new file mode 100644
index 000000000..f52b65618
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/non_ascii_character_input/expected/8b5c72eb.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+<!-- adventures_with_non_ascii_characters.qdoc -->
+ <title>NonAsciiCharacterInput</title>
+</head>
+<body>
+<div class="sidebar"><div class="sidebar-content" id="sidebar-content"></div></div>
+<!-- $$$圣马苏里拉.html-description -->
+<div class="descr" id="details">
+<p>This page exists solely to understand how QDoc will generate the file name for a page with non-latin characters in its name.</p>
+</div>
+<!-- @@@圣马苏里拉.html -->
+</body>
+</html>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/non_ascii_character_input/expected/adventures-with-non-ascii-characters.html b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/non_ascii_character_input/expected/adventures-with-non-ascii-characters.html
new file mode 100644
index 000000000..9552c867f
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/non_ascii_character_input/expected/adventures-with-non-ascii-characters.html
@@ -0,0 +1,46 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+<!-- adventures_with_non_ascii_characters.qdoc -->
+ <meta name="description" content="Test that non-ascii characters work as input to QDoc commands.">
+ <title>Adventures with non-ascii characters in QDoc | NonAsciiCharacterInput</title>
+</head>
+<body>
+<div class="sidebar">
+<div class="toc">
+<h3 id="toc">Contents</h3>
+<ul>
+<li class="level1"><a href="#a-713da3e8">A 大纲视图</a></li>
+<li class="level1"><a href="#3d-42faee45">3D场景视图</a></li>
+<li class="level1"><a href="#3d-c57e864e">这就是3D场景视图</a></li>
+<li class="level1"><a href="#662952c1">属性视图</a></li>
+<li class="level1"><a href="#further-details">Further details</a></li>
+<li class="level2"><a href="#ascii-characters-that-are-non-printable-ascii-such-as-or-521d09f0">Ascii characters that are non-printable ascii, such as ß, ü, or ø</a></li>
+</ul>
+</div>
+<div class="sidebar-content" id="sidebar-content"></div></div>
+<h1 class="title">Adventures with non-ascii characters in QDoc</h1>
+<!-- $$$adventures_with_non_ascii_characters.html-description -->
+<div class="descr" id="details">
+<p>The purpose of this test data is to provide a regression mechanism as part of QDoc's end-to-end test, tst_generatedOutput, for an issue (QTBUG-64506) that was reported against QDoc's \section1 command. The issue, as experienced by the reporter of the bug, is that if the \section1 command is followed by a non-ascii character (for example Chinese characters), navigation links aren't generated for the section title. The bug was filed against Qt 5.9.2. This test case aims at reproducing the issue as filed by the reporter.</p>
+<p>Such this document snippet:</p>
+<h2 id="a-713da3e8">A 大纲视图</h2>
+<p>The reporter states that this link works, presumably because it begins with the ascii character &quot;A&quot;.</p>
+<p>这就是大纲视图</p>
+<h2 id="3d-42faee45">3D场景视图</h2>
+<p>The reporter states that this link works, presumably because it begins with the digit &quot;3&quot;.</p>
+<p>这就是3D场景视图</p>
+<h2 id="3d-c57e864e">这就是3D场景视图</h2>
+<p>If this section generates a duplicate anchor, &quot;3D&quot;, it's because 3D is the only part of the section title QDoc recognizes. This is an error caused by the same bug, and the link should somehow reflect that QDoc encountered the Chinese (or any non-ascii) characters instead.</p>
+<h2 id="662952c1">属性视图</h2>
+<p>The reporter states that this link doesn't work, presumably because it begins with the Chinese character &quot;属&quot;.</p>
+<p>这就是属性视图</p>
+<h2 id="further-details">Further details</h2>
+<p>The bug report is at <a href="https://bugreports.qt.io/browse/QTBUG-64506">https://bugreports.qt.io/browse/QTBUG-64506</a>. It contains the content used to trigger the behavior in this test case. The Chinese characters are copied verbatim from the report.</p>
+<h3 id="ascii-characters-that-are-non-printable-ascii-such-as-or-521d09f0">Ascii characters that are non-printable ascii, such as ß, ü, or ø</h3>
+<p>A whole range of ascii characters are not printable ascii characters. These could also cause issues for QDoc. This section is here to confirm linking to such section titles works as expected. It's made a section2 to exercise the behavior for other section levels than 1.</p>
+</div>
+<!-- @@@adventures_with_non_ascii_characters.html -->
+</body>
+</html>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/non_ascii_character_input/expected/e85685de.html b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/non_ascii_character_input/expected/e85685de.html
new file mode 100644
index 000000000..ea52873e7
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/non_ascii_character_input/expected/e85685de.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+<!-- adventures_with_non_ascii_characters.qdoc -->
+ <title>NonAsciiCharacterInput</title>
+</head>
+<body>
+<div class="sidebar"><div class="sidebar-content" id="sidebar-content"></div></div>
+<!-- $$$موزاريلا-description -->
+<div class="descr" id="details">
+<p>This page exists solely to understand how QDoc will generate the file name for a page with right-to-left script in its name.</p>
+</div>
+<!-- @@@موزاريلا -->
+</body>
+</html>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/non_ascii_character_input/expected/html/8b5c72eb.webxml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/non_ascii_character_input/expected/html/8b5c72eb.webxml
new file mode 100644
index 000000000..375c43732
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/non_ascii_character_input/expected/html/8b5c72eb.webxml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<WebXML>
+ <document>
+ <page name="圣马苏里拉.html" href="8b5c72eb.html" status="active" location="adventures_with_non_ascii_characters.qdoc" documented="true" subtype="page" title="" fulltitle="" subtitle="">
+ <description>
+ <para>This page exists solely to understand how QDoc will generate the file name for a page with non-latin characters in its name.</para>
+ </description>
+ </page>
+ </document>
+</WebXML>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/non_ascii_character_input/expected/html/adventures-with-non-ascii-characters.webxml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/non_ascii_character_input/expected/html/adventures-with-non-ascii-characters.webxml
new file mode 100644
index 000000000..91059c165
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/non_ascii_character_input/expected/html/adventures-with-non-ascii-characters.webxml
@@ -0,0 +1,45 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<WebXML>
+ <document>
+ <page name="adventures_with_non_ascii_characters.html" href="adventures-with-non-ascii-characters.html" status="active" location="adventures_with_non_ascii_characters.qdoc" documented="true" subtype="page" title="Adventures with non-ascii characters in QDoc" fulltitle="Adventures with non-ascii characters in QDoc" subtitle="" brief="Test that non-ascii characters work as input to QDoc commands">
+ <contents name="a-713da3e8" title="A 大纲视图" level="1"/>
+ <contents name="3d-42faee45" title="3D场景视图" level="1"/>
+ <contents name="3d-c57e864e" title="这就是3D场景视图" level="1"/>
+ <contents name="662952c1" title="属性视图" level="1"/>
+ <contents name="further-details" title="Further details" level="1"/>
+ <contents name="ascii-characters-that-are-non-printable-ascii-such-as-or-521d09f0" title="Ascii characters that are non-printable ascii, such as ß, ü, or ø" level="2"/>
+ <description>
+ <brief>Test that non-ascii characters work as input to QDoc commands.</brief>
+ <para>The purpose of this test data is to provide a regression mechanism as part of QDoc's end-to-end test, tst_generatedOutput, for an issue (QTBUG-64506) that was reported against QDoc's \section1 command. The issue, as experienced by the reporter of the bug, is that if the \section1 command is followed by a non-ascii character (for example Chinese characters), navigation links aren't generated for the section title. The bug was filed against Qt 5.9.2. This test case aims at reproducing the issue as filed by the reporter.</para>
+ <para>Such this document snippet:</para>
+ <section id="a-713da3e8">
+ <heading level="1">A 大纲视图</heading>
+ <para>The reporter states that this link works, presumably because it begins with the ascii character &quot;A&quot;.</para>
+ <para>这就是大纲视图</para>
+ </section>
+ <section id="3d-42faee45">
+ <heading level="1">3D场景视图</heading>
+ <para>The reporter states that this link works, presumably because it begins with the digit &quot;3&quot;.</para>
+ <para>这就是3D场景视图</para>
+ </section>
+ <section id="3d-c57e864e">
+ <heading level="1">这就是3D场景视图</heading>
+ <para>If this section generates a duplicate anchor, &quot;3D&quot;, it's because 3D is the only part of the section title QDoc recognizes. This is an error caused by the same bug, and the link should somehow reflect that QDoc encountered the Chinese (or any non-ascii) characters instead.</para>
+ </section>
+ <section id="662952c1">
+ <heading level="1">属性视图</heading>
+ <para>The reporter states that this link doesn't work, presumably because it begins with the Chinese character &quot;属&quot;.</para>
+ <para>这就是属性视图</para>
+ </section>
+ <section id="further-details">
+ <heading level="1">Further details</heading>
+ <para>The bug report is at <link raw="https://bugreports.qt.io/browse/QTBUG-64506" href="https://bugreports.qt.io/browse/QTBUG-64506" type="external">https://bugreports.qt.io/browse/QTBUG-64506</link>. It contains the content used to trigger the behavior in this test case. The Chinese characters are copied verbatim from the report.</para>
+ </section>
+ <section id="ascii-characters-that-are-non-printable-ascii-such-as-or-521d09f0">
+ <heading level="2">Ascii characters that are non-printable ascii, such as ß, ü, or ø</heading>
+ <para>A whole range of ascii characters are not printable ascii characters. These could also cause issues for QDoc. This section is here to confirm linking to such section titles works as expected. It's made a section2 to exercise the behavior for other section levels than 1.</para>
+ </section>
+ </description>
+ </page>
+ </document>
+</WebXML>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/non_ascii_character_input/expected/html/e85685de.webxml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/non_ascii_character_input/expected/html/e85685de.webxml
new file mode 100644
index 000000000..beb4df518
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/non_ascii_character_input/expected/html/e85685de.webxml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<WebXML>
+ <document>
+ <page name="موزاريلا" href="e85685de.html" status="active" location="adventures_with_non_ascii_characters.qdoc" documented="true" subtype="page" title="" fulltitle="" subtitle="">
+ <description>
+ <para>This page exists solely to understand how QDoc will generate the file name for a page with right-to-left script in its name.</para>
+ </description>
+ </page>
+ </document>
+</WebXML>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/non_ascii_character_input/expected/html/mozzarella-7c883eff.webxml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/non_ascii_character_input/expected/html/mozzarella-7c883eff.webxml
new file mode 100644
index 000000000..13ce91b72
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/non_ascii_character_input/expected/html/mozzarella-7c883eff.webxml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<WebXML>
+ <document>
+ <page name="桑塔mozzarella.html" href="mozzarella-7c883eff.html" status="active" location="adventures_with_non_ascii_characters.qdoc" documented="true" subtype="page" title="" fulltitle="" subtitle="">
+ <description>
+ <para>This page exists solely to understand how QDoc will generate the file name for a page that mixes printable ascii with non-latin characters in its name.</para>
+ </description>
+ </page>
+ </document>
+</WebXML>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/non_ascii_character_input/expected/html/nonasciicharacterinput.index b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/non_ascii_character_input/expected/html/nonasciicharacterinput.index
new file mode 100644
index 000000000..aa306d7eb
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/non_ascii_character_input/expected/html/nonasciicharacterinput.index
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE QDOCINDEX>
+<INDEX url="" title="NonAsciiCharacterInput Reference Documentation" version="" project="NonAsciiCharacterInput">
+ <namespace name="" status="active" access="public" module="nonasciicharacterinput">
+ <page name="adventures_with_non_ascii_characters.html" href="adventures-with-non-ascii-characters.html" status="active" location="adventures_with_non_ascii_characters.qdoc" documented="true" subtype="page" title="Adventures with non-ascii characters in QDoc" fulltitle="Adventures with non-ascii characters in QDoc" subtitle="" brief="Test that non-ascii characters work as input to QDoc commands">
+ <contents name="a-713da3e8" title="A 大纲视图" level="1"/>
+ <contents name="3d-42faee45" title="3D场景视图" level="1"/>
+ <contents name="3d-c57e864e" title="这就是3D场景视图" level="1"/>
+ <contents name="662952c1" title="属性视图" level="1"/>
+ <contents name="further-details" title="Further details" level="1"/>
+ <contents name="ascii-characters-that-are-non-printable-ascii-such-as-or-521d09f0" title="Ascii characters that are non-printable ascii, such as ß, ü, or ø" level="2"/>
+ </page>
+ <page name="SEITE_MIT_AUSSCHLIEßLICH_GROßBUCHSTABEN_IM_TITEL_ÜBERSCHRIFT.htm" href="seite-mit-ausschlie-lich-gro-buchstaben-im-titel-berschrift-htm-bfa91582.html" status="active" location="adventures_with_non_ascii_characters.qdoc" documented="true" subtype="page" title="" fulltitle="" subtitle=""/>
+ <page name="santaموزاريلا.html" href="santa-14209312.html" status="active" location="adventures_with_non_ascii_characters.qdoc" documented="true" subtype="page" title="" fulltitle="" subtitle=""/>
+ <page name="موزاريلا" href="e85685de.html" status="active" location="adventures_with_non_ascii_characters.qdoc" documented="true" subtype="page" title="" fulltitle="" subtitle=""/>
+ <page name="圣马苏里拉.html" href="8b5c72eb.html" status="active" location="adventures_with_non_ascii_characters.qdoc" documented="true" subtype="page" title="" fulltitle="" subtitle=""/>
+ <page name="桑塔mozzarella.html" href="mozzarella-7c883eff.html" status="active" location="adventures_with_non_ascii_characters.qdoc" documented="true" subtype="page" title="" fulltitle="" subtitle=""/>
+ </namespace>
+</INDEX>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/non_ascii_character_input/expected/html/santa-14209312.webxml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/non_ascii_character_input/expected/html/santa-14209312.webxml
new file mode 100644
index 000000000..9d07c9da8
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/non_ascii_character_input/expected/html/santa-14209312.webxml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<WebXML>
+ <document>
+ <page name="santaموزاريلا.html" href="santa-14209312.html" status="active" location="adventures_with_non_ascii_characters.qdoc" documented="true" subtype="page" title="" fulltitle="" subtitle="">
+ <description>
+ <para>This page exists solely to understand how QDoc will generate the file name for a page that mixes printable ascii with right-to-left script in its name.</para>
+ </description>
+ </page>
+ </document>
+</WebXML>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/non_ascii_character_input/expected/html/seite-mit-ausschlie-lich-gro-buchstaben-im-titel-berschrift-htm-bfa91582.webxml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/non_ascii_character_input/expected/html/seite-mit-ausschlie-lich-gro-buchstaben-im-titel-berschrift-htm-bfa91582.webxml
new file mode 100644
index 000000000..8fe3c93ee
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/non_ascii_character_input/expected/html/seite-mit-ausschlie-lich-gro-buchstaben-im-titel-berschrift-htm-bfa91582.webxml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<WebXML>
+ <document>
+ <page name="SEITE_MIT_AUSSCHLIEßLICH_GROßBUCHSTABEN_IM_TITEL_ÜBERSCHRIFT.htm" href="seite-mit-ausschlie-lich-gro-buchstaben-im-titel-berschrift-htm-bfa91582.html" status="active" location="adventures_with_non_ascii_characters.qdoc" documented="true" subtype="page" title="" fulltitle="" subtitle="">
+ <description>
+ <para>This page exists solely to understand how QDoc will generate the file name for a page with non-ascii-printable latin characters in its name.</para>
+ </description>
+ </page>
+ </document>
+</WebXML>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/non_ascii_character_input/expected/mozzarella-7c883eff.html b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/non_ascii_character_input/expected/mozzarella-7c883eff.html
new file mode 100644
index 000000000..bb4b3651d
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/non_ascii_character_input/expected/mozzarella-7c883eff.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+<!-- adventures_with_non_ascii_characters.qdoc -->
+ <title>NonAsciiCharacterInput</title>
+</head>
+<body>
+<div class="sidebar"><div class="sidebar-content" id="sidebar-content"></div></div>
+<!-- $$$桑塔mozzarella.html-description -->
+<div class="descr" id="details">
+<p>This page exists solely to understand how QDoc will generate the file name for a page that mixes printable ascii with non-latin characters in its name.</p>
+</div>
+<!-- @@@桑塔mozzarella.html -->
+</body>
+</html>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/non_ascii_character_input/expected/nonasciicharacterinput.index b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/non_ascii_character_input/expected/nonasciicharacterinput.index
new file mode 100644
index 000000000..aa306d7eb
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/non_ascii_character_input/expected/nonasciicharacterinput.index
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE QDOCINDEX>
+<INDEX url="" title="NonAsciiCharacterInput Reference Documentation" version="" project="NonAsciiCharacterInput">
+ <namespace name="" status="active" access="public" module="nonasciicharacterinput">
+ <page name="adventures_with_non_ascii_characters.html" href="adventures-with-non-ascii-characters.html" status="active" location="adventures_with_non_ascii_characters.qdoc" documented="true" subtype="page" title="Adventures with non-ascii characters in QDoc" fulltitle="Adventures with non-ascii characters in QDoc" subtitle="" brief="Test that non-ascii characters work as input to QDoc commands">
+ <contents name="a-713da3e8" title="A 大纲视图" level="1"/>
+ <contents name="3d-42faee45" title="3D场景视图" level="1"/>
+ <contents name="3d-c57e864e" title="这就是3D场景视图" level="1"/>
+ <contents name="662952c1" title="属性视图" level="1"/>
+ <contents name="further-details" title="Further details" level="1"/>
+ <contents name="ascii-characters-that-are-non-printable-ascii-such-as-or-521d09f0" title="Ascii characters that are non-printable ascii, such as ß, ü, or ø" level="2"/>
+ </page>
+ <page name="SEITE_MIT_AUSSCHLIEßLICH_GROßBUCHSTABEN_IM_TITEL_ÜBERSCHRIFT.htm" href="seite-mit-ausschlie-lich-gro-buchstaben-im-titel-berschrift-htm-bfa91582.html" status="active" location="adventures_with_non_ascii_characters.qdoc" documented="true" subtype="page" title="" fulltitle="" subtitle=""/>
+ <page name="santaموزاريلا.html" href="santa-14209312.html" status="active" location="adventures_with_non_ascii_characters.qdoc" documented="true" subtype="page" title="" fulltitle="" subtitle=""/>
+ <page name="موزاريلا" href="e85685de.html" status="active" location="adventures_with_non_ascii_characters.qdoc" documented="true" subtype="page" title="" fulltitle="" subtitle=""/>
+ <page name="圣马苏里拉.html" href="8b5c72eb.html" status="active" location="adventures_with_non_ascii_characters.qdoc" documented="true" subtype="page" title="" fulltitle="" subtitle=""/>
+ <page name="桑塔mozzarella.html" href="mozzarella-7c883eff.html" status="active" location="adventures_with_non_ascii_characters.qdoc" documented="true" subtype="page" title="" fulltitle="" subtitle=""/>
+ </namespace>
+</INDEX>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/non_ascii_character_input/expected/santa-14209312.html b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/non_ascii_character_input/expected/santa-14209312.html
new file mode 100644
index 000000000..f40feed36
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/non_ascii_character_input/expected/santa-14209312.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+<!-- adventures_with_non_ascii_characters.qdoc -->
+ <title>NonAsciiCharacterInput</title>
+</head>
+<body>
+<div class="sidebar"><div class="sidebar-content" id="sidebar-content"></div></div>
+<!-- $$$santaموزاريلا.html-description -->
+<div class="descr" id="details">
+<p>This page exists solely to understand how QDoc will generate the file name for a page that mixes printable ascii with right-to-left script in its name.</p>
+</div>
+<!-- @@@santaموزاريلا.html -->
+</body>
+</html>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/non_ascii_character_input/expected/seite-mit-ausschlie-lich-gro-buchstaben-im-titel-berschrift-htm-bfa91582.html b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/non_ascii_character_input/expected/seite-mit-ausschlie-lich-gro-buchstaben-im-titel-berschrift-htm-bfa91582.html
new file mode 100644
index 000000000..16df49755
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/non_ascii_character_input/expected/seite-mit-ausschlie-lich-gro-buchstaben-im-titel-berschrift-htm-bfa91582.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+<!-- adventures_with_non_ascii_characters.qdoc -->
+ <title>NonAsciiCharacterInput</title>
+</head>
+<body>
+<div class="sidebar"><div class="sidebar-content" id="sidebar-content"></div></div>
+<!-- $$$SEITE_MIT_AUSSCHLIEßLICH_GROßBUCHSTABEN_IM_TITEL_ÜBERSCHRIFT.htm-description -->
+<div class="descr" id="details">
+<p>This page exists solely to understand how QDoc will generate the file name for a page with non-ascii-printable latin characters in its name.</p>
+</div>
+<!-- @@@SEITE_MIT_AUSSCHLIEßLICH_GROßBUCHSTABEN_IM_TITEL_ÜBERSCHRIFT.htm -->
+</body>
+</html>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/non_ascii_character_input/non_ascii_character_input.qdocconf b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/non_ascii_character_input/non_ascii_character_input.qdocconf
new file mode 100644
index 000000000..0177b51ac
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/non_ascii_character_input/non_ascii_character_input.qdocconf
@@ -0,0 +1,30 @@
+# Copyright (C) 2023 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+
+project = NonAsciiCharacterInput
+
+headerdirs = ./src
+sourcedirs = ./src
+exampledirs = ./src
+
+outputformats = WebXML HTML
+WebXML.quotinginformation = true
+WebXML.nosubdirs = true
+
+sources.fileextensions = "*.qml *.cpp *.qdoc"
+headers.fileextensions = "*.h"
+
+# images
+imagedirs = ./src/images
+
+# zero warning policy
+warninglimit = 0
+warninglimit.enabled = true
+
+# don't write host system-specific paths to index files
+locationinfo = false
+
+# By default, use -outputdir directly, no subdir.
+# outputsubdir '.' matches the root of expected_output/
+HTML.nosubdirs = true
+HTML.outputsubdir = .
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/non_ascii_character_input/src/adventures_with_non_ascii_characters.qdoc b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/non_ascii_character_input/src/adventures_with_non_ascii_characters.qdoc
new file mode 100644
index 000000000..c5f09cb1c
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/non_ascii_character_input/src/adventures_with_non_ascii_characters.qdoc
@@ -0,0 +1,89 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+
+/*!
+ \page adventures_with_non_ascii_characters.html
+ \title Adventures with non-ascii characters in QDoc
+ \brief Test that non-ascii characters work as input to QDoc commands.
+
+ The purpose of this test data is to provide a regression mechanism as part
+ of QDoc's end-to-end test, tst_generatedOutput, for an issue (QTBUG-64506)
+ that was reported against QDoc's \\section1 command. The issue, as
+ experienced by the reporter of the bug, is that if the \\section1 command
+ is followed by a non-ascii character (for example Chinese characters),
+ navigation links aren't generated for the section title. The bug was filed
+ against Qt 5.9.2. This test case aims at reproducing the issue as filed by
+ the reporter.
+
+ Such this document snippet:
+
+ \section1 A 大纲视图
+ The reporter states that this link works, presumably because it begins with
+ the ascii character "A".
+
+ 这就是大纲视图
+
+ \section1 3D场景视图
+ The reporter states that this link works, presumably because it begins with
+ the digit "3".
+
+ 这就是3D场景视图
+
+ \section1 这就是3D场景视图
+ If this section generates a duplicate anchor, "3D", it's because 3D
+ is the only part of the section title QDoc recognizes. This is an error
+ caused by the same bug, and the link should somehow reflect that QDoc
+ encountered the Chinese (or any non-ascii) characters instead.
+
+ \section1 属性视图
+ The reporter states that this link doesn't work, presumably because it
+ begins with the Chinese character "属".
+
+ 这就是属性视图
+
+ \section1 Further details
+ The bug report is at \l https://bugreports.qt.io/browse/QTBUG-64506. It
+ contains the content used to trigger the behavior in this test case. The
+ Chinese characters are copied verbatim from the report.
+
+ \section2 Ascii characters that are non-printable ascii, such as ß, ü, or ø
+ A whole range of ascii characters are not printable ascii characters. These
+ could also cause issues for QDoc. This section is here to confirm linking
+ to such section titles works as expected. It's made a section2 to exercise
+ the behavior for other section levels than 1.
+*/
+
+/*!
+ \page SEITE_MIT_AUSSCHLIEßLICH_GROßBUCHSTABEN_IM_TITEL_ÜBERSCHRIFT.htm
+
+ This page exists solely to understand how QDoc will generate the file name
+ for a page with non-ascii-printable latin characters in its name.
+*/
+
+/*!
+ \page موزاريلا سانتا.html
+
+ This page exists solely to understand how QDoc will generate the file name
+ for a page with right-to-left script in its name.
+*/
+
+/*!
+ \page 圣马苏里拉.html
+
+ This page exists solely to understand how QDoc will generate the file name
+ for a page with non-latin characters in its name.
+*/
+
+/*!
+ \page santaموزاريلا.html
+
+ This page exists solely to understand how QDoc will generate the file name
+ for a page that mixes printable ascii with right-to-left script in its name.
+*/
+
+/*!
+ \page 桑塔mozzarella.html
+
+ This page exists solely to understand how QDoc will generate the file name
+ for a page that mixes printable ascii with non-latin characters in its name.
+*/
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/outputfromqdocfiles/expected/docbook/crash.xml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/outputfromqdocfiles/expected/docbook/crash.xml
new file mode 100644
index 000000000..04e27b4a3
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/outputfromqdocfiles/expected/docbook/crash.xml
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<db:article xmlns:db="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" version="5.2" xml:lang="en">
+<db:info>
+<db:title></db:title>
+<db:productname>OutputFromQDocFiles</db:productname>
+<db:edition>OutputFromQDocFiles - A test project for QDoc build artifacts</db:edition>
+<db:titleabbrev>A test project for QDoc build artifacts</db:titleabbrev>
+<db:abstract>
+<db:para>A test project for QDoc build artifacts.</db:para></db:abstract>
+</db:info>
+<db:para><db:link xlink:href=""></db:link></db:para>
+</db:article>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/outputfromqdocfiles/expected/docbook/images/01.png b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/outputfromqdocfiles/expected/docbook/images/01.png
new file mode 100644
index 000000000..d73ab969b
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/outputfromqdocfiles/expected/docbook/images/01.png
Binary files differ
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/outputfromqdocfiles/expected/docbook/images/leonardo-da-vinci.png b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/outputfromqdocfiles/expected/docbook/images/leonardo-da-vinci.png
new file mode 100644
index 000000000..854acb4ca
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/outputfromqdocfiles/expected/docbook/images/leonardo-da-vinci.png
Binary files differ
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/outputfromqdocfiles/expected/docbook/qdoctests-qdocfileoutput-exhaustive.xml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/outputfromqdocfiles/expected/docbook/qdoctests-qdocfileoutput-exhaustive.xml
new file mode 100644
index 000000000..237896629
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/outputfromqdocfiles/expected/docbook/qdoctests-qdocfileoutput-exhaustive.xml
@@ -0,0 +1,129 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<db:article xmlns:db="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" version="5.2" xml:lang="en">
+<db:info>
+<db:title>Exhaustive testing of QDoc commands</db:title>
+<db:productname>OutputFromQDocFiles</db:productname>
+<db:edition>OutputFromQDocFiles - A test project for QDoc build artifacts</db:edition>
+<db:titleabbrev>A test project for QDoc build artifacts</db:titleabbrev>
+<db:abstract>
+<db:para>This page is a dumping ground for QDoc commands under test.</db:para>
+</db:abstract>
+</db:info>
+<db:section xml:id="this-is-a-section1">
+<db:title>This is a section1</db:title>
+<db:section xml:id="this-is-a-section2">
+<db:title>This is a section2</db:title>
+<db:section xml:id="this-is-a-section3">
+<db:title>This is a section3</db:title>
+<db:section xml:id="this-is-a-section4">
+<db:title>This is a section4</db:title>
+<db:programlisting language="cpp" role="bad">This is bad code
+</db:programlisting>
+<db:para>This text should have a line break riiiiight noooow.</db:para>
+<db:para><db:emphasis role="bold">All your text belong to bold</db:emphasis> ...And this is an examble of only <db:emphasis role="bold">bold</db:emphasis> being, well, bold.</db:para>
+<db:programlisting language="cpp"> ...
+</db:programlisting>
+<db:title>This a caption</db:title>
+<db:para>Lorem legal ipsum</db:para>
+<db:blockquote><db:para>This is a quotation.</db:para>
+</db:blockquote>
+<db:sidebar><db:para>Look, ma! I made a sidebar!</db:para>
+</db:sidebar>
+<db:informaltable style="generic">
+<db:tr valign="top">
+<db:td>
+<db:para>Table item in a table row</db:para>
+</db:td>
+</db:tr>
+<db:tr valign="top">
+<db:td>
+<db:para>Another item in a different row</db:para>
+</db:td>
+</db:tr>
+</db:informaltable>
+<db:important>
+<db:para>This is really important.</db:para>
+</db:important>
+<db:note>
+<db:para>The code above doesn't compile</db:para>
+</db:note>
+</db:section>
+</db:section>
+</db:section>
+</db:section>
+<db:section xml:id="images">
+<db:title>Images</db:title>
+<db:para>An image without any text:</db:para>
+<db:mediaobject>
+<db:imageobject>
+<db:imagedata fileref="images/leonardo-da-vinci.png"/>
+</db:imageobject>
+</db:mediaobject>
+<db:para>An image with just an alternative text:</db:para>
+<db:mediaobject>
+<db:alt>Image alt</db:alt>
+<db:imageobject>
+<db:imagedata fileref="images/leonardo-da-vinci.png"/>
+</db:imageobject>
+</db:mediaobject>
+<db:para>An image with alternative text and 1-atom caption:</db:para>
+<db:figure>
+<db:title>Image caption</db:title>
+<db:mediaobject>
+<db:alt>Image alt</db:alt>
+<db:imageobject>
+<db:imagedata fileref="images/leonardo-da-vinci.png"/>
+</db:imageobject>
+</db:mediaobject>
+</db:figure>
+<db:para>An image with alternative text and 2-atom caption:</db:para>
+<db:figure>
+<db:title>Image caption with <db:emphasis role="bold">bold</db:emphasis> text</db:title>
+<db:mediaobject>
+<db:alt>Image alt</db:alt>
+<db:imageobject>
+<db:imagedata fileref="images/leonardo-da-vinci.png"/>
+</db:imageobject>
+</db:mediaobject>
+</db:figure>
+<db:para>A bordered image:</db:para>
+<db:mediaobject>
+<db:imageobject>
+<db:imagedata fileref="images/leonardo-da-vinci.png"/>
+</db:imageobject>
+</db:mediaobject>
+<db:para>A bordered image with a caption:</db:para>
+<db:figure>
+<db:title>Screenshot of the System Tray Icon</db:title>
+<db:mediaobject>
+<db:imageobject>
+<db:imagedata fileref="images/leonardo-da-vinci.png"/>
+</db:imageobject>
+</db:mediaobject>
+</db:figure>
+<db:para>An inline image:</db:para>
+<db:para>The is a paragraph containing an <db:inlinemediaobject>
+<db:imageobject>
+<db:imagedata fileref="images/01.png"/>
+</db:imageobject>
+</db:inlinemediaobject> inline image to test if qdoc handles them properly, without considering rest of the line as alt text for the image.</db:para>
+<db:para>An inline image with alt text:</db:para>
+<db:para>Here is another example of <db:inlinemediaobject>
+<db:alt>No. 1</db:alt>
+<db:imageobject>
+<db:imagedata fileref="images/01.png"/>
+</db:imageobject>
+</db:inlinemediaobject> inline image with alternative text, which should be added as an attribute to the inline image.</db:para>
+<db:para>File quoting:</db:para>
+<db:programlisting language="cpp"> if (false) {
+ return 1;
+ }
+</db:programlisting>
+</db:section>
+<db:section xml:id="commands-not-yet-tested">
+<db:title>Commands not yet tested</db:title>
+<db:warning>
+<db:para>The following commands have yet to be tested: footnote link sincelist header index topicref // or just don’t care, remove it inlineimage printline printto quotefile skipline skipuntil span snippet codeline overload sub sup tableofcontents tt uicontrol endmapref endomit underline unicode</db:para>
+</db:warning>
+</db:section>
+</db:article>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/outputfromqdocfiles/expected/docbook/qdoctests-qdocfileoutput-linking.xml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/outputfromqdocfiles/expected/docbook/qdoctests-qdocfileoutput-linking.xml
new file mode 100644
index 000000000..51b7e62b3
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/outputfromqdocfiles/expected/docbook/qdoctests-qdocfileoutput-linking.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<db:article xmlns:db="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" version="5.2" xml:lang="en">
+<db:info>
+<db:title>Testing QDoc's link command</db:title>
+<db:productname>OutputFromQDocFiles</db:productname>
+<db:edition>OutputFromQDocFiles - A test project for QDoc build artifacts</db:edition>
+<db:titleabbrev>A test project for QDoc build artifacts</db:titleabbrev>
+<db:extendedlink xlink:type="extended"><db:link xlink:to="qdoctests-qdocfileoutput.xml" xlink:type="arc" xlink:arcrole="prev" xlink:title="QDoc Testing"/></db:extendedlink>
+<db:extendedlink xlink:type="extended"><db:link xlink:to="toc.xml" xlink:type="arc" xlink:arcrole="next" xlink:title="Table of Contents"/></db:extendedlink>
+<db:abstract>
+<db:para>This is a page for testing QDoc's link command.</db:para>
+</db:abstract>
+</db:info>
+<db:anchor xml:id="link-test-target"/>
+<db:section xml:id="link-targets">
+<db:title>Link targets</db:title>
+<db:para>Valid parameters for the link command (<db:code>\l</db:code>) are page and section titles, targets defined with \target or \keyword commands, and API reference keywords (types, methods, namespaces, and so on).</db:para>
+</db:section>
+</db:article>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/outputfromqdocfiles/expected/docbook/qdoctests-qdocfileoutput.xml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/outputfromqdocfiles/expected/docbook/qdoctests-qdocfileoutput.xml
new file mode 100644
index 000000000..72905681d
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/outputfromqdocfiles/expected/docbook/qdoctests-qdocfileoutput.xml
@@ -0,0 +1,80 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<db:article xmlns:db="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" version="5.2" xml:lang="en">
+<db:info>
+<db:title>Testing QDoc output from .qdoc files</db:title>
+<db:productname>OutputFromQDocFiles</db:productname>
+<db:edition>OutputFromQDocFiles - A test project for QDoc build artifacts</db:edition>
+<db:titleabbrev>A test project for QDoc build artifacts</db:titleabbrev>
+<db:extendedlink xlink:type="extended"><db:link xlink:to="qdoctests-qdocfileoutput-linking.xml" xlink:type="arc" xlink:arcrole="next" xlink:title="QDoc Linking Test"/></db:extendedlink>
+<db:abstract>
+<db:para>This is a simple page for testing purposes only.</db:para>
+</db:abstract>
+</db:info>
+<db:para>QDoc generates documentation for software projects. It does this by extracting <db:emphasis>QDoc comments</db:emphasis> from project source files. QDoc comments are signified by a C-style-like comment tag followed by an exclamation point, like this: <db:code>/*!</db:code> <db:code>This text is contained within QDoc comment tags.</db:code> <db:code>*/</db:code>.</db:para>
+<db:section xml:id="supported-file-types">
+<db:title>Supported file types</db:title>
+<db:para>QDoc parses <db:code>.cpp</db:code> and <db:code>.qdoc</db:code> files. It does extract comments from header (<db:code>.h</db:code>) files.</db:para>
+</db:section>
+<db:section xml:id="further-information">
+<db:title>Further information</db:title>
+<db:para>This test document is written with the purpose of testing the output QDoc generates when parsing <db:code>.qdoc</db:code> files. It is fairly simple and makes use of a limited subset of QDoc's command. Those commands are:</db:para>
+<db:itemizedlist>
+<db:listitem>
+<db:para><db:code>\page</db:code></db:para>
+</db:listitem>
+<db:listitem>
+<db:para><db:code>\title</db:code></db:para>
+</db:listitem>
+<db:listitem>
+<db:para><db:code>\brief</db:code></db:para>
+</db:listitem>
+<db:listitem>
+<db:para><db:code>\e</db:code> (for emphasizing &quot;QDoc comments&quot;)</db:para>
+</db:listitem>
+<db:listitem>
+<db:para><db:code>\c</db:code> (for multiple monospace-formatted entries)</db:para>
+</db:listitem>
+<db:listitem>
+<db:para><db:code>\section1</db:code></db:para>
+</db:listitem>
+<db:listitem>
+<db:para><db:code>\list</db:code></db:para>
+</db:listitem>
+<db:listitem>
+<db:para><db:code>\li</db:code></db:para>
+</db:listitem>
+<db:listitem>
+<db:para><db:code>\endlist</db:code></db:para>
+</db:listitem>
+</db:itemizedlist>
+</db:section>
+<db:section xml:id="linking">
+<db:title>Linking</db:title>
+<db:para>There are multiple ways to create hyperlinks to other topics:</db:para>
+<db:itemizedlist>
+<db:listitem>
+<db:para><db:link xlink:href="qdoctests-qdocfileoutput-linking.xml">Linking to a page title</db:link></db:para>
+</db:listitem>
+<db:listitem>
+<db:para><db:link xlink:href="qdoctests-qdocfileoutput-linking.xml#link-targets">Linking to a section title</db:link></db:para>
+</db:listitem>
+<db:listitem>
+<db:para><db:link xlink:href="qdoctests-qdocfileoutput-linking.xml#link-test-target">Linking using a \target string</db:link></db:para>
+</db:listitem>
+<db:listitem>
+<db:para><db:link xlink:href="qdoctests-qdocfileoutput-linking.xml">Linking using a \keyword string</db:link></db:para>
+</db:listitem>
+</db:itemizedlist>
+</db:section>
+<db:section xml:id="qdoc-linking-test">
+<db:title>QDoc Linking Test</db:title>
+<db:para>This section title is overridden by another target which takes precedence.</db:para>
+</db:section>
+<db:section xml:id="linking-to-something-in-a-section-title">
+<db:title>Linking to <db:link xlink:href="qdoctests-qdocfileoutput.xml#further-information">something</db:link> in a section title</db:title>
+<db:para>This is allowed but a questionable practice.</db:para>
+<db:note>
+<db:para>You're looking at detailed information.</db:para>
+</db:note>
+</db:section>
+</db:article>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/outputfromqdocfiles/expected/docbook/qdoctests-qdocmanuallikefileoutput.xml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/outputfromqdocfiles/expected/docbook/qdoctests-qdocmanuallikefileoutput.xml
new file mode 100644
index 000000000..38e3887ef
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/outputfromqdocfiles/expected/docbook/qdoctests-qdocmanuallikefileoutput.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<db:article xmlns:db="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" version="5.2" xml:lang="en">
+<db:info>
+<db:title>Document Navigation</db:title>
+<db:productname>OutputFromQDocFiles</db:productname>
+<db:edition>OutputFromQDocFiles - A test project for QDoc build artifacts</db:edition>
+<db:titleabbrev>A test project for QDoc build artifacts</db:titleabbrev>
+<db:abstract>
+<db:para>A test project for QDoc build artifacts.</db:para></db:abstract>
+</db:info>
+<db:para>The navigation commands...</db:para>
+<db:blockquote/>
+<db:programlisting language="cpp">&amp;lt;head&amp;gt;
+ ...
+ &amp;lt;link rel=&amp;quot;start&amp;quot; href=&amp;quot;basicqt.html&amp;quot; /&amp;gt;
+ ...
+&amp;lt;/head&amp;gt;
+</db:programlisting>
+<db:section xml:id="commands">
+<db:title>Commands</db:title>
+<db:anchor xml:id="previouspage-command"/>
+<db:section xml:id="previouspage">
+<db:title>\previouspage</db:title>
+<db:para>The \previouspage command...</db:para>
+</db:section>
+</db:section>
+</db:article>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/outputfromqdocfiles/expected/docbook/toc.xml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/outputfromqdocfiles/expected/docbook/toc.xml
new file mode 100644
index 000000000..14ccd795d
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/outputfromqdocfiles/expected/docbook/toc.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<db:article xmlns:db="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" version="5.2" xml:lang="en">
+<db:info>
+<db:title>Table of Contents</db:title>
+<db:productname>OutputFromQDocFiles</db:productname>
+<db:edition>OutputFromQDocFiles - A test project for QDoc build artifacts</db:edition>
+<db:titleabbrev>A test project for QDoc build artifacts</db:titleabbrev>
+<db:extendedlink xlink:type="extended"><db:link xlink:to="qdoctests-qdocfileoutput-linking.xml" xlink:type="arc" xlink:arcrole="prev" xlink:title="QDoc Linking Test"/></db:extendedlink>
+<db:abstract>
+<db:para>A test project for QDoc build artifacts.</db:para></db:abstract>
+</db:info>
+<db:itemizedlist>
+<db:listitem>
+<db:para><db:link xlink:href="qdoctests-qdocfileoutput.xml">QDoc Testing</db:link></db:para>
+</db:listitem>
+<db:listitem>
+<db:para><db:link xlink:href="qdoctests-qdocfileoutput-linking.xml">QDoc Linking Test</db:link></db:para>
+</db:listitem>
+<db:listitem>
+<db:para><db:link xlink:href="toc.xml">Table of Contents</db:link></db:para>
+</db:listitem>
+</db:itemizedlist>
+</db:article>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/outputfromqdocfiles/expected/html/crash.html b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/outputfromqdocfiles/expected/html/crash.html
new file mode 100644
index 000000000..7ac452663
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/outputfromqdocfiles/expected/html/crash.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+<!-- qdoctests-outputfromqdocfiles.qdoc -->
+ <title>OutputFromQDocFiles</title>
+</head>
+<body>
+<div class="sidebar"><div class="sidebar-content" id="sidebar-content"></div></div>
+<!-- $$$crash.html-description -->
+<div class="descr" id="details">
+<p></p>
+</div>
+<!-- @@@crash.html -->
+</body>
+</html>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/outputfromqdocfiles/expected/html/images/01.png b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/outputfromqdocfiles/expected/html/images/01.png
new file mode 100644
index 000000000..d73ab969b
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/outputfromqdocfiles/expected/html/images/01.png
Binary files differ
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/outputfromqdocfiles/expected/html/images/leonardo-da-vinci.png b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/outputfromqdocfiles/expected/html/images/leonardo-da-vinci.png
new file mode 100644
index 000000000..854acb4ca
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/outputfromqdocfiles/expected/html/images/leonardo-da-vinci.png
Binary files differ
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/outputfromqdocfiles/expected/html/outputfromqdocfiles.index b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/outputfromqdocfiles/expected/html/outputfromqdocfiles.index
new file mode 100644
index 000000000..eb63b02c2
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/outputfromqdocfiles/expected/html/outputfromqdocfiles.index
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE QDOCINDEX>
+<INDEX url="" title="A test project for QDoc build artifacts" version="" project="OutputFromQDocFiles">
+ <namespace name="" status="active" access="public" module="outputfromqdocfiles">
+ <page name="qdoctests-qdocmanuallikefileoutput.html" href="qdoctests-qdocmanuallikefileoutput.html" status="active" location="qdoctests-outputfromqdocmanuallikefiles.qdoc" documented="true" subtype="page" title="Document Navigation" fulltitle="Document Navigation" subtitle="">
+ <target name="previouspage-command"/>
+ <contents name="commands" title="Commands" level="1"/>
+ <contents name="previouspage" title="\previouspage" level="2"/>
+ </page>
+ <page name="qdoctests-qdocfileoutput-exhaustive.html" href="qdoctests-qdocfileoutput-exhaustive.html" status="active" location="qdoctests-outputfromqdocfiles.qdoc" documented="true" subtype="page" title="Exhaustive testing of QDoc commands" fulltitle="Exhaustive testing of QDoc commands" subtitle="" brief="This page is a dumping ground for QDoc commands under test">
+ <contents name="this-is-a-section1" title="This is a section1" level="1"/>
+ <contents name="this-is-a-section2" title="This is a section2" level="2"/>
+ <contents name="this-is-a-section3" title="This is a section3" level="3"/>
+ <contents name="this-is-a-section4" title="This is a section4" level="4"/>
+ <contents name="images" title="Images" level="1"/>
+ <contents name="commands-not-yet-tested" title="Commands not yet tested" level="1"/>
+ </page>
+ <page name="toc.html" href="toc.html" status="active" location="qdoctests-outputfromqdocfiles.qdoc" documented="true" subtype="page" title="Table of Contents" fulltitle="Table of Contents" subtitle=""/>
+ <page name="qdoctests-qdocfileoutput.html" href="qdoctests-qdocfileoutput.html" status="active" location="qdoctests-outputfromqdocfiles.qdoc" documented="true" subtype="page" title="Testing QDoc output from .qdoc files" fulltitle="Testing QDoc output from .qdoc files" subtitle="" brief="This is a simple page for testing purposes only">
+ <contents name="supported-file-types" title="Supported file types" level="1"/>
+ <contents name="further-information" title="Further information" level="1"/>
+ <contents name="linking" title="Linking" level="1"/>
+ <contents name="qdoc-linking-test" title="QDoc Linking Test" level="1"/>
+ <contents name="linking-to-something-in-a-section-title" title="Linking to something in a section title" level="1"/>
+ </page>
+ <page name="qdoctests-qdocfileoutput-linking.html" href="qdoctests-qdocfileoutput-linking.html" status="active" location="qdoctests-outputfromqdocfiles.qdoc" documented="true" subtype="page" title="Testing QDoc's link command" fulltitle="Testing QDoc's link command" subtitle="" brief="This is a page for testing QDoc's link command">
+ <target name="link-test-target"/>
+ <keyword name="qdoc-linking-test" title="QDoc Linking Test"/>
+ <contents name="link-targets" title="Link targets" level="1"/>
+ </page>
+ <page name="crash.html" href="crash.html" status="active" location="qdoctests-outputfromqdocfiles.qdoc" documented="true" subtype="page" title="" fulltitle="" subtitle=""/>
+ </namespace>
+</INDEX>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/outputfromqdocfiles/expected/html/qdoctests-qdocfileoutput-exhaustive.html b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/outputfromqdocfiles/expected/html/qdoctests-qdocfileoutput-exhaustive.html
new file mode 100644
index 000000000..c4cb46759
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/outputfromqdocfiles/expected/html/qdoctests-qdocfileoutput-exhaustive.html
@@ -0,0 +1,78 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+<!-- qdoctests-outputfromqdocfiles.qdoc -->
+ <meta name="description" content="This page is a dumping ground for QDoc commands under test.">
+ <title>Exhaustive testing of QDoc commands | OutputFromQDocFiles</title>
+</head>
+<body>
+<li id="buildversion">OutputFromQDocFiles - A test project for QDoc build artifacts</li>
+<div class="sidebar">
+<div class="toc">
+<h3 id="toc">Contents</h3>
+<ul>
+<li class="level1"><a href="#this-is-a-section1">This is a section1</a></li>
+<li class="level2"><a href="#this-is-a-section2">This is a section2</a></li>
+<li class="level3"><a href="#this-is-a-section3">This is a section3</a></li>
+<li class="level4"><a href="#this-is-a-section4">This is a section4</a></li>
+<li class="level1"><a href="#images">Images</a></li>
+<li class="level1"><a href="#commands-not-yet-tested">Commands not yet tested</a></li>
+</ul>
+</div>
+<div class="sidebar-content" id="sidebar-content"></div></div>
+<h1 class="title">Exhaustive testing of QDoc commands</h1>
+<!-- $$$qdoctests-qdocfileoutput-exhaustive.html-description -->
+<div class="descr" id="details">
+<h2 id="this-is-a-section1">This is a section1</h2>
+<h3 id="this-is-a-section2">This is a section2</h3>
+<h4 id="this-is-a-section3">This is a section3</h4>
+<h5 id="this-is-a-section4">This is a section4</h5>
+<pre class="cpp plain" translate="no">This is bad code</pre>
+<p>This text should have a line break riiiiight <br />
+ noooow.</p>
+<p><b>All your text belong to bold</b> ...And this is an examble of only <b>bold</b> being, well, bold.</p>
+<pre class="cpp" translate="no"> ...</pre>
+<p class="figCaption">This a caption</p>
+<div class="LegaleseLeft"><p>Lorem legal ipsum</p>
+</div><blockquote><p>This is a quotation.</p>
+</blockquote>
+ <html><body>This is <b>raw</b>. Like the <h1>Eddie Murphy</h1> movie. Just not as funny.</body></html>
+ <p>Look, ma! I made a sidebar!</p>
+<div class="table"><table class="generic">
+ <tr valign="top" class="odd"><td >Table item in a table row</td></tr>
+<tr valign="top" class="even"><td >Another item in a different row</td></tr>
+</table></div>
+<div class="admonition important">
+<p><b>Important: </b>This is really important.</p>
+</div>
+<div class="admonition note">
+<p><b>Note: </b>The code above doesn't compile</p>
+</div>
+<hr />
+<h2 id="images">Images</h2>
+<p>An image without any text:</p>
+<p class="centerAlign"><img src="images/leonardo-da-vinci.png" alt="" /></p><p>An image with just an alternative text:</p>
+<p class="centerAlign"><img src="images/leonardo-da-vinci.png" alt="Image alt" /></p><p>An image with alternative text and 1-atom caption:</p>
+<p class="centerAlign"><img src="images/leonardo-da-vinci.png" alt="Image alt" /></p><p class="figCaption">Image caption</p>
+<p>An image with alternative text and 2-atom caption:</p>
+<p class="centerAlign"><img src="images/leonardo-da-vinci.png" alt="Image alt" /></p><p class="figCaption">Image caption with <b>bold</b> text</p>
+<p>A bordered image:</p>
+<div class="border"><p class="centerAlign"><img src="images/leonardo-da-vinci.png" alt="" /></p></div><p>A bordered image with a caption:</p>
+<div class="border"><p class="centerAlign"><img src="images/leonardo-da-vinci.png" alt="" /></p></div><p class="figCaption">Screenshot of the System Tray Icon</p>
+<p>An inline image:</p>
+<p>The is a paragraph containing an <img src="images/01.png" alt="" /> inline image to test if qdoc handles them properly, without considering rest of the line as alt text for the image.</p>
+<p>An inline image with alt text:</p>
+<p>Here is another example of <img src="images/01.png" alt="No. 1" /> inline image with alternative text, which should be added as an attribute to the inline image.</p>
+<p>File quoting:</p>
+<pre class="cpp" translate="no"> <span class="keyword">if</span> (<span class="keyword">false</span>) {
+ <span class="keyword">return</span> <span class="number">1</span>;
+ }</pre>
+<h2 id="commands-not-yet-tested">Commands not yet tested</h2>
+<div class="admonition warning">
+<p><b>Warning: </b>The following commands have yet to be tested: footnote link sincelist header index topicref // or just don’t care, remove it inlineimage printline printto quotefile skipline skipuntil span snippet codeline overload sub sup tableofcontents tt uicontrol endmapref endomit underline unicode</p>
+</div>
+</div>
+<!-- @@@qdoctests-qdocfileoutput-exhaustive.html -->
+</body>
+</html>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/outputfromqdocfiles/expected/html/qdoctests-qdocfileoutput-linking.html b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/outputfromqdocfiles/expected/html/qdoctests-qdocfileoutput-linking.html
new file mode 100644
index 000000000..b4d63e76e
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/outputfromqdocfiles/expected/html/qdoctests-qdocfileoutput-linking.html
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+<!-- qdoctests-outputfromqdocfiles.qdoc -->
+ <meta name="description" content="This is a page for testing QDoc's link command.">
+ <title>Testing QDoc's link command | OutputFromQDocFiles</title>
+</head>
+<body>
+<li id="buildversion">OutputFromQDocFiles - A test project for QDoc build artifacts</li>
+ <link rel="prev" href="qdoctests-qdocfileoutput.html" />
+ <link rel="next" href="toc.html" />
+<p class="naviNextPrevious headerNavi">
+<a class="prevPage" href="qdoctests-qdocfileoutput.html">QDoc Testing</a>
+<a class="nextPage" href="toc.html">Table of Contents</a>
+</p>
+<div class="sidebar">
+<div class="toc">
+<h3 id="toc">Contents</h3>
+<ul>
+<li class="level1"><a href="#link-targets">Link targets</a></li>
+</ul>
+</div>
+<div class="sidebar-content" id="sidebar-content"></div></div>
+<h1 class="title">Testing QDoc's link command</h1>
+<!-- $$$qdoctests-qdocfileoutput-linking.html-description -->
+<div class="descr" id="details">
+<span id="link-test-target"></span><h2 id="link-targets">Link targets</h2>
+<p>Valid parameters for the link command (<code translate="no">\l</code>) are page and section titles, targets defined with \target or \keyword commands, and API reference keywords (types, methods, namespaces, and so on).</p>
+</div>
+<!-- @@@qdoctests-qdocfileoutput-linking.html -->
+<p class="naviNextPrevious footerNavi">
+<a class="prevPage" href="qdoctests-qdocfileoutput.html">QDoc Testing</a>
+<a class="nextPage" href="toc.html">Table of Contents</a>
+</p>
+</body>
+</html>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/outputfromqdocfiles/expected/html/qdoctests-qdocfileoutput.html b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/outputfromqdocfiles/expected/html/qdoctests-qdocfileoutput.html
new file mode 100644
index 000000000..a38d4e3b4
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/outputfromqdocfiles/expected/html/qdoctests-qdocfileoutput.html
@@ -0,0 +1,70 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+<!-- qdoctests-outputfromqdocfiles.qdoc -->
+ <meta name="description" content="This is a simple page for testing purposes only.">
+ <title>Testing QDoc output from .qdoc files | OutputFromQDocFiles</title>
+</head>
+<body>
+<li id="buildversion">OutputFromQDocFiles - A test project for QDoc build artifacts</li>
+ <link rel="next" href="qdoctests-qdocfileoutput-linking.html" />
+<p class="naviNextPrevious headerNavi">
+<a class="nextPage" href="qdoctests-qdocfileoutput-linking.html">QDoc Linking Test</a>
+</p>
+<div class="sidebar">
+<div class="toc">
+<h3 id="toc">Contents</h3>
+<ul>
+<li class="level1"><a href="#supported-file-types">Supported file types</a></li>
+<li class="level1"><a href="#further-information">Further information</a></li>
+<li class="level1"><a href="#linking">Linking</a></li>
+<li class="level1"><a href="#qdoc-linking-test">QDoc Linking Test</a></li>
+<li class="level1"><a href="#linking-to-something-in-a-section-title">Linking to something in a section title</a></li>
+</ul>
+</div>
+<div class="sidebar-content" id="sidebar-content"></div></div>
+<h1 class="title">Testing QDoc output from .qdoc files</h1>
+<!-- $$$qdoctests-qdocfileoutput.html-description -->
+<div class="descr" id="details">
+<p>QDoc generates documentation for software projects. It does this by extracting <i>QDoc comments</i> from project source files. QDoc comments are signified by a C-style-like comment tag followed by an exclamation point, like this: <code translate="no">/*!</code> <code translate="no">This text is contained within QDoc comment tags.</code> <code translate="no">*/</code>.</p>
+<h2 id="supported-file-types">Supported file types</h2>
+<p>QDoc parses <code translate="no">.cpp</code> and <code translate="no">.qdoc</code> files. It does extract comments from header (<code translate="no">.h</code>) files.</p>
+<h2 id="further-information">Further information</h2>
+<p>This test document is written with the purpose of testing the output QDoc generates when parsing <code translate="no">.qdoc</code> files. It is fairly simple and makes use of a limited subset of QDoc's command. Those commands are:</p>
+<ul>
+<li><code translate="no">\page</code></li>
+<li><code translate="no">\title</code></li>
+<li><code translate="no">\brief</code></li>
+<li><code translate="no">\e</code> (for emphasizing &quot;QDoc comments&quot;)</li>
+<li><code translate="no">\c</code> (for multiple monospace-formatted entries)</li>
+<li><code translate="no">\section1</code></li>
+<li><code translate="no">\list</code></li>
+<li><code translate="no">\li</code></li>
+<li><code translate="no">\endlist</code></li>
+</ul>
+<h2 id="linking">Linking</h2>
+<p>There are multiple ways to create hyperlinks to other topics:</p>
+<ul>
+<li><a href="qdoctests-qdocfileoutput-linking.html">Linking to a page title</a></li>
+<li><a href="qdoctests-qdocfileoutput-linking.html#link-targets">Linking to a section title</a></li>
+<li><a href="qdoctests-qdocfileoutput-linking.html#link-test-target">Linking using a \target string</a></li>
+<li><a href="qdoctests-qdocfileoutput-linking.html">Linking using a \keyword string</a></li>
+</ul>
+<h2 id="qdoc-linking-test">QDoc Linking Test</h2>
+<p>This section title is overridden by another target which takes precedence.</p>
+<h2 id="linking-to-something-in-a-section-title">Linking to <a href="qdoctests-qdocfileoutput.html#further-information">something</a> in a section title</h2>
+<p>This is allowed but a questionable practice.</p>
+<details>
+<summary>QDoc details</summary>
+<div class="admonition note">
+<p><b>Note: </b>You're looking at detailed information.</p>
+</div>
+</details>
+</div>
+<!-- @@@qdoctests-qdocfileoutput.html -->
+<p class="naviNextPrevious footerNavi">
+<a class="nextPage" href="qdoctests-qdocfileoutput-linking.html">QDoc Linking Test</a>
+</p>
+</body>
+</html>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/outputfromqdocfiles/expected/html/qdoctests-qdocmanuallikefileoutput.html b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/outputfromqdocfiles/expected/html/qdoctests-qdocmanuallikefileoutput.html
new file mode 100644
index 000000000..e4b120bd0
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/outputfromqdocfiles/expected/html/qdoctests-qdocmanuallikefileoutput.html
@@ -0,0 +1,63 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+<!-- qdoctests-outputfromqdocmanuallikefiles.qdoc -->
+ <title>Document Navigation | OutputFromQDocFiles</title>
+</head>
+<body>
+<li id="buildversion">OutputFromQDocFiles - A test project for QDoc build artifacts</li>
+<div class="sidebar">
+<div class="toc">
+<h3 id="toc">Contents</h3>
+<ul>
+<li class="level1"><a href="#commands">Commands</a></li>
+<li class="level2"><a href="#previouspage">\previouspage</a></li>
+</ul>
+</div>
+<div class="sidebar-content" id="sidebar-content"></div></div>
+<h1 class="title">Document Navigation</h1>
+<!-- $$$qdoctests-qdocmanuallikefileoutput.html-description -->
+<div class="descr" id="details">
+<p>The navigation commands...</p>
+<blockquote> <table border="0" cellpadding="0" cellspacing="5" width="100%">
+
+ <tr>
+ <p>
+ [Previous: <a href="15-qdoc-commands-navigation.html#deadlink">
+ Basic Qt</a>]
+ [<a href="15-qdoc-commands-navigation.html#deadlink">Contents</a>]
+ [Next: <a href="15-qdoc-commands-navigation.html#deadlink">
+ Creating Dialogs</a>]
+ </p>
+
+ <h1 align="center">Getting Started<br /></h1>
+
+ <p>
+ This chapter shows how to combine basic C++ with the
+ functionality provided by Qt to create a few small graphical
+ interface (GUI) applications.
+ </p>
+
+ <p>
+ [Previous: <a href="15-qdoc-commands-navigation.html#deadlink">
+ Basic Qt</a>]
+ [<a href="15-qdoc-commands-navigation.html#deadlink">Contents</a>]
+ [Next: <a href="15-qdoc-commands-navigation.html#deadlink">
+ Creating Dialogs</a>]
+ </p>
+
+ </table>
+ </blockquote>
+<pre class="cpp" translate="no"><span class="operator">&lt;</span>head<span class="operator">&gt;</span>
+ <span class="operator">.</span><span class="operator">.</span><span class="operator">.</span>
+ <span class="operator">&lt;</span>link rel<span class="operator">=</span><span class="string">&quot;start&quot;</span> href<span class="operator">=</span><span class="string">&quot;basicqt.html&quot;</span> <span class="operator">/</span><span class="operator">&gt;</span>
+ <span class="operator">.</span><span class="operator">.</span><span class="operator">.</span>
+<span class="operator">&lt;</span><span class="operator">/</span>head<span class="operator">&gt;</span></pre>
+<h2 id="commands">Commands</h2>
+<span id="previouspage-command"></span><h3 id="previouspage">\previouspage</h3>
+<p>The \previouspage command...</p>
+</div>
+<!-- @@@qdoctests-qdocmanuallikefileoutput.html -->
+</body>
+</html>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/outputfromqdocfiles/expected/html/toc.html b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/outputfromqdocfiles/expected/html/toc.html
new file mode 100644
index 000000000..f34ce0db6
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/outputfromqdocfiles/expected/html/toc.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+<!-- qdoctests-outputfromqdocfiles.qdoc -->
+ <title>Table of Contents | OutputFromQDocFiles</title>
+</head>
+<body>
+<li id="buildversion">OutputFromQDocFiles - A test project for QDoc build artifacts</li>
+ <link rel="prev" href="qdoctests-qdocfileoutput-linking.html" />
+<p class="naviNextPrevious headerNavi">
+<a class="prevPage" href="qdoctests-qdocfileoutput-linking.html">QDoc Linking Test</a>
+</p>
+<div class="sidebar"><div class="sidebar-content" id="sidebar-content"></div></div>
+<h1 class="title">Table of Contents</h1>
+<!-- $$$toc.html-description -->
+<div class="descr" id="details">
+<ul>
+<li><a href="qdoctests-qdocfileoutput.html">QDoc Testing</a></li>
+<li><a href="qdoctests-qdocfileoutput-linking.html">QDoc Linking Test</a></li>
+<li><a href="toc.html">Table of Contents</a></li>
+</ul>
+</div>
+<!-- @@@toc.html -->
+<p class="naviNextPrevious footerNavi">
+<a class="prevPage" href="qdoctests-qdocfileoutput-linking.html">QDoc Linking Test</a>
+</p>
+</body>
+</html>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/outputfromqdocfiles/expected/webxml/crash.webxml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/outputfromqdocfiles/expected/webxml/crash.webxml
new file mode 100644
index 000000000..38c761f7d
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/outputfromqdocfiles/expected/webxml/crash.webxml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<WebXML>
+ <document>
+ <page name="crash.html" href="crash.html" status="active" location="qdoctests-outputfromqdocfiles.qdoc" documented="true" subtype="page" title="" fulltitle="" subtitle="">
+ <description>
+ <para></para>
+ </description>
+ </page>
+ </document>
+</WebXML>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/outputfromqdocfiles/expected/webxml/images/01.png b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/outputfromqdocfiles/expected/webxml/images/01.png
new file mode 100644
index 000000000..d73ab969b
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/outputfromqdocfiles/expected/webxml/images/01.png
Binary files differ
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/outputfromqdocfiles/expected/webxml/images/leonardo-da-vinci.png b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/outputfromqdocfiles/expected/webxml/images/leonardo-da-vinci.png
new file mode 100644
index 000000000..854acb4ca
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/outputfromqdocfiles/expected/webxml/images/leonardo-da-vinci.png
Binary files differ
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/outputfromqdocfiles/expected/webxml/outputfromqdocfiles.index b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/outputfromqdocfiles/expected/webxml/outputfromqdocfiles.index
new file mode 100644
index 000000000..eb63b02c2
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/outputfromqdocfiles/expected/webxml/outputfromqdocfiles.index
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE QDOCINDEX>
+<INDEX url="" title="A test project for QDoc build artifacts" version="" project="OutputFromQDocFiles">
+ <namespace name="" status="active" access="public" module="outputfromqdocfiles">
+ <page name="qdoctests-qdocmanuallikefileoutput.html" href="qdoctests-qdocmanuallikefileoutput.html" status="active" location="qdoctests-outputfromqdocmanuallikefiles.qdoc" documented="true" subtype="page" title="Document Navigation" fulltitle="Document Navigation" subtitle="">
+ <target name="previouspage-command"/>
+ <contents name="commands" title="Commands" level="1"/>
+ <contents name="previouspage" title="\previouspage" level="2"/>
+ </page>
+ <page name="qdoctests-qdocfileoutput-exhaustive.html" href="qdoctests-qdocfileoutput-exhaustive.html" status="active" location="qdoctests-outputfromqdocfiles.qdoc" documented="true" subtype="page" title="Exhaustive testing of QDoc commands" fulltitle="Exhaustive testing of QDoc commands" subtitle="" brief="This page is a dumping ground for QDoc commands under test">
+ <contents name="this-is-a-section1" title="This is a section1" level="1"/>
+ <contents name="this-is-a-section2" title="This is a section2" level="2"/>
+ <contents name="this-is-a-section3" title="This is a section3" level="3"/>
+ <contents name="this-is-a-section4" title="This is a section4" level="4"/>
+ <contents name="images" title="Images" level="1"/>
+ <contents name="commands-not-yet-tested" title="Commands not yet tested" level="1"/>
+ </page>
+ <page name="toc.html" href="toc.html" status="active" location="qdoctests-outputfromqdocfiles.qdoc" documented="true" subtype="page" title="Table of Contents" fulltitle="Table of Contents" subtitle=""/>
+ <page name="qdoctests-qdocfileoutput.html" href="qdoctests-qdocfileoutput.html" status="active" location="qdoctests-outputfromqdocfiles.qdoc" documented="true" subtype="page" title="Testing QDoc output from .qdoc files" fulltitle="Testing QDoc output from .qdoc files" subtitle="" brief="This is a simple page for testing purposes only">
+ <contents name="supported-file-types" title="Supported file types" level="1"/>
+ <contents name="further-information" title="Further information" level="1"/>
+ <contents name="linking" title="Linking" level="1"/>
+ <contents name="qdoc-linking-test" title="QDoc Linking Test" level="1"/>
+ <contents name="linking-to-something-in-a-section-title" title="Linking to something in a section title" level="1"/>
+ </page>
+ <page name="qdoctests-qdocfileoutput-linking.html" href="qdoctests-qdocfileoutput-linking.html" status="active" location="qdoctests-outputfromqdocfiles.qdoc" documented="true" subtype="page" title="Testing QDoc's link command" fulltitle="Testing QDoc's link command" subtitle="" brief="This is a page for testing QDoc's link command">
+ <target name="link-test-target"/>
+ <keyword name="qdoc-linking-test" title="QDoc Linking Test"/>
+ <contents name="link-targets" title="Link targets" level="1"/>
+ </page>
+ <page name="crash.html" href="crash.html" status="active" location="qdoctests-outputfromqdocfiles.qdoc" documented="true" subtype="page" title="" fulltitle="" subtitle=""/>
+ </namespace>
+</INDEX>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/outputfromqdocfiles/expected/webxml/qdoctests-qdocfileoutput-exhaustive.webxml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/outputfromqdocfiles/expected/webxml/qdoctests-qdocfileoutput-exhaustive.webxml
new file mode 100644
index 000000000..05d560a7e
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/outputfromqdocfiles/expected/webxml/qdoctests-qdocfileoutput-exhaustive.webxml
@@ -0,0 +1,90 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<WebXML>
+ <document>
+ <page name="qdoctests-qdocfileoutput-exhaustive.html" href="qdoctests-qdocfileoutput-exhaustive.html" status="active" location="qdoctests-outputfromqdocfiles.qdoc" documented="true" subtype="page" title="Exhaustive testing of QDoc commands" fulltitle="Exhaustive testing of QDoc commands" subtitle="" brief="This page is a dumping ground for QDoc commands under test">
+ <contents name="this-is-a-section1" title="This is a section1" level="1"/>
+ <contents name="this-is-a-section2" title="This is a section2" level="2"/>
+ <contents name="this-is-a-section3" title="This is a section3" level="3"/>
+ <contents name="this-is-a-section4" title="This is a section4" level="4"/>
+ <contents name="images" title="Images" level="1"/>
+ <contents name="commands-not-yet-tested" title="Commands not yet tested" level="1"/>
+ <description>
+ <brief>This page is a dumping ground for QDoc commands under test.</brief>
+ <section id="this-is-a-section1">
+ <heading level="1">This is a section1</heading>
+ </section>
+ <section id="this-is-a-section2">
+ <heading level="2">This is a section2</heading>
+ </section>
+ <section id="this-is-a-section3">
+ <heading level="3">This is a section3</heading>
+ </section>
+ <section id="this-is-a-section4">
+ <heading level="4">This is a section4</heading>
+ </section>
+ </description>
+ </page>
+ </document>
+ <badcode>This is bad code</badcode>
+ <para>This text should have a line break riiiiight noooow.</para>
+ <para>
+ <bold>All your text belong to bold</bold> ...And this is an examble of only <bold>bold</bold> being, well, bold.</para>
+ <dots indent="4">...</dots>
+ <para>This a caption</para>
+ <legalese>
+ <para>Lorem legal ipsum</para>
+ </legalese>
+ <quote>
+ <para>This is a quotation.</para>
+ </quote>
+ <raw format="HTML"> &lt;html&gt;&lt;body&gt;This is &lt;b&gt;raw&lt;/b&gt;. Like the &lt;h1&gt;Eddie Murphy&lt;/h1&gt; movie. Just not as funny.&lt;/body&gt;&lt;/html&gt;
+ </raw>
+ <para>Look, ma! I made a sidebar!</para>
+ <table>
+ <row>
+ <item>
+ <para>Table item in a table row</para>
+ </item>
+ </row>
+ <row>
+ <item>
+ <para>Another item in a different row</para>
+ </item>
+ </row>
+ </table>
+ <para>
+ <bold>Important:</bold> This is really important.</para>
+ <para>
+ <bold>Note:</bold> The code above doesn't compile</para>
+ <section id="images">
+ <heading level="1">Images</heading>
+ <para>An image without any text:</para>
+ <image href="images/leonardo-da-vinci.png"/>
+ <para>An image with just an alternative text:</para>
+ <image href="images/leonardo-da-vinci.png"/>
+ <para>An image with alternative text and 1-atom caption:</para>
+ <image href="images/leonardo-da-vinci.png"/>
+ <para>Image caption</para>
+ <para>An image with alternative text and 2-atom caption:</para>
+ <image href="images/leonardo-da-vinci.png"/>
+ <para>Image caption with <bold>bold</bold> text</para>
+ <para>A bordered image:</para>
+ <image href="images/leonardo-da-vinci.png"/>
+ <para>A bordered image with a caption:</para>
+ <image href="images/leonardo-da-vinci.png"/>
+ <para>Screenshot of the System Tray Icon</para>
+ <para>An inline image:</para>
+ <para>The is a paragraph containing an <inlineimage href="images/01.png"/> inline image to test if qdoc handles them properly, without considering rest of the line as alt text for the image.</para>
+ <para>An inline image with alt text:</para>
+ <para>Here is another example of <inlineimage href="images/01.png"/> inline image with alternative text, which should be added as an attribute to the inline image.</para>
+ <para>File quoting:</para>
+ <quotefromfile>main.cpp</quotefromfile>
+ <skipto>/if \(/</skipto>
+ <printuntil>/^ \}/</printuntil>
+ </section>
+ <section id="commands-not-yet-tested">
+ <heading level="1">Commands not yet tested</heading>
+ <para>
+ <bold>Warning:</bold> The following commands have yet to be tested: footnote link sincelist header index topicref // or just don’t care, remove it inlineimage printline printto quotefile skipline skipuntil span snippet codeline overload sub sup tableofcontents tt uicontrol endmapref endomit underline unicode</para>
+ </section>
+</WebXML>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/outputfromqdocfiles/expected/webxml/qdoctests-qdocfileoutput-linking.webxml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/outputfromqdocfiles/expected/webxml/qdoctests-qdocfileoutput-linking.webxml
new file mode 100644
index 000000000..1bb19e10e
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/outputfromqdocfiles/expected/webxml/qdoctests-qdocfileoutput-linking.webxml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<WebXML>
+ <document>
+ <page name="qdoctests-qdocfileoutput-linking.html" href="qdoctests-qdocfileoutput-linking.html" status="active" location="qdoctests-outputfromqdocfiles.qdoc" documented="true" subtype="page" title="Testing QDoc's link command" fulltitle="Testing QDoc's link command" subtitle="" brief="This is a page for testing QDoc's link command">
+ <target name="link-test-target"/>
+ <keyword name="qdoc-linking-test" title="QDoc Linking Test"/>
+ <contents name="link-targets" title="Link targets" level="1"/>
+ <description>
+ <relation href="toc.html" type="page" meta="next" description="Table of Contents"/>
+ <relation href="qdoctests-qdocfileoutput.html" type="page" meta="previous" description="Testing QDoc output from .qdoc files"/>
+ <brief>This is a page for testing QDoc's link command.</brief>
+ <target name="link-test-target"/>
+ <section id="link-targets">
+ <heading level="1">Link targets</heading>
+ <para>Valid parameters for the link command (<teletype type="highlighted">\l</teletype>) are page and section titles, targets defined with \target or \keyword commands, and API reference keywords (types, methods, namespaces, and so on).</para>
+ </section>
+ </description>
+ </page>
+ </document>
+</WebXML>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/outputfromqdocfiles/expected/webxml/qdoctests-qdocfileoutput.webxml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/outputfromqdocfiles/expected/webxml/qdoctests-qdocfileoutput.webxml
new file mode 100644
index 000000000..13b3e5c51
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/outputfromqdocfiles/expected/webxml/qdoctests-qdocfileoutput.webxml
@@ -0,0 +1,95 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<WebXML>
+ <document>
+ <page name="qdoctests-qdocfileoutput.html" href="qdoctests-qdocfileoutput.html" status="active" location="qdoctests-outputfromqdocfiles.qdoc" documented="true" subtype="page" title="Testing QDoc output from .qdoc files" fulltitle="Testing QDoc output from .qdoc files" subtitle="" brief="This is a simple page for testing purposes only">
+ <contents name="supported-file-types" title="Supported file types" level="1"/>
+ <contents name="further-information" title="Further information" level="1"/>
+ <contents name="linking" title="Linking" level="1"/>
+ <contents name="qdoc-linking-test" title="QDoc Linking Test" level="1"/>
+ <contents name="linking-to-something-in-a-section-title" title="Linking to something in a section title" level="1"/>
+ <description>
+ <relation href="qdoctests-qdocfileoutput-linking.html" type="page" meta="next" description="Testing QDoc's link command"/>
+ <brief>This is a simple page for testing purposes only.</brief>
+ <para>QDoc generates documentation for software projects. It does this by extracting <italic>QDoc comments</italic> from project source files. QDoc comments are signified by a C-style-like comment tag followed by an exclamation point, like this: <teletype type="highlighted">/*!</teletype> <teletype type="highlighted">This text is contained within QDoc comment tags.</teletype> <teletype type="highlighted">*/</teletype>.</para>
+ <section id="supported-file-types">
+ <heading level="1">Supported file types</heading>
+ <para>QDoc parses <teletype type="highlighted">.cpp</teletype> and <teletype type="highlighted">.qdoc</teletype> files. It does extract comments from header (<teletype type="highlighted">.h</teletype>) files.</para>
+ </section>
+ <section id="further-information">
+ <heading level="1">Further information</heading>
+ <para>This test document is written with the purpose of testing the output QDoc generates when parsing <teletype type="highlighted">.qdoc</teletype> files. It is fairly simple and makes use of a limited subset of QDoc's command. Those commands are:</para>
+ <list type="bullet">
+ <item>
+ <para>
+ <teletype type="highlighted">\page</teletype></para>
+ </item>
+ <item>
+ <para>
+ <teletype type="highlighted">\title</teletype></para>
+ </item>
+ <item>
+ <para>
+ <teletype type="highlighted">\brief</teletype></para>
+ </item>
+ <item>
+ <para>
+ <teletype type="highlighted">\e</teletype> (for emphasizing &quot;QDoc comments&quot;)</para>
+ </item>
+ <item>
+ <para>
+ <teletype type="highlighted">\c</teletype> (for multiple monospace-formatted entries)</para>
+ </item>
+ <item>
+ <para>
+ <teletype type="highlighted">\section1</teletype></para>
+ </item>
+ <item>
+ <para>
+ <teletype type="highlighted">\list</teletype></para>
+ </item>
+ <item>
+ <para>
+ <teletype type="highlighted">\li</teletype></para>
+ </item>
+ <item>
+ <para>
+ <teletype type="highlighted">\endlist</teletype></para>
+ </item>
+ </list>
+ </section>
+ <section id="linking">
+ <heading level="1">Linking</heading>
+ <para>There are multiple ways to create hyperlinks to other topics:</para>
+ <list type="bullet">
+ <item>
+ <para>
+ <link raw="Testing QDoc's link command" href="qdoctests-qdocfileoutput-linking.html" type="page" page="Testing QDoc's link command">Linking to a page title</link></para>
+ </item>
+ <item>
+ <para>
+ <link raw="Link targets" href="qdoctests-qdocfileoutput-linking.html#link-targets" type="page" page="Testing QDoc's link command">Linking to a section title</link></para>
+ </item>
+ <item>
+ <para>
+ <link raw="link-test-target" href="qdoctests-qdocfileoutput-linking.html#link-test-target" type="page" page="Testing QDoc's link command">Linking using a \target string</link></para>
+ </item>
+ <item>
+ <para>
+ <link raw="QDoc Linking Test" href="qdoctests-qdocfileoutput-linking.html" type="page" page="Testing QDoc's link command">Linking using a \keyword string</link></para>
+ </item>
+ </list>
+ </section>
+ <section id="qdoc-linking-test">
+ <heading level="1">QDoc Linking Test</heading>
+ <para>This section title is overridden by another target which takes precedence.</para>
+ </section>
+ <section id="linking-to-something-in-a-section-title">
+ <heading level="1">Linking to <link raw="Further information" href="qdoctests-qdocfileoutput.html#further-information" type="page" page="Testing QDoc output from .qdoc files">something</link> in a section title</heading>
+ <para>This is allowed but a questionable practice.</para>
+ <para>
+ <bold>Note:</bold> You're looking at detailed information.</para>
+ </section>
+ </description>
+ </page>
+ </document>
+</WebXML>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/outputfromqdocfiles/expected/webxml/qdoctests-qdocmanuallikefileoutput.webxml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/outputfromqdocfiles/expected/webxml/qdoctests-qdocmanuallikefileoutput.webxml
new file mode 100644
index 000000000..4502dcf7f
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/outputfromqdocfiles/expected/webxml/qdoctests-qdocmanuallikefileoutput.webxml
@@ -0,0 +1,57 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<WebXML>
+ <document>
+ <page name="qdoctests-qdocmanuallikefileoutput.html" href="qdoctests-qdocmanuallikefileoutput.html" status="active" location="qdoctests-outputfromqdocmanuallikefiles.qdoc" documented="true" subtype="page" title="Document Navigation" fulltitle="Document Navigation" subtitle="">
+ <target name="previouspage-command"/>
+ <contents name="commands" title="Commands" level="1"/>
+ <contents name="previouspage" title="\previouspage" level="2"/>
+ <description>
+ <para>The navigation commands...</para>
+ <quote>
+ <raw format="HTML"> &lt;table border=&quot;0&quot; cellpadding=&quot;0&quot; cellspacing=&quot;5&quot; width=&quot;100%&quot;&gt;
+
+ &lt;tr&gt;
+ &lt;p&gt;
+ [Previous: &lt;a href=&quot;15-qdoc-commands-navigation.html#deadlink&quot;&gt;
+ Basic Qt&lt;/a&gt;]
+ [&lt;a href=&quot;15-qdoc-commands-navigation.html#deadlink&quot;&gt;Contents&lt;/a&gt;]
+ [Next: &lt;a href=&quot;15-qdoc-commands-navigation.html#deadlink&quot;&gt;
+ Creating Dialogs&lt;/a&gt;]
+ &lt;/p&gt;
+
+ &lt;h1 align=&quot;center&quot;&gt;Getting Started&lt;br /&gt;&lt;/h1&gt;
+
+ &lt;p&gt;
+ This chapter shows how to combine basic C++ with the
+ functionality provided by Qt to create a few small graphical
+ interface (GUI) applications.
+ &lt;/p&gt;
+
+ &lt;p&gt;
+ [Previous: &lt;a href=&quot;15-qdoc-commands-navigation.html#deadlink&quot;&gt;
+ Basic Qt&lt;/a&gt;]
+ [&lt;a href=&quot;15-qdoc-commands-navigation.html#deadlink&quot;&gt;Contents&lt;/a&gt;]
+ [Next: &lt;a href=&quot;15-qdoc-commands-navigation.html#deadlink&quot;&gt;
+ Creating Dialogs&lt;/a&gt;]
+ &lt;/p&gt;
+
+ &lt;/table&gt;
+ </raw>
+ </quote>
+ <code>&lt;head&gt;
+ ...
+ &lt;link rel=&quot;start&quot; href=&quot;basicqt.html&quot; /&gt;
+ ...
+&lt;/head&gt;</code>
+ <section id="commands">
+ <heading level="1">Commands</heading>
+ <target name="previouspage-command"/>
+ </section>
+ <section id="previouspage">
+ <heading level="2">\previouspage</heading>
+ <para>The \previouspage command...</para>
+ </section>
+ </description>
+ </page>
+ </document>
+</WebXML>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/outputfromqdocfiles/expected/webxml/toc.webxml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/outputfromqdocfiles/expected/webxml/toc.webxml
new file mode 100644
index 000000000..bef07db18
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/outputfromqdocfiles/expected/webxml/toc.webxml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<WebXML>
+ <document>
+ <page name="toc.html" href="toc.html" status="active" location="qdoctests-outputfromqdocfiles.qdoc" documented="true" subtype="page" title="Table of Contents" fulltitle="Table of Contents" subtitle="">
+ <description>
+ <relation href="qdoctests-qdocfileoutput-linking.html" type="page" meta="previous" description="Testing QDoc's link command"/>
+ <list type="bullet">
+ <item>
+ <para>
+ <link raw="Testing QDoc output from .qdoc files" href="qdoctests-qdocfileoutput.html" type="page" page="Testing QDoc output from .qdoc files">QDoc Testing</link></para>
+ </item>
+ <item>
+ <para>
+ <link raw="QDoc Linking Test" href="qdoctests-qdocfileoutput-linking.html" type="page" page="Testing QDoc's link command">QDoc Linking Test</link></para>
+ </item>
+ <item>
+ <para>
+ <link raw="Table of Contents" href="toc.html" type="page" page="Table of Contents">Table of Contents</link></para>
+ </item>
+ </list>
+ </description>
+ </page>
+ </document>
+</WebXML>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/outputfromqdocfiles/outputfromqdocfiles.qdocconf b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/outputfromqdocfiles/outputfromqdocfiles.qdocconf
new file mode 100644
index 000000000..8a053de44
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/outputfromqdocfiles/outputfromqdocfiles.qdocconf
@@ -0,0 +1,43 @@
+project = OutputFromQDocFiles
+description = "A test project for QDoc build artifacts"
+buildversion = "$project - $description"
+moduleheader =
+
+sources.fileextensions = "*.qml *.cpp *.qdoc"
+headers.fileextensions = "*.h"
+
+# images
+imagedirs = ./src/images
+
+# zero warning policy -- here; allow one which is (qdoc) warning: Can't link to ''
+warninglimit = 1
+warninglimit.enabled = true
+
+# don't write host system-specific paths to index files
+locationinfo = false
+
+outputformats = HTML WebXML DocBook
+HTML.nosubdirs = true
+HTML.outputsubdir = html
+WebXML.quotinginformation = true
+WebXML.nosubdirs = true
+WebXML.outputsubdir = webxml
+DocBook.nosubdirs = true
+DocBook.outputsubdir = docbook
+DocBook.usedocbookextensions = true
+
+sources = ./src/qdoctests-outputfromqdocfiles.qdoc \
+ ./src/qdoctests-outputfromqdocmanuallikefiles.qdoc
+
+exampledirs = ./src/snippets
+
+macro.beginqdoc = "\\c {/*!}"
+macro.endqdoc = "\\c */"
+macro.PROD = QDoc
+
+# Macro from qtbase/doc/global/macros.qdocconf
+# The file cannot be included directly, because it requires many
+# variables to be set, like QT_VER
+macro.borderedimage = "\\div {class=\"border\"} \\image \1\n\\enddiv"
+
+defines = test_navigation
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/outputfromqdocfiles/src/images/01.png b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/outputfromqdocfiles/src/images/01.png
new file mode 100644
index 000000000..d73ab969b
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/outputfromqdocfiles/src/images/01.png
Binary files differ
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/outputfromqdocfiles/src/images/leonardo-da-vinci.png b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/outputfromqdocfiles/src/images/leonardo-da-vinci.png
new file mode 100644
index 000000000..854acb4ca
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/outputfromqdocfiles/src/images/leonardo-da-vinci.png
Binary files differ
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/outputfromqdocfiles/src/qdoctests-outputfromqdocfiles.qdoc b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/outputfromqdocfiles/src/qdoctests-outputfromqdocfiles.qdoc
new file mode 100644
index 000000000..b19905b7e
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/outputfromqdocfiles/src/qdoctests-outputfromqdocfiles.qdoc
@@ -0,0 +1,241 @@
+// Copyright (C) 2020 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+
+/*!
+\if defined(test_navigation)
+ \nextpage {qdoctests-qdocfileoutput-linking.html}{QDoc Linking Test}
+\endif
+
+ \page qdoctests-qdocfileoutput.html
+ \title Testing \PROD output from .qdoc files
+ \brief This is a simple page for testing purposes only.
+
+ QDoc generates documentation for software projects. It does this by
+ extracting \e {QDoc comments} from project source files. QDoc comments are
+ signified by a C-style-like comment tag followed by an exclamation point,
+ like this:
+ \beginqdoc \c {This text is contained within QDoc comment tags.} \endqdoc.
+
+ \section1 Supported file types
+ QDoc parses \c .cpp and \c .qdoc files. It does extract comments from
+ header (\c {.h}) files.
+
+ \section1 Further information
+ This test document is written with the purpose of testing the output QDoc
+ generates when parsing \c .qdoc files. It is fairly simple and makes use of
+ a limited subset of QDoc's command. Those commands are:
+ \list
+ \li \c {\page}
+ \li \c {\title}
+ \li \c {\brief}
+ \li \c {\e} (for emphasizing "QDoc comments")
+ \li \c {\c} (for multiple monospace-formatted entries)
+ \li \c {\section1}
+ \li \c {\list}
+ \li \c {\li}
+ \li \c {\endlist}
+ \endlist
+
+ \section1 Linking
+
+ There are multiple ways to create hyperlinks to other topics:
+
+ \list
+ \li \l {Testing QDoc's link command}{Linking to a page title}
+ \li \l {Link targets}{Linking to a section title}
+ \li \l {link-test-target}{Linking using a \\target string}
+ \li \l {QDoc Linking Test}{Linking using a \\keyword string}
+ \endlist
+
+ \section1 QDoc Linking Test
+
+ This section title is overridden by another target which takes
+ precedence.
+
+ \section1 Linking to \l {Further information}{something} in a section title
+
+ This is allowed but a questionable practice.
+
+ \details {\PROD details}
+ \note You're looking at detailed information.
+ \enddetails
+*/
+
+/*!
+\if defined(test_navigation)
+ \previouspage qdoctests-qdocfileoutput.html \PROD Testing
+ \nextpage Table of Contents
+\endif
+
+ \keyword QDoc Linking Test
+ \page qdoctests-qdocfileoutput-linking.html
+ \title Testing QDoc's link command
+ \brief This is a page for testing QDoc's link command.
+
+ \target link-test-target
+ \section1 Link targets
+
+ Valid parameters for the link command (\c {\l}) are page and section
+ titles, targets defined with \\target or \\keyword commands, and API
+ reference keywords (types, methods, namespaces, and so on).
+*/
+
+/*!
+\if defined(test_navigation)
+ \previouspage {Testing QDoc's link command}{QDoc Linking Test}
+\endif
+
+ \page toc.html
+ \title Table of Contents
+
+ \list
+ \li \l {Testing \PROD output from .qdoc files}{\PROD Testing}
+ \li \l {QDoc Linking Test}
+ \li \l {Table of Contents}
+ \endlist
+*/
+
+/*!
+ \page qdoctests-qdocfileoutput-exhaustive.html
+ \title Exhaustive testing of QDoc commands
+ \brief This page is a dumping ground for QDoc commands under test.
+
+ \section1 This is a section1
+ \section2 This is a section2
+ \section3 This is a section3
+ \section4 This is a section4
+ \endsection4
+ \endsection3
+ \endsection2
+ \endsection1
+
+ \badcode
+ This is bad code
+ \endcode
+
+ This text should have a line break riiiiight \br noooow.
+
+ \b{All your text belong to bold}
+ ...And this is an examble of only \b bold being, well, bold.
+
+ \dots
+
+ \caption This a caption
+
+ \legalese
+ Lorem legal ipsum
+ \endlegalese
+
+ \quotation
+ This is a quotation.
+ \endquotation
+
+ \raw HTML
+ <html><body>This is <b>raw</b>. Like the <h1>Eddie Murphy</h1> movie. Just not as funny.</body></html>
+ \endraw
+
+ \sidebar
+ Look, ma! I made a sidebar!
+ \endsidebar
+
+ \table
+ \row \li Table item in a table row
+ \row \li Another item in a different row
+ \endtable
+
+ \important This is really important.
+
+ \note The code above doesn't compile
+
+ \hr
+
+ \section1 Images
+
+ An image without any text:
+
+ \image leonardo-da-vinci.png
+
+ An image with just an alternative text:
+
+ \image leonardo-da-vinci.png Image alt
+
+ An image with alternative text and 1-atom caption:
+
+ \image leonardo-da-vinci.png Image alt
+ \caption Image caption
+
+ An image with alternative text and 2-atom caption:
+
+ \image leonardo-da-vinci.png Image alt
+ \caption Image caption with \b {bold} text
+
+ A bordered image:
+
+ \borderedimage leonardo-da-vinci.png
+
+ //! A bordered image with alternative text:
+ //!
+ //! \borderedimage leonardo-da-vinci.png Screenshot of the Drill Down Example
+ //! It looks like this macro is not written to handle alternative text (no \2)
+
+ A bordered image with a caption:
+
+ \borderedimage leonardo-da-vinci.png
+ \caption Screenshot of the System Tray Icon
+
+ An inline image:
+
+ The is a paragraph containing an \inlineimage 01.png inline image to test
+ if qdoc handles them properly, without considering rest of the line as
+ alt text for the image.
+
+ An inline image with alt text:
+
+ Here is another example of \inlineimage 01.png {No. 1} inline image with
+ alternative text, which should be added as an attribute to the inline
+ image.
+
+ File quoting:
+
+ \quotefromfile main.cpp
+ \skipto /if \(/
+ \printuntil /^ \}/
+
+ \section1 Commands not yet tested
+
+ \warning The following commands have yet to be tested:
+ footnote
+ link
+ //! Check why above two (when used in this order) cause missing linefeeds on Windows/webxml
+ sincelist
+ header
+ index
+ topicref // or just don’t care, remove it
+ inlineimage
+ printline
+ printto
+ quotefile
+ skipline
+ skipuntil
+ span
+ snippet
+ codeline
+ overload
+ sub
+ sup
+ tableofcontents
+ tt
+ uicontrol
+ endmapref
+ endomit
+ underline
+ unicode
+
+*/
+
+// Empty link target that was known to assert
+/*!
+ \page crash.html
+
+ \l {}
+*/
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/outputfromqdocfiles/src/qdoctests-outputfromqdocmanuallikefiles.qdoc b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/outputfromqdocfiles/src/qdoctests-outputfromqdocmanuallikefiles.qdoc
new file mode 100644
index 000000000..23f229745
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/outputfromqdocfiles/src/qdoctests-outputfromqdocmanuallikefiles.qdoc
@@ -0,0 +1,59 @@
+// Copyright (C) 2022 Thibaut Cuvelier
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// Excerpts from src/qdoc/doc/qdoc-guide.qdoc
+
+/*!
+ \page qdoctests-qdocmanuallikefileoutput.html
+
+ \title Document Navigation
+
+ The navigation commands...
+
+ \quotation
+ \raw HTML
+ <table border="0" cellpadding="0" cellspacing="5" width="100%">
+
+ <tr>
+ <p>
+ [Previous: <a href="15-qdoc-commands-navigation.html#deadlink">
+ Basic Qt</a>]
+ [<a href="15-qdoc-commands-navigation.html#deadlink">Contents</a>]
+ [Next: <a href="15-qdoc-commands-navigation.html#deadlink">
+ Creating Dialogs</a>]
+ </p>
+
+ <h1 align="center">Getting Started<br /></h1>
+
+ <p>
+ This chapter shows how to combine basic C++ with the
+ functionality provided by Qt to create a few small graphical
+ interface (GUI) applications.
+ </p>
+
+ <p>
+ [Previous: <a href="15-qdoc-commands-navigation.html#deadlink">
+ Basic Qt</a>]
+ [<a href="15-qdoc-commands-navigation.html#deadlink">Contents</a>]
+ [Next: <a href="15-qdoc-commands-navigation.html#deadlink">
+ Creating Dialogs</a>]
+ </p>
+
+ </table>
+ \endraw
+ \endquotation
+
+ \code
+ <head>
+ ...
+ <link rel="start" href="basicqt.html" />
+ ...
+ </head>
+ \endcode
+
+ \section1 Commands
+
+ \target previouspage-command
+ \section2 \\previouspage
+
+ The \\previouspage command...
+*/ \ No newline at end of file
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/outputfromqdocfiles/src/snippets/main.cpp b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/outputfromqdocfiles/src/snippets/main.cpp
new file mode 100644
index 000000000..1c886ace4
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/outputfromqdocfiles/src/snippets/main.cpp
@@ -0,0 +1,10 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+int main()
+{
+ if (false) {
+ return 1;
+ }
+ return 0;
+}
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/proxypage/expected/docbook/stdpair-proxypage-proxy.xml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/proxypage/expected/docbook/stdpair-proxypage-proxy.xml
new file mode 100644
index 000000000..5476d7a21
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/proxypage/expected/docbook/stdpair-proxypage-proxy.xml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<db:article xmlns:db="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" version="5.2" xml:lang="en">
+<db:info>
+<db:title>StdPair</db:title>
+<db:productname>ProxyPage</db:productname>
+<db:titleabbrev>ProxyPage Reference Documentation</db:titleabbrev>
+<db:abstract>
+<db:para>ProxyPage Reference Documentation.</db:para></db:abstract>
+</db:info>
+<db:section xml:id="type-documentation">
+<db:title>Type Documentation</db:title>
+<db:section xml:id="StdPair-typedef">
+<db:title>[alias] template &lt;typename T1, typename T2&gt; StdPair</db:title>
+</db:section>
+</db:section>
+</db:article>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/proxypage/expected/html/proxypage.index b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/proxypage/expected/html/proxypage.index
new file mode 100644
index 000000000..96a5a3d55
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/proxypage/expected/html/proxypage.index
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE QDOCINDEX>
+<INDEX url="" title="ProxyPage Reference Documentation" version="" project="ProxyPage">
+ <namespace name="" status="active" access="public" module="proxypage">
+ <typedef name="StdPair" href="stdpair-proxypage-proxy.html#StdPair-typedef" status="active" access="public" location="proxy.h" related="0" documented="true" aliasedtype="std::pair&lt;T1, T2&gt;"/>
+ <proxy name="StdPair" href="stdpair-proxypage-proxy.html" status="active" access="public">
+ <typedef name="StdPair" href="stdpair-proxypage-proxy.html#StdPair-typedef" status="active" access="public" location="proxy.h" related="0" documented="true" aliasedtype="std::pair&lt;T1, T2&gt;"/>
+ </proxy>
+ </namespace>
+</INDEX>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/proxypage/expected/html/stdpair-proxypage-proxy.html b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/proxypage/expected/html/stdpair-proxypage-proxy.html
new file mode 100644
index 000000000..25241ddcb
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/proxypage/expected/html/stdpair-proxypage-proxy.html
@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+ <title>StdPair Proxy Page | ProxyPage</title>
+</head>
+<body>
+<h1 class="title">StdPair Proxy Page</h1>
+<h2 id="types">Types</h2>
+<div class="table"><table class="alignedsummary" translate="no">
+<tr><td class="memItemLeft rightAlign topAlign"> </td><td class="memItemRight bottomAlign"><b><a href="stdpair-proxypage-proxy.html#StdPair-typedef" translate="no">StdPair</a></b></td></tr>
+</table></div>
+<div class="types">
+<h2>Type Documentation</h2>
+<!-- $$$StdPair -->
+<h3 class="fn" translate="no" id="StdPair-typedef"><code class="details extra" translate="no">[alias]</code> template &lt;typename T1, typename T2&gt; <span class="name">StdPair</span></h3>
+<!-- @@@StdPair -->
+</div>
+</body>
+</html>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/proxypage/proxypage.qdocconf b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/proxypage/proxypage.qdocconf
new file mode 100644
index 000000000..2f686a44c
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/proxypage/proxypage.qdocconf
@@ -0,0 +1,24 @@
+sources.fileextensions = "*.qml *.cpp *.qdoc"
+headers.fileextensions = "*.h"
+
+# images
+imagedirs = ./src/images
+
+# zero warning policy
+warninglimit = 0
+warninglimit.enabled = true
+
+# don't write host system-specific paths to index files
+locationinfo = false
+
+project = ProxyPage
+includepaths += ./src
+{sourcedirs,headerdirs} = ./src
+
+moduleheader = proxy.h
+
+outputformats = HTML DocBook
+{HTML.nosubdirs,DocBook.nosubdirs} = true
+HTML.outputsubdir = html
+DocBook.outputsubdir = docbook
+
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/proxypage/src/proxy.h b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/proxypage/src/proxy.h
new file mode 100644
index 000000000..05a17f826
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/proxypage/src/proxy.h
@@ -0,0 +1,11 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+#pragma once
+
+// dummy declaration
+namespace std {
+ template<class T1, class T2> struct pair;
+}
+
+template <class T1, class T2>
+using StdPair = std::pair<T1, T2>;
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/proxypage/src/proxy.qdoc b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/proxypage/src/proxy.qdoc
new file mode 100644
index 000000000..43bf8a013
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/proxypage/src/proxy.qdoc
@@ -0,0 +1,9 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+/*!
+ //! Misuse of QDoc commands that results in the generation of an orphaned
+ //! proxy page; Relate the topic command to itself.
+ \typealias StdPair
+ \relates StdPair
+*/
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/qml_nativetype_synopsis/expected/docbook/cppcar.xml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/qml_nativetype_synopsis/expected/docbook/cppcar.xml
new file mode 100644
index 000000000..0780ec93b
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/qml_nativetype_synopsis/expected/docbook/cppcar.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<db:article xmlns:db="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" version="5.2" xml:lang="en">
+<db:info>
+<db:title>CppCar Class</db:title>
+<db:productname>QML nativetype synopsis test</db:productname>
+<db:titleabbrev>QML nativetype synopsis test Reference Documentation</db:titleabbrev>
+<db:abstract>
+<db:para>A test class with an inventive name.</db:para>
+</db:abstract>
+</db:info>
+<db:variablelist>
+<db:varlistentry>
+<db:term>Header</db:term>
+<db:listitem>
+<db:para>CppCar</db:para>
+</db:listitem>
+</db:varlistentry>
+<db:varlistentry>
+<db:term>In QML</db:term>
+<db:listitem>
+<db:para><db:link xlink:href="qml-qmlnativetypesynopsis-car.xml" xlink:role="">Car</db:link> and <db:link xlink:href="qml-qmlnativetypesynopsis-truck.xml" xlink:role="">Truck</db:link></db:para>
+</db:listitem>
+</db:varlistentry>
+</db:variablelist>
+<db:section xml:id="details">
+<db:title>Detailed Description</db:title>
+</db:section>
+</db:article>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/qml_nativetype_synopsis/expected/docbook/qml-qmlnativetypesynopsis-car.xml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/qml_nativetype_synopsis/expected/docbook/qml-qmlnativetypesynopsis-car.xml
new file mode 100644
index 000000000..196c705a4
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/qml_nativetype_synopsis/expected/docbook/qml-qmlnativetypesynopsis-car.xml
@@ -0,0 +1,66 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<db:article xmlns:db="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" version="5.2" xml:lang="en">
+<db:info>
+<db:title>Car QML Type</db:title>
+<db:productname>QML nativetype synopsis test</db:productname>
+<db:titleabbrev>QML nativetype synopsis test Reference Documentation</db:titleabbrev>
+<db:abstract>
+<db:para>A car.</db:para>
+<db:para>This type was introduced in Qt 6.8.</db:para>
+</db:abstract>
+</db:info>
+<db:variablelist>
+<db:varlistentry>
+<db:term>Import Statement</db:term>
+<db:listitem>
+<db:para>import QmlNativeTypeSynopsis</db:para>
+</db:listitem>
+</db:varlistentry>
+<db:varlistentry>
+<db:term>Since:</db:term>
+<db:listitem>
+<db:para>Qt 6.8</db:para>
+</db:listitem>
+</db:varlistentry>
+<db:varlistentry>
+<db:term>In C++:</db:term>
+<db:listitem>
+<db:para><db:link xlink:href="cppcar.xml">CppCar</db:link></db:para>
+</db:listitem>
+</db:varlistentry>
+<db:varlistentry>
+<db:term>Group</db:term>
+<db:listitem>
+<db:para>Car is part of <db:simplelist><db:member>qml_nativetype_synopsis</db:member></db:simplelist>
+</db:para>
+</db:listitem>
+</db:varlistentry>
+</db:variablelist>
+<db:section xml:id="details">
+<db:title>Detailed Description</db:title>
+<db:section xml:id="properties">
+<db:title>Properties</db:title>
+<db:itemizedlist>
+<db:listitem>
+<db:para><db:code>color</db:code> The color of the car.</db:para>
+</db:listitem>
+</db:itemizedlist>
+</db:section>
+<db:section xml:id="signals">
+<db:title>Signals</db:title>
+<db:itemizedlist>
+<db:listitem>
+<db:para><db:code>engineStarted()</db:code> Emitted when the engine is started.</db:para>
+</db:listitem>
+</db:itemizedlist>
+</db:section>
+<db:section xml:id="methods">
+<db:title>Methods</db:title>
+<db:itemizedlist>
+<db:listitem>
+<db:para><db:code>startEngine()</db:code> Starts the engine.</db:para>
+</db:listitem>
+</db:itemizedlist>
+</db:section>
+</db:section>
+</db:article>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/qml_nativetype_synopsis/expected/docbook/qml-qmlnativetypesynopsis-truck.xml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/qml_nativetype_synopsis/expected/docbook/qml-qmlnativetypesynopsis-truck.xml
new file mode 100644
index 000000000..c4203a54d
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/qml_nativetype_synopsis/expected/docbook/qml-qmlnativetypesynopsis-truck.xml
@@ -0,0 +1,66 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<db:article xmlns:db="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" version="5.2" xml:lang="en">
+<db:info>
+<db:title>Truck QML Type</db:title>
+<db:productname>QML nativetype synopsis test</db:productname>
+<db:titleabbrev>QML nativetype synopsis test Reference Documentation</db:titleabbrev>
+<db:abstract>
+<db:para>A big car.</db:para>
+<db:para>This type was introduced in Qt 6.8.</db:para>
+</db:abstract>
+</db:info>
+<db:variablelist>
+<db:varlistentry>
+<db:term>Import Statement</db:term>
+<db:listitem>
+<db:para>import QmlNativeTypeSynopsis</db:para>
+</db:listitem>
+</db:varlistentry>
+<db:varlistentry>
+<db:term>Since:</db:term>
+<db:listitem>
+<db:para>Qt 6.8</db:para>
+</db:listitem>
+</db:varlistentry>
+<db:varlistentry>
+<db:term>In C++:</db:term>
+<db:listitem>
+<db:para><db:link xlink:href="cppcar.xml">CppCar</db:link></db:para>
+</db:listitem>
+</db:varlistentry>
+<db:varlistentry>
+<db:term>Group</db:term>
+<db:listitem>
+<db:para>Truck is part of <db:simplelist><db:member>qml_nativetype_synopsis</db:member></db:simplelist>
+</db:para>
+</db:listitem>
+</db:varlistentry>
+</db:variablelist>
+<db:section xml:id="details">
+<db:title>Detailed Description</db:title>
+<db:section xml:id="properties">
+<db:title>Properties</db:title>
+<db:itemizedlist>
+<db:listitem>
+<db:para><db:code>color</db:code> The color of the big car.</db:para>
+</db:listitem>
+</db:itemizedlist>
+</db:section>
+<db:section xml:id="signals">
+<db:title>Signals</db:title>
+<db:itemizedlist>
+<db:listitem>
+<db:para><db:code>engineStarted()</db:code> Emitted when the big engine is started, along with more emissions than from a <db:link xlink:href="qml-qmlnativetypesynopsis-car.xml">Car</db:link>.</db:para>
+</db:listitem>
+</db:itemizedlist>
+</db:section>
+<db:section xml:id="methods">
+<db:title>Methods</db:title>
+<db:itemizedlist>
+<db:listitem>
+<db:para><db:code>startEngine()</db:code> Starts the big engine.</db:para>
+</db:listitem>
+</db:itemizedlist>
+</db:section>
+</db:section>
+</db:article>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/qml_nativetype_synopsis/expected/docbook/qmlnativetypesynopsis-qmlmodule.xml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/qml_nativetype_synopsis/expected/docbook/qmlnativetypesynopsis-qmlmodule.xml
new file mode 100644
index 000000000..b00c11267
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/qml_nativetype_synopsis/expected/docbook/qmlnativetypesynopsis-qmlmodule.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<db:article xmlns:db="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" version="5.2" xml:lang="en">
+<db:info>
+<db:title></db:title>
+<db:productname>QML nativetype synopsis test</db:productname>
+<db:titleabbrev>QML nativetype synopsis test Reference Documentation</db:titleabbrev>
+<db:abstract>
+<db:para>QML nativetype synopsis test Reference Documentation.</db:para></db:abstract>
+</db:info>
+<db:anchor xml:id="details"/>
+<db:variablelist role="members">
+<db:varlistentry>
+<db:term><db:link xlink:href="qml-qmlnativetypesynopsis-car.xml" xlink:role="">Car</db:link></db:term>
+<db:listitem>
+<db:para>A car.</db:para>
+</db:listitem>
+</db:varlistentry>
+<db:varlistentry>
+<db:term><db:link xlink:href="qml-qmlnativetypesynopsis-truck.xml" xlink:role="">Truck</db:link></db:term>
+<db:listitem>
+<db:para>A big car.</db:para>
+</db:listitem>
+</db:varlistentry>
+</db:variablelist>
+</db:article>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/qml_nativetype_synopsis/expected/html/cppcar.html b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/qml_nativetype_synopsis/expected/html/cppcar.html
new file mode 100644
index 000000000..fcee3e277
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/qml_nativetype_synopsis/expected/html/cppcar.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+<!-- cppcar.cpp -->
+ <meta name="description" content="A test class with an inventive name.">
+ <title>CppCar Class | QML nativetype synopsis test</title>
+</head>
+<body>
+<li>CppCar</li>
+<div class="sidebar">
+<div class="toc">
+<h3 id="toc">Contents</h3>
+<ul>
+<li class="level1"><a href="#details">Detailed Description</a></li>
+</ul>
+</div>
+<div class="sidebar-content" id="sidebar-content"></div></div>
+<h1 class="title" translate="no">CppCar Class</h1>
+<!-- $$$CppCar-brief -->
+<p>A test class with an inventive name. <a href="#details">More...</a></p>
+<!-- @@@CppCar -->
+<div class="table"><table class="alignedsummary" translate="no">
+<tr><td class="memItemLeft rightAlign topAlign"> Header:</td><td class="memItemRight bottomAlign"> <span class="preprocessor">#include &lt;CppCar&gt;</span></td></tr>
+<tr><td class="memItemLeft rightAlign topAlign"> In QML:</td><td class="memItemRight bottomAlign"> <a href="qml-qmlnativetypesynopsis-car.html" translate="no">Car</a> and <a href="qml-qmlnativetypesynopsis-truck.html" translate="no">Truck</a></td></tr>
+</table></div>
+<!-- $$$CppCar-description -->
+<div class="descr">
+<h2 id="details">Detailed Description</h2>
+</div>
+<!-- @@@CppCar -->
+</body>
+</html>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/qml_nativetype_synopsis/expected/html/qml-nativetype-synopsis-test.index b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/qml_nativetype_synopsis/expected/html/qml-nativetype-synopsis-test.index
new file mode 100644
index 000000000..a34218849
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/qml_nativetype_synopsis/expected/html/qml-nativetype-synopsis-test.index
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE QDOCINDEX>
+<INDEX url="" title="QML nativetype synopsis test Reference Documentation" version="" project="QML nativetype synopsis test">
+ <namespace name="" status="active" access="public" module="qml nativetype synopsis test">
+ <qmlclass name="Car" qml-module-name="QmlNativeTypeSynopsis" fullname="QmlNativeTypeSynopsis.Car" href="qml-qmlnativetypesynopsis-car.html" status="active" access="public" since="6.8" documented="true" groups="qml_nativetype_synopsis" title="Car" fulltitle="Car" subtitle="" brief="A car">
+ <contents name="properties" title="Properties" level="1"/>
+ <contents name="signals" title="Signals" level="1"/>
+ <contents name="methods" title="Methods" level="1"/>
+ </qmlclass>
+ <class name="CppCar" href="cppcar.html" status="active" access="public" location="cppcar.h" documented="true" module="QmlNativeTypeSynopsis" brief="A test class with an inventive name"/>
+ <qmlclass name="Truck" qml-module-name="QmlNativeTypeSynopsis" fullname="QmlNativeTypeSynopsis.Truck" href="qml-qmlnativetypesynopsis-truck.html" status="active" access="public" since="6.8" documented="true" groups="qml_nativetype_synopsis" title="Truck" fulltitle="Truck" subtitle="" brief="A big car">
+ <contents name="properties" title="Properties" level="1"/>
+ <contents name="signals" title="Signals" level="1"/>
+ <contents name="methods" title="Methods" level="1"/>
+ </qmlclass>
+ <group name="qml_nativetype_synopsis" href="qml-nativetype-synopsis.html" status="internal" seen="false" title=""/>
+ <module name="QmlNativeTypeSynopsis" href="qmlnativetypesynopsis-module.html" status="internal" seen="false" title=""/>
+ <qmlmodule name="QmlNativeTypeSynopsis" qml-module-name="QmlNativeTypeSynopsis" href="qmlnativetypesynopsis-qmlmodule.html" status="active" documented="true" seen="true" title=""/>
+ </namespace>
+</INDEX>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/qml_nativetype_synopsis/expected/html/qml-qmlnativetypesynopsis-car-members.html b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/qml_nativetype_synopsis/expected/html/qml-qmlnativetypesynopsis-car-members.html
new file mode 100644
index 000000000..0b42d6968
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/qml_nativetype_synopsis/expected/html/qml-qmlnativetypesynopsis-car-members.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+<!-- cppcar.cpp -->
+ <meta name="description" content="A car.">
+ <title>List of All Members for Car | QML nativetype synopsis test</title>
+</head>
+<body>
+<li>Car</li>
+<div class="sidebar"><div class="sidebar-content" id="sidebar-content"></div></div>
+<h1 class="title" translate="no">List of All Members for Car</h1>
+<p>This is the complete list of members for <a href="qml-qmlnativetypesynopsis-car.html">Car</a>, including inherited members.</p>
+</body>
+</html>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/qml_nativetype_synopsis/expected/html/qml-qmlnativetypesynopsis-car.html b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/qml_nativetype_synopsis/expected/html/qml-qmlnativetypesynopsis-car.html
new file mode 100644
index 000000000..5a97c514d
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/qml_nativetype_synopsis/expected/html/qml-qmlnativetypesynopsis-car.html
@@ -0,0 +1,46 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+<!-- cppcar.cpp -->
+ <meta name="description" content="A car.">
+ <title>Car QML Type | QML nativetype synopsis test</title>
+</head>
+<body>
+<li>Car</li>
+<div class="sidebar">
+<div class="toc">
+<h3 id="toc">Contents</h3>
+<ul>
+<li class="level1"><a href="#details">Detailed Description</a></li>
+<li class="level2"><a href="#properties">Properties</a></li>
+<li class="level2"><a href="#signals">Signals</a></li>
+<li class="level2"><a href="#methods">Methods</a></li>
+</ul>
+</div>
+<div class="sidebar-content" id="sidebar-content"></div></div>
+<h1 class="title" translate="no">Car QML Type</h1>
+<!-- $$$Car-brief -->
+<p>A car. <a href="#details">More...</a></p>
+<!-- @@@Car -->
+<div class="table"><table class="alignedsummary" translate="no">
+<tr><td class="memItemLeft rightAlign topAlign"> Import Statement:</td><td class="memItemRight bottomAlign"> import QmlNativeTypeSynopsis</td></tr><tr><td class="memItemLeft rightAlign topAlign"> Since:</td><td class="memItemRight bottomAlign"> Qt 6.8</td></tr><tr><td class="memItemLeft rightAlign topAlign"> In C++:</td><td class="memItemRight bottomAlign"> <a href="cppcar.html" translate="no">CppCar</a></td></tr></table></div><ul>
+<li><a href="qml-qmlnativetypesynopsis-car-members.html">List of all members, including inherited members</a></li>
+</ul>
+<!-- $$$Car-description -->
+<h2 id="details">Detailed Description</h2>
+<h2 id="properties">Properties</h2>
+<ul>
+<li><code translate="no">color</code> The color of the car.</li>
+</ul>
+<h2 id="signals">Signals</h2>
+<ul>
+<li><code translate="no">engineStarted()</code> Emitted when the engine is started.</li>
+</ul>
+<h2 id="methods">Methods</h2>
+<ul>
+<li><code translate="no">startEngine()</code> Starts the engine.</li>
+</ul>
+<!-- @@@Car -->
+</body>
+</html>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/qml_nativetype_synopsis/expected/html/qml-qmlnativetypesynopsis-truck-members.html b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/qml_nativetype_synopsis/expected/html/qml-qmlnativetypesynopsis-truck-members.html
new file mode 100644
index 000000000..bd7527632
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/qml_nativetype_synopsis/expected/html/qml-qmlnativetypesynopsis-truck-members.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+<!-- cppcar.cpp -->
+ <meta name="description" content="A big car.">
+ <title>List of All Members for Truck | QML nativetype synopsis test</title>
+</head>
+<body>
+<li>Truck</li>
+<div class="sidebar"><div class="sidebar-content" id="sidebar-content"></div></div>
+<h1 class="title" translate="no">List of All Members for Truck</h1>
+<p>This is the complete list of members for <a href="qml-qmlnativetypesynopsis-truck.html">Truck</a>, including inherited members.</p>
+</body>
+</html>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/qml_nativetype_synopsis/expected/html/qml-qmlnativetypesynopsis-truck.html b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/qml_nativetype_synopsis/expected/html/qml-qmlnativetypesynopsis-truck.html
new file mode 100644
index 000000000..35d2954e2
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/qml_nativetype_synopsis/expected/html/qml-qmlnativetypesynopsis-truck.html
@@ -0,0 +1,46 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+<!-- cppcar.cpp -->
+ <meta name="description" content="A big car.">
+ <title>Truck QML Type | QML nativetype synopsis test</title>
+</head>
+<body>
+<li>Truck</li>
+<div class="sidebar">
+<div class="toc">
+<h3 id="toc">Contents</h3>
+<ul>
+<li class="level1"><a href="#details">Detailed Description</a></li>
+<li class="level2"><a href="#properties">Properties</a></li>
+<li class="level2"><a href="#signals">Signals</a></li>
+<li class="level2"><a href="#methods">Methods</a></li>
+</ul>
+</div>
+<div class="sidebar-content" id="sidebar-content"></div></div>
+<h1 class="title" translate="no">Truck QML Type</h1>
+<!-- $$$Truck-brief -->
+<p>A big car. <a href="#details">More...</a></p>
+<!-- @@@Truck -->
+<div class="table"><table class="alignedsummary" translate="no">
+<tr><td class="memItemLeft rightAlign topAlign"> Import Statement:</td><td class="memItemRight bottomAlign"> import QmlNativeTypeSynopsis</td></tr><tr><td class="memItemLeft rightAlign topAlign"> Since:</td><td class="memItemRight bottomAlign"> Qt 6.8</td></tr><tr><td class="memItemLeft rightAlign topAlign"> In C++:</td><td class="memItemRight bottomAlign"> <a href="cppcar.html" translate="no">CppCar</a></td></tr></table></div><ul>
+<li><a href="qml-qmlnativetypesynopsis-truck-members.html">List of all members, including inherited members</a></li>
+</ul>
+<!-- $$$Truck-description -->
+<h2 id="details">Detailed Description</h2>
+<h2 id="properties">Properties</h2>
+<ul>
+<li><code translate="no">color</code> The color of the big car.</li>
+</ul>
+<h2 id="signals">Signals</h2>
+<ul>
+<li><code translate="no">engineStarted()</code> Emitted when the big engine is started, along with more emissions than from a <a href="qml-qmlnativetypesynopsis-car.html" translate="no">Car</a>.</li>
+</ul>
+<h2 id="methods">Methods</h2>
+<ul>
+<li><code translate="no">startEngine()</code> Starts the big engine.</li>
+</ul>
+<!-- @@@Truck -->
+</body>
+</html>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/qml_nativetype_synopsis/expected/html/qmlnativetypesynopsis-qmlmodule.html b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/qml_nativetype_synopsis/expected/html/qmlnativetypesynopsis-qmlmodule.html
new file mode 100644
index 000000000..51b6bc311
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/qml_nativetype_synopsis/expected/html/qmlnativetypesynopsis-qmlmodule.html
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+<!-- cppcar.cpp -->
+ <title>QML nativetype synopsis test</title>
+</head>
+<body>
+<div class="sidebar"><div class="sidebar-content" id="sidebar-content"></div></div>
+<!-- $$$QmlNativeTypeSynopsis-description -->
+<div class="descr" id="details">
+</div>
+<!-- @@@QmlNativeTypeSynopsis -->
+<div class="table"><table class="annotated">
+<tr class="odd topAlign"><td class="tblName" translate="no"><p><a href="qml-qmlnativetypesynopsis-car.html">Car</a></p></td><td class="tblDescr"><p>A car</p></td></tr>
+<tr class="even topAlign"><td class="tblName" translate="no"><p><a href="qml-qmlnativetypesynopsis-truck.html">Truck</a></p></td><td class="tblDescr"><p>A big car</p></td></tr>
+</table></div>
+</body>
+</html>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/qml_nativetype_synopsis/expected/webxml/cppcar.webxml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/qml_nativetype_synopsis/expected/webxml/cppcar.webxml
new file mode 100644
index 000000000..4ff09cfa1
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/qml_nativetype_synopsis/expected/webxml/cppcar.webxml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<WebXML>
+ <document>
+ <class name="CppCar" href="cppcar.html" status="active" access="public" location="cppcar.h" documented="true" module="QmlNativeTypeSynopsis" brief="A test class with an inventive name">
+ <description>
+ <brief>A test class with an inventive name.</brief>
+ </description>
+ </class>
+ </document>
+</WebXML>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/qml_nativetype_synopsis/expected/webxml/qml-nativetype-synopsis-test.index b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/qml_nativetype_synopsis/expected/webxml/qml-nativetype-synopsis-test.index
new file mode 100644
index 000000000..a34218849
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/qml_nativetype_synopsis/expected/webxml/qml-nativetype-synopsis-test.index
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE QDOCINDEX>
+<INDEX url="" title="QML nativetype synopsis test Reference Documentation" version="" project="QML nativetype synopsis test">
+ <namespace name="" status="active" access="public" module="qml nativetype synopsis test">
+ <qmlclass name="Car" qml-module-name="QmlNativeTypeSynopsis" fullname="QmlNativeTypeSynopsis.Car" href="qml-qmlnativetypesynopsis-car.html" status="active" access="public" since="6.8" documented="true" groups="qml_nativetype_synopsis" title="Car" fulltitle="Car" subtitle="" brief="A car">
+ <contents name="properties" title="Properties" level="1"/>
+ <contents name="signals" title="Signals" level="1"/>
+ <contents name="methods" title="Methods" level="1"/>
+ </qmlclass>
+ <class name="CppCar" href="cppcar.html" status="active" access="public" location="cppcar.h" documented="true" module="QmlNativeTypeSynopsis" brief="A test class with an inventive name"/>
+ <qmlclass name="Truck" qml-module-name="QmlNativeTypeSynopsis" fullname="QmlNativeTypeSynopsis.Truck" href="qml-qmlnativetypesynopsis-truck.html" status="active" access="public" since="6.8" documented="true" groups="qml_nativetype_synopsis" title="Truck" fulltitle="Truck" subtitle="" brief="A big car">
+ <contents name="properties" title="Properties" level="1"/>
+ <contents name="signals" title="Signals" level="1"/>
+ <contents name="methods" title="Methods" level="1"/>
+ </qmlclass>
+ <group name="qml_nativetype_synopsis" href="qml-nativetype-synopsis.html" status="internal" seen="false" title=""/>
+ <module name="QmlNativeTypeSynopsis" href="qmlnativetypesynopsis-module.html" status="internal" seen="false" title=""/>
+ <qmlmodule name="QmlNativeTypeSynopsis" qml-module-name="QmlNativeTypeSynopsis" href="qmlnativetypesynopsis-qmlmodule.html" status="active" documented="true" seen="true" title=""/>
+ </namespace>
+</INDEX>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/qml_nativetype_synopsis/expected/webxml/qmlnativetypesynopsis-qmlmodule.webxml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/qml_nativetype_synopsis/expected/webxml/qmlnativetypesynopsis-qmlmodule.webxml
new file mode 100644
index 000000000..5d24b3077
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/qml_nativetype_synopsis/expected/webxml/qmlnativetypesynopsis-qmlmodule.webxml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<WebXML>
+ <document/>
+</WebXML>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/qml_nativetype_synopsis/qml_nativetype_synopsis.qdocconf b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/qml_nativetype_synopsis/qml_nativetype_synopsis.qdocconf
new file mode 100644
index 000000000..522746eb6
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/qml_nativetype_synopsis/qml_nativetype_synopsis.qdocconf
@@ -0,0 +1,23 @@
+project = QML nativetype synopsis test
+
+locationinfo = false
+
+headers.fileextensions = "*.h *.hpp"
+sources.fileextensions = "*.cpp *.qml *.qdoc"
+
+headerdirs = ./src
+sourcedirs = ./src
+
+warninglimit = 0
+warninglimit.enabled = true
+
+outputformats = WebXML HTML DocBook
+WebXML.quotinginformation = true
+WebXML.nosubdirs = true
+WebXML.outputsubdir = webxml
+
+HTML.nosubdirs = true
+HTML.outputsubdir = html
+
+DocBook.nosubdirs = true
+DocBook.outputsubdir = docbook
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/qml_nativetype_synopsis/src/cppcar.cpp b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/qml_nativetype_synopsis/src/cppcar.cpp
new file mode 100644
index 000000000..451ec6010
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/qml_nativetype_synopsis/src/cppcar.cpp
@@ -0,0 +1,68 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "cppcar.h"
+
+/*!
+ \qmlmodule QmlNativeTypeSynopsis
+ */
+
+/*!
+ \qmltype Car
+ \nativetype CppCar
+ \inqmlmodule QmlNativeTypeSynopsis
+ \ingroup qml_nativetype_synopsis
+ \since 6.8
+ \brief A car.
+
+ \section1 Properties
+
+ \list
+ \li \c{color} The color of the car.
+ \endlist
+
+ \section1 Signals
+
+ \list
+ \li \c{engineStarted()} Emitted when the engine is started.
+ \endlist
+
+ \section1 Methods
+
+ \list
+ \li \c{startEngine()} Starts the engine.
+ \endlist
+ */
+
+/*!
+ \class CppCar
+ \inmodule QmlNativeTypeSynopsis
+ \brief A test class with an inventive name.
+ */
+
+/*!
+ \qmltype Truck
+ \nativetype CppCar
+ \inqmlmodule QmlNativeTypeSynopsis
+ \ingroup qml_nativetype_synopsis
+ \since 6.8
+ \brief A big car.
+
+ \section1 Properties
+
+ \list
+ \li \c{color} The color of the big car.
+ \endlist
+
+ \section1 Signals
+
+ \list
+ \li \c{engineStarted()} Emitted when the big engine is started, along with more emissions than from a \l Car.
+ \endlist
+
+ \section1 Methods
+
+ \list
+ \li \c{startEngine()} Starts the big engine.
+ \endlist
+ */
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/qml_nativetype_synopsis/src/cppcar.h b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/qml_nativetype_synopsis/src/cppcar.h
new file mode 100644
index 000000000..dce5afb32
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/qml_nativetype_synopsis/src/cppcar.h
@@ -0,0 +1,10 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#ifndef CAR_H
+#define CAR_H
+
+class CppCar {
+};
+
+#endif // CAR_H
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/qmlenumvaluesfromcpp/expected/docbook/class.xml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/qmlenumvaluesfromcpp/expected/docbook/class.xml
new file mode 100644
index 000000000..d0bf4bac1
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/qmlenumvaluesfromcpp/expected/docbook/class.xml
@@ -0,0 +1,136 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<db:article xmlns:db="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:its="http://www.w3.org/2005/11/its" version="5.2" xml:lang="en">
+<db:info>
+<db:title its:translate="no">Class Class</db:title>
+<db:productname>QmlEnumValuesFromCpp</db:productname>
+<db:titleabbrev>QmlEnumValuesFromCpp Reference Documentation</db:titleabbrev>
+<db:abstract>
+<db:para>QmlEnumValuesFromCpp Reference Documentation.</db:para></db:abstract>
+</db:info>
+<db:variablelist its:translate="no">
+<db:varlistentry>
+<db:term>Header</db:term>
+<db:listitem>
+<db:para>Class</db:para>
+</db:listitem>
+</db:varlistentry>
+</db:variablelist>
+<db:section xml:id="details">
+<db:title>Detailed Description</db:title>
+</db:section>
+<db:section xml:id="member-type-documentation">
+<db:title>Member Type Documentation</db:title>
+<db:section xml:id="MoreValues-enum">
+<db:title its:translate="no">enum Class::MoreValues</db:title>
+<db:enumsynopsis>
+<db:enumname>MoreValues</db:enumname>
+<db:enumitem>
+<db:enumidentifier>Something</db:enumidentifier>
+<db:enumvalue>0</db:enumvalue>
+</db:enumitem>
+<db:enumitem>
+<db:enumidentifier>Else</db:enumidentifier>
+<db:enumvalue>1</db:enumvalue>
+</db:enumitem>
+<db:synopsisinfo role="access">public</db:synopsisinfo>
+<db:synopsisinfo role="status">active</db:synopsisinfo>
+<db:synopsisinfo role="threadsafeness">unspecified</db:synopsisinfo>
+</db:enumsynopsis>
+<db:informaltable>
+<db:thead>
+<db:tr>
+<db:th>Constant</db:th>
+<db:th>Value</db:th>
+<db:th>Description</db:th>
+</db:tr>
+</db:thead>
+<db:tr>
+<db:td>
+<db:para its:translate="no"><db:code><db:emphasis role="bold"><db:link xlink:href="class.xml">Class</db:link></db:emphasis>::<db:emphasis role="bold"><db:link xlink:href="">MoreValues</db:link></db:emphasis>::Something</db:code></db:para>
+</db:td>
+<db:td><db:code its:translate="no">0</db:code></db:td>
+<db:td>
+<db:para>something</db:para>
+</db:td>
+</db:tr>
+<db:tr>
+<db:td>
+<db:para its:translate="no"><db:code><db:emphasis role="bold"><db:link xlink:href="class.xml">Class</db:link></db:emphasis>::<db:emphasis role="bold"><db:link xlink:href="">MoreValues</db:link></db:emphasis>::Else</db:code></db:para>
+</db:td>
+<db:td><db:code its:translate="no">1</db:code></db:td>
+<db:td>
+<db:para>entirely</db:para>
+</db:td>
+</db:tr>
+</db:informaltable>
+</db:section>
+<db:section xml:id="Values-enum">
+<db:title its:translate="no">enum Class::Values</db:title>
+<db:enumsynopsis>
+<db:enumname>Values</db:enumname>
+<db:enumitem>
+<db:enumidentifier>One</db:enumidentifier>
+<db:enumvalue>0</db:enumvalue>
+</db:enumitem>
+<db:enumitem>
+<db:enumidentifier>Two</db:enumidentifier>
+<db:enumvalue>1</db:enumvalue>
+</db:enumitem>
+<db:enumitem>
+<db:enumidentifier>Three</db:enumidentifier>
+<db:enumvalue>2</db:enumvalue>
+</db:enumitem>
+<db:synopsisinfo role="access">public</db:synopsisinfo>
+<db:synopsisinfo role="status">active</db:synopsisinfo>
+<db:synopsisinfo role="threadsafeness">unspecified</db:synopsisinfo>
+</db:enumsynopsis>
+<db:para>Try now these exciting values in your C++ code:</db:para>
+<db:informaltable>
+<db:thead>
+<db:tr>
+<db:th>Constant</db:th>
+<db:th>Value</db:th>
+<db:th>Description</db:th>
+</db:tr>
+</db:thead>
+<db:tr>
+<db:td>
+<db:para its:translate="no"><db:code><db:emphasis role="bold"><db:link xlink:href="class.xml">Class</db:link></db:emphasis>::One</db:code></db:para>
+</db:td>
+<db:td><db:code its:translate="no">0</db:code></db:td>
+<db:td>
+<db:para>One value</db:para>
+</db:td>
+</db:tr>
+</db:informaltable>
+<db:para>Wait, that's not all!</db:para>
+<db:informaltable>
+<db:thead>
+<db:tr>
+<db:th>Constant</db:th>
+<db:th>Value</db:th>
+<db:th>Description</db:th>
+</db:tr>
+</db:thead>
+<db:tr>
+<db:td>
+<db:para its:translate="no"><db:code><db:emphasis role="bold"><db:link xlink:href="class.xml">Class</db:link></db:emphasis>::Two</db:code></db:para>
+</db:td>
+<db:td><db:code its:translate="no">1</db:code></db:td>
+<db:td>
+<db:para>2nd value</db:para>
+</db:td>
+</db:tr>
+<db:tr>
+<db:td>
+<db:para its:translate="no"><db:code><db:emphasis role="bold"><db:link xlink:href="class.xml">Class</db:link></db:emphasis>::Three</db:code></db:para>
+</db:td>
+<db:td><db:code its:translate="no">2</db:code></db:td>
+<db:td>
+<db:para>3rd value</db:para>
+</db:td>
+</db:tr>
+</db:informaltable>
+</db:section>
+</db:section>
+</db:article>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/qmlenumvaluesfromcpp/expected/docbook/module-module.xml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/qmlenumvaluesfromcpp/expected/docbook/module-module.xml
new file mode 100644
index 000000000..972603249
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/qmlenumvaluesfromcpp/expected/docbook/module-module.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<db:article xmlns:db="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:its="http://www.w3.org/2005/11/its" version="5.2" xml:lang="en">
+<db:info>
+<db:title its:translate="no"></db:title>
+<db:productname>QmlEnumValuesFromCpp</db:productname>
+<db:titleabbrev>QmlEnumValuesFromCpp Reference Documentation</db:titleabbrev>
+<db:abstract>
+<db:para>QmlEnumValuesFromCpp Reference Documentation.</db:para></db:abstract>
+</db:info>
+<db:section xml:id="classes">
+<db:title>Classes</db:title>
+<db:itemizedlist role="classes">
+<db:listitem>
+<db:para><db:link xlink:href="class.xml" xlink:role="class">Class</db:link></db:para>
+</db:listitem>
+</db:itemizedlist>
+</db:section>
+</db:article>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/qmlenumvaluesfromcpp/expected/docbook/qml-qmlmodule-type.xml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/qmlenumvaluesfromcpp/expected/docbook/qml-qmlmodule-type.xml
new file mode 100644
index 000000000..e98a0ff68
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/qmlenumvaluesfromcpp/expected/docbook/qml-qmlmodule-type.xml
@@ -0,0 +1,103 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<db:article xmlns:db="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:its="http://www.w3.org/2005/11/its" version="5.2" xml:lang="en">
+<db:info>
+<db:title its:translate="no">Type QML Type</db:title>
+<db:productname>QmlEnumValuesFromCpp</db:productname>
+<db:titleabbrev>QmlEnumValuesFromCpp Reference Documentation</db:titleabbrev>
+<db:abstract>
+<db:para>QmlEnumValuesFromCpp Reference Documentation.</db:para></db:abstract>
+</db:info>
+<db:variablelist its:translate="no">
+<db:varlistentry>
+<db:term>Import Statement</db:term>
+<db:listitem>
+<db:para>import QmlModule</db:para>
+</db:listitem>
+</db:varlistentry>
+</db:variablelist>
+<db:section xml:id="details">
+<db:title>Detailed Description</db:title>
+</db:section>
+<db:section xml:id="property-documentation">
+<db:title>Property Documentation</db:title>
+<db:section xml:id="something-prop">
+<db:title>something : enumeration</db:title>
+<db:fieldsynopsis>
+<db:type>enumeration</db:type>
+<db:varname>something</db:varname>
+<db:modifier>writable</db:modifier>
+<db:synopsisinfo role="access">public</db:synopsisinfo>
+<db:synopsisinfo role="status">active</db:synopsisinfo>
+<db:synopsisinfo role="threadsafeness">unspecified</db:synopsisinfo>
+</db:fieldsynopsis>
+<db:informaltable>
+<db:thead>
+<db:tr>
+<db:th>Constant</db:th>
+<db:th>Description</db:th>
+</db:tr>
+</db:thead>
+<db:tr>
+<db:td>
+<db:para its:translate="no">SomeType.Something</db:para>
+</db:td>
+<db:td>
+<db:para>something</db:para>
+</db:td>
+</db:tr>
+<db:tr>
+<db:td>
+<db:para its:translate="no">SomeType.Else</db:para>
+</db:td>
+<db:td>
+<db:para>entirely</db:para>
+</db:td>
+</db:tr>
+</db:informaltable>
+</db:section>
+<db:section xml:id="values-prop">
+<db:title>values : enumeration</db:title>
+<db:fieldsynopsis>
+<db:type>enumeration</db:type>
+<db:varname>values</db:varname>
+<db:modifier>writable</db:modifier>
+<db:synopsisinfo role="access">public</db:synopsisinfo>
+<db:synopsisinfo role="status">active</db:synopsisinfo>
+<db:synopsisinfo role="threadsafeness">unspecified</db:synopsisinfo>
+</db:fieldsynopsis>
+<db:para>You can even use these values in QML.</db:para>
+<db:informaltable>
+<db:thead>
+<db:tr>
+<db:th>Constant</db:th>
+<db:th>Description</db:th>
+</db:tr>
+</db:thead>
+<db:tr>
+<db:td>
+<db:para its:translate="no">Type.One</db:para>
+</db:td>
+<db:td>
+<db:para>One value</db:para>
+</db:td>
+</db:tr>
+<db:tr>
+<db:td>
+<db:para its:translate="no">Type.Two</db:para>
+</db:td>
+<db:td>
+<db:para>2nd value</db:para>
+</db:td>
+</db:tr>
+<db:tr>
+<db:td>
+<db:para its:translate="no">Type.Three</db:para>
+</db:td>
+<db:td>
+<db:para>3rd value</db:para>
+</db:td>
+</db:tr>
+</db:informaltable>
+</db:section>
+</db:section>
+</db:article>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/qmlenumvaluesfromcpp/expected/docbook/qmlmodule-qmlmodule.xml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/qmlenumvaluesfromcpp/expected/docbook/qmlmodule-qmlmodule.xml
new file mode 100644
index 000000000..12466c566
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/qmlenumvaluesfromcpp/expected/docbook/qmlmodule-qmlmodule.xml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<db:article xmlns:db="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:its="http://www.w3.org/2005/11/its" version="5.2" xml:lang="en">
+<db:info>
+<db:title its:translate="no"></db:title>
+<db:productname>QmlEnumValuesFromCpp</db:productname>
+<db:titleabbrev>QmlEnumValuesFromCpp Reference Documentation</db:titleabbrev>
+<db:abstract>
+<db:para>QmlEnumValuesFromCpp Reference Documentation.</db:para></db:abstract>
+</db:info>
+<db:anchor xml:id="details"/>
+<db:itemizedlist role="members">
+<db:listitem>
+<db:para><db:link xlink:href="qml-qmlmodule-type.xml" xlink:role="">Type</db:link></db:para>
+</db:listitem>
+</db:itemizedlist>
+</db:article>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/qmlenumvaluesfromcpp/expected/html/class-members.html b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/qmlenumvaluesfromcpp/expected/html/class-members.html
new file mode 100644
index 000000000..8b5efb662
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/qmlenumvaluesfromcpp/expected/html/class-members.html
@@ -0,0 +1,18 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+<!-- class.cpp -->
+ <title>List of All Members for Class | QmlEnumValuesFromCpp</title>
+</head>
+<body>
+<li>Class</li>
+<div class="sidebar"><div class="sidebar-content" id="sidebar-content"></div></div>
+<h1 class="title" translate="no">List of All Members for Class</h1>
+<p>This is the complete list of members for <a href="class.html">Class</a>, including inherited members.</p>
+<ul>
+<li class="fn" translate="no">enum class <span class="name"><b><a href="class.html#MoreValues-enum" translate="no">MoreValues</a></b></span></li>
+<li class="fn" translate="no">enum <span class="name"><b><a href="class.html#Values-enum" translate="no">Values</a></b></span></li>
+</ul>
+</body>
+</html>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/qmlenumvaluesfromcpp/expected/html/class.html b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/qmlenumvaluesfromcpp/expected/html/class.html
new file mode 100644
index 000000000..e7d4ddc3d
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/qmlenumvaluesfromcpp/expected/html/class.html
@@ -0,0 +1,59 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+<!-- class.cpp -->
+ <title>Class Class | QmlEnumValuesFromCpp</title>
+</head>
+<body>
+<li>Class</li>
+<div class="sidebar">
+<div class="toc">
+<h3 id="toc">Contents</h3>
+<ul>
+<li class="level1"><a href="#public-types">Public Types</a></li>
+<li class="level1"><a href="#details">Detailed Description</a></li>
+</ul>
+</div>
+<div class="sidebar-content" id="sidebar-content"></div></div>
+<h1 class="title" translate="no">Class Class</h1>
+<div class="table"><table class="alignedsummary" translate="no">
+<tr><td class="memItemLeft rightAlign topAlign"> Header:</td><td class="memItemRight bottomAlign"> <span class="preprocessor">#include &lt;Class&gt;</span></td></tr>
+</table></div>
+<ul>
+<li><a href="class-members.html">List of all members, including inherited members</a></li>
+</ul>
+<h2 id="public-types">Public Types</h2>
+<div class="table"><table class="alignedsummary" translate="no">
+<tr><td class="memItemLeft rightAlign topAlign"> enum class </td><td class="memItemRight bottomAlign"><b><a href="class.html#MoreValues-enum" translate="no">MoreValues</a></b> { Something, Else }</td></tr>
+<tr><td class="memItemLeft rightAlign topAlign"> enum </td><td class="memItemRight bottomAlign"><b><a href="class.html#Values-enum" translate="no">Values</a></b> { One, Two, Three }</td></tr>
+</table></div>
+<!-- $$$Class-description -->
+<div class="descr">
+<h2 id="details">Detailed Description</h2>
+</div>
+<!-- @@@Class -->
+<div class="types">
+<h2>Member Type Documentation</h2>
+<!-- $$$MoreValues$$$Something$$$Else -->
+<h3 class="fn" translate="no" id="MoreValues-enum">enum class Class::<span class="name">MoreValues</span></h3>
+<div class="table"><table class="valuelist"><tr valign="top" class="odd"><th class="tblConst">Constant</th><th class="tblval">Value</th><th class="tbldscr">Description</th></tr>
+<tr><td class="topAlign"><code translate="no">Class::MoreValues::Something</code></td><td class="topAlign tblval"><code translate="no">0</code></td><td class="topAlign">something</td></tr>
+<tr><td class="topAlign"><code translate="no">Class::MoreValues::Else</code></td><td class="topAlign tblval"><code translate="no">1</code></td><td class="topAlign">entirely</td></tr>
+</table></div>
+<!-- @@@MoreValues -->
+<!-- $$$Values$$$One$$$Two$$$Three -->
+<h3 class="fn" translate="no" id="Values-enum">enum Class::<span class="name">Values</span></h3>
+<p>Try now these exciting values in your C++ code:</p>
+<div class="table"><table class="valuelist"><tr valign="top" class="odd"><th class="tblConst">Constant</th><th class="tblval">Value</th><th class="tbldscr">Description</th></tr>
+<tr><td class="topAlign"><code translate="no">Class::One</code></td><td class="topAlign tblval"><code translate="no">0</code></td><td class="topAlign">One value</td></tr>
+</table></div>
+<p>Wait, that's not all!</p>
+<div class="table"><table class="valuelist"><tr valign="top" class="even"><th class="tblConst">Constant</th><th class="tblval">Value</th><th class="tbldscr">Description</th></tr>
+<tr><td class="topAlign"><code translate="no">Class::Two</code></td><td class="topAlign tblval"><code translate="no">1</code></td><td class="topAlign">2nd value</td></tr>
+<tr><td class="topAlign"><code translate="no">Class::Three</code></td><td class="topAlign tblval"><code translate="no">2</code></td><td class="topAlign">3rd value</td></tr>
+</table></div>
+<!-- @@@Values -->
+</div>
+</body>
+</html>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/qmlenumvaluesfromcpp/expected/html/module-module.html b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/qmlenumvaluesfromcpp/expected/html/module-module.html
new file mode 100644
index 000000000..8bcf61f6b
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/qmlenumvaluesfromcpp/expected/html/module-module.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+<!-- class.cpp -->
+ <title>QmlEnumValuesFromCpp</title>
+</head>
+<body>
+<div class="sidebar">
+<div class="toc">
+<h3 id="toc">Contents</h3>
+<ul>
+<li class="level1"><a href="#namespaces">Namespaces</a></li>
+<li class="level1"><a href="#classes">Classes</a></li>
+<li class="level1"><a href="#details">Detailed Description</a></li>
+</ul>
+</div>
+<div class="sidebar-content" id="sidebar-content"></div></div>
+<h2 id="classes">Classes</h2>
+<div class="table"><table class="annotated">
+<tr class="odd topAlign"><td class="tblName" translate="no"><p><a href="class.html">Class</a></p></td></tr>
+</table></div>
+<!-- $$$Module-description -->
+<div class="descr" id="details">
+</div>
+<!-- @@@Module -->
+</body>
+</html>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/qmlenumvaluesfromcpp/expected/html/qml-qmlmodule-type-members.html b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/qmlenumvaluesfromcpp/expected/html/qml-qmlmodule-type-members.html
new file mode 100644
index 000000000..3270d7158
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/qmlenumvaluesfromcpp/expected/html/qml-qmlmodule-type-members.html
@@ -0,0 +1,18 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+<!-- qmltype.qdoc -->
+ <title>List of All Members for Type | QmlEnumValuesFromCpp</title>
+</head>
+<body>
+<li>Type</li>
+<div class="sidebar"><div class="sidebar-content" id="sidebar-content"></div></div>
+<h1 class="title" translate="no">List of All Members for Type</h1>
+<p>This is the complete list of members for <a href="qml-qmlmodule-type.html">Type</a>, including inherited members.</p>
+<ul>
+<li class="fn" translate="no"><b><a href="qml-qmlmodule-type.html#something-prop" translate="no">something</a></b> : enumeration</li>
+<li class="fn" translate="no"><b><a href="qml-qmlmodule-type.html#values-prop" translate="no">values</a></b> : enumeration</li>
+</ul>
+</body>
+</html>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/qmlenumvaluesfromcpp/expected/html/qml-qmlmodule-type.html b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/qmlenumvaluesfromcpp/expected/html/qml-qmlmodule-type.html
new file mode 100644
index 000000000..509dd2431
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/qmlenumvaluesfromcpp/expected/html/qml-qmlmodule-type.html
@@ -0,0 +1,62 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+<!-- qmltype.qdoc -->
+ <title>Type QML Type | QmlEnumValuesFromCpp</title>
+</head>
+<body>
+<li>Type</li>
+<div class="sidebar">
+<div class="toc">
+<h3 id="toc">Contents</h3>
+<ul>
+<li class="level1"><a href="#properties">Properties</a></li>
+<li class="level1"><a href="#details">Detailed Description</a></li>
+</ul>
+</div>
+<div class="sidebar-content" id="sidebar-content"></div></div>
+<h1 class="title" translate="no">Type QML Type</h1>
+<div class="table"><table class="alignedsummary" translate="no">
+<tr><td class="memItemLeft rightAlign topAlign"> Import Statement:</td><td class="memItemRight bottomAlign"> import QmlModule</td></tr></table></div><ul>
+<li><a href="qml-qmlmodule-type-members.html">List of all members, including inherited members</a></li>
+</ul>
+<h2 id="properties">Properties</h2>
+<ul>
+<li class="fn" translate="no"><b><a href="qml-qmlmodule-type.html#something-prop" translate="no">something</a></b> : enumeration</li>
+<li class="fn" translate="no"><b><a href="qml-qmlmodule-type.html#values-prop" translate="no">values</a></b> : enumeration</li>
+</ul>
+<!-- $$$Type-description -->
+<h2 id="details">Detailed Description</h2>
+<!-- @@@Type -->
+<h2>Property Documentation</h2>
+<!-- $$$something -->
+<div class="qmlitem"><div class="qmlproto" translate="no">
+<div class="table"><table class="qmlname">
+<tr valign="top" class="odd" id="something-prop">
+<td class="tblQmlPropNode"><p>
+<span class="name">something</span> : <span class="type">enumeration</span></p></td></tr>
+</table></div></div>
+<div class="qmldoc"><div class="table"><table class="valuelist"><tr valign="top" class="odd"><th class="tblConst">Constant</th><th class="tbldscr">Description</th></tr>
+<tr><td class="topAlign"><code translate="no">SomeType.Something</code></td><td class="topAlign">something</td></tr>
+<tr><td class="topAlign"><code translate="no">SomeType.Else</code></td><td class="topAlign">entirely</td></tr>
+</table></div>
+</div></div><!-- @@@something -->
+<br/>
+<!-- $$$values -->
+<div class="qmlitem"><div class="qmlproto" translate="no">
+<div class="table"><table class="qmlname">
+<tr valign="top" class="odd" id="values-prop">
+<td class="tblQmlPropNode"><p>
+<span class="name">values</span> : <span class="type">enumeration</span></p></td></tr>
+</table></div></div>
+<div class="qmldoc"><p>You can even use these values in QML.</p>
+<div class="table"><table class="valuelist"><tr valign="top" class="odd"><th class="tblConst">Constant</th><th class="tbldscr">Description</th></tr>
+<tr><td class="topAlign"><code translate="no">Type.One</code></td><td class="topAlign">One value</td></tr>
+<tr><td class="topAlign"><code translate="no">Type.Two</code></td><td class="topAlign">2nd value</td></tr>
+<tr><td class="topAlign"><code translate="no">Type.Three</code></td><td class="topAlign">3rd value</td></tr>
+</table></div>
+</div></div><!-- @@@values -->
+<br/>
+</body>
+</html>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/qmlenumvaluesfromcpp/expected/html/qmlenumvaluesfromcpp.index b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/qmlenumvaluesfromcpp/expected/html/qmlenumvaluesfromcpp.index
new file mode 100644
index 000000000..3d9e172a1
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/qmlenumvaluesfromcpp/expected/html/qmlenumvaluesfromcpp.index
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE QDOCINDEX>
+<INDEX url="" title="QmlEnumValuesFromCpp Reference Documentation" version="" project="QmlEnumValuesFromCpp">
+ <namespace name="" status="active" access="public" module="qmlenumvaluesfromcpp">
+ <class name="Class" href="class.html" status="active" access="public" location="class.h" documented="true" module="Module">
+ <enum name="MoreValues" fullname="Class::MoreValues" href="class.html#MoreValues-enum" status="active" access="public" location="class.h" documented="true" scoped="true">
+ <value name="Something" value="0"/>
+ <value name="Else" value="1"/>
+ </enum>
+ <enum name="Values" fullname="Class::Values" href="class.html#Values-enum" status="active" access="public" location="class.h" documented="true">
+ <value name="One" value="0"/>
+ <value name="Two" value="1"/>
+ <value name="Three" value="2"/>
+ </enum>
+ </class>
+ <qmlclass name="Type" qml-module-name="QmlModule" fullname="QmlModule.Type" href="qml-qmlmodule-type.html" status="active" access="public" location="qmltype.qdoc" documented="true" title="Type" fulltitle="Type" subtitle="">
+ <qmlproperty name="something" fullname="QmlModule.Type.something" href="qml-qmlmodule-type.html#something-prop" status="active" access="public" location="qmltype.qdoc" documented="true" type="enumeration" attached="false" writable="true"/>
+ <qmlproperty name="values" fullname="QmlModule.Type.values" href="qml-qmlmodule-type.html#values-prop" status="active" access="public" location="qmltype.qdoc" documented="true" type="enumeration" attached="false" writable="true"/>
+ </qmlclass>
+ <module name="Module" href="module-module.html" status="active" documented="true" seen="true" title=""/>
+ <qmlmodule name="QmlModule" qml-module-name="QmlModule" href="qmlmodule-qmlmodule.html" status="active" location="qmltype.qdoc" documented="true" seen="true" title=""/>
+ </namespace>
+</INDEX>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/qmlenumvaluesfromcpp/expected/html/qmlmodule-qmlmodule.html b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/qmlenumvaluesfromcpp/expected/html/qmlmodule-qmlmodule.html
new file mode 100644
index 000000000..9cbff451f
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/qmlenumvaluesfromcpp/expected/html/qmlmodule-qmlmodule.html
@@ -0,0 +1,18 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+<!-- qmltype.qdoc -->
+ <title>QmlEnumValuesFromCpp</title>
+</head>
+<body>
+<div class="sidebar"><div class="sidebar-content" id="sidebar-content"></div></div>
+<!-- $$$QmlModule-description -->
+<div class="descr" id="details">
+</div>
+<!-- @@@QmlModule -->
+<div class="table"><table class="annotated">
+<tr class="odd topAlign"><td class="tblName" translate="no"><p><a href="qml-qmlmodule-type.html">Type</a></p></td></tr>
+</table></div>
+</body>
+</html>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/qmlenumvaluesfromcpp/expected/webxml/class.webxml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/qmlenumvaluesfromcpp/expected/webxml/class.webxml
new file mode 100644
index 000000000..d6b062fa7
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/qmlenumvaluesfromcpp/expected/webxml/class.webxml
@@ -0,0 +1,54 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<WebXML>
+ <document>
+ <class name="Class" href="class.html" status="active" access="public" location="class.h" documented="true" module="Module">
+ <description/>
+ <enum name="MoreValues" fullname="Class::MoreValues" href="class.html#MoreValues-enum" status="active" access="public" location="class.h" documented="true" scoped="true">
+ <value name="Something" value="0"/>
+ <value name="Else" value="1"/>
+ <description>
+ <list type="enum">
+ <definition>
+ <term>Class::MoreValues::Something</term>Something</definition>
+ <item>
+ <para>something</para>
+ </item>
+ <definition>
+ <term>Class::MoreValues::Else</term>Else</definition>
+ <item>
+ <para>entirely</para>
+ </item>
+ </list>
+ </description>
+ </enum>
+ <enum name="Values" fullname="Class::Values" href="class.html#Values-enum" status="active" access="public" location="class.h" documented="true">
+ <value name="One" value="0"/>
+ <value name="Two" value="1"/>
+ <value name="Three" value="2"/>
+ <description>
+ <para>Try now these exciting values in your C++ code:</para>
+ <list type="enum">
+ <definition>
+ <term>Class::One</term>One</definition>
+ <item>
+ <para>One value</para>
+ </item>
+ </list>
+ <para>Wait, that's not all!</para>
+ <list type="enum">
+ <definition>
+ <term>Class::Two</term>Two</definition>
+ <item>
+ <para>2nd value</para>
+ </item>
+ <definition>
+ <term>Class::Three</term>Three</definition>
+ <item>
+ <para>3rd value</para>
+ </item>
+ </list>
+ </description>
+ </enum>
+ </class>
+ </document>
+</WebXML>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/qmlenumvaluesfromcpp/expected/webxml/module-module.webxml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/qmlenumvaluesfromcpp/expected/webxml/module-module.webxml
new file mode 100644
index 000000000..5d24b3077
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/qmlenumvaluesfromcpp/expected/webxml/module-module.webxml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<WebXML>
+ <document/>
+</WebXML>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/qmlenumvaluesfromcpp/expected/webxml/qmlenumvaluesfromcpp.index b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/qmlenumvaluesfromcpp/expected/webxml/qmlenumvaluesfromcpp.index
new file mode 100644
index 000000000..3d9e172a1
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/qmlenumvaluesfromcpp/expected/webxml/qmlenumvaluesfromcpp.index
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE QDOCINDEX>
+<INDEX url="" title="QmlEnumValuesFromCpp Reference Documentation" version="" project="QmlEnumValuesFromCpp">
+ <namespace name="" status="active" access="public" module="qmlenumvaluesfromcpp">
+ <class name="Class" href="class.html" status="active" access="public" location="class.h" documented="true" module="Module">
+ <enum name="MoreValues" fullname="Class::MoreValues" href="class.html#MoreValues-enum" status="active" access="public" location="class.h" documented="true" scoped="true">
+ <value name="Something" value="0"/>
+ <value name="Else" value="1"/>
+ </enum>
+ <enum name="Values" fullname="Class::Values" href="class.html#Values-enum" status="active" access="public" location="class.h" documented="true">
+ <value name="One" value="0"/>
+ <value name="Two" value="1"/>
+ <value name="Three" value="2"/>
+ </enum>
+ </class>
+ <qmlclass name="Type" qml-module-name="QmlModule" fullname="QmlModule.Type" href="qml-qmlmodule-type.html" status="active" access="public" location="qmltype.qdoc" documented="true" title="Type" fulltitle="Type" subtitle="">
+ <qmlproperty name="something" fullname="QmlModule.Type.something" href="qml-qmlmodule-type.html#something-prop" status="active" access="public" location="qmltype.qdoc" documented="true" type="enumeration" attached="false" writable="true"/>
+ <qmlproperty name="values" fullname="QmlModule.Type.values" href="qml-qmlmodule-type.html#values-prop" status="active" access="public" location="qmltype.qdoc" documented="true" type="enumeration" attached="false" writable="true"/>
+ </qmlclass>
+ <module name="Module" href="module-module.html" status="active" documented="true" seen="true" title=""/>
+ <qmlmodule name="QmlModule" qml-module-name="QmlModule" href="qmlmodule-qmlmodule.html" status="active" location="qmltype.qdoc" documented="true" seen="true" title=""/>
+ </namespace>
+</INDEX>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/qmlenumvaluesfromcpp/expected/webxml/qmlmodule-qmlmodule.webxml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/qmlenumvaluesfromcpp/expected/webxml/qmlmodule-qmlmodule.webxml
new file mode 100644
index 000000000..5d24b3077
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/qmlenumvaluesfromcpp/expected/webxml/qmlmodule-qmlmodule.webxml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<WebXML>
+ <document/>
+</WebXML>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/qmlenumvaluesfromcpp/qmlenumvaluesfromcpp.qdocconf b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/qmlenumvaluesfromcpp/qmlenumvaluesfromcpp.qdocconf
new file mode 100644
index 000000000..218005310
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/qmlenumvaluesfromcpp/qmlenumvaluesfromcpp.qdocconf
@@ -0,0 +1,22 @@
+project = QmlEnumValuesFromCpp
+moduleheader = class.h
+includepaths = ./src
+
+{sourcedirs,headerdirs} = ./src
+
+# zero warning policy
+warninglimit = 0
+warninglimit.enabled = true
+
+# don't write host system-specific paths to index files
+locationinfo = false
+
+outputformats = HTML WebXML DocBook
+{HTML.nosubdirs,WebXML.nosubdirs,DocBook.nosubdirs} = true
+
+HTML.outputsubdir = html
+WebXML.outputsubdir = webxml
+DocBook.outputsubdir = docbook
+
+DocBook.its = true
+DocBook.usedocbookextensions = true
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/qmlenumvaluesfromcpp/src/class.cpp b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/qmlenumvaluesfromcpp/src/class.cpp
new file mode 100644
index 000000000..fa3cc1e31
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/qmlenumvaluesfromcpp/src/class.cpp
@@ -0,0 +1,36 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "class.h"
+
+/*!
+ \module Module
+*/
+
+/*!
+ \class Class
+ \inmodule Module
+*/
+
+/*!
+ \enum Class::Values
+
+ Try now these exciting values in your C++ code:
+
+ \value One
+ One value
+
+ Wait, that's not all!
+
+ \value Two
+ 2nd value
+ \value Three
+ 3rd value
+*/
+
+/*!
+ \enum Class::MoreValues
+
+ \value Something something
+ \value Else entirely
+*/
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/qmlenumvaluesfromcpp/src/class.h b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/qmlenumvaluesfromcpp/src/class.h
new file mode 100644
index 000000000..8c4967a69
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/qmlenumvaluesfromcpp/src/class.h
@@ -0,0 +1,10 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+#pragma once
+
+class Class {
+
+public:
+ enum Values { One, Two, Three };
+ enum class MoreValues { Something, Else };
+};
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/qmlenumvaluesfromcpp/src/qmltype.qdoc b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/qmlenumvaluesfromcpp/src/qmltype.qdoc
new file mode 100644
index 000000000..3cd739c29
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/qmlenumvaluesfromcpp/src/qmltype.qdoc
@@ -0,0 +1,23 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+/*!
+ \qmlmodule QmlModule
+*/
+
+/*!
+ \qmltype Type
+ \inqmlmodule QmlModule
+*/
+
+/*!
+ \qmlproperty enumeration Type::values
+ \qmlenumeratorsfrom Class::Values
+
+ You can even use these values in QML.
+*/
+
+/*!
+ \qmlproperty enumeration Type::something
+ \qmlenumeratorsfrom [SomeType] Class::MoreValues
+*/
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/relatesordering/a.cpp b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/relatesordering/a.cpp
new file mode 100644
index 000000000..c2dd411d7
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/relatesordering/a.cpp
@@ -0,0 +1,11 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "a.h"
+
+/*!
+ \typealias foo
+ \relates <Bar>
+
+ Related to a header file whose source might be parsed later.
+*/
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/relatesordering/a.h b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/relatesordering/a.h
new file mode 100644
index 000000000..40c4add88
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/relatesordering/a.h
@@ -0,0 +1,5 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+#pragma once
+
+using foo = int;
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/relatesordering/b.cpp b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/relatesordering/b.cpp
new file mode 100644
index 000000000..7e3df2f24
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/relatesordering/b.cpp
@@ -0,0 +1,12 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+/*!
+ \module Module
+*/
+
+/*!
+ \headerfile <Bar>
+ \title A header
+ \inmodule Module
+*/
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/relatesordering/expected/bar.html b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/relatesordering/expected/bar.html
new file mode 100644
index 000000000..b8dc9412a
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/relatesordering/expected/bar.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+<!-- b.cpp -->
+ <title>&lt;Bar&gt; - A header | RelatesOrdering</title>
+</head>
+<body>
+<div class="sidebar">
+<div class="toc">
+<h3 id="toc">Contents</h3>
+</div>
+<div class="sidebar-content" id="sidebar-content"></div></div>
+<h1 class="title" translate="no">&lt;Bar&gt; - A header</h1>
+<div class="table"><table class="alignedsummary" translate="no">
+<tr><td class="memItemLeft rightAlign topAlign"> Header:</td><td class="memItemRight bottomAlign"> <span class="preprocessor">#include &lt;Bar&gt;</span></td></tr>
+</table></div>
+<h2 id="types">Types</h2>
+<div class="table"><table class="alignedsummary" translate="no">
+<tr><td class="memItemLeft rightAlign topAlign"> </td><td class="memItemRight bottomAlign"><b><a href="bar.html#foo-typedef" translate="no">foo</a></b></td></tr>
+</table></div>
+<!-- $$$<Bar>-description -->
+<div class="descr">
+<h2 id="details">Detailed Description</h2>
+</div>
+<!-- @@@<Bar> -->
+<div class="types">
+<h2>Type Documentation</h2>
+<!-- $$$foo -->
+<h3 class="fn" translate="no" id="foo-typedef"><code class="details extra" translate="no">[alias]</code> <span class="name">foo</span></h3>
+<p>Related to a header file whose source might be parsed later.</p>
+<!-- @@@foo -->
+</div>
+</body>
+</html>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/relatesordering/expected/module-module.html b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/relatesordering/expected/module-module.html
new file mode 100644
index 000000000..3f123509c
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/relatesordering/expected/module-module.html
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+<!-- b.cpp -->
+ <title>RelatesOrdering</title>
+</head>
+<body>
+<div class="sidebar">
+<div class="toc">
+<h3 id="toc">Contents</h3>
+<ul>
+<li class="level1"><a href="#details">Detailed Description</a></li>
+</ul>
+</div>
+<div class="sidebar-content" id="sidebar-content"></div></div>
+<!-- $$$Module-description -->
+<div class="descr" id="details">
+</div>
+<!-- @@@Module -->
+</body>
+</html>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/relatesordering/expected/relatesordering.index b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/relatesordering/expected/relatesordering.index
new file mode 100644
index 000000000..905c657aa
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/relatesordering/expected/relatesordering.index
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE QDOCINDEX>
+<INDEX url="" title="RelatesOrdering Reference Documentation" version="" project="RelatesOrdering">
+ <namespace name="" status="active" access="public" module="relatesordering">
+ <header name="&lt;Bar&gt;" href="bar.html" status="active" documented="true" module="Module" title="A header" fulltitle="&lt;Bar&gt; - A header" subtitle="">
+ <typedef name="foo" href="bar.html#foo-typedef" status="active" access="public" location="a.h" related="0" documented="true" aliasedtype="int"/>
+ </header>
+ <typedef name="foo" href="bar.html#foo-typedef" status="active" access="public" location="a.h" related="0" documented="true" aliasedtype="int"/>
+ <module name="Module" href="module-module.html" status="active" documented="true" seen="true" title=""/>
+ </namespace>
+</INDEX>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/relatesordering/relatesordering.qdocconf b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/relatesordering/relatesordering.qdocconf
new file mode 100644
index 000000000..b943729a3
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/relatesordering/relatesordering.qdocconf
@@ -0,0 +1,7 @@
+project = RelatesOrdering
+
+{sourcedirs,headerdirs} = .
+
+locationinfo = false
+warninglimit = 0
+warninglimit.enabled = true
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/scopedenum/expected/docbook/autolinking.xml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/scopedenum/expected/docbook/autolinking.xml
new file mode 100644
index 000000000..b976dd6fe
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/scopedenum/expected/docbook/autolinking.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<db:article xmlns:db="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" version="5.2" xml:lang="en">
+<db:info>
+<db:title>Autolinking</db:title>
+<db:productname>TestCPP</db:productname>
+<db:titleabbrev>TestCPP Reference Documentation</db:titleabbrev>
+<db:abstract>
+<db:para>TestCPP Reference Documentation.</db:para></db:abstract>
+</db:info>
+<db:section xml:id="testqdoc">
+<db:title>TestQDoc</db:title>
+<db:para>The string <db:link xlink:href="testqdoc.xml">TestQDoc</db:link> links to the C++ namespace unless linking explicitly, <db:link xlink:href="autolinking.xml#testqdoc">like this</db:link>, or <db:link xlink:href="testqdoc.xml">this</db:link>. Also,</db:para>
+<db:para>Autolinks:</db:para>
+<db:itemizedlist>
+<db:listitem>
+<db:para><db:link xlink:href="testqdoc-testderived.xml">TestQDoc::TestDerived</db:link></db:para>
+</db:listitem>
+</db:itemizedlist>
+<db:para>Explicit links:</db:para>
+<db:itemizedlist>
+<db:listitem>
+<db:para><db:link xlink:href="testqdoc-testderived.xml">TestQDoc::TestDerived</db:link></db:para>
+</db:listitem>
+<db:listitem>
+<db:para><db:link xlink:href="obsolete-classes.xml#testqdoc">Obsolete Classes#TestQDoc</db:link></db:para>
+</db:listitem>
+</db:itemizedlist>
+</db:section>
+<db:section xml:id="someprop">
+<db:title>someProp</db:title>
+</db:section>
+</db:article>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/scopedenum/expected/docbook/cpptypes.xml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/scopedenum/expected/docbook/cpptypes.xml
new file mode 100644
index 000000000..4c4d39790
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/scopedenum/expected/docbook/cpptypes.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<db:article xmlns:db="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" version="5.2" xml:lang="en">
+<db:info>
+<db:title>Test C++ Types</db:title>
+<db:productname>TestCPP</db:productname>
+<db:titleabbrev>TestCPP Reference Documentation</db:titleabbrev>
+<db:abstract>
+<db:para>TestCPP Reference Documentation.</db:para></db:abstract>
+</db:info>
+<db:anchor xml:id="details"/>
+<db:itemizedlist role="testgroup">
+<db:listitem>
+<db:para><db:link xlink:href="testqdoc-test.xml" xlink:role="class">TestQDoc::Test</db:link></db:para>
+</db:listitem>
+<db:listitem>
+<db:para><db:link xlink:href="testqdoc-test.xml#QDOCTEST_MACRO2" xlink:role="function">TestQDoc::Test::QDOCTEST_MACRO2</db:link></db:para>
+</db:listitem>
+<db:listitem>
+<db:para><db:link xlink:href="testqdoc-test.xml#someFunctionDefaultArg" xlink:role="function">TestQDoc::Test::someFunctionDefaultArg()</db:link></db:para>
+</db:listitem>
+</db:itemizedlist>
+<db:variablelist role="members">
+<db:varlistentry>
+<db:term><db:link xlink:href="testqdoc-test.xml" xlink:role="class">TestQDoc::Test</db:link></db:term>
+<db:listitem>
+<db:para>A class in a namespace.</db:para>
+</db:listitem>
+</db:varlistentry>
+</db:variablelist>
+</db:article>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/scopedenum/expected/docbook/crossmoduleref.xml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/scopedenum/expected/docbook/crossmoduleref.xml
new file mode 100644
index 000000000..6a16d52f6
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/scopedenum/expected/docbook/crossmoduleref.xml
@@ -0,0 +1,49 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<db:article xmlns:db="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" version="5.2" xml:lang="en">
+<db:info>
+<db:title>CrossModuleRef Namespace</db:title>
+<db:productname>TestCPP</db:productname>
+<db:titleabbrev>TestCPP Reference Documentation</db:titleabbrev>
+<db:abstract>
+<db:para>Namespace that has documented functions in multiple modules.</db:para>
+<db:para>This namespace was introduced in Qt 3.0.</db:para>
+</db:abstract>
+</db:info>
+<db:variablelist>
+<db:varlistentry>
+<db:term>Header</db:term>
+<db:listitem>
+<db:para>CrossModuleRef</db:para>
+</db:listitem>
+</db:varlistentry>
+<db:varlistentry>
+<db:term>Since</db:term>
+<db:listitem>
+<db:para>Qt 3.0</db:para>
+</db:listitem>
+</db:varlistentry>
+<db:varlistentry>
+<db:term>CMake</db:term>
+<db:listitem>
+<db:para>find_package(Qt6 REQUIRED COMPONENTS QDocTest)</db:para>
+<db:para>target_link_libraries(mytarget PRIVATE Qt6::QDocTest)</db:para>
+</db:listitem>
+</db:varlistentry>
+<db:varlistentry>
+<db:term>qmake</db:term>
+<db:listitem>
+<db:para>QT += testcpp</db:para>
+</db:listitem>
+</db:varlistentry>
+</db:variablelist>
+<db:section xml:id="details">
+<db:title>Detailed Description</db:title>
+</db:section>
+<db:section xml:id="function-documentation">
+<db:title>Function Documentation</db:title>
+<db:section xml:id="documentMe">
+<db:title>void CrossModuleRef::documentMe()</db:title>
+<db:para>Document me!</db:para>
+</db:section>
+</db:section>
+</db:article>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/scopedenum/expected/docbook/obsolete-classes.xml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/scopedenum/expected/docbook/obsolete-classes.xml
new file mode 100644
index 000000000..62928d5f8
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/scopedenum/expected/docbook/obsolete-classes.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<db:article xmlns:db="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" version="5.2" xml:lang="en">
+<db:info>
+<db:title>Obsolete Classes</db:title>
+<db:productname>TestCPP</db:productname>
+<db:titleabbrev>TestCPP Reference Documentation</db:titleabbrev>
+<db:abstract>
+<db:para>TestCPP Reference Documentation.</db:para></db:abstract>
+</db:info>
+<db:section xml:id="classes-with-obsolete-members">
+<db:title>Classes with obsolete members</db:title>
+<db:variablelist role="obsoletecppmembers">
+<db:varlistentry>
+<db:term><db:emphasis role="bold">T</db:emphasis></db:term>
+<db:listitem>
+<db:itemizedlist>
+<db:listitem>
+<db:para><db:link xlink:href="testqdoc-test.xml" role="class">Test</db:link> (<db:link xlink:href="testqdoc.xml" xlink:role="namespace">TestQDoc</db:link>)</db:para>
+</db:listitem>
+<db:listitem>
+<db:para><db:link xlink:href="testqdoc-testderived.xml" role="class">TestDerived</db:link> (<db:link xlink:href="testqdoc.xml" xlink:role="namespace">TestQDoc</db:link>)</db:para>
+</db:listitem>
+</db:itemizedlist>
+</db:listitem>
+</db:varlistentry>
+</db:variablelist>
+<db:section xml:id="testqdoc">
+<db:title>TestQDoc</db:title>
+</db:section>
+</db:section>
+</db:article>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/scopedenum/expected/docbook/scoped-enum-linking.xml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/scopedenum/expected/docbook/scoped-enum-linking.xml
new file mode 100644
index 000000000..f5c4a05cd
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/scopedenum/expected/docbook/scoped-enum-linking.xml
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<db:article xmlns:db="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" version="5.2" xml:lang="en">
+<db:info>
+<db:title>Enum Linking</db:title>
+<db:productname>TestCPP</db:productname>
+<db:titleabbrev>TestCPP Reference Documentation</db:titleabbrev>
+<db:abstract>
+<db:para>TestCPP Reference Documentation.</db:para></db:abstract>
+</db:info>
+<db:para>Linking to <db:link xlink:href="testqdoc-test.xml#ScopedEnum-enum">All</db:link>.</db:para>
+<db:para>TestQDoc::Test::ClassicEnum::Howdy does not link, but <db:link xlink:href="testqdoc-test.xml#ClassicEnum-enum">TestQDoc::Test::Howdy</db:link> might.</db:para>
+</db:article>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/scopedenum/expected/docbook/testcpp-module.xml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/scopedenum/expected/docbook/testcpp-module.xml
new file mode 100644
index 000000000..14eddeb8d
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/scopedenum/expected/docbook/testcpp-module.xml
@@ -0,0 +1,77 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<db:article xmlns:db="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" version="5.2" xml:lang="en">
+<db:info>
+<db:title>QDoc Test C++ Classes</db:title>
+<db:productname>TestCPP</db:productname>
+<db:titleabbrev>TestCPP Reference Documentation</db:titleabbrev>
+<db:abstract>
+<db:para>A test module page.</db:para>
+<db:para>This module was introduced in Qt 2.0.</db:para>
+</db:abstract>
+</db:info>
+<db:para>A test module page.</db:para>
+<db:para>This module was introduced in Qt 2.0.</db:para>
+<db:section xml:id="namespaces">
+<db:title>Namespaces</db:title>
+<db:variablelist role="namespaces">
+<db:varlistentry>
+<db:term><db:link xlink:href="crossmoduleref.xml" xlink:role="namespace">CrossModuleRef</db:link></db:term>
+<db:listitem>
+<db:para>Namespace that has documented functions in multiple modules.</db:para>
+</db:listitem>
+</db:varlistentry>
+<db:varlistentry>
+<db:term><db:link xlink:href="testqdoc.xml" xlink:role="namespace">TestQDoc</db:link></db:term>
+<db:listitem>
+<db:para>A namespace.</db:para>
+</db:listitem>
+</db:varlistentry>
+</db:variablelist>
+</db:section>
+<db:section xml:id="classes">
+<db:title>Classes</db:title>
+<db:variablelist role="classes">
+<db:varlistentry>
+<db:term><db:link xlink:href="testqdoc-test.xml" xlink:role="class">TestQDoc::Test</db:link></db:term>
+<db:listitem>
+<db:para>A class in a namespace.</db:para>
+</db:listitem>
+</db:varlistentry>
+<db:varlistentry>
+<db:term><db:link xlink:href="testqdoc-testderived.xml" xlink:role="class">TestQDoc::TestDerived</db:link></db:term>
+<db:listitem>
+<db:para>A class in a namespace, derived from Test.</db:para>
+</db:listitem>
+</db:varlistentry>
+</db:variablelist>
+</db:section>
+<db:section xml:id="details">
+<db:title>Detailed Description</db:title>
+<db:note>
+<db:para>This is just a test. /* Look, Ma! {I'm made of arguments!} */</db:para>
+</db:note>
+<db:section xml:id="linking-to-function-like-things">
+<db:title>Linking to function-like things</db:title>
+<db:itemizedlist>
+<db:listitem>
+<db:para><db:link xlink:href="testqdoc-test.xml#someFunctionDefaultArg">someFunctionDefaultArg</db:link>()</db:para>
+</db:listitem>
+<db:listitem>
+<db:para><db:link xlink:href="testqdoc-test.xml#QDOCTEST_MACRO2">QDOCTEST_MACRO2</db:link>()</db:para>
+</db:listitem>
+<db:listitem>
+<db:para><db:link xlink:href="testqdoc-test.xml#QDOCTEST_MACRO2">QDOCTEST_MACRO2</db:link>(int &amp;x)</db:para>
+</db:listitem>
+<db:listitem>
+<db:para><db:link xlink:href="testcpp-module.xml#section">section()</db:link></db:para>
+</db:listitem>
+<db:listitem>
+<db:para><db:link xlink:href="testcpp-module.xml#section">section() is a section title</db:link></db:para>
+</db:listitem>
+</db:itemizedlist>
+<db:section xml:id="section">
+<db:title>section()</db:title>
+</db:section>
+</db:section>
+</db:section>
+</db:article>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/scopedenum/expected/docbook/testqdoc-test.xml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/scopedenum/expected/docbook/testqdoc-test.xml
new file mode 100644
index 000000000..1603be377
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/scopedenum/expected/docbook/testqdoc-test.xml
@@ -0,0 +1,253 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<db:article xmlns:db="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" version="5.2" xml:lang="en">
+<db:info>
+<db:title>Test Class</db:title>
+<db:subtitle>TestQDoc::Test</db:subtitle>
+<db:productname>TestCPP</db:productname>
+<db:titleabbrev>TestCPP Reference Documentation</db:titleabbrev>
+<db:abstract>
+<db:para>A class in a namespace.</db:para>
+<db:para>This class was introduced in Qt 2.0.</db:para>
+<db:note>
+<db:para>All functions in this class are <db:link xlink:href="https://doc.qt.io/qt/17-qdoc-commands-thread.html#reentrant-command">reentrant</db:link> with the following exceptions:</db:para>
+<db:para>These functions are not <db:link xlink:href="https://doc.qt.io/qt/17-qdoc-commands-thread.html#reentrant-command">reentrant</db:link>:</db:para>
+<db:itemizedlist>
+<db:listitem>
+<db:para><db:link xlink:href="testqdoc-test.xml#someFunctionDefaultArg">someFunctionDefaultArg(int i, bool b) const</db:link></db:para>
+</db:listitem>
+</db:itemizedlist>
+</db:note>
+</db:abstract>
+</db:info>
+<db:variablelist>
+<db:varlistentry>
+<db:term>Header</db:term>
+<db:listitem>
+<db:para>Test</db:para>
+</db:listitem>
+</db:varlistentry>
+<db:varlistentry>
+<db:term>Since</db:term>
+<db:listitem>
+<db:para>Qt 2.0</db:para>
+</db:listitem>
+</db:varlistentry>
+<db:varlistentry>
+<db:term>CMake</db:term>
+<db:listitem>
+<db:para>find_package(Qt6 REQUIRED COMPONENTS QDocTest)</db:para>
+<db:para>target_link_libraries(mytarget PRIVATE Qt6::QDocTest)</db:para>
+</db:listitem>
+</db:varlistentry>
+<db:varlistentry>
+<db:term>qmake</db:term>
+<db:listitem>
+<db:para>QT += testcpp</db:para>
+</db:listitem>
+</db:varlistentry>
+<db:varlistentry>
+<db:term>Inherited By</db:term>
+<db:listitem>
+<db:para><db:link xlink:href="testqdoc-testderived.xml" xlink:role="class">TestQDoc::TestDerived</db:link></db:para>
+</db:listitem>
+</db:varlistentry>
+<db:varlistentry>
+<db:term>Group</db:term>
+<db:listitem>
+<db:para>Test is part of <db:simplelist><db:member>testgroup</db:member><db:member><db:link xlink:href="cpptypes.xml">Test C++ Types</db:link></db:member></db:simplelist>
+</db:para>
+</db:listitem>
+</db:varlistentry>
+</db:variablelist>
+<db:section xml:id="details">
+<db:title>Detailed Description</db:title>
+</db:section>
+<db:section xml:id="member-type-documentation">
+<db:title>Member Type Documentation</db:title>
+<db:section xml:id="ClassicEnum-enum">
+<db:title>enum Test::ClassicEnum</db:title>
+<db:informaltable>
+<db:thead>
+<db:tr>
+<db:th>Constant</db:th>
+</db:tr>
+</db:thead>
+<db:tr>
+<db:td>
+<db:para><db:code><db:emphasis role="bold"><db:link xlink:href="testqdoc.xml">TestQDoc</db:link></db:emphasis>::<db:emphasis role="bold"><db:link xlink:href="testqdoc-test.xml">Test</db:link></db:emphasis>::Yee</db:code></db:para>
+</db:td>
+<db:td><db:code>0</db:code></db:td>
+</db:tr>
+<db:tr>
+<db:td>
+<db:para><db:code><db:emphasis role="bold"><db:link xlink:href="testqdoc.xml">TestQDoc</db:link></db:emphasis>::<db:emphasis role="bold"><db:link xlink:href="testqdoc-test.xml">Test</db:link></db:emphasis>::Haw</db:code></db:para>
+</db:td>
+<db:td><db:code>1</db:code></db:td>
+</db:tr>
+<db:tr>
+<db:td>
+<db:para><db:code><db:emphasis role="bold"><db:link xlink:href="testqdoc.xml">TestQDoc</db:link></db:emphasis>::<db:emphasis role="bold"><db:link xlink:href="testqdoc-test.xml">Test</db:link></db:emphasis>::Howdy</db:code></db:para>
+</db:td>
+<db:td><db:code>2</db:code></db:td>
+</db:tr>
+<db:tr>
+<db:td>
+<db:para><db:code><db:emphasis role="bold"><db:link xlink:href="testqdoc.xml">TestQDoc</db:link></db:emphasis>::<db:emphasis role="bold"><db:link xlink:href="testqdoc-test.xml">Test</db:link></db:emphasis>::Partner</db:code></db:para>
+</db:td>
+<db:td><db:code>3</db:code></db:td>
+</db:tr>
+</db:informaltable>
+</db:section>
+<db:section xml:id="ScopedEnum-enum">
+<db:title>enum Test::ScopedEnum</db:title>
+<db:para>This enum has a brief to trigger a bug in CMD_BRIEF.</db:para>
+<db:informaltable>
+<db:thead>
+<db:tr>
+<db:th>Constant</db:th>
+<db:th>Value</db:th>
+<db:th>Description</db:th>
+</db:tr>
+</db:thead>
+<db:tr>
+<db:td>
+<db:para><db:code><db:emphasis role="bold"><db:link xlink:href="testqdoc.xml">TestQDoc</db:link></db:emphasis>::<db:emphasis role="bold"><db:link xlink:href="testqdoc-test.xml">Test</db:link></db:emphasis>::<db:emphasis role="bold"><db:link xlink:href="">ScopedEnum</db:link></db:emphasis>::This</db:code></db:para>
+</db:td>
+<db:td><db:code>0x01</db:code></db:td>
+<db:td>
+<db:para>Something</db:para>
+</db:td>
+</db:tr>
+<db:tr>
+<db:td>
+<db:para><db:code><db:emphasis role="bold"><db:link xlink:href="testqdoc.xml">TestQDoc</db:link></db:emphasis>::<db:emphasis role="bold"><db:link xlink:href="testqdoc-test.xml">Test</db:link></db:emphasis>::<db:emphasis role="bold"><db:link xlink:href="">ScopedEnum</db:link></db:emphasis>::That</db:code></db:para>
+</db:td>
+<db:td><db:code>0x02</db:code></db:td>
+<db:td>
+<db:para>Something else</db:para>
+</db:td>
+</db:tr>
+<db:tr>
+<db:td>
+<db:para><db:code><db:emphasis role="bold"><db:link xlink:href="testqdoc.xml">TestQDoc</db:link></db:emphasis>::<db:emphasis role="bold"><db:link xlink:href="testqdoc-test.xml">Test</db:link></db:emphasis>::<db:emphasis role="bold"><db:link xlink:href="">ScopedEnum</db:link></db:emphasis>::All (since Qt 2.0)</db:code></db:para>
+</db:td>
+<db:td><db:code>This | That</db:code></db:td>
+<db:td>
+<db:para>Everything</db:para>
+</db:td>
+</db:tr>
+</db:informaltable>
+<db:para>A scoped enum.</db:para>
+</db:section>
+<db:section xml:id="SomeType-typedef">
+<db:title>Test::SomeType</db:title>
+<db:para>A typedef.</db:para>
+</db:section>
+</db:section>
+<db:section xml:id="member-function-documentation">
+<db:title>Member Function Documentation</db:title>
+<db:section xml:id="overload">
+<db:title>[protected] void Test::overload()</db:title>
+<db:bridgehead renderas="sect2" xml:id="overload-1">[protected, since Test 1.2] void Test::overload(bool <db:emphasis>b</db:emphasis>)</db:bridgehead>
+<db:para>Overloads that share a documentation comment, optionally taking a parameter <db:code role="parameter">b</db:code>.</db:para>
+</db:section>
+<db:section xml:id="funcPtr">
+<db:title>void (*)(bool) Test::funcPtr(bool <db:emphasis>b</db:emphasis>, const char *<db:emphasis>s</db:emphasis>)</db:title>
+<db:para>Returns a pointer to a function that takes a boolean. Uses <db:code role="parameter">b</db:code> and <db:code role="parameter">s</db:code>.</db:para>
+</db:section>
+<db:section xml:id="inlineFunction">
+<db:title>void Test::inlineFunction()</db:title>
+<db:para>An inline function, documented using the \fn QDoc command.</db:para>
+</db:section>
+<db:section xml:id="methodWithEmDashInItsDocs">
+<db:title>void Test::methodWithEmDashInItsDocs()</db:title>
+<db:para>This method has em dashes in its documentation—as you'll find represented by <db:code>---</db:code> in the sources—here and there. The important bit to note is that when passed e.g. to the \c command, the three hyphens are processed as input to the command and not replaced by an em dash.</db:para>
+<db:para>-----------------------------------------------------------------------</db:para>
+<db:para>People can still add a bunch of dashes, though, without QDoc replacing them all with a series of em dashes.</db:para>
+<db:para>—You can also start a new paragraph with an em dash, if you want to.</db:para>
+<db:section>
+<db:title>See Also</db:title>
+<db:para><db:emphasis>See also </db:emphasis>
+<db:simplelist type="vert" role="see-also">
+<db:member><db:link xlink:href="testqdoc-test.xml#methodWithEnDashInItsDocs">methodWithEnDashInItsDocs</db:link></db:member>
+</db:simplelist>
+</db:para>
+</db:section>
+</db:section>
+<db:section xml:id="methodWithEnDashInItsDocs">
+<db:title>void Test::methodWithEnDashInItsDocs()</db:title>
+<db:para>This method has en dashes in its documentation – as you'll find represented by <db:code>--</db:code> in the sources – here and there. The important bit to note is that when passed e.g. to the \c command, the two hyphens are processed as input to the command and not replaced by an en dash. This also applies to code blocks, where otherwise, the decrement operator would get completely borked:</db:para>
+<db:programlisting language="cpp">for (int i = 42; i &amp;gt; 0; --i)
+ // Do something cool during countdown.
+</db:programlisting>
+<db:para>...as it would be silly if this would output –i instead of <db:code>--i</db:code>.</db:para>
+<db:para>-----------------------------------------------------------------------</db:para>
+<db:para>It still allows people to add a bunch of dashes, though, without replacing them all with a series of en dashes. Of course, they might want to use the \hr command instead, like this:</db:para>
+<db:para>– You can also start a new paragraph with an en dash, if you want to.</db:para>
+<db:section>
+<db:title>See Also</db:title>
+<db:para><db:emphasis>See also </db:emphasis>
+<db:simplelist type="vert" role="see-also">
+<db:member><db:link xlink:href="">methodWithEnDashInItsDocs</db:link></db:member>
+</db:simplelist>
+</db:para>
+</db:section>
+</db:section>
+<db:section xml:id="someFunction">
+<db:title>int Test::someFunction(<db:emphasis>int</db:emphasis>, int <db:emphasis>v</db:emphasis> = 0)</db:title>
+<db:para>Function that takes a parameter <db:code role="parameter">v</db:code>. Also returns the value of <db:code role="parameter">v</db:code>.</db:para>
+</db:section>
+<db:section xml:id="someFunctionDefaultArg">
+<db:title>void Test::someFunctionDefaultArg(int <db:emphasis>i</db:emphasis>, bool <db:emphasis>b</db:emphasis> = false) const</db:title>
+<db:para>Function that takes a parameter <db:code role="parameter">i</db:code> and <db:code role="parameter">b</db:code>.</db:para>
+<db:warning>
+<db:para>This function is not <db:link xlink:href="https://doc.qt.io/qt/17-qdoc-commands-thread.html#reentrant-command">reentrant</db:link>.</db:para>
+</db:warning></db:section>
+<db:section xml:id="virtualFun">
+<db:title>[virtual] void Test::virtualFun()</db:title>
+<db:para>Function that must be reimplemented.</db:para>
+</db:section>
+</db:section>
+<db:section xml:id="related-non-members">
+<db:title>Related Non-Members</db:title>
+<db:section xml:id="operator-eq-eq">
+<db:title>bool operator==(const TestQDoc::Test &amp;<db:emphasis>lhs</db:emphasis>, const TestQDoc::Test &amp;<db:emphasis>rhs</db:emphasis>)</db:title>
+<db:para>Returns true if <db:code role="parameter">lhs</db:code> and <db:code role="parameter">rhs</db:code> are equal.</db:para>
+</db:section>
+</db:section>
+<db:section xml:id="macro-documentation">
+<db:title>Macro Documentation</db:title>
+<db:section xml:id="QDOCTEST_MACRO2">
+<db:title>[since Test 1.1] QDOCTEST_MACRO2(int &amp;<db:emphasis>x</db:emphasis>)</db:title>
+<db:para>A macro with argument <db:code role="parameter">x</db:code>.</db:para>
+<db:para>This macro was introduced in Test 1.1.</db:para>
+</db:section>
+</db:section>
+<db:section xml:id="obsolete">
+<db:title>Obsolete Members for Test</db:title>
+<db:para><db:emphasis role="bold">The following members of class <db:link xlink:href="testqdoc-test.xml">Test</db:link> are deprecated.</db:emphasis> We strongly advise against using them in new code.</db:para>
+<db:section xml:id="obsolete-member-function-documentation">
+<db:title>Obsolete Member Function Documentation</db:title>
+<db:section xml:id="operator-2b-2b">
+<db:title>[deprecated] TestQDoc::Test &amp;Test::operator++()</db:title>
+<db:bridgehead renderas="sect2" xml:id="operator--">[deprecated] TestQDoc::Test &amp;Test::operator--()</db:bridgehead>
+<db:para>This function is deprecated. We strongly advise against using it in new code.</db:para>
+</db:section>
+<db:section xml:id="anotherObsoleteMember">
+<db:title>[deprecated] void Test::anotherObsoleteMember()</db:title>
+<db:para>This function is deprecated. We strongly advise against using it in new code.</db:para>
+<db:para>Use <db:link xlink:href="testqdoc-test.xml#obsoleteMember">obsoleteMember</db:link>() instead.</db:para>
+</db:section>
+<db:section xml:id="deprecatedMember">
+<db:title>[deprecated in 6.0] void Test::deprecatedMember()</db:title>
+<db:para>This function is deprecated since 6.0. We strongly advise against using it in new code.</db:para>
+<db:para>Use <db:link xlink:href="testqdoc-test.xml#someFunction">someFunction</db:link>() instead.</db:para>
+</db:section>
+<db:section xml:id="obsoleteMember">
+<db:title>[deprecated] void Test::obsoleteMember()</db:title>
+<db:para>This function is deprecated. We strongly advise against using it in new code.</db:para>
+<db:para>Use <db:link xlink:href="testqdoc-test.xml#someFunction">someFunction</db:link>() instead.</db:para>
+</db:section>
+</db:section>
+</db:section>
+</db:article>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/scopedenum/expected/docbook/testqdoc-testderived.xml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/scopedenum/expected/docbook/testqdoc-testderived.xml
new file mode 100644
index 000000000..a54ecaf6f
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/scopedenum/expected/docbook/testqdoc-testderived.xml
@@ -0,0 +1,83 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<db:article xmlns:db="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" version="5.2" xml:lang="en">
+<db:info>
+<db:title>TestDerived Class</db:title>
+<db:subtitle>TestQDoc::TestDerived</db:subtitle>
+<db:productname>TestCPP</db:productname>
+<db:titleabbrev>TestCPP Reference Documentation</db:titleabbrev>
+<db:abstract>
+<db:para>A class in a namespace, derived from <db:link xlink:href="testqdoc-test.xml">Test</db:link>.</db:para>
+<db:para>This class was introduced in Qt 2.0.</db:para>
+</db:abstract>
+</db:info>
+<db:variablelist>
+<db:varlistentry>
+<db:term>Header</db:term>
+<db:listitem>
+<db:para>TestDerived</db:para>
+</db:listitem>
+</db:varlistentry>
+<db:varlistentry>
+<db:term>Since</db:term>
+<db:listitem>
+<db:para>Qt 2.0</db:para>
+</db:listitem>
+</db:varlistentry>
+<db:varlistentry>
+<db:term>CMake</db:term>
+<db:listitem>
+<db:para>find_package(Qt6 REQUIRED COMPONENTS QDocTest)</db:para>
+<db:para>target_link_libraries(mytarget PRIVATE Qt6::QDocTest)</db:para>
+</db:listitem>
+</db:varlistentry>
+<db:varlistentry>
+<db:term>qmake</db:term>
+<db:listitem>
+<db:para>QT += testcpp</db:para>
+</db:listitem>
+</db:varlistentry>
+<db:varlistentry>
+<db:term>Inherits</db:term>
+<db:listitem>
+<db:para><db:link xlink:href="testqdoc-test.xml" xlink:role="class">TestQDoc::Test</db:link></db:para>
+</db:listitem>
+</db:varlistentry>
+</db:variablelist>
+<db:section xml:id="details">
+<db:title>Detailed Description</db:title>
+</db:section>
+<db:section xml:id="member-type-documentation">
+<db:title>Member Type Documentation</db:title>
+<db:section xml:id="DerivedType-typedef">
+<db:title>[alias] TestDerived::DerivedType</db:title>
+<db:para>An aliased typedef.</db:para>
+</db:section>
+<db:section xml:id="NotTypedef-typedef">
+<db:title>[alias] TestDerived::NotTypedef</db:title>
+<db:para>I'm an alias, not a typedef.</db:para>
+</db:section>
+</db:section>
+<db:section xml:id="member-function-documentation">
+<db:title>Member Function Documentation</db:title>
+<db:section xml:id="someValue">
+<db:title>TestQDoc::TestDerived::NotTypedef TestDerived::someValue()</db:title>
+<db:para>Returns a value using an aliases type.</db:para>
+</db:section>
+<db:section xml:id="virtualFun">
+<db:title>[override virtual] void TestDerived::virtualFun()</db:title>
+<db:para>Reimplements: <db:link xlink:href="testqdoc-test.xml#virtualFun" role="function">Test::virtualFun()</db:link>.</db:para>
+</db:section>
+</db:section>
+<db:section xml:id="obsolete">
+<db:title>Obsolete Members for TestDerived</db:title>
+<db:para><db:emphasis role="bold">The following members of class <db:link xlink:href="testqdoc-testderived.xml">TestDerived</db:link> are deprecated.</db:emphasis> We strongly advise against using them in new code.</db:para>
+<db:section xml:id="obsolete-member-function-documentation">
+<db:title>Obsolete Member Function Documentation</db:title>
+<db:section xml:id="staticObsoleteMember">
+<db:title>[static, deprecated] void TestDerived::staticObsoleteMember()</db:title>
+<db:para>This function is deprecated. We strongly advise against using it in new code.</db:para>
+<db:para>Static obsolete method.</db:para>
+</db:section>
+</db:section>
+</db:section>
+</db:article>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/scopedenum/expected/docbook/testqdoc.xml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/scopedenum/expected/docbook/testqdoc.xml
new file mode 100644
index 000000000..99a2422ee
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/scopedenum/expected/docbook/testqdoc.xml
@@ -0,0 +1,63 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<db:article xmlns:db="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" version="5.2" xml:lang="en">
+<db:info>
+<db:title>TestQDoc Namespace</db:title>
+<db:productname>TestCPP</db:productname>
+<db:titleabbrev>TestCPP Reference Documentation</db:titleabbrev>
+<db:abstract>
+<db:para>A namespace.</db:para>
+<db:para>This namespace was introduced in Qt 2.0.</db:para>
+</db:abstract>
+</db:info>
+<db:variablelist>
+<db:varlistentry>
+<db:term>Header</db:term>
+<db:listitem>
+<db:para>TestCPP</db:para>
+</db:listitem>
+</db:varlistentry>
+<db:varlistentry>
+<db:term>Since</db:term>
+<db:listitem>
+<db:para>Qt 2.0</db:para>
+</db:listitem>
+</db:varlistentry>
+<db:varlistentry>
+<db:term>CMake</db:term>
+<db:listitem>
+<db:para>find_package(Qt6 REQUIRED COMPONENTS QDocTest)</db:para>
+<db:para>target_link_libraries(mytarget PRIVATE Qt6::QDocTest)</db:para>
+</db:listitem>
+</db:varlistentry>
+<db:varlistentry>
+<db:term>qmake</db:term>
+<db:listitem>
+<db:para>QT += testcpp</db:para>
+</db:listitem>
+</db:varlistentry>
+</db:variablelist>
+<db:section xml:id="details">
+<db:title>Detailed Description</db:title>
+<db:section xml:id="usage">
+<db:title>Usage</db:title>
+<db:para>This namespace is for testing QDoc output.</db:para>
+</db:section>
+</db:section>
+<db:section xml:id="classes">
+<db:title>Classes</db:title>
+<db:section>
+<db:title>class <db:link xlink:href="testqdoc-test.xml" xlink:role="class">Test</db:link></db:title>
+<db:para>A class in a namespace.</db:para>
+</db:section>
+<db:section>
+<db:title>class <db:link xlink:href="testqdoc-testderived.xml" xlink:role="class">TestDerived</db:link></db:title>
+<db:para>A class in a namespace, derived from <db:link xlink:href="testqdoc-test.xml">Test</db:link>.</db:para>
+</db:section>
+</db:section>
+<db:section xml:id="macro-documentation">
+<db:title>Macro Documentation</db:title>
+<db:section xml:id="QDOCTEST_MACRO">
+<db:title>QDOCTEST_MACRO</db:title>
+</db:section>
+</db:section>
+</db:article>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/scopedenum/expected/docbook/whatsnew.xml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/scopedenum/expected/docbook/whatsnew.xml
new file mode 100644
index 000000000..b87618d59
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/scopedenum/expected/docbook/whatsnew.xml
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<db:article xmlns:db="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" version="5.2" xml:lang="en">
+<db:info>
+<db:title>New Classes and Functions</db:title>
+<db:productname>TestCPP</db:productname>
+<db:titleabbrev>TestCPP Reference Documentation</db:titleabbrev>
+<db:abstract>
+<db:para>New classes documentation.</db:para>
+</db:abstract>
+</db:info>
+</db:article>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/scopedenum/expected/html/autolinking.html b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/scopedenum/expected/html/autolinking.html
new file mode 100644
index 000000000..ac04c33cd
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/scopedenum/expected/html/autolinking.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+<!-- classlists.qdoc -->
+ <title>Autolinking | TestCPP</title>
+</head>
+<body>
+<div class="sidebar">
+<div class="toc">
+<h3 id="toc">Contents</h3>
+<ul>
+<li class="level1"><a href="#testqdoc">TestQDoc</a></li>
+<li class="level1"><a href="#someprop">someProp</a></li>
+</ul>
+</div>
+<div class="sidebar-content" id="sidebar-content"></div></div>
+<h1 class="title">Autolinking</h1>
+<!-- $$$autolinking.html-description -->
+<div class="descr" id="details">
+<h2 id="testqdoc">TestQDoc</h2>
+<p>The string <a href="testqdoc.html" translate="no">TestQDoc</a> links to the C++ namespace unless linking explicitly, <a href="autolinking.html#testqdoc">like this</a>, or <a href="testqdoc.html" translate="no">this</a>. Also,</p>
+<p>Autolinks:</p>
+<ul>
+<li><a href="testqdoc-testderived.html" translate="no">TestQDoc::TestDerived</a></li>
+</ul>
+<p>Explicit links:</p>
+<ul>
+<li><a href="testqdoc-testderived.html" translate="no">TestQDoc::TestDerived</a></li>
+<li><a href="obsolete-classes.html#testqdoc">Obsolete Classes#TestQDoc</a></li>
+</ul>
+<h2 id="someprop">someProp</h2>
+</div>
+<!-- @@@autolinking.html -->
+</body>
+</html>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/scopedenum/expected/html/cpptypes.html b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/scopedenum/expected/html/cpptypes.html
new file mode 100644
index 000000000..633ca939b
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/scopedenum/expected/html/cpptypes.html
@@ -0,0 +1,24 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+<!-- classlists.qdoc -->
+ <title>Test C++ Types | TestCPP</title>
+</head>
+<body>
+<div class="sidebar"><div class="sidebar-content" id="sidebar-content"></div></div>
+<h1 class="title">Test C++ Types</h1>
+<!-- $$$cpptypes-description -->
+<div class="descr" id="details">
+<ul>
+<li translate="no"><a href="testqdoc-test.html">TestQDoc::Test</a></li>
+<li translate="no"><a href="testqdoc-test.html#QDOCTEST_MACRO2">TestQDoc::Test::QDOCTEST_MACRO2</a></li>
+<li translate="no"><a href="testqdoc-test.html#someFunctionDefaultArg">TestQDoc::Test::someFunctionDefaultArg()</a></li>
+</ul>
+</div>
+<!-- @@@cpptypes -->
+<div class="table"><table class="annotated">
+<tr class="odd topAlign"><td class="tblName" translate="no"><p><a href="testqdoc-test.html">TestQDoc::Test</a></p></td><td class="tblDescr"><p>A class in a namespace</p></td></tr>
+</table></div>
+</body>
+</html>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/scopedenum/expected/html/crossmoduleref.html b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/scopedenum/expected/html/crossmoduleref.html
new file mode 100644
index 000000000..2e938d336
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/scopedenum/expected/html/crossmoduleref.html
@@ -0,0 +1,47 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+<!-- testcpp.cpp -->
+ <meta name="description" content="Namespace that has documented functions in multiple modules.">
+ <title>CrossModuleRef Namespace | TestCPP</title>
+</head>
+<body>
+<div class="sidebar">
+<div class="toc">
+<h3 id="toc">Contents</h3>
+<ul>
+<li class="level1"><a href="#functions">Functions</a></li>
+<li class="level1"><a href="#details">Detailed Description</a></li>
+</ul>
+</div>
+<div class="sidebar-content" id="sidebar-content"></div></div>
+<h1 class="title" translate="no">CrossModuleRef Namespace</h1>
+<!-- $$$CrossModuleRef-brief -->
+<p>Namespace that has documented functions in multiple modules. <a href="#details">More...</a></p>
+<!-- @@@CrossModuleRef -->
+<div class="table"><table class="alignedsummary" translate="no">
+<tr><td class="memItemLeft rightAlign topAlign"> Header:</td><td class="memItemRight bottomAlign"> <span class="preprocessor">#include &lt;CrossModuleRef&gt;</span></td></tr>
+<tr><td class="memItemLeft rightAlign topAlign"> CMake:</td><td class="memItemRight bottomAlign"> find_package(Qt6 REQUIRED COMPONENTS QDocTest) <br/>
+target_link_libraries(mytarget PRIVATE Qt6::QDocTest)</td></tr>
+<tr><td class="memItemLeft rightAlign topAlign"> qmake:</td><td class="memItemRight bottomAlign"> QT += testcpp</td></tr>
+<tr><td class="memItemLeft rightAlign topAlign"> Since:</td><td class="memItemRight bottomAlign"> Qt 3.0</td></tr>
+</table></div>
+<h2 id="functions">Functions</h2>
+<div class="table"><table class="alignedsummary" translate="no">
+<tr><td class="memItemLeft rightAlign topAlign"> void </td><td class="memItemRight bottomAlign"><b><a href="crossmoduleref.html#documentMe" translate="no">documentMe</a></b>()</td></tr>
+</table></div>
+<!-- $$$CrossModuleRef-description -->
+<div class="descr">
+<h2 id="details">Detailed Description</h2>
+</div>
+<!-- @@@CrossModuleRef -->
+<div class="func">
+<h2>Function Documentation</h2>
+<!-- $$$documentMe[overload1]$$$documentMe -->
+<h3 class="fn" translate="no" id="documentMe"><span class="type">void</span> CrossModuleRef::<span class="name">documentMe</span>()</h3>
+<p>Document me!</p>
+<!-- @@@documentMe -->
+</div>
+</body>
+</html>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/scopedenum/expected/html/obsolete-classes.html b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/scopedenum/expected/html/obsolete-classes.html
new file mode 100644
index 000000000..01198c82d
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/scopedenum/expected/html/obsolete-classes.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+<!-- classlists.qdoc -->
+ <title>Obsolete Classes | TestCPP</title>
+</head>
+<body>
+<div class="sidebar">
+<div class="toc">
+<h3 id="toc">Contents</h3>
+<ul>
+<li class="level1"><a href="#classes-with-obsolete-members">Classes with obsolete members</a></li>
+<li class="level2"><a href="#testqdoc">TestQDoc</a></li>
+</ul>
+</div>
+<div class="sidebar-content" id="sidebar-content"></div></div>
+<h1 class="title">Obsolete Classes</h1>
+<!-- $$$obsolete-classes.html-description -->
+<div class="descr" id="details">
+<h2 id="classes-with-obsolete-members">Classes with obsolete members</h2>
+<div class="flowListDiv" translate="no">
+<dl class="flowList odd"><dt class="alphaChar"><b>T</b></dt>
+<dd><a href="testqdoc-test-obsolete.html">Test</a> (<a href="testqdoc.html">TestQDoc</a>)</dd>
+<dd><a href="testqdoc-testderived-obsolete.html">TestDerived</a> (<a href="testqdoc.html">TestQDoc</a>)</dd>
+</dl>
+</div>
+<h3 id="testqdoc">TestQDoc</h3>
+</div>
+<!-- @@@obsolete-classes.html -->
+</body>
+</html>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/scopedenum/expected/html/scoped-enum-linking.html b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/scopedenum/expected/html/scoped-enum-linking.html
new file mode 100644
index 000000000..c6229dac2
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/scopedenum/expected/html/scoped-enum-linking.html
@@ -0,0 +1,18 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+<!-- scopedenum.qdoc -->
+ <title>Enum Linking | TestCPP</title>
+</head>
+<body>
+<div class="sidebar"><div class="sidebar-content" id="sidebar-content"></div></div>
+<h1 class="title">Enum Linking</h1>
+<!-- $$$scoped-enum-linking.html-description -->
+<div class="descr" id="details">
+<p>Linking to <a href="testqdoc-test.html#ScopedEnum-enum" translate="no">All</a>.</p>
+<p>TestQDoc::Test::ClassicEnum::Howdy does not link, but <a href="testqdoc-test.html#ClassicEnum-enum" translate="no">TestQDoc::Test::Howdy</a> might.</p>
+</div>
+<!-- @@@scoped-enum-linking.html -->
+</body>
+</html>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/scopedenum/expected/html/testcpp-module.html b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/scopedenum/expected/html/testcpp-module.html
new file mode 100644
index 000000000..56f7f5a08
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/scopedenum/expected/html/testcpp-module.html
@@ -0,0 +1,55 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+<!-- testcpp.cpp -->
+ <meta name="description" content="A test module page.">
+ <title>QDoc Test C++ Classes | TestCPP</title>
+</head>
+<body>
+<div class="sidebar">
+<div class="toc">
+<h3 id="toc">Contents</h3>
+<ul>
+<li class="level1"><a href="#namespaces">Namespaces</a></li>
+<li class="level1"><a href="#classes">Classes</a></li>
+<li class="level1"><a href="#details">Detailed Description</a></li>
+<li class="level2"><a href="#linking-to-function-like-things">Linking to function-like things</a></li>
+<li class="level3"><a href="#section">section()</a></li>
+</ul>
+</div>
+<div class="sidebar-content" id="sidebar-content"></div></div>
+<h1 class="title" translate="no">QDoc Test C++ Classes</h1>
+<!-- $$$TestCPP-brief -->
+<p>A test module page. <a href="#details">More...</a></p>
+<!-- @@@TestCPP -->
+<p>This module was introduced in Qt 2.0.</p>
+<h2 id="namespaces">Namespaces</h2>
+<div class="table"><table class="annotated">
+<tr class="odd topAlign"><td class="tblName" translate="no"><p><a href="crossmoduleref.html">CrossModuleRef</a></p></td><td class="tblDescr"><p>Namespace that has documented functions in multiple modules</p></td></tr>
+<tr class="even topAlign"><td class="tblName" translate="no"><p><a href="testqdoc.html">TestQDoc</a></p></td><td class="tblDescr"><p>A namespace</p></td></tr>
+</table></div>
+<h2 id="classes">Classes</h2>
+<div class="table"><table class="annotated">
+<tr class="odd topAlign"><td class="tblName" translate="no"><p><a href="testqdoc-test.html">TestQDoc::Test</a></p></td><td class="tblDescr"><p>A class in a namespace</p></td></tr>
+<tr class="even topAlign"><td class="tblName" translate="no"><p><a href="testqdoc-testderived.html">TestQDoc::TestDerived</a></p></td><td class="tblDescr"><p>A class in a namespace, derived from Test</p></td></tr>
+</table></div>
+<!-- $$$TestCPP-description -->
+<div class="descr">
+<h2 id="details">Detailed Description</h2>
+<div class="admonition note">
+<p><b>Note: </b>This is just a test. /* Look, Ma! {I'm made of arguments!} */</p>
+</div>
+<h3 id="linking-to-function-like-things">Linking to function-like things</h3>
+<ul>
+<li><a href="testqdoc-test.html#someFunctionDefaultArg" translate="no">someFunctionDefaultArg</a>()</li>
+<li><a href="testqdoc-test.html#QDOCTEST_MACRO2" translate="no">QDOCTEST_MACRO2</a>()</li>
+<li><a href="testqdoc-test.html#QDOCTEST_MACRO2" translate="no">QDOCTEST_MACRO2</a>(int &amp;x)</li>
+<li><a href="testcpp-module.html#section" translate="no">section()</a></li>
+<li><a href="testcpp-module.html#section" translate="no">section() is a section title</a></li>
+</ul>
+<h4 id="section">section()</h4>
+</div>
+<!-- @@@TestCPP -->
+</body>
+</html>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/scopedenum/expected/html/testcpp.index b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/scopedenum/expected/html/testcpp.index
new file mode 100644
index 000000000..0ea9571f0
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/scopedenum/expected/html/testcpp.index
@@ -0,0 +1,91 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE QDOCINDEX>
+<INDEX url="" title="TestCPP Reference Documentation" version="" project="TestCPP">
+ <namespace name="" status="active" access="public" module="testcpp">
+ <function name="QDOCTEST_MACRO" href="testqdoc.html#QDOCTEST_MACRO" status="active" access="public" documented="true" related="0" meta="macrowithoutparams" signature="QDOCTEST_MACRO"/>
+ <function name="QDOCTEST_MACRO2" href="testqdoc-test.html#QDOCTEST_MACRO2" status="active" access="public" documented="true" related="1" since="Test 1.1" meta="macrowithparams" brief="A macro with argument x" signature="QDOCTEST_MACRO2(int &amp;x)" groups="testgroup">
+ <parameter type="int &amp;" name="x" default=""/>
+ </function>
+ <page name="autolinking.html" href="autolinking.html" status="active" location="classlists.qdoc" documented="true" subtype="page" title="Autolinking" fulltitle="Autolinking" subtitle="">
+ <contents name="testqdoc" title="TestQDoc" level="1"/>
+ <contents name="someprop" title="someProp" level="1"/>
+ </page>
+ <namespace name="CrossModuleRef" href="crossmoduleref.html" status="active" access="public" location="testcpp.h" since="3.0" documented="true" module="TestCPP" brief="Namespace that has documented functions in multiple modules">
+ <function name="documentMe" fullname="CrossModuleRef::documentMe" href="crossmoduleref.html#documentMe" status="active" access="public" location="testcpp.h" documented="true" meta="plain" type="void" signature="void documentMe()"/>
+ </namespace>
+ <class name="DontLinkToMe" href="dontlinktome.html" status="ignored" access="public" location="testcpp.h" since="2.0" documented="true" module="TestCPP" brief="Class that does not generate documentation"/>
+ <page name="scoped-enum-linking.html" href="scoped-enum-linking.html" status="active" location="scopedenum.qdoc" documented="true" subtype="page" title="Enum Linking" fulltitle="Enum Linking" subtitle=""/>
+ <page name="whatsnew.html" href="whatsnew.html" status="active" location="scopedenum.qdoc" documented="true" subtype="page" title="New Classes and Functions" fulltitle="New Classes and Functions" subtitle="" brief="New classes documentation"/>
+ <page name="obsolete-classes.html" href="obsolete-classes.html" status="active" location="classlists.qdoc" documented="true" subtype="page" title="Obsolete Classes" fulltitle="Obsolete Classes" subtitle="">
+ <contents name="classes-with-obsolete-members" title="Classes with obsolete members" level="1"/>
+ <contents name="testqdoc" title="TestQDoc" level="2"/>
+ </page>
+ <namespace name="TestQDoc" href="testqdoc.html" status="active" access="public" location="testcpp.h" since="2.0" documented="true" module="TestCPP" brief="A namespace">
+ <contents name="usage" title="Usage" level="1"/>
+ <function name="QDOCTEST_MACRO" href="testqdoc.html#QDOCTEST_MACRO" status="active" access="public" documented="true" related="0" meta="macrowithoutparams" signature="QDOCTEST_MACRO"/>
+ <class threadsafety="reentrant" name="Test" fullname="TestQDoc::Test" href="testqdoc-test.html" status="active" access="public" location="testcpp.h" since="2.0" documented="true" groups="cpptypes,testgroup" module="TestCPP" brief="A class in a namespace">
+ <function name="QDOCTEST_MACRO2" href="testqdoc-test.html#QDOCTEST_MACRO2" status="active" access="public" documented="true" related="1" since="Test 1.1" meta="macrowithparams" brief="A macro with argument x" signature="QDOCTEST_MACRO2(int &amp;x)" groups="testgroup">
+ <parameter type="int &amp;" name="x" default=""/>
+ </function>
+ <function name="anotherObsoleteMember" fullname="TestQDoc::Test::anotherObsoleteMember" href="testqdoc-test-obsolete.html#anotherObsoleteMember" status="deprecated" access="public" location="testcpp.h" documented="true" meta="plain" type="void" signature="void anotherObsoleteMember()"/>
+ <function name="deprecatedMember" fullname="TestQDoc::Test::deprecatedMember" href="testqdoc-test-obsolete.html#deprecatedMember" status="deprecated" access="public" location="testcpp.h" documented="true" meta="plain" type="void" signature="void deprecatedMember()"/>
+ <function name="funcPtr" fullname="TestQDoc::Test::funcPtr" href="testqdoc-test.html#funcPtr" status="active" access="public" location="testcpp.h" documented="true" meta="plain" type="void (*)(bool)" signature="void (*)(bool) funcPtr(bool b, const char *s)">
+ <parameter type="bool" name="b" default=""/>
+ <parameter type="const char *" name="s" default=""/>
+ </function>
+ <function name="inlineFunction" fullname="TestQDoc::Test::inlineFunction" href="testqdoc-test.html#inlineFunction" status="active" access="public" location="testcpp.h" documented="true" meta="plain" type="void" brief="An inline function, documented using the \fn QDoc command" signature="void inlineFunction()"/>
+ <function name="methodWithEmDashInItsDocs" fullname="TestQDoc::Test::methodWithEmDashInItsDocs" href="testqdoc-test.html#methodWithEmDashInItsDocs" status="active" access="public" location="testcpp.h" documented="true" meta="plain" type="void" signature="void methodWithEmDashInItsDocs()"/>
+ <function name="methodWithEnDashInItsDocs" fullname="TestQDoc::Test::methodWithEnDashInItsDocs" href="testqdoc-test.html#methodWithEnDashInItsDocs" status="active" access="public" location="testcpp.h" documented="true" meta="plain" type="void" signature="void methodWithEnDashInItsDocs()"/>
+ <function name="obsoleteMember" fullname="TestQDoc::Test::obsoleteMember" href="testqdoc-test-obsolete.html#obsoleteMember" status="deprecated" access="public" location="testcpp.h" documented="true" meta="plain" type="void" signature="void obsoleteMember()"/>
+ <function name="operator++" fullname="TestQDoc::Test::operator++" href="testqdoc-test-obsolete.html#operator-2b-2b" status="deprecated" access="public" location="testcpp.h" documented="true" meta="plain" type="TestQDoc::Test &amp;" signature="TestQDoc::Test &amp; operator++()"/>
+ <function name="operator--" fullname="TestQDoc::Test::operator--" href="testqdoc-test-obsolete.html#operator--" status="deprecated" access="public" location="testcpp.h" documented="true" meta="plain" type="TestQDoc::Test &amp;" signature="TestQDoc::Test &amp; operator--()"/>
+ <function name="operator==" href="testqdoc-test.html#operator-eq-eq" status="active" access="public" location="testcpp.h" documented="true" related="2" meta="plain" type="bool" signature="bool operator==(const TestQDoc::Test &amp;lhs, const TestQDoc::Test &amp;rhs)">
+ <parameter type="const TestQDoc::Test &amp;" name="lhs" default=""/>
+ <parameter type="const TestQDoc::Test &amp;" name="rhs" default=""/>
+ </function>
+ <function name="overload" fullname="TestQDoc::Test::overload" href="testqdoc-test.html#overload" status="active" access="protected" location="testcpp.h" documented="true" meta="plain" type="void" signature="void overload()"/>
+ <function name="overload" fullname="TestQDoc::Test::overload" href="testqdoc-test.html#overload-1" status="active" access="protected" location="testcpp.h" documented="true" since="Test 1.2" meta="plain" overload="true" overload-number="1" type="void" signature="void overload(bool b)">
+ <parameter type="bool" name="b" default=""/>
+ </function>
+ <function name="someFunction" fullname="TestQDoc::Test::someFunction" href="testqdoc-test.html#someFunction" status="active" access="public" location="testcpp.h" documented="true" meta="plain" type="int" signature="int someFunction(int, int v)">
+ <parameter type="int" name="" default=""/>
+ <parameter type="int" name="v" default="0"/>
+ </function>
+ <function name="someFunctionDefaultArg" fullname="TestQDoc::Test::someFunctionDefaultArg" href="testqdoc-test.html#someFunctionDefaultArg" threadsafety="non-reentrant" status="active" access="public" location="testcpp.h" documented="true" meta="plain" const="true" type="void" signature="void someFunctionDefaultArg(int i, bool b) const" groups="testgroup">
+ <parameter type="int" name="i" default=""/>
+ <parameter type="bool" name="b" default="false"/>
+ </function>
+ <function name="virtualFun" fullname="TestQDoc::Test::virtualFun" href="testqdoc-test.html#virtualFun" status="active" access="public" location="testcpp.h" documented="true" meta="plain" virtual="virtual" type="void" signature="void virtualFun()"/>
+ <enum name="ClassicEnum" fullname="TestQDoc::Test::ClassicEnum" href="testqdoc-test.html#ClassicEnum-enum" status="active" access="public" location="testcpp.h" documented="true">
+ <value name="Yee" value="0"/>
+ <value name="Haw" value="1"/>
+ <value name="Howdy" value="2"/>
+ <value name="Partner" value="3"/>
+ </enum>
+ <enum name="ScopedEnum" fullname="TestQDoc::Test::ScopedEnum" href="testqdoc-test.html#ScopedEnum-enum" status="active" access="public" location="testcpp.h" documented="true" scoped="true">
+ <value name="This" value="0x01"/>
+ <value name="That" value="0x02"/>
+ <value name="All" value="This | That" since="2.0"/>
+ <value name="OmittedValue" value="99"/>
+ <value name="UselessValue" value="100"/>
+ <value name="VeryLastValue" value="101"/>
+ </enum>
+ <typedef name="SomeType" fullname="TestQDoc::Test::SomeType" href="testqdoc-test.html#SomeType-typedef" status="active" access="public" location="testcpp.h" documented="true"/>
+ </class>
+ <class name="TestDerived" fullname="TestQDoc::TestDerived" href="testqdoc-testderived.html" status="active" access="public" location="testcpp.h" since="2.0" documented="true" bases="TestQDoc::Test" module="TestCPP" brief="A class in a namespace, derived from Test">
+ <function name="someValue" fullname="TestQDoc::TestDerived::someValue" href="testqdoc-testderived.html#someValue" status="active" access="public" location="testcpp.h" documented="true" meta="plain" type="TestQDoc::TestDerived::NotTypedef" signature="TestQDoc::TestDerived::NotTypedef someValue()"/>
+ <function name="staticObsoleteMember" fullname="TestQDoc::TestDerived::staticObsoleteMember" href="testqdoc-testderived-obsolete.html#staticObsoleteMember" status="deprecated" access="public" location="testcpp.h" documented="true" meta="plain" static="true" type="void" signature="void staticObsoleteMember()"/>
+ <function name="virtualFun" fullname="TestQDoc::TestDerived::virtualFun" href="testqdoc-testderived.html#virtualFun" status="active" access="public" location="testcpp.h" documented="true" meta="plain" virtual="virtual" override="true" type="void" signature="void virtualFun() override"/>
+ <typedef name="DerivedType" fullname="TestQDoc::TestDerived::DerivedType" href="testqdoc-testderived.html#DerivedType-typedef" status="active" access="public" location="testcpp.h" documented="true" aliasedtype="Test::SomeType"/>
+ <typedef name="NotTypedef" fullname="TestQDoc::TestDerived::NotTypedef" href="testqdoc-testderived.html#NotTypedef-typedef" status="active" access="public" location="testcpp.h" documented="true" aliasedtype="int"/>
+ </class>
+ </namespace>
+ <page name="https://doc.qt.io/qt/17-qdoc-commands-thread.html#reentrant-command" href="https://doc.qt.io/qt/17-qdoc-commands-thread.html#reentrant-command" status="active" location="classlists.qdoc" documented="true" subtype="externalpage" title="reentrant" fulltitle="reentrant" subtitle=""/>
+ <group name="cpptypes" href="cpptypes.html" status="active" location="classlists.qdoc" documented="true" seen="true" title="Test C++ Types"/>
+ <group name="testgroup" href="testgroup.html" status="internal" seen="false" title=""/>
+ <module name="TestCPP" href="testcpp-module.html" status="active" since="2.0" documented="true" seen="true" title="QDoc Test C++ Classes" brief="A test module page">
+ <contents name="linking-to-function-like-things" title="Linking to function-like things" level="1"/>
+ <contents name="section" title="section()" level="2"/>
+ </module>
+ </namespace>
+</INDEX>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/scopedenum/expected/html/testqdoc-test-members.html b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/scopedenum/expected/html/testqdoc-test-members.html
new file mode 100644
index 000000000..8f065c3b0
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/scopedenum/expected/html/testqdoc-test-members.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+<!-- testcpp.cpp -->
+ <meta name="description" content="A class in a namespace.">
+ <title>List of All Members for Test | TestCPP</title>
+</head>
+<body>
+<li><a href="testcpp-module.html" translate="no">C++ Classes</a></li>
+<li>Test</li>
+<div class="sidebar"><div class="sidebar-content" id="sidebar-content"></div></div>
+<h1 class="title" translate="no">List of All Members for Test</h1>
+<p>This is the complete list of members for <a href="testqdoc-test.html">TestQDoc::Test</a>, including inherited members.</p>
+<ul>
+<li class="fn" translate="no">enum <span class="name"><b><a href="testqdoc-test.html#ClassicEnum-enum" translate="no">ClassicEnum</a></b></span></li>
+<li class="fn" translate="no">enum class <span class="name"><b><a href="testqdoc-test.html#ScopedEnum-enum" translate="no">ScopedEnum</a></b></span></li>
+<li class="fn" translate="no"><span class="name"><b><a href="testqdoc-test.html#SomeType-typedef" translate="no">SomeType</a></b></span></li>
+<li class="fn" translate="no"><span class="name"><b><a href="testqdoc-test.html#funcPtr" translate="no">funcPtr</a></b></span>(bool, const char *) : void (*)(bool)</li>
+<li class="fn" translate="no"><span class="name"><b><a href="testqdoc-test.html#inlineFunction" translate="no">inlineFunction</a></b></span>()</li>
+<li class="fn" translate="no"><span class="name"><b><a href="testqdoc-test.html#methodWithEmDashInItsDocs" translate="no">methodWithEmDashInItsDocs</a></b></span>()</li>
+<li class="fn" translate="no"><span class="name"><b><a href="testqdoc-test.html#methodWithEnDashInItsDocs" translate="no">methodWithEnDashInItsDocs</a></b></span>()</li>
+<li class="fn" translate="no"><span class="name"><b><a href="testqdoc-test.html#overload" translate="no">overload</a></b></span>()</li>
+<li class="fn" translate="no"><span class="name"><b><a href="testqdoc-test.html#overload-1" translate="no">overload</a></b></span>(bool)</li>
+<li class="fn" translate="no"><span class="name"><b><a href="testqdoc-test.html#someFunction" translate="no">someFunction</a></b></span>(int, int) : int</li>
+<li class="fn" translate="no"><span class="name"><b><a href="testqdoc-test.html#someFunctionDefaultArg" translate="no">someFunctionDefaultArg</a></b></span>(int, bool) const</li>
+<li class="fn" translate="no"><span class="name"><b><a href="testqdoc-test.html#virtualFun" translate="no">virtualFun</a></b></span>()</li>
+</ul>
+</body>
+</html>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/scopedenum/expected/html/testqdoc-test-obsolete.html b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/scopedenum/expected/html/testqdoc-test-obsolete.html
new file mode 100644
index 000000000..2b5192da1
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/scopedenum/expected/html/testqdoc-test-obsolete.html
@@ -0,0 +1,45 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+<!-- testcpp.cpp -->
+ <meta name="description" content="A class in a namespace.">
+ <title>Obsolete Members for Test | TestCPP</title>
+</head>
+<body>
+<li><a href="testcpp-module.html" translate="no">C++ Classes</a></li>
+<li>Test</li>
+<div class="sidebar"><div class="sidebar-content" id="sidebar-content"></div></div>
+<h1 class="title" translate="no">Obsolete Members for Test</h1>
+<p><b>The following members of class <a href="testqdoc-test.html" translate="no">Test</a> are deprecated.</b> They are provided to keep old source code working. We strongly advise against using them in new code.</p>
+<h2>Public Functions</h2>
+<div class="table"><table class="alignedsummary" translate="no">
+<tr><td class="memItemLeft topAlign rightAlign"> <code class="summary extra" translate="no">(deprecated)</code> void </td><td class="memItemRight bottomAlign"><b><a href="testqdoc-test-obsolete.html#anotherObsoleteMember" translate="no">anotherObsoleteMember</a></b>()</td></tr>
+<tr><td class="memItemLeft topAlign rightAlign"> <code class="summary extra" translate="no">(deprecated in 6.0)</code> void </td><td class="memItemRight bottomAlign"><b><a href="testqdoc-test-obsolete.html#deprecatedMember" translate="no">deprecatedMember</a></b>()</td></tr>
+<tr><td class="memItemLeft topAlign rightAlign"> <code class="summary extra" translate="no">(deprecated)</code> void </td><td class="memItemRight bottomAlign"><b><a href="testqdoc-test-obsolete.html#obsoleteMember" translate="no">obsoleteMember</a></b>()</td></tr>
+<tr><td class="memItemLeft topAlign rightAlign"> <code class="summary extra" translate="no">(deprecated)</code> TestQDoc::Test &amp;</td><td class="memItemRight bottomAlign"><b><a href="testqdoc-test-obsolete.html#operator-2b-2b" translate="no">operator++</a></b>()</td></tr>
+<tr><td class="memItemLeft topAlign rightAlign"> <code class="summary extra" translate="no">(deprecated)</code> TestQDoc::Test &amp;</td><td class="memItemRight bottomAlign"><b><a href="testqdoc-test-obsolete.html#operator--" translate="no">operator--</a></b>()</td></tr>
+</table></div>
+<h2>Member Function Documentation</h2>
+<!-- $$$ -->
+<div class="fngroup">
+<h3 class="fn fngroupitem" translate="no" id="operator-2b-2b"><code class="details extra" translate="no">[deprecated]</code> <span class="type"><a href="testqdoc-test.html" translate="no">TestQDoc::Test</a></span> &amp;Test::<span class="name">operator++</span>()</h3><h3 class="fn fngroupitem" translate="no" id="operator--"><code class="details extra" translate="no">[deprecated]</code> <span class="type"><a href="testqdoc-test.html" translate="no">TestQDoc::Test</a></span> &amp;Test::<span class="name">operator--</span>()</h3></div>
+<p>This function is deprecated. We strongly advise against using it in new code.</p>
+<!-- @@@ -->
+<!-- $$$anotherObsoleteMember[overload1]$$$anotherObsoleteMember -->
+<h3 class="fn" translate="no" id="anotherObsoleteMember"><code class="details extra" translate="no">[deprecated]</code> <span class="type">void</span> Test::<span class="name">anotherObsoleteMember</span>()</h3>
+<p>This function is deprecated. We strongly advise against using it in new code.</p>
+<p>Use <a href="testqdoc-test-obsolete.html#obsoleteMember" translate="no">obsoleteMember</a>() instead.</p>
+<!-- @@@anotherObsoleteMember -->
+<!-- $$$deprecatedMember[overload1]$$$deprecatedMember -->
+<h3 class="fn" translate="no" id="deprecatedMember"><code class="details extra" translate="no">[deprecated in 6.0]</code> <span class="type">void</span> Test::<span class="name">deprecatedMember</span>()</h3>
+<p>This function is deprecated since 6.0. We strongly advise against using it in new code.</p>
+<p>Use <a href="testqdoc-test.html#someFunction" translate="no">someFunction</a>() instead.</p>
+<!-- @@@deprecatedMember -->
+<!-- $$$obsoleteMember[overload1]$$$obsoleteMember -->
+<h3 class="fn" translate="no" id="obsoleteMember"><code class="details extra" translate="no">[deprecated]</code> <span class="type">void</span> Test::<span class="name">obsoleteMember</span>()</h3>
+<p>This function is deprecated. We strongly advise against using it in new code.</p>
+<p>Use <a href="testqdoc-test.html#someFunction" translate="no">someFunction</a>() instead.</p>
+<!-- @@@obsoleteMember -->
+</body>
+</html>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/scopedenum/expected/html/testqdoc-test.html b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/scopedenum/expected/html/testqdoc-test.html
new file mode 100644
index 000000000..b89711932
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/scopedenum/expected/html/testqdoc-test.html
@@ -0,0 +1,173 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+<!-- testcpp.cpp -->
+ <meta name="description" content="A class in a namespace.">
+ <title>Test Class | TestCPP</title>
+</head>
+<body>
+<li><a href="testcpp-module.html" translate="no">C++ Classes</a></li>
+<li>Test</li>
+<div class="sidebar">
+<div class="toc">
+<h3 id="toc">Contents</h3>
+<ul>
+<li class="level1"><a href="#public-types">Public Types</a></li>
+<li class="level1"><a href="#public-functions">Public Functions</a></li>
+<li class="level1"><a href="#protected-functions">Protected Functions</a></li>
+<li class="level1"><a href="#related-non-members">Related Non-Members</a></li>
+<li class="level1"><a href="#macros">Macros</a></li>
+<li class="level1"><a href="#details">Detailed Description</a></li>
+</ul>
+</div>
+<div class="sidebar-content" id="sidebar-content"></div></div>
+<h1 class="title" translate="no">Test Class</h1>
+<span class="small-subtitle" translate="no">class <a href="testqdoc.html" translate="no">TestQDoc</a>::Test</span>
+<!-- $$$Test-brief -->
+<p>A class in a namespace. <a href="#details">More...</a></p>
+<!-- @@@Test -->
+<div class="table"><table class="alignedsummary" translate="no">
+<tr><td class="memItemLeft rightAlign topAlign"> Header:</td><td class="memItemRight bottomAlign"> <span class="preprocessor">#include &lt;Test&gt;</span></td></tr>
+<tr><td class="memItemLeft rightAlign topAlign"> CMake:</td><td class="memItemRight bottomAlign"> find_package(Qt6 REQUIRED COMPONENTS QDocTest) <br/>
+target_link_libraries(mytarget PRIVATE Qt6::QDocTest)</td></tr>
+<tr><td class="memItemLeft rightAlign topAlign"> qmake:</td><td class="memItemRight bottomAlign"> QT += testcpp</td></tr>
+<tr><td class="memItemLeft rightAlign topAlign"> Since:</td><td class="memItemRight bottomAlign"> Qt 2.0</td></tr>
+<tr><td class="memItemLeft rightAlign topAlign"> Inherited By:</td><td class="memItemRight bottomAlign"> <p><a href="testqdoc-testderived.html" translate="no">TestQDoc::TestDerived</a></p>
+</td></tr>
+</table></div>
+<ul>
+<li><a href="testqdoc-test-members.html">List of all members, including inherited members</a></li>
+<li><a href="testqdoc-test-obsolete.html">Deprecated members</a></li>
+<li>Test is part of <a href="cpptypes.html">Test C++ Types</a>.</li>
+</ul>
+<p><b>Note:</b> All functions in this class are <a href="https://doc.qt.io/qt/17-qdoc-commands-thread.html#reentrant-command">reentrant</a> with the following exceptions:</p>
+<ul>
+<li><a href="testqdoc-test.html#someFunctionDefaultArg" translate="no">someFunctionDefaultArg</a>(int i, bool b) const</li>
+</ul>
+<h2 id="public-types">Public Types</h2>
+<div class="table"><table class="alignedsummary" translate="no">
+<tr><td class="memItemLeft rightAlign topAlign"> enum </td><td class="memItemRight bottomAlign"><b><a href="testqdoc-test.html#ClassicEnum-enum" translate="no">ClassicEnum</a></b> { Yee, Haw, Howdy, Partner }</td></tr>
+<tr><td class="memItemLeft rightAlign topAlign"> enum class </td><td class="memItemRight bottomAlign"><b><a href="testqdoc-test.html#ScopedEnum-enum" translate="no">ScopedEnum</a></b> { This, That, All }</td></tr>
+<tr><td class="memItemLeft rightAlign topAlign"> </td><td class="memItemRight bottomAlign"><b><a href="testqdoc-test.html#SomeType-typedef" translate="no">SomeType</a></b></td></tr>
+</table></div>
+<h2 id="public-functions">Public Functions</h2>
+<div class="table"><table class="alignedsummary" translate="no">
+<tr><td class="memItemLeft rightAlign topAlign"> void (*)(bool) </td><td class="memItemRight bottomAlign"><b><a href="testqdoc-test.html#funcPtr" translate="no">funcPtr</a></b>(bool <i>b</i>, const char *<i>s</i>)</td></tr>
+<tr><td class="memItemLeft rightAlign topAlign"> void </td><td class="memItemRight bottomAlign"><b><a href="testqdoc-test.html#inlineFunction" translate="no">inlineFunction</a></b>()</td></tr>
+<tr><td class="memItemLeft rightAlign topAlign"> void </td><td class="memItemRight bottomAlign"><b><a href="testqdoc-test.html#methodWithEmDashInItsDocs" translate="no">methodWithEmDashInItsDocs</a></b>()</td></tr>
+<tr><td class="memItemLeft rightAlign topAlign"> void </td><td class="memItemRight bottomAlign"><b><a href="testqdoc-test.html#methodWithEnDashInItsDocs" translate="no">methodWithEnDashInItsDocs</a></b>()</td></tr>
+<tr><td class="memItemLeft rightAlign topAlign"> int </td><td class="memItemRight bottomAlign"><b><a href="testqdoc-test.html#someFunction" translate="no">someFunction</a></b>(int, int <i>v</i> = 0)</td></tr>
+<tr><td class="memItemLeft rightAlign topAlign"> void </td><td class="memItemRight bottomAlign"><b><a href="testqdoc-test.html#someFunctionDefaultArg" translate="no">someFunctionDefaultArg</a></b>(int <i>i</i>, bool <i>b</i> = false) const</td></tr>
+<tr><td class="memItemLeft rightAlign topAlign"> virtual void </td><td class="memItemRight bottomAlign"><b><a href="testqdoc-test.html#virtualFun" translate="no">virtualFun</a></b>()</td></tr>
+</table></div>
+<h2 id="protected-functions">Protected Functions</h2>
+<div class="table"><table class="alignedsummary" translate="no">
+<tr><td class="memItemLeft rightAlign topAlign"> void </td><td class="memItemRight bottomAlign"><b><a href="testqdoc-test.html#overload" translate="no">overload</a></b>()</td></tr>
+<tr><td class="memItemLeft rightAlign topAlign"> <code class="summary extra" translate="no">(since Test 1.2)</code> void </td><td class="memItemRight bottomAlign"><b><a href="testqdoc-test.html#overload-1" translate="no">overload</a></b>(bool <i>b</i>)</td></tr>
+</table></div>
+<h2 id="related-non-members">Related Non-Members</h2>
+<div class="table"><table class="alignedsummary" translate="no">
+<tr><td class="memItemLeft rightAlign topAlign"> bool </td><td class="memItemRight bottomAlign"><b><a href="testqdoc-test.html#operator-eq-eq" translate="no">operator==</a></b>(const TestQDoc::Test &amp;<i>lhs</i>, const TestQDoc::Test &amp;<i>rhs</i>)</td></tr>
+</table></div>
+<h2 id="macros">Macros</h2>
+<div class="table"><table class="alignedsummary" translate="no">
+<tr><td class="memItemLeft rightAlign topAlign"> <code class="summary extra" translate="no">(since Test 1.1)</code> </td><td class="memItemRight bottomAlign"><b><a href="testqdoc-test.html#QDOCTEST_MACRO2" translate="no">QDOCTEST_MACRO2</a></b>(int &amp;<i>x</i>)</td></tr>
+</table></div>
+<!-- $$$Test-description -->
+<div class="descr">
+<h2 id="details">Detailed Description</h2>
+</div>
+<!-- @@@Test -->
+<div class="types">
+<h2>Member Type Documentation</h2>
+<!-- $$$ClassicEnum$$$Yee$$$Haw$$$Howdy$$$Partner -->
+<h3 class="fn" translate="no" id="ClassicEnum-enum">enum Test::<span class="name">ClassicEnum</span></h3>
+<div class="table"><table class="valuelist"><tr><th class="tblConst">Constant</th><th class="tblVal">Value</th></tr>
+<tr><td class="topAlign"><code translate="no">TestQDoc::Test::Yee</code></td><td class="topAlign tblval"><code translate="no">0</code></td></tr>
+<tr><td class="topAlign"><code translate="no">TestQDoc::Test::Haw</code></td><td class="topAlign tblval"><code translate="no">1</code></td></tr>
+<tr><td class="topAlign"><code translate="no">TestQDoc::Test::Howdy</code></td><td class="topAlign tblval"><code translate="no">2</code></td></tr>
+<tr><td class="topAlign"><code translate="no">TestQDoc::Test::Partner</code></td><td class="topAlign tblval"><code translate="no">3</code></td></tr>
+</table></div>
+<!-- @@@ClassicEnum -->
+<!-- $$$ScopedEnum$$$This$$$That$$$All$$$OmittedValue$$$UselessValue$$$VeryLastValue -->
+<h3 class="fn" translate="no" id="ScopedEnum-enum">enum class Test::<span class="name">ScopedEnum</span></h3>
+<p>This enum has a brief to trigger a bug in CMD_BRIEF.</p>
+<div class="table"><table class="valuelist"><tr valign="top" class="odd"><th class="tblConst">Constant</th><th class="tblval">Value</th><th class="tbldscr">Description</th></tr>
+<tr><td class="topAlign"><code translate="no">TestQDoc::Test::ScopedEnum::This</code></td><td class="topAlign tblval"><code translate="no">0x01</code></td><td class="topAlign">Something</td></tr>
+<tr><td class="topAlign"><code translate="no">TestQDoc::Test::ScopedEnum::That</code></td><td class="topAlign tblval"><code translate="no">0x02</code></td><td class="topAlign">Something else</td></tr>
+<tr><td class="topAlign"><code translate="no">TestQDoc::Test::ScopedEnum::All (since Qt 2.0)</code></td><td class="topAlign tblval"><code translate="no">This | That</code></td><td class="topAlign">Everything</td></tr>
+</table></div>
+<p>A scoped enum.</p>
+<!-- @@@ScopedEnum -->
+<!-- $$$SomeType -->
+<h3 class="fn" translate="no" id="SomeType-typedef">Test::<span class="name">SomeType</span></h3>
+<p>A typedef.</p>
+<!-- @@@SomeType -->
+</div>
+<div class="func">
+<h2>Member Function Documentation</h2>
+<!-- $$$ -->
+<div class="fngroup">
+<h3 class="fn fngroupitem" translate="no" id="overload"><code class="details extra" translate="no">[protected]</code> <span class="type">void</span> Test::<span class="name">overload</span>()</h3><h3 class="fn fngroupitem" translate="no" id="overload-1"><code class="details extra" translate="no">[protected, since Test 1.2]</code> <span class="type">void</span> Test::<span class="name">overload</span>(<span class="type">bool</span> <i>b</i>)</h3></div>
+<p>Overloads that share a documentation comment, optionally taking a parameter <i translate="no">b</i>.</p>
+<!-- @@@ -->
+<!-- $$$funcPtr[overload1]$$$funcPtrboolconstchar* -->
+<h3 class="fn" translate="no" id="funcPtr"><span class="type">void</span> (*)(<span class="type">bool</span>) Test::<span class="name">funcPtr</span>(<span class="type">bool</span> <i>b</i>, const <span class="type">char</span> *<i>s</i>)</h3>
+<p>Returns a pointer to a function that takes a boolean. Uses <i translate="no">b</i> and <i translate="no">s</i>.</p>
+<!-- @@@funcPtr -->
+<!-- $$$inlineFunction[overload1]$$$inlineFunction -->
+<h3 class="fn" translate="no" id="inlineFunction"><span class="type">void</span> Test::<span class="name">inlineFunction</span>()</h3>
+<p>An inline function, documented using the \fn QDoc command.</p>
+<!-- @@@inlineFunction -->
+<!-- $$$methodWithEmDashInItsDocs[overload1]$$$methodWithEmDashInItsDocs -->
+<h3 class="fn" translate="no" id="methodWithEmDashInItsDocs"><span class="type">void</span> Test::<span class="name">methodWithEmDashInItsDocs</span>()</h3>
+<p>This method has em dashes in its documentation&mdash;as you'll find represented by <code translate="no">---</code> in the sources&mdash;here and there. The important bit to note is that when passed e.g. to the \c command, the three hyphens are processed as input to the command and not replaced by an em dash.</p>
+<p>-----------------------------------------------------------------------</p>
+<p>People can still add a bunch of dashes, though, without QDoc replacing them all with a series of em dashes.</p>
+<p>&mdash;You can also start a new paragraph with an em dash, if you want to.</p>
+<p><b>See also </b><a href="testqdoc-test.html#methodWithEnDashInItsDocs" translate="no">methodWithEnDashInItsDocs</a>.</p>
+<!-- @@@methodWithEmDashInItsDocs -->
+<!-- $$$methodWithEnDashInItsDocs[overload1]$$$methodWithEnDashInItsDocs -->
+<h3 class="fn" translate="no" id="methodWithEnDashInItsDocs"><span class="type">void</span> Test::<span class="name">methodWithEnDashInItsDocs</span>()</h3>
+<p>This method has en dashes in its documentation &ndash; as you'll find represented by <code translate="no">--</code> in the sources &ndash; here and there. The important bit to note is that when passed e.g. to the \c command, the two hyphens are processed as input to the command and not replaced by an en dash. This also applies to code blocks, where otherwise, the decrement operator would get completely borked:</p>
+<pre class="cpp" translate="no"><span class="keyword">for</span> (<span class="type">int</span> i <span class="operator">=</span> <span class="number">42</span>; i <span class="operator">&gt;</span> <span class="number">0</span>; <span class="operator">-</span><span class="operator">-</span>i)
+ <span class="comment">// Do something cool during countdown.</span></pre>
+<p>...as it would be silly if this would output &ndash;i instead of <code translate="no">--i</code>.</p>
+<p>-----------------------------------------------------------------------</p>
+<p>It still allows people to add a bunch of dashes, though, without replacing them all with a series of en dashes. Of course, they might want to use the \hr command instead, like this:</p>
+<hr />
+<p>&ndash; You can also start a new paragraph with an en dash, if you want to.</p>
+<p><b>See also </b>methodWithEnDashInItsDocs.</p>
+<!-- @@@methodWithEnDashInItsDocs -->
+<!-- $$$someFunction[overload1]$$$someFunctionintint -->
+<h3 class="fn" translate="no" id="someFunction"><span class="type">int</span> Test::<span class="name">someFunction</span>(<span class="type">int</span>, <span class="type">int</span> <i>v</i> = 0)</h3>
+<p>Function that takes a parameter <i translate="no">v</i>. Also returns the value of <i translate="no">v</i>.</p>
+<!-- @@@someFunction -->
+<!-- $$$someFunctionDefaultArg[overload1]$$$someFunctionDefaultArgintbool -->
+<h3 class="fn" translate="no" id="someFunctionDefaultArg"><span class="type">void</span> Test::<span class="name">someFunctionDefaultArg</span>(<span class="type">int</span> <i>i</i>, <span class="type">bool</span> <i>b</i> = false) const</h3>
+<p>Function that takes a parameter <i translate="no">i</i> and <i translate="no">b</i>.</p>
+<p><b>Warning:</b> This function is not <a href="https://doc.qt.io/qt/17-qdoc-commands-thread.html#reentrant-command">reentrant</a>.</p>
+<!-- @@@someFunctionDefaultArg -->
+<!-- $$$virtualFun[overload1]$$$virtualFun -->
+<h3 class="fn" translate="no" id="virtualFun"><code class="details extra" translate="no">[virtual]</code> <span class="type">void</span> Test::<span class="name">virtualFun</span>()</h3>
+<p>Function that must be reimplemented.</p>
+<!-- @@@virtualFun -->
+</div>
+<div class="relnonmem">
+<h2>Related Non-Members</h2>
+<!-- $$$operator==[overload1]$$$operator==constTestQDoc::Test&constTestQDoc::Test& -->
+<h3 class="fn" translate="no" id="operator-eq-eq"><span class="type">bool</span> <span class="name">operator==</span>(const <span class="type"><a href="testqdoc-test.html" translate="no">TestQDoc::Test</a></span> &amp;<i>lhs</i>, const <span class="type"><a href="testqdoc-test.html" translate="no">TestQDoc::Test</a></span> &amp;<i>rhs</i>)</h3>
+<p>Returns true if <i translate="no">lhs</i> and <i translate="no">rhs</i> are equal.</p>
+<!-- @@@operator== -->
+</div>
+<div class="macros">
+<h2>Macro Documentation</h2>
+<!-- $$$QDOCTEST_MACRO2[overload1]$$$QDOCTEST_MACRO2int& -->
+<h3 class="fn" translate="no" id="QDOCTEST_MACRO2"><code class="details extra" translate="no">[since Test 1.1]</code> <span class="name">QDOCTEST_MACRO2</span>(<span class="type">int</span> &amp;<i>x</i>)</h3>
+<p>A macro with argument <i translate="no">x</i>.</p>
+<p>This macro was introduced in Test 1.1.</p>
+<!-- @@@QDOCTEST_MACRO2 -->
+</div>
+</body>
+</html>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/scopedenum/expected/html/testqdoc-testderived-members.html b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/scopedenum/expected/html/testqdoc-testderived-members.html
new file mode 100644
index 000000000..201aba603
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/scopedenum/expected/html/testqdoc-testderived-members.html
@@ -0,0 +1,38 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+<!-- testcpp.cpp -->
+ <meta name="description" content="A class in a namespace, derived from Test.">
+ <title>List of All Members for TestDerived | TestCPP</title>
+</head>
+<body>
+<li><a href="testcpp-module.html" translate="no">C++ Classes</a></li>
+<li>TestDerived</li>
+<div class="sidebar"><div class="sidebar-content" id="sidebar-content"></div></div>
+<h1 class="title" translate="no">List of All Members for TestDerived</h1>
+<p>This is the complete list of members for <a href="testqdoc-testderived.html">TestQDoc::TestDerived</a>, including inherited members.</p>
+<div class="table"><table class="propsummary" translate="no">
+<tr><td class="topAlign"><ul>
+<li class="fn" translate="no">enum <span class="name"><b><a href="testqdoc-test.html#ClassicEnum-enum" translate="no">ClassicEnum</a></b></span></li>
+<li class="fn" translate="no"><span class="name"><b><a href="testqdoc-testderived.html#DerivedType-typedef" translate="no">DerivedType</a></b></span></li>
+<li class="fn" translate="no"><span class="name"><b><a href="testqdoc-testderived.html#NotTypedef-typedef" translate="no">NotTypedef</a></b></span></li>
+<li class="fn" translate="no">enum class <span class="name"><b><a href="testqdoc-test.html#ScopedEnum-enum" translate="no">ScopedEnum</a></b></span></li>
+<li class="fn" translate="no"><span class="name"><b><a href="testqdoc-test.html#SomeType-typedef" translate="no">SomeType</a></b></span></li>
+<li class="fn" translate="no"><span class="name"><b><a href="testqdoc-test.html#funcPtr" translate="no">funcPtr</a></b></span>(bool, const char *) : void (*)(bool)</li>
+<li class="fn" translate="no"><span class="name"><b><a href="testqdoc-test.html#inlineFunction" translate="no">inlineFunction</a></b></span>()</li>
+<li class="fn" translate="no"><span class="name"><b><a href="testqdoc-test.html#methodWithEmDashInItsDocs" translate="no">methodWithEmDashInItsDocs</a></b></span>()</li>
+</ul></td><td class="topAlign"><ul>
+<li class="fn" translate="no"><span class="name"><b><a href="testqdoc-test.html#methodWithEnDashInItsDocs" translate="no">methodWithEnDashInItsDocs</a></b></span>()</li>
+<li class="fn" translate="no"><span class="name"><b><a href="testqdoc-test.html#overload" translate="no">overload</a></b></span>()</li>
+<li class="fn" translate="no"><span class="name"><b><a href="testqdoc-test.html#overload-1" translate="no">overload</a></b></span>(bool)</li>
+<li class="fn" translate="no"><span class="name"><b><a href="testqdoc-test.html#someFunction" translate="no">someFunction</a></b></span>(int, int) : int</li>
+<li class="fn" translate="no"><span class="name"><b><a href="testqdoc-test.html#someFunctionDefaultArg" translate="no">someFunctionDefaultArg</a></b></span>(int, bool) const</li>
+<li class="fn" translate="no"><span class="name"><b><a href="testqdoc-testderived.html#someValue" translate="no">someValue</a></b></span>() : TestQDoc::TestDerived::NotTypedef</li>
+<li class="fn" translate="no"><span class="name"><b><a href="testqdoc-testderived.html#virtualFun" translate="no">virtualFun</a></b></span>()</li>
+<li class="fn" translate="no"><span class="name"><b><a href="testqdoc-test.html#virtualFun" translate="no">virtualFun</a></b></span>()</li>
+</ul>
+</td></tr>
+</table></div>
+</body>
+</html>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/scopedenum/expected/html/testqdoc-testderived-obsolete.html b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/scopedenum/expected/html/testqdoc-testderived-obsolete.html
new file mode 100644
index 000000000..42b536e09
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/scopedenum/expected/html/testqdoc-testderived-obsolete.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+<!-- testcpp.cpp -->
+ <meta name="description" content="A class in a namespace, derived from Test.">
+ <title>Obsolete Members for TestDerived | TestCPP</title>
+</head>
+<body>
+<li><a href="testcpp-module.html" translate="no">C++ Classes</a></li>
+<li>TestDerived</li>
+<div class="sidebar"><div class="sidebar-content" id="sidebar-content"></div></div>
+<h1 class="title" translate="no">Obsolete Members for TestDerived</h1>
+<p><b>The following members of class <a href="testqdoc-testderived.html" translate="no">TestDerived</a> are deprecated.</b> They are provided to keep old source code working. We strongly advise against using them in new code.</p>
+<h2>Static Public Members</h2>
+<div class="table"><table class="alignedsummary" translate="no">
+<tr><td class="memItemLeft topAlign rightAlign"> <code class="summary extra" translate="no">(deprecated)</code> void </td><td class="memItemRight bottomAlign"><b><a href="testqdoc-testderived-obsolete.html#staticObsoleteMember" translate="no">staticObsoleteMember</a></b>()</td></tr>
+</table></div>
+<h2>Member Function Documentation</h2>
+<!-- $$$staticObsoleteMember[overload1]$$$staticObsoleteMember -->
+<h3 class="fn" translate="no" id="staticObsoleteMember"><code class="details extra" translate="no">[static, deprecated]</code> <span class="type">void</span> TestDerived::<span class="name">staticObsoleteMember</span>()</h3>
+<p>This function is deprecated. We strongly advise against using it in new code.</p>
+<p>Static obsolete method.</p>
+<!-- @@@staticObsoleteMember -->
+</body>
+</html>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/scopedenum/expected/html/testqdoc-testderived.html b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/scopedenum/expected/html/testqdoc-testderived.html
new file mode 100644
index 000000000..04f789abb
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/scopedenum/expected/html/testqdoc-testderived.html
@@ -0,0 +1,81 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+<!-- testcpp.cpp -->
+ <meta name="description" content="A class in a namespace, derived from Test.">
+ <title>TestDerived Class | TestCPP</title>
+</head>
+<body>
+<li><a href="testcpp-module.html" translate="no">C++ Classes</a></li>
+<li>TestDerived</li>
+<div class="sidebar">
+<div class="toc">
+<h3 id="toc">Contents</h3>
+<ul>
+<li class="level1"><a href="#public-types">Public Types</a></li>
+<li class="level1"><a href="#public-functions">Public Functions</a></li>
+<li class="level1"><a href="#reimplemented-public-functions">Reimplemented Public Functions</a></li>
+<li class="level1"><a href="#details">Detailed Description</a></li>
+</ul>
+</div>
+<div class="sidebar-content" id="sidebar-content"></div></div>
+<h1 class="title" translate="no">TestDerived Class</h1>
+<span class="small-subtitle" translate="no">class <a href="testqdoc.html" translate="no">TestQDoc</a>::TestDerived</span>
+<!-- $$$TestDerived-brief -->
+<p>A class in a namespace, derived from <a href="testqdoc-test.html" translate="no">Test</a>. <a href="#details">More...</a></p>
+<!-- @@@TestDerived -->
+<div class="table"><table class="alignedsummary" translate="no">
+<tr><td class="memItemLeft rightAlign topAlign"> Header:</td><td class="memItemRight bottomAlign"> <span class="preprocessor">#include &lt;TestDerived&gt;</span></td></tr>
+<tr><td class="memItemLeft rightAlign topAlign"> CMake:</td><td class="memItemRight bottomAlign"> find_package(Qt6 REQUIRED COMPONENTS QDocTest) <br/>
+target_link_libraries(mytarget PRIVATE Qt6::QDocTest)</td></tr>
+<tr><td class="memItemLeft rightAlign topAlign"> qmake:</td><td class="memItemRight bottomAlign"> QT += testcpp</td></tr>
+<tr><td class="memItemLeft rightAlign topAlign"> Since:</td><td class="memItemRight bottomAlign"> Qt 2.0</td></tr>
+<tr><td class="memItemLeft rightAlign topAlign"> Inherits:</td><td class="memItemRight bottomAlign"> <a href="testqdoc-test.html" translate="no">TestQDoc::Test</a></td></tr>
+</table></div>
+<ul>
+<li><a href="testqdoc-testderived-members.html">List of all members, including inherited members</a></li>
+<li><a href="testqdoc-testderived-obsolete.html">Deprecated members</a></li>
+</ul>
+<h2 id="public-types">Public Types</h2>
+<div class="table"><table class="alignedsummary" translate="no">
+<tr><td class="memItemLeft rightAlign topAlign"> </td><td class="memItemRight bottomAlign"><b><a href="testqdoc-testderived.html#DerivedType-typedef" translate="no">DerivedType</a></b></td></tr>
+<tr><td class="memItemLeft rightAlign topAlign"> </td><td class="memItemRight bottomAlign"><b><a href="testqdoc-testderived.html#NotTypedef-typedef" translate="no">NotTypedef</a></b></td></tr>
+</table></div>
+<h2 id="public-functions">Public Functions</h2>
+<div class="table"><table class="alignedsummary" translate="no">
+<tr><td class="memItemLeft rightAlign topAlign"> TestQDoc::TestDerived::NotTypedef </td><td class="memItemRight bottomAlign"><b><a href="testqdoc-testderived.html#someValue" translate="no">someValue</a></b>()</td></tr>
+</table></div>
+<h2 id="reimplemented-public-functions">Reimplemented Public Functions</h2>
+<div class="table"><table class="alignedsummary" translate="no">
+<tr><td class="memItemLeft rightAlign topAlign"> virtual void </td><td class="memItemRight bottomAlign"><b><a href="testqdoc-testderived.html#virtualFun" translate="no">virtualFun</a></b>() override</td></tr>
+</table></div>
+<!-- $$$TestDerived-description -->
+<div class="descr">
+<h2 id="details">Detailed Description</h2>
+</div>
+<!-- @@@TestDerived -->
+<div class="types">
+<h2>Member Type Documentation</h2>
+<!-- $$$DerivedType -->
+<h3 class="fn" translate="no" id="DerivedType-typedef"><code class="details extra" translate="no">[alias]</code> TestDerived::<span class="name">DerivedType</span></h3>
+<p>An aliased typedef.</p>
+<!-- @@@DerivedType -->
+<!-- $$$NotTypedef -->
+<h3 class="fn" translate="no" id="NotTypedef-typedef"><code class="details extra" translate="no">[alias]</code> TestDerived::<span class="name">NotTypedef</span></h3>
+<p>I'm an alias, not a typedef.</p>
+<!-- @@@NotTypedef -->
+</div>
+<div class="func">
+<h2>Member Function Documentation</h2>
+<!-- $$$someValue[overload1]$$$someValue -->
+<h3 class="fn" translate="no" id="someValue"><span class="type"><a href="testqdoc-testderived.html#NotTypedef-typedef" translate="no">TestQDoc::TestDerived::NotTypedef</a></span> TestDerived::<span class="name">someValue</span>()</h3>
+<p>Returns a value using an aliases type.</p>
+<!-- @@@someValue -->
+<!-- $$$virtualFun[overload1]$$$virtualFun -->
+<h3 class="fn" translate="no" id="virtualFun"><code class="details extra" translate="no">[override virtual]</code> <span class="type">void</span> TestDerived::<span class="name">virtualFun</span>()</h3>
+<p>Reimplements: <a href="testqdoc-test.html#virtualFun" translate="no">Test::virtualFun</a>().</p>
+<!-- @@@virtualFun -->
+</div>
+</body>
+</html>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/scopedenum/expected/html/testqdoc.html b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/scopedenum/expected/html/testqdoc.html
new file mode 100644
index 000000000..425c7b396
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/scopedenum/expected/html/testqdoc.html
@@ -0,0 +1,64 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+<!-- testcpp.cpp -->
+ <meta name="description" content="A namespace.">
+ <title>TestQDoc Namespace | TestCPP</title>
+</head>
+<body>
+<div class="sidebar">
+<div class="toc">
+<h3 id="toc">Contents</h3>
+<ul>
+<li class="level1"><a href="#classes">Classes</a></li>
+<li class="level1"><a href="#macros">Macros</a></li>
+<li class="level1"><a href="#details">Detailed Description</a></li>
+<li class="level2"><a href="#usage">Usage</a></li>
+</ul>
+</div>
+<div class="sidebar-content" id="sidebar-content"></div></div>
+<h1 class="title" translate="no">TestQDoc Namespace</h1>
+<!-- $$$TestQDoc-brief -->
+<p>A namespace. <a href="#details">More...</a></p>
+<!-- @@@TestQDoc -->
+<div class="table"><table class="alignedsummary" translate="no">
+<tr><td class="memItemLeft rightAlign topAlign"> Header:</td><td class="memItemRight bottomAlign"> <span class="preprocessor">#include &lt;TestCPP&gt;</span></td></tr>
+<tr><td class="memItemLeft rightAlign topAlign"> CMake:</td><td class="memItemRight bottomAlign"> find_package(Qt6 REQUIRED COMPONENTS QDocTest) <br/>
+target_link_libraries(mytarget PRIVATE Qt6::QDocTest)</td></tr>
+<tr><td class="memItemLeft rightAlign topAlign"> qmake:</td><td class="memItemRight bottomAlign"> QT += testcpp</td></tr>
+<tr><td class="memItemLeft rightAlign topAlign"> Since:</td><td class="memItemRight bottomAlign"> Qt 2.0</td></tr>
+</table></div>
+<h2 id="classes">Classes</h2>
+<div class="table"><table class="alignedsummary" translate="no">
+<tr><td class="memItemLeft rightAlign topAlign"> <code class="summary extra" translate="no">(since 2.0)</code> class </td><td class="memItemRight bottomAlign"><b><a href="testqdoc-test.html" translate="no">Test</a></b></td></tr>
+<tr><td class="memItemLeft rightAlign topAlign"> <code class="summary extra" translate="no">(since 2.0)</code> class </td><td class="memItemRight bottomAlign"><b><a href="testqdoc-testderived.html" translate="no">TestDerived</a></b></td></tr>
+</table></div>
+<h2 id="macros">Macros</h2>
+<div class="table"><table class="alignedsummary" translate="no">
+<tr><td class="memItemLeft rightAlign topAlign"> </td><td class="memItemRight bottomAlign"><b><a href="testqdoc.html#QDOCTEST_MACRO" translate="no">QDOCTEST_MACRO</a></b></td></tr>
+</table></div>
+<!-- $$$TestQDoc-description -->
+<div class="descr">
+<h2 id="details">Detailed Description</h2>
+<h3 id="usage">Usage</h3>
+<p>This namespace is for testing QDoc output.</p>
+</div>
+<!-- @@@TestQDoc -->
+<div class="classes">
+<h2>Classes</h2>
+<h3> class <a href="testqdoc-test.html">Test</a></h3><!-- $$$Test-brief -->
+<p>A class in a namespace. <a href="testqdoc-test.html#details">More...</a></p>
+<!-- @@@Test -->
+<h3> class <a href="testqdoc-testderived.html">TestDerived</a></h3><!-- $$$TestDerived-brief -->
+<p>A class in a namespace, derived from <a href="testqdoc-test.html" translate="no">Test</a>. <a href="testqdoc-testderived.html#details">More...</a></p>
+<!-- @@@TestDerived -->
+</div>
+<div class="macros">
+<h2>Macro Documentation</h2>
+<!-- $$$QDOCTEST_MACRO[overload1]$$$QDOCTEST_MACRO -->
+<h3 class="fn" translate="no" id="QDOCTEST_MACRO"><span class="name">QDOCTEST_MACRO</span></h3>
+<!-- @@@QDOCTEST_MACRO -->
+</div>
+</body>
+</html>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/scopedenum/expected/html/whatsnew.html b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/scopedenum/expected/html/whatsnew.html
new file mode 100644
index 000000000..356731918
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/scopedenum/expected/html/whatsnew.html
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+<!-- scopedenum.qdoc -->
+ <meta name="description" content="New classes documentation">
+ <title>New Classes and Functions | TestCPP</title>
+</head>
+<body>
+<div class="sidebar"><div class="sidebar-content" id="sidebar-content"></div></div>
+<h1 class="title">New Classes and Functions</h1>
+<!-- $$$whatsnew.html-description -->
+<div class="descr" id="details">
+<ul>
+<li><a href="#new-namespaces">New Namespaces</a></li>
+<li><a href="#new-classes">New Classes</a></li>
+<li><a href="#new-enum-values">New Enum Values</a></li>
+</ul>
+<h3 id="new-namespaces">New Namespaces</h3>
+<div class="table"><table class="alignedsummary" translate="no">
+<tr><td class="memItemLeft rightAlign topAlign"> <code class="summary extra" translate="no">(since 2.0)</code> namespace </td><td class="memItemRight bottomAlign"><b><a href="testqdoc.html" translate="no">TestQDoc</a></b></td></tr>
+</table></div>
+<h3 id="new-classes">New Classes</h3>
+<div class="flowListDiv" translate="no">
+<dl class="flowList odd"><dt class="alphaChar"><b>T</b></dt>
+<dd><a href="testqdoc-test.html">Test</a> (<a href="testqdoc.html">TestQDoc</a>)</dd>
+<dd><a href="testqdoc-testderived.html">TestDerived</a> (<a href="testqdoc.html">TestQDoc</a>)</dd>
+</dl>
+</div>
+<h3 id="new-enum-values">New Enum Values</h3>
+<div class="table"><table class="alignedsummary" translate="no">
+<tr><td class="memItemLeft"> enum value </td><td class="memItemRight"><b><a href="testqdoc-test.html#ScopedEnum-enum">ScopedEnum::All</a></b></td></tr>
+</table></div>
+</div>
+<!-- @@@whatsnew.html -->
+</body>
+</html>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/scopedenum/expected/webxml/autolinking.webxml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/scopedenum/expected/webxml/autolinking.webxml
new file mode 100644
index 000000000..91f35d5d7
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/scopedenum/expected/webxml/autolinking.webxml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<WebXML>
+ <document>
+ <page name="autolinking.html" href="autolinking.html" status="active" location="classlists.qdoc" documented="true" subtype="page" title="Autolinking" fulltitle="Autolinking" subtitle="">
+ <contents name="testqdoc" title="TestQDoc" level="1"/>
+ <contents name="someprop" title="someProp" level="1"/>
+ <description>
+ <section id="testqdoc">
+ <heading level="1">TestQDoc</heading>
+ <para>The string <link raw="TestQDoc" href="testqdoc.html" type="namespace">TestQDoc</link> links to the C++ namespace unless linking explicitly, <link raw="#TestQDoc" href="autolinking.html#testqdoc" type="page" page="Autolinking">like this</link>, or <link raw="TestQDoc" href="testqdoc.html" type="namespace">this</link>. Also,</para>
+ <para>Autolinks:</para>
+ <list type="bullet">
+ <item>
+ <para>
+ <link raw="TestQDoc::TestDerived" href="testqdoc-testderived.html" type="class">TestQDoc::TestDerived</link></para>
+ </item>
+ </list>
+ <para>Explicit links:</para>
+ <list type="bullet">
+ <item>
+ <para>
+ <link raw="TestQDoc::TestDerived" href="testqdoc-testderived.html" type="class">TestQDoc::TestDerived</link></para>
+ </item>
+ <item>
+ <para>
+ <link raw="Obsolete Classes#TestQDoc" href="obsolete-classes.html#testqdoc" type="page" page="Obsolete Classes">Obsolete Classes#TestQDoc</link></para>
+ </item>
+ </list>
+ </section>
+ <section id="someprop">
+ <heading level="1">someProp</heading>
+ </section>
+ </description>
+ </page>
+ </document>
+</WebXML>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/scopedenum/expected/webxml/cpptypes.webxml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/scopedenum/expected/webxml/cpptypes.webxml
new file mode 100644
index 000000000..df7cd7024
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/scopedenum/expected/webxml/cpptypes.webxml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<WebXML>
+ <document>
+ <group name="cpptypes" href="cpptypes.html" status="active" location="classlists.qdoc" documented="true" seen="true" title="Test C++ Types">
+ <description>
+ <generatedlist contents="testgroup"/>
+ <table width="100%">
+ <row>
+ <item>
+ <para>
+ <link raw="TestQDoc::Test" href="testqdoc-test.html" type="class"/>
+ </para>
+ </item>
+ <item>
+ <para>A class in a namespace.</para>
+ </item>
+ </row>
+ </table>
+ </description>
+ </group>
+ </document>
+</WebXML>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/scopedenum/expected/webxml/crossmoduleref.webxml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/scopedenum/expected/webxml/crossmoduleref.webxml
new file mode 100644
index 000000000..682799bd8
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/scopedenum/expected/webxml/crossmoduleref.webxml
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<WebXML>
+ <document>
+ <namespace name="CrossModuleRef" href="crossmoduleref.html" status="active" access="public" location="testcpp.h" since="3.0" documented="true" module="TestCPP" brief="Namespace that has documented functions in multiple modules">
+ <description>
+ <brief>Namespace that has documented functions in multiple modules.</brief>
+ </description>
+ <function name="documentMe" fullname="CrossModuleRef::documentMe" href="crossmoduleref.html#documentMe" status="active" access="public" location="testcpp.h" documented="true" meta="plain" type="void" signature="void documentMe()">
+ <description>
+ <para>Document me!</para>
+ </description>
+ </function>
+ </namespace>
+ </document>
+</WebXML>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/scopedenum/expected/webxml/obsolete-classes.webxml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/scopedenum/expected/webxml/obsolete-classes.webxml
new file mode 100644
index 000000000..dda841458
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/scopedenum/expected/webxml/obsolete-classes.webxml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<WebXML>
+ <document>
+ <page name="obsolete-classes.html" href="obsolete-classes.html" status="active" location="classlists.qdoc" documented="true" subtype="page" title="Obsolete Classes" fulltitle="Obsolete Classes" subtitle="">
+ <contents name="classes-with-obsolete-members" title="Classes with obsolete members" level="1"/>
+ <contents name="testqdoc" title="TestQDoc" level="2"/>
+ <description>
+ <section id="classes-with-obsolete-members">
+ <heading level="1">Classes with obsolete members</heading>
+ <generatedlist contents="obsoletecppmembers"/>
+ </section>
+ <section id="testqdoc">
+ <heading level="2">TestQDoc</heading>
+ </section>
+ </description>
+ </page>
+ </document>
+</WebXML>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/scopedenum/expected/webxml/scoped-enum-linking.webxml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/scopedenum/expected/webxml/scoped-enum-linking.webxml
new file mode 100644
index 000000000..62091e656
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/scopedenum/expected/webxml/scoped-enum-linking.webxml
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<WebXML>
+ <document>
+ <page name="scoped-enum-linking.html" href="scoped-enum-linking.html" status="active" location="scopedenum.qdoc" documented="true" subtype="page" title="Enum Linking" fulltitle="Enum Linking" subtitle="">
+ <description>
+ <para>Linking to <link raw="TestQDoc::Test::ScopedEnum::All" href="testqdoc-test.html#ScopedEnum-enum" type="enum" enum="TestQDoc::Test::ScopedEnum">All</link>.</para>
+ <para>TestQDoc::Test::ClassicEnum::Howdy does not link, but <link raw="TestQDoc::Test::Howdy" href="testqdoc-test.html#ClassicEnum-enum" type="enum" enum="TestQDoc::Test::ClassicEnum">TestQDoc::Test::Howdy</link> might.</para>
+ </description>
+ </page>
+ </document>
+</WebXML>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/scopedenum/expected/webxml/testcpp-module.webxml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/scopedenum/expected/webxml/testcpp-module.webxml
new file mode 100644
index 000000000..5d24b3077
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/scopedenum/expected/webxml/testcpp-module.webxml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<WebXML>
+ <document/>
+</WebXML>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/scopedenum/expected/webxml/testcpp.index b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/scopedenum/expected/webxml/testcpp.index
new file mode 100644
index 000000000..0ea9571f0
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/scopedenum/expected/webxml/testcpp.index
@@ -0,0 +1,91 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE QDOCINDEX>
+<INDEX url="" title="TestCPP Reference Documentation" version="" project="TestCPP">
+ <namespace name="" status="active" access="public" module="testcpp">
+ <function name="QDOCTEST_MACRO" href="testqdoc.html#QDOCTEST_MACRO" status="active" access="public" documented="true" related="0" meta="macrowithoutparams" signature="QDOCTEST_MACRO"/>
+ <function name="QDOCTEST_MACRO2" href="testqdoc-test.html#QDOCTEST_MACRO2" status="active" access="public" documented="true" related="1" since="Test 1.1" meta="macrowithparams" brief="A macro with argument x" signature="QDOCTEST_MACRO2(int &amp;x)" groups="testgroup">
+ <parameter type="int &amp;" name="x" default=""/>
+ </function>
+ <page name="autolinking.html" href="autolinking.html" status="active" location="classlists.qdoc" documented="true" subtype="page" title="Autolinking" fulltitle="Autolinking" subtitle="">
+ <contents name="testqdoc" title="TestQDoc" level="1"/>
+ <contents name="someprop" title="someProp" level="1"/>
+ </page>
+ <namespace name="CrossModuleRef" href="crossmoduleref.html" status="active" access="public" location="testcpp.h" since="3.0" documented="true" module="TestCPP" brief="Namespace that has documented functions in multiple modules">
+ <function name="documentMe" fullname="CrossModuleRef::documentMe" href="crossmoduleref.html#documentMe" status="active" access="public" location="testcpp.h" documented="true" meta="plain" type="void" signature="void documentMe()"/>
+ </namespace>
+ <class name="DontLinkToMe" href="dontlinktome.html" status="ignored" access="public" location="testcpp.h" since="2.0" documented="true" module="TestCPP" brief="Class that does not generate documentation"/>
+ <page name="scoped-enum-linking.html" href="scoped-enum-linking.html" status="active" location="scopedenum.qdoc" documented="true" subtype="page" title="Enum Linking" fulltitle="Enum Linking" subtitle=""/>
+ <page name="whatsnew.html" href="whatsnew.html" status="active" location="scopedenum.qdoc" documented="true" subtype="page" title="New Classes and Functions" fulltitle="New Classes and Functions" subtitle="" brief="New classes documentation"/>
+ <page name="obsolete-classes.html" href="obsolete-classes.html" status="active" location="classlists.qdoc" documented="true" subtype="page" title="Obsolete Classes" fulltitle="Obsolete Classes" subtitle="">
+ <contents name="classes-with-obsolete-members" title="Classes with obsolete members" level="1"/>
+ <contents name="testqdoc" title="TestQDoc" level="2"/>
+ </page>
+ <namespace name="TestQDoc" href="testqdoc.html" status="active" access="public" location="testcpp.h" since="2.0" documented="true" module="TestCPP" brief="A namespace">
+ <contents name="usage" title="Usage" level="1"/>
+ <function name="QDOCTEST_MACRO" href="testqdoc.html#QDOCTEST_MACRO" status="active" access="public" documented="true" related="0" meta="macrowithoutparams" signature="QDOCTEST_MACRO"/>
+ <class threadsafety="reentrant" name="Test" fullname="TestQDoc::Test" href="testqdoc-test.html" status="active" access="public" location="testcpp.h" since="2.0" documented="true" groups="cpptypes,testgroup" module="TestCPP" brief="A class in a namespace">
+ <function name="QDOCTEST_MACRO2" href="testqdoc-test.html#QDOCTEST_MACRO2" status="active" access="public" documented="true" related="1" since="Test 1.1" meta="macrowithparams" brief="A macro with argument x" signature="QDOCTEST_MACRO2(int &amp;x)" groups="testgroup">
+ <parameter type="int &amp;" name="x" default=""/>
+ </function>
+ <function name="anotherObsoleteMember" fullname="TestQDoc::Test::anotherObsoleteMember" href="testqdoc-test-obsolete.html#anotherObsoleteMember" status="deprecated" access="public" location="testcpp.h" documented="true" meta="plain" type="void" signature="void anotherObsoleteMember()"/>
+ <function name="deprecatedMember" fullname="TestQDoc::Test::deprecatedMember" href="testqdoc-test-obsolete.html#deprecatedMember" status="deprecated" access="public" location="testcpp.h" documented="true" meta="plain" type="void" signature="void deprecatedMember()"/>
+ <function name="funcPtr" fullname="TestQDoc::Test::funcPtr" href="testqdoc-test.html#funcPtr" status="active" access="public" location="testcpp.h" documented="true" meta="plain" type="void (*)(bool)" signature="void (*)(bool) funcPtr(bool b, const char *s)">
+ <parameter type="bool" name="b" default=""/>
+ <parameter type="const char *" name="s" default=""/>
+ </function>
+ <function name="inlineFunction" fullname="TestQDoc::Test::inlineFunction" href="testqdoc-test.html#inlineFunction" status="active" access="public" location="testcpp.h" documented="true" meta="plain" type="void" brief="An inline function, documented using the \fn QDoc command" signature="void inlineFunction()"/>
+ <function name="methodWithEmDashInItsDocs" fullname="TestQDoc::Test::methodWithEmDashInItsDocs" href="testqdoc-test.html#methodWithEmDashInItsDocs" status="active" access="public" location="testcpp.h" documented="true" meta="plain" type="void" signature="void methodWithEmDashInItsDocs()"/>
+ <function name="methodWithEnDashInItsDocs" fullname="TestQDoc::Test::methodWithEnDashInItsDocs" href="testqdoc-test.html#methodWithEnDashInItsDocs" status="active" access="public" location="testcpp.h" documented="true" meta="plain" type="void" signature="void methodWithEnDashInItsDocs()"/>
+ <function name="obsoleteMember" fullname="TestQDoc::Test::obsoleteMember" href="testqdoc-test-obsolete.html#obsoleteMember" status="deprecated" access="public" location="testcpp.h" documented="true" meta="plain" type="void" signature="void obsoleteMember()"/>
+ <function name="operator++" fullname="TestQDoc::Test::operator++" href="testqdoc-test-obsolete.html#operator-2b-2b" status="deprecated" access="public" location="testcpp.h" documented="true" meta="plain" type="TestQDoc::Test &amp;" signature="TestQDoc::Test &amp; operator++()"/>
+ <function name="operator--" fullname="TestQDoc::Test::operator--" href="testqdoc-test-obsolete.html#operator--" status="deprecated" access="public" location="testcpp.h" documented="true" meta="plain" type="TestQDoc::Test &amp;" signature="TestQDoc::Test &amp; operator--()"/>
+ <function name="operator==" href="testqdoc-test.html#operator-eq-eq" status="active" access="public" location="testcpp.h" documented="true" related="2" meta="plain" type="bool" signature="bool operator==(const TestQDoc::Test &amp;lhs, const TestQDoc::Test &amp;rhs)">
+ <parameter type="const TestQDoc::Test &amp;" name="lhs" default=""/>
+ <parameter type="const TestQDoc::Test &amp;" name="rhs" default=""/>
+ </function>
+ <function name="overload" fullname="TestQDoc::Test::overload" href="testqdoc-test.html#overload" status="active" access="protected" location="testcpp.h" documented="true" meta="plain" type="void" signature="void overload()"/>
+ <function name="overload" fullname="TestQDoc::Test::overload" href="testqdoc-test.html#overload-1" status="active" access="protected" location="testcpp.h" documented="true" since="Test 1.2" meta="plain" overload="true" overload-number="1" type="void" signature="void overload(bool b)">
+ <parameter type="bool" name="b" default=""/>
+ </function>
+ <function name="someFunction" fullname="TestQDoc::Test::someFunction" href="testqdoc-test.html#someFunction" status="active" access="public" location="testcpp.h" documented="true" meta="plain" type="int" signature="int someFunction(int, int v)">
+ <parameter type="int" name="" default=""/>
+ <parameter type="int" name="v" default="0"/>
+ </function>
+ <function name="someFunctionDefaultArg" fullname="TestQDoc::Test::someFunctionDefaultArg" href="testqdoc-test.html#someFunctionDefaultArg" threadsafety="non-reentrant" status="active" access="public" location="testcpp.h" documented="true" meta="plain" const="true" type="void" signature="void someFunctionDefaultArg(int i, bool b) const" groups="testgroup">
+ <parameter type="int" name="i" default=""/>
+ <parameter type="bool" name="b" default="false"/>
+ </function>
+ <function name="virtualFun" fullname="TestQDoc::Test::virtualFun" href="testqdoc-test.html#virtualFun" status="active" access="public" location="testcpp.h" documented="true" meta="plain" virtual="virtual" type="void" signature="void virtualFun()"/>
+ <enum name="ClassicEnum" fullname="TestQDoc::Test::ClassicEnum" href="testqdoc-test.html#ClassicEnum-enum" status="active" access="public" location="testcpp.h" documented="true">
+ <value name="Yee" value="0"/>
+ <value name="Haw" value="1"/>
+ <value name="Howdy" value="2"/>
+ <value name="Partner" value="3"/>
+ </enum>
+ <enum name="ScopedEnum" fullname="TestQDoc::Test::ScopedEnum" href="testqdoc-test.html#ScopedEnum-enum" status="active" access="public" location="testcpp.h" documented="true" scoped="true">
+ <value name="This" value="0x01"/>
+ <value name="That" value="0x02"/>
+ <value name="All" value="This | That" since="2.0"/>
+ <value name="OmittedValue" value="99"/>
+ <value name="UselessValue" value="100"/>
+ <value name="VeryLastValue" value="101"/>
+ </enum>
+ <typedef name="SomeType" fullname="TestQDoc::Test::SomeType" href="testqdoc-test.html#SomeType-typedef" status="active" access="public" location="testcpp.h" documented="true"/>
+ </class>
+ <class name="TestDerived" fullname="TestQDoc::TestDerived" href="testqdoc-testderived.html" status="active" access="public" location="testcpp.h" since="2.0" documented="true" bases="TestQDoc::Test" module="TestCPP" brief="A class in a namespace, derived from Test">
+ <function name="someValue" fullname="TestQDoc::TestDerived::someValue" href="testqdoc-testderived.html#someValue" status="active" access="public" location="testcpp.h" documented="true" meta="plain" type="TestQDoc::TestDerived::NotTypedef" signature="TestQDoc::TestDerived::NotTypedef someValue()"/>
+ <function name="staticObsoleteMember" fullname="TestQDoc::TestDerived::staticObsoleteMember" href="testqdoc-testderived-obsolete.html#staticObsoleteMember" status="deprecated" access="public" location="testcpp.h" documented="true" meta="plain" static="true" type="void" signature="void staticObsoleteMember()"/>
+ <function name="virtualFun" fullname="TestQDoc::TestDerived::virtualFun" href="testqdoc-testderived.html#virtualFun" status="active" access="public" location="testcpp.h" documented="true" meta="plain" virtual="virtual" override="true" type="void" signature="void virtualFun() override"/>
+ <typedef name="DerivedType" fullname="TestQDoc::TestDerived::DerivedType" href="testqdoc-testderived.html#DerivedType-typedef" status="active" access="public" location="testcpp.h" documented="true" aliasedtype="Test::SomeType"/>
+ <typedef name="NotTypedef" fullname="TestQDoc::TestDerived::NotTypedef" href="testqdoc-testderived.html#NotTypedef-typedef" status="active" access="public" location="testcpp.h" documented="true" aliasedtype="int"/>
+ </class>
+ </namespace>
+ <page name="https://doc.qt.io/qt/17-qdoc-commands-thread.html#reentrant-command" href="https://doc.qt.io/qt/17-qdoc-commands-thread.html#reentrant-command" status="active" location="classlists.qdoc" documented="true" subtype="externalpage" title="reentrant" fulltitle="reentrant" subtitle=""/>
+ <group name="cpptypes" href="cpptypes.html" status="active" location="classlists.qdoc" documented="true" seen="true" title="Test C++ Types"/>
+ <group name="testgroup" href="testgroup.html" status="internal" seen="false" title=""/>
+ <module name="TestCPP" href="testcpp-module.html" status="active" since="2.0" documented="true" seen="true" title="QDoc Test C++ Classes" brief="A test module page">
+ <contents name="linking-to-function-like-things" title="Linking to function-like things" level="1"/>
+ <contents name="section" title="section()" level="2"/>
+ </module>
+ </namespace>
+</INDEX>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/scopedenum/expected/webxml/testqdoc-test.webxml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/scopedenum/expected/webxml/testqdoc-test.webxml
new file mode 100644
index 000000000..b96225ee4
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/scopedenum/expected/webxml/testqdoc-test.webxml
@@ -0,0 +1,160 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<WebXML>
+ <document>
+ <class threadsafety="reentrant" name="Test" fullname="TestQDoc::Test" href="testqdoc-test.html" status="active" access="public" location="testcpp.h" since="2.0" documented="true" groups="cpptypes,testgroup" module="TestCPP" brief="A class in a namespace">
+ <description>
+ <brief>A class in a namespace.</brief>
+ </description>
+ <function name="QDOCTEST_MACRO2" href="testqdoc-test.html#QDOCTEST_MACRO2" status="active" access="public" documented="true" related="1" since="Test 1.1" meta="macrowithparams" brief="A macro with argument x" signature="QDOCTEST_MACRO2(int &amp;x)" groups="testgroup">
+ <parameter type="int &amp;" name="x" default=""/>
+ <description>
+ <brief>A macro with argument <argument>x</argument>.</brief>
+ </description>
+ </function>
+ <function name="anotherObsoleteMember" fullname="TestQDoc::Test::anotherObsoleteMember" href="testqdoc-test-obsolete.html#anotherObsoleteMember" status="deprecated" access="public" location="testcpp.h" documented="true" meta="plain" type="void" signature="void anotherObsoleteMember()">
+ <description>
+ <para>Use <link raw="obsoleteMember()" href="testqdoc-test.html#obsoleteMember" type="function">obsoleteMember()</link> instead.</para>
+ </description>
+ </function>
+ <function name="deprecatedMember" fullname="TestQDoc::Test::deprecatedMember" href="testqdoc-test-obsolete.html#deprecatedMember" status="deprecated" access="public" location="testcpp.h" documented="true" meta="plain" type="void" signature="void deprecatedMember()">
+ <description>
+ <para>Use <link raw="someFunction()" href="testqdoc-test.html#someFunction" type="function">someFunction()</link> instead.</para>
+ </description>
+ </function>
+ <function name="funcPtr" fullname="TestQDoc::Test::funcPtr" href="testqdoc-test.html#funcPtr" status="active" access="public" location="testcpp.h" documented="true" meta="plain" type="void (*)(bool)" signature="void (*)(bool) funcPtr(bool b, const char *s)">
+ <parameter type="bool" name="b" default=""/>
+ <parameter type="const char *" name="s" default=""/>
+ <description>
+ <para>Returns a pointer to a function that takes a boolean. Uses <argument>b</argument> and <argument>s</argument>.</para>
+ </description>
+ </function>
+ <function name="inlineFunction" fullname="TestQDoc::Test::inlineFunction" href="testqdoc-test.html#inlineFunction" status="active" access="public" location="testcpp.h" documented="true" meta="plain" type="void" brief="An inline function, documented using the \fn QDoc command" signature="void inlineFunction()">
+ <description>
+ <brief>An inline function, documented using the \fn QDoc command.</brief>
+ </description>
+ </function>
+ <function name="methodWithEmDashInItsDocs" fullname="TestQDoc::Test::methodWithEmDashInItsDocs" href="testqdoc-test.html#methodWithEmDashInItsDocs" status="active" access="public" location="testcpp.h" documented="true" meta="plain" type="void" signature="void methodWithEmDashInItsDocs()">
+ <description>
+ <para>This method has em dashes in its documentation—as you'll find represented by <teletype type="highlighted">---</teletype> in the sources—here and there. The important bit to note is that when passed e.g. to the \c command, the three hyphens are processed as input to the command and not replaced by an em dash.</para>
+ <para>-----------------------------------------------------------------------</para>
+ <para>People can still add a bunch of dashes, though, without QDoc replacing them all with a series of em dashes.</para>
+ <para>—You can also start a new paragraph with an em dash, if you want to.</para>
+ <see-also>
+ <link raw="methodWithEnDashInItsDocs" href="testqdoc-test.html#methodWithEnDashInItsDocs" type="function">methodWithEnDashInItsDocs</link>
+ </see-also>
+ </description>
+ </function>
+ <function name="methodWithEnDashInItsDocs" fullname="TestQDoc::Test::methodWithEnDashInItsDocs" href="testqdoc-test.html#methodWithEnDashInItsDocs" status="active" access="public" location="testcpp.h" documented="true" meta="plain" type="void" signature="void methodWithEnDashInItsDocs()">
+ <description>
+ <para>This method has en dashes in its documentation – as you'll find represented by <teletype type="highlighted">--</teletype> in the sources – here and there. The important bit to note is that when passed e.g. to the \c command, the two hyphens are processed as input to the command and not replaced by an en dash. This also applies to code blocks, where otherwise, the decrement operator would get completely borked:</para>
+ <code>for (int i = 42; i &gt; 0; --i)
+ // Do something cool during countdown.</code>
+ <para>...as it would be silly if this would output –i instead of <teletype type="highlighted">--i</teletype>.</para>
+ <para>-----------------------------------------------------------------------</para>
+ <para>It still allows people to add a bunch of dashes, though, without replacing them all with a series of en dashes. Of course, they might want to use the \hr command instead, like this:</para>
+ <para>– You can also start a new paragraph with an en dash, if you want to.</para>
+ <see-also>methodWithEnDashInItsDocs</see-also>
+ </description>
+ </function>
+ <function name="obsoleteMember" fullname="TestQDoc::Test::obsoleteMember" href="testqdoc-test-obsolete.html#obsoleteMember" status="deprecated" access="public" location="testcpp.h" documented="true" meta="plain" type="void" signature="void obsoleteMember()">
+ <description>
+ <para>Use <link raw="someFunction()" href="testqdoc-test.html#someFunction" type="function">someFunction()</link> instead.</para>
+ </description>
+ </function>
+ <function name="operator++" fullname="TestQDoc::Test::operator++" href="testqdoc-test-obsolete.html#operator-2b-2b" status="deprecated" access="public" location="testcpp.h" documented="true" meta="plain" type="TestQDoc::Test &amp;" signature="TestQDoc::Test &amp; operator++()">
+ <description/>
+ </function>
+ <function name="operator--" fullname="TestQDoc::Test::operator--" href="testqdoc-test-obsolete.html#operator--" status="deprecated" access="public" location="testcpp.h" documented="true" meta="plain" type="TestQDoc::Test &amp;" signature="TestQDoc::Test &amp; operator--()">
+ <description/>
+ </function>
+ <function name="operator==" href="testqdoc-test.html#operator-eq-eq" status="active" access="public" location="testcpp.h" documented="true" related="2" meta="plain" type="bool" signature="bool operator==(const TestQDoc::Test &amp;lhs, const TestQDoc::Test &amp;rhs)">
+ <parameter type="const TestQDoc::Test &amp;" name="lhs" default=""/>
+ <parameter type="const TestQDoc::Test &amp;" name="rhs" default=""/>
+ <description>
+ <para>Returns true if <argument>lhs</argument> and <argument>rhs</argument> are equal.</para>
+ </description>
+ </function>
+ <function name="overload" fullname="TestQDoc::Test::overload" href="testqdoc-test.html#overload" status="active" access="protected" location="testcpp.h" documented="true" meta="plain" type="void" signature="void overload()">
+ <description/>
+ </function>
+ <function name="overload" fullname="TestQDoc::Test::overload" href="testqdoc-test.html#overload-1" status="active" access="protected" location="testcpp.h" documented="true" since="Test 1.2" meta="plain" overload="true" overload-number="1" type="void" signature="void overload(bool b)">
+ <parameter type="bool" name="b" default=""/>
+ <description/>
+ </function>
+ <function name="someFunction" fullname="TestQDoc::Test::someFunction" href="testqdoc-test.html#someFunction" status="active" access="public" location="testcpp.h" documented="true" meta="plain" type="int" signature="int someFunction(int, int v)">
+ <parameter type="int" name="" default=""/>
+ <parameter type="int" name="v" default="0"/>
+ <description>
+ <para>Function that takes a parameter <argument>v</argument>. Also returns the value of <argument>v</argument>.</para>
+ </description>
+ </function>
+ <function name="someFunctionDefaultArg" fullname="TestQDoc::Test::someFunctionDefaultArg" href="testqdoc-test.html#someFunctionDefaultArg" threadsafety="non-reentrant" status="active" access="public" location="testcpp.h" documented="true" meta="plain" const="true" type="void" signature="void someFunctionDefaultArg(int i, bool b) const" groups="testgroup">
+ <parameter type="int" name="i" default=""/>
+ <parameter type="bool" name="b" default="false"/>
+ <description>
+ <para>Function that takes a parameter <argument>i</argument> and <argument>b</argument>.</para>
+ </description>
+ </function>
+ <function name="virtualFun" fullname="TestQDoc::Test::virtualFun" href="testqdoc-test.html#virtualFun" status="active" access="public" location="testcpp.h" documented="true" meta="plain" virtual="virtual" type="void" signature="void virtualFun()">
+ <description>
+ <para>Function that must be reimplemented.</para>
+ </description>
+ </function>
+ <enum name="ClassicEnum" fullname="TestQDoc::Test::ClassicEnum" href="testqdoc-test.html#ClassicEnum-enum" status="active" access="public" location="testcpp.h" documented="true">
+ <value name="Yee" value="0"/>
+ <value name="Haw" value="1"/>
+ <value name="Howdy" value="2"/>
+ <value name="Partner" value="3"/>
+ <description>
+ <list type="enum">
+ <definition>
+ <term>TestQDoc::Test::Yee</term>Yee</definition>
+ <item/>
+ <definition>
+ <term>TestQDoc::Test::Haw</term>Haw</definition>
+ <item/>
+ <definition>
+ <term>TestQDoc::Test::Howdy</term>Howdy</definition>
+ <item/>
+ <definition>
+ <term>TestQDoc::Test::Partner</term>Partner</definition>
+ <item/>
+ </list>
+ </description>
+ </enum>
+ <enum name="ScopedEnum" fullname="TestQDoc::Test::ScopedEnum" href="testqdoc-test.html#ScopedEnum-enum" status="active" access="public" location="testcpp.h" documented="true" scoped="true">
+ <value name="This" value="0x01"/>
+ <value name="That" value="0x02"/>
+ <value name="All" value="This | That" since="2.0"/>
+ <value name="OmittedValue" value="99"/>
+ <value name="UselessValue" value="100"/>
+ <value name="VeryLastValue" value="101"/>
+ <description>
+ <brief>This enum has a brief to trigger a bug in CMD_BRIEF.</brief>
+ <list type="enum">
+ <definition>
+ <term>TestQDoc::Test::ScopedEnum::This</term>This</definition>
+ <item>
+ <para>Something</para>
+ </item>
+ <definition>
+ <term>TestQDoc::Test::ScopedEnum::That</term>That</definition>
+ <item>
+ <para>Something else</para>
+ </item>
+ <definition>
+ <term>TestQDoc::Test::ScopedEnum::All</term>All</definition>2.0<item>
+ <para>Everything</para>
+ </item>
+ </list>
+ <para>A scoped enum.</para>
+ </description>
+ </enum>
+ <typedef name="SomeType" fullname="TestQDoc::Test::SomeType" href="testqdoc-test.html#SomeType-typedef" status="active" access="public" location="testcpp.h" documented="true">
+ <description>
+ <brief>A typedef.</brief>
+ </description>
+ </typedef>
+ </class>
+ </document>
+</WebXML>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/scopedenum/expected/webxml/testqdoc-testderived.webxml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/scopedenum/expected/webxml/testqdoc-testderived.webxml
new file mode 100644
index 000000000..3752e9950
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/scopedenum/expected/webxml/testqdoc-testderived.webxml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<WebXML>
+ <document>
+ <class name="TestDerived" fullname="TestQDoc::TestDerived" href="testqdoc-testderived.html" status="active" access="public" location="testcpp.h" since="2.0" documented="true" bases="TestQDoc::Test" module="TestCPP" brief="A class in a namespace, derived from Test">
+ <description>
+ <brief>A class in a namespace, derived from <link raw="Test" href="testqdoc-test.html" type="class">Test</link>.</brief>
+ </description>
+ <function name="someValue" fullname="TestQDoc::TestDerived::someValue" href="testqdoc-testderived.html#someValue" status="active" access="public" location="testcpp.h" documented="true" meta="plain" type="TestQDoc::TestDerived::NotTypedef" signature="TestQDoc::TestDerived::NotTypedef someValue()">
+ <description>
+ <para>Returns a value using an aliases type.</para>
+ </description>
+ </function>
+ <function name="staticObsoleteMember" fullname="TestQDoc::TestDerived::staticObsoleteMember" href="testqdoc-testderived-obsolete.html#staticObsoleteMember" status="deprecated" access="public" location="testcpp.h" documented="true" meta="plain" static="true" type="void" signature="void staticObsoleteMember()">
+ <description>
+ <para>Static obsolete method.</para>
+ </description>
+ </function>
+ <function name="virtualFun" fullname="TestQDoc::TestDerived::virtualFun" href="testqdoc-testderived.html#virtualFun" status="active" access="public" location="testcpp.h" documented="true" meta="plain" virtual="virtual" override="true" type="void" signature="void virtualFun() override">
+ <description/>
+ </function>
+ <typedef name="DerivedType" fullname="TestQDoc::TestDerived::DerivedType" href="testqdoc-testderived.html#DerivedType-typedef" status="active" access="public" location="testcpp.h" documented="true" aliasedtype="Test::SomeType">
+ <description>
+ <para>An aliased typedef.</para>
+ </description>
+ </typedef>
+ <typedef name="NotTypedef" fullname="TestQDoc::TestDerived::NotTypedef" href="testqdoc-testderived.html#NotTypedef-typedef" status="active" access="public" location="testcpp.h" documented="true" aliasedtype="int">
+ <description>
+ <para>I'm an alias, not a typedef.</para>
+ </description>
+ </typedef>
+ </class>
+ </document>
+</WebXML>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/scopedenum/expected/webxml/testqdoc.webxml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/scopedenum/expected/webxml/testqdoc.webxml
new file mode 100644
index 000000000..780b35080
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/scopedenum/expected/webxml/testqdoc.webxml
@@ -0,0 +1,201 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<WebXML>
+ <document>
+ <namespace name="TestQDoc" href="testqdoc.html" status="active" access="public" location="testcpp.h" since="2.0" documented="true" module="TestCPP" brief="A namespace">
+ <contents name="usage" title="Usage" level="1"/>
+ <description>
+ <brief>A namespace.</brief>
+ <section id="usage">
+ <heading level="1">Usage</heading>
+ <para>This namespace is for testing QDoc output.</para>
+ </section>
+ </description>
+ <function name="QDOCTEST_MACRO" href="testqdoc.html#QDOCTEST_MACRO" status="active" access="public" documented="true" related="0" meta="macrowithoutparams" signature="QDOCTEST_MACRO">
+ <description/>
+ </function>
+ <class threadsafety="reentrant" name="Test" fullname="TestQDoc::Test" href="testqdoc-test.html" status="active" access="public" location="testcpp.h" since="2.0" documented="true" groups="cpptypes,testgroup" module="TestCPP" brief="A class in a namespace">
+ <description>
+ <brief>A class in a namespace.</brief>
+ </description>
+ <function name="QDOCTEST_MACRO2" href="testqdoc-test.html#QDOCTEST_MACRO2" status="active" access="public" documented="true" related="1" since="Test 1.1" meta="macrowithparams" brief="A macro with argument x" signature="QDOCTEST_MACRO2(int &amp;x)" groups="testgroup">
+ <parameter type="int &amp;" name="x" default=""/>
+ <description>
+ <brief>A macro with argument <argument>x</argument>.</brief>
+ </description>
+ </function>
+ <function name="anotherObsoleteMember" fullname="TestQDoc::Test::anotherObsoleteMember" href="testqdoc-test-obsolete.html#anotherObsoleteMember" status="deprecated" access="public" location="testcpp.h" documented="true" meta="plain" type="void" signature="void anotherObsoleteMember()">
+ <description>
+ <para>Use <link raw="obsoleteMember()" href="testqdoc-test.html#obsoleteMember" type="function">obsoleteMember()</link> instead.</para>
+ </description>
+ </function>
+ <function name="deprecatedMember" fullname="TestQDoc::Test::deprecatedMember" href="testqdoc-test-obsolete.html#deprecatedMember" status="deprecated" access="public" location="testcpp.h" documented="true" meta="plain" type="void" signature="void deprecatedMember()">
+ <description>
+ <para>Use <link raw="someFunction()" href="testqdoc-test.html#someFunction" type="function">someFunction()</link> instead.</para>
+ </description>
+ </function>
+ <function name="funcPtr" fullname="TestQDoc::Test::funcPtr" href="testqdoc-test.html#funcPtr" status="active" access="public" location="testcpp.h" documented="true" meta="plain" type="void (*)(bool)" signature="void (*)(bool) funcPtr(bool b, const char *s)">
+ <parameter type="bool" name="b" default=""/>
+ <parameter type="const char *" name="s" default=""/>
+ <description>
+ <para>Returns a pointer to a function that takes a boolean. Uses <argument>b</argument> and <argument>s</argument>.</para>
+ </description>
+ </function>
+ <function name="inlineFunction" fullname="TestQDoc::Test::inlineFunction" href="testqdoc-test.html#inlineFunction" status="active" access="public" location="testcpp.h" documented="true" meta="plain" type="void" brief="An inline function, documented using the \fn QDoc command" signature="void inlineFunction()">
+ <description>
+ <brief>An inline function, documented using the \fn QDoc command.</brief>
+ </description>
+ </function>
+ <function name="methodWithEmDashInItsDocs" fullname="TestQDoc::Test::methodWithEmDashInItsDocs" href="testqdoc-test.html#methodWithEmDashInItsDocs" status="active" access="public" location="testcpp.h" documented="true" meta="plain" type="void" signature="void methodWithEmDashInItsDocs()">
+ <description>
+ <para>This method has em dashes in its documentation—as you'll find represented by <teletype type="highlighted">---</teletype> in the sources—here and there. The important bit to note is that when passed e.g. to the \c command, the three hyphens are processed as input to the command and not replaced by an em dash.</para>
+ <para>-----------------------------------------------------------------------</para>
+ <para>People can still add a bunch of dashes, though, without QDoc replacing them all with a series of em dashes.</para>
+ <para>—You can also start a new paragraph with an em dash, if you want to.</para>
+ <see-also>
+ <link raw="methodWithEnDashInItsDocs" href="testqdoc-test.html#methodWithEnDashInItsDocs" type="function">methodWithEnDashInItsDocs</link>
+ </see-also>
+ </description>
+ </function>
+ <function name="methodWithEnDashInItsDocs" fullname="TestQDoc::Test::methodWithEnDashInItsDocs" href="testqdoc-test.html#methodWithEnDashInItsDocs" status="active" access="public" location="testcpp.h" documented="true" meta="plain" type="void" signature="void methodWithEnDashInItsDocs()">
+ <description>
+ <para>This method has en dashes in its documentation – as you'll find represented by <teletype type="highlighted">--</teletype> in the sources – here and there. The important bit to note is that when passed e.g. to the \c command, the two hyphens are processed as input to the command and not replaced by an en dash. This also applies to code blocks, where otherwise, the decrement operator would get completely borked:</para>
+ <code>for (int i = 42; i &gt; 0; --i)
+ // Do something cool during countdown.</code>
+ <para>...as it would be silly if this would output –i instead of <teletype type="highlighted">--i</teletype>.</para>
+ <para>-----------------------------------------------------------------------</para>
+ <para>It still allows people to add a bunch of dashes, though, without replacing them all with a series of en dashes. Of course, they might want to use the \hr command instead, like this:</para>
+ <para>– You can also start a new paragraph with an en dash, if you want to.</para>
+ <see-also>methodWithEnDashInItsDocs</see-also>
+ </description>
+ </function>
+ <function name="obsoleteMember" fullname="TestQDoc::Test::obsoleteMember" href="testqdoc-test-obsolete.html#obsoleteMember" status="deprecated" access="public" location="testcpp.h" documented="true" meta="plain" type="void" signature="void obsoleteMember()">
+ <description>
+ <para>Use <link raw="someFunction()" href="testqdoc-test.html#someFunction" type="function">someFunction()</link> instead.</para>
+ </description>
+ </function>
+ <function name="operator++" fullname="TestQDoc::Test::operator++" href="testqdoc-test-obsolete.html#operator-2b-2b" status="deprecated" access="public" location="testcpp.h" documented="true" meta="plain" type="TestQDoc::Test &amp;" signature="TestQDoc::Test &amp; operator++()">
+ <description/>
+ </function>
+ <function name="operator--" fullname="TestQDoc::Test::operator--" href="testqdoc-test-obsolete.html#operator--" status="deprecated" access="public" location="testcpp.h" documented="true" meta="plain" type="TestQDoc::Test &amp;" signature="TestQDoc::Test &amp; operator--()">
+ <description/>
+ </function>
+ <function name="operator==" href="testqdoc-test.html#operator-eq-eq" status="active" access="public" location="testcpp.h" documented="true" related="2" meta="plain" type="bool" signature="bool operator==(const TestQDoc::Test &amp;lhs, const TestQDoc::Test &amp;rhs)">
+ <parameter type="const TestQDoc::Test &amp;" name="lhs" default=""/>
+ <parameter type="const TestQDoc::Test &amp;" name="rhs" default=""/>
+ <description>
+ <para>Returns true if <argument>lhs</argument> and <argument>rhs</argument> are equal.</para>
+ </description>
+ </function>
+ <function name="overload" fullname="TestQDoc::Test::overload" href="testqdoc-test.html#overload" status="active" access="protected" location="testcpp.h" documented="true" meta="plain" type="void" signature="void overload()">
+ <description/>
+ </function>
+ <function name="overload" fullname="TestQDoc::Test::overload" href="testqdoc-test.html#overload-1" status="active" access="protected" location="testcpp.h" documented="true" since="Test 1.2" meta="plain" overload="true" overload-number="1" type="void" signature="void overload(bool b)">
+ <parameter type="bool" name="b" default=""/>
+ <description/>
+ </function>
+ <function name="someFunction" fullname="TestQDoc::Test::someFunction" href="testqdoc-test.html#someFunction" status="active" access="public" location="testcpp.h" documented="true" meta="plain" type="int" signature="int someFunction(int, int v)">
+ <parameter type="int" name="" default=""/>
+ <parameter type="int" name="v" default="0"/>
+ <description>
+ <para>Function that takes a parameter <argument>v</argument>. Also returns the value of <argument>v</argument>.</para>
+ </description>
+ </function>
+ <function name="someFunctionDefaultArg" fullname="TestQDoc::Test::someFunctionDefaultArg" href="testqdoc-test.html#someFunctionDefaultArg" threadsafety="non-reentrant" status="active" access="public" location="testcpp.h" documented="true" meta="plain" const="true" type="void" signature="void someFunctionDefaultArg(int i, bool b) const" groups="testgroup">
+ <parameter type="int" name="i" default=""/>
+ <parameter type="bool" name="b" default="false"/>
+ <description>
+ <para>Function that takes a parameter <argument>i</argument> and <argument>b</argument>.</para>
+ </description>
+ </function>
+ <function name="virtualFun" fullname="TestQDoc::Test::virtualFun" href="testqdoc-test.html#virtualFun" status="active" access="public" location="testcpp.h" documented="true" meta="plain" virtual="virtual" type="void" signature="void virtualFun()">
+ <description>
+ <para>Function that must be reimplemented.</para>
+ </description>
+ </function>
+ <enum name="ClassicEnum" fullname="TestQDoc::Test::ClassicEnum" href="testqdoc-test.html#ClassicEnum-enum" status="active" access="public" location="testcpp.h" documented="true">
+ <value name="Yee" value="0"/>
+ <value name="Haw" value="1"/>
+ <value name="Howdy" value="2"/>
+ <value name="Partner" value="3"/>
+ <description>
+ <list type="enum">
+ <definition>
+ <term>TestQDoc::Test::Yee</term>Yee</definition>
+ <item/>
+ <definition>
+ <term>TestQDoc::Test::Haw</term>Haw</definition>
+ <item/>
+ <definition>
+ <term>TestQDoc::Test::Howdy</term>Howdy</definition>
+ <item/>
+ <definition>
+ <term>TestQDoc::Test::Partner</term>Partner</definition>
+ <item/>
+ </list>
+ </description>
+ </enum>
+ <enum name="ScopedEnum" fullname="TestQDoc::Test::ScopedEnum" href="testqdoc-test.html#ScopedEnum-enum" status="active" access="public" location="testcpp.h" documented="true" scoped="true">
+ <value name="This" value="0x01"/>
+ <value name="That" value="0x02"/>
+ <value name="All" value="This | That" since="2.0"/>
+ <value name="OmittedValue" value="99"/>
+ <value name="UselessValue" value="100"/>
+ <value name="VeryLastValue" value="101"/>
+ <description>
+ <brief>This enum has a brief to trigger a bug in CMD_BRIEF.</brief>
+ <list type="enum">
+ <definition>
+ <term>TestQDoc::Test::ScopedEnum::This</term>This</definition>
+ <item>
+ <para>Something</para>
+ </item>
+ <definition>
+ <term>TestQDoc::Test::ScopedEnum::That</term>That</definition>
+ <item>
+ <para>Something else</para>
+ </item>
+ <definition>
+ <term>TestQDoc::Test::ScopedEnum::All</term>All</definition>2.0<item>
+ <para>Everything</para>
+ </item>
+ </list>
+ <para>A scoped enum.</para>
+ </description>
+ </enum>
+ <typedef name="SomeType" fullname="TestQDoc::Test::SomeType" href="testqdoc-test.html#SomeType-typedef" status="active" access="public" location="testcpp.h" documented="true">
+ <description>
+ <brief>A typedef.</brief>
+ </description>
+ </typedef>
+ </class>
+ <class name="TestDerived" fullname="TestQDoc::TestDerived" href="testqdoc-testderived.html" status="active" access="public" location="testcpp.h" since="2.0" documented="true" bases="TestQDoc::Test" module="TestCPP" brief="A class in a namespace, derived from Test">
+ <description>
+ <brief>A class in a namespace, derived from <link raw="Test" href="testqdoc-test.html" type="class">Test</link>.</brief>
+ </description>
+ <function name="someValue" fullname="TestQDoc::TestDerived::someValue" href="testqdoc-testderived.html#someValue" status="active" access="public" location="testcpp.h" documented="true" meta="plain" type="TestQDoc::TestDerived::NotTypedef" signature="TestQDoc::TestDerived::NotTypedef someValue()">
+ <description>
+ <para>Returns a value using an aliases type.</para>
+ </description>
+ </function>
+ <function name="staticObsoleteMember" fullname="TestQDoc::TestDerived::staticObsoleteMember" href="testqdoc-testderived-obsolete.html#staticObsoleteMember" status="deprecated" access="public" location="testcpp.h" documented="true" meta="plain" static="true" type="void" signature="void staticObsoleteMember()">
+ <description>
+ <para>Static obsolete method.</para>
+ </description>
+ </function>
+ <function name="virtualFun" fullname="TestQDoc::TestDerived::virtualFun" href="testqdoc-testderived.html#virtualFun" status="active" access="public" location="testcpp.h" documented="true" meta="plain" virtual="virtual" override="true" type="void" signature="void virtualFun() override">
+ <description/>
+ </function>
+ <typedef name="DerivedType" fullname="TestQDoc::TestDerived::DerivedType" href="testqdoc-testderived.html#DerivedType-typedef" status="active" access="public" location="testcpp.h" documented="true" aliasedtype="Test::SomeType">
+ <description>
+ <para>An aliased typedef.</para>
+ </description>
+ </typedef>
+ <typedef name="NotTypedef" fullname="TestQDoc::TestDerived::NotTypedef" href="testqdoc-testderived.html#NotTypedef-typedef" status="active" access="public" location="testcpp.h" documented="true" aliasedtype="int">
+ <description>
+ <para>I'm an alias, not a typedef.</para>
+ </description>
+ </typedef>
+ </class>
+ </namespace>
+ </document>
+</WebXML>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/scopedenum/expected/webxml/whatsnew.webxml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/scopedenum/expected/webxml/whatsnew.webxml
new file mode 100644
index 000000000..99a8c473e
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/scopedenum/expected/webxml/whatsnew.webxml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<WebXML>
+ <document>
+ <page name="whatsnew.html" href="whatsnew.html" status="active" location="scopedenum.qdoc" documented="true" subtype="page" title="New Classes and Functions" fulltitle="New Classes and Functions" subtitle="" brief="New classes documentation">
+ <description>
+ <brief>New classes documentation</brief>
+ </description>
+ </page>
+ </document>
+</WebXML>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/scopedenum/scopedenum.qdocconf b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/scopedenum/scopedenum.qdocconf
new file mode 100644
index 000000000..f20a92472
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/scopedenum/scopedenum.qdocconf
@@ -0,0 +1,37 @@
+sources.fileextensions = "*.qml *.cpp *.qdoc"
+headers.fileextensions = "*.h"
+
+# images
+imagedirs = ./src/images
+
+# zero warning policy
+warninglimit = 0
+warninglimit.enabled = true
+
+# don't write host system-specific paths to index files
+locationinfo = false
+
+project = TestCPP
+includepaths += -I./src
+
+headerdirs = ./src
+sources = ./src/testcpp.cpp \
+ ./src/classlists.qdoc \
+ ./src/scopedenum.qdoc
+exampledirs = snippets
+
+macro.CMDFN = \\\\fn
+macro.nothing = \\dontdocument ()
+macro.testnoautolist = \\if defined(test_noautolist)\n\\noautolist\n\\endif
+
+navigation.cppclassespage = "QDoc Test C++ Classes"
+
+defines += test_scopedenum
+
+outputformats = HTML WebXML DocBook
+
+{HTML.nosubdirs,DocBook.nosubdirs} = true
+HTML.outputsubdir = html
+WebXML.nosubdirs = true
+WebXML.outputsubdir = webxml
+DocBook.outputsubdir = docbook
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/scopedenum/src/classlists.qdoc b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/scopedenum/src/classlists.qdoc
new file mode 100644
index 000000000..2954e5beb
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/scopedenum/src/classlists.qdoc
@@ -0,0 +1,51 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+/*!
+ \page obsolete-classes.html
+ \title Obsolete Classes
+
+ \section1 Classes with obsolete members
+ \generatelist obsoletecppmembers
+
+ \section2 TestQDoc
+*/
+
+/*!
+ \page autolinking.html
+ \title Autolinking
+
+ //! a section title that qualifies for autolinking
+ \section1 TestQDoc
+
+ The string TestQDoc links to the C++ namespace unless linking explicitly,
+ \l {#TestQDoc}{like this}, or \l {TestQDoc}{this}. Also,
+
+ Autolinks:
+
+ \list
+ \li TestQDoc::TestDerived
+ \endlist
+
+ Explicit links:
+
+ \list
+ \li \l [CPP] {TestQDoc::TestDerived}
+ \li \l {Obsolete Classes#TestQDoc}
+ \endlist
+
+ //! a section title shadowing a known property name
+ \section1 someProp
+*/
+
+/*!
+ \group cpptypes
+ \title Test C++ Types
+
+ \generatelist testgroup
+*/
+
+/*!
+ \externalpage https://doc.qt.io/qt/17-qdoc-commands-thread.html#reentrant-command
+ \title reentrant
+*/
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/scopedenum/src/scopedenum.qdoc b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/scopedenum/src/scopedenum.qdoc
new file mode 100644
index 000000000..22ef9e3f3
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/scopedenum/src/scopedenum.qdoc
@@ -0,0 +1,43 @@
+// Copyright (C) 2020 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+
+/*!
+ \enum TestQDoc::Test::ClassicEnum
+
+ \value Yee
+ \value Haw
+ \value Howdy
+ \value Partner
+*/
+
+/*!
+ \enum TestQDoc::Test::ScopedEnum
+ \brief This enum has a brief to trigger a bug in CMD_BRIEF.
+ \omitvalue UselessValue
+ \value This Something
+ \value That Something else
+ \omitvalue OmittedValue \omit Unused -
+ This decription is omitted \endomit
+ \value [since 2.0] All Everything
+ \omitvalue VeryLastValue
+ Nothing here
+
+ A scoped enum.
+*/
+
+/*!
+ \page scoped-enum-linking.html
+ \title Enum Linking
+
+ Linking to \l {TestQDoc::Test::ScopedEnum::}{All}.
+
+ TestQDoc::Test::ClassicEnum::Howdy does not link,
+ but TestQDoc::Test::Howdy might.
+*/
+
+/*!
+ \page whatsnew.html
+ \title New Classes and Functions
+ \brief New classes documentation
+ \sincelist 2.0
+*/
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/scopedenum/src/snippets/snippet_testcpp.cpp b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/scopedenum/src/snippets/snippet_testcpp.cpp
new file mode 100644
index 000000000..1660fbc2b
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/scopedenum/src/snippets/snippet_testcpp.cpp
@@ -0,0 +1,3 @@
+//! [random tag]
+You're not supposed to see this.
+//! [random tag]
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/scopedenum/src/testcpp.cpp b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/scopedenum/src/testcpp.cpp
new file mode 100644
index 000000000..25dc960ff
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/scopedenum/src/testcpp.cpp
@@ -0,0 +1,402 @@
+// Copyright (C) 2019 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+#include "testcpp.h"
+
+namespace TestQDoc {
+
+/*
+//! [random tag]
+\note This is just a test.
+//! [random tag]
+
+//! [args]
+\1\2 \3 \2\1
+//! [args]
+*/
+
+/*!
+ \module TestCPP
+ \qtvariable testcpp
+ \qtcmakepackage QDocTest
+ \title QDoc Test C++ Classes
+ \brief A test module page.
+ \since 2.0
+
+ \testnoautolist
+
+ \include testcpp.cpp random tag
+ \include testcpp.cpp {args} {/} {*} {Look, Ma! {I'm made of arguments!}}
+
+\if defined(test_nestedmacro)
+ \versionnote {module} {\ver 5.15.0}
+ \ver 1.0.0
+\endif
+
+ \section1 Linking to function-like things
+
+ \list
+ \li \l {TestQDoc::Test::someFunctionDefaultArg}
+ {someFunctionDefaultArg()}
+ \li QDOCTEST_MACRO2()
+ \li \l {TestQDoc::Test::}{QDOCTEST_MACRO2(int &x)}
+ \li \l {section()}
+ \li \l {section()} {section() is a section title}
+ \endlist
+
+ \section2 section()
+*/
+
+/*!
+ \namespace TestQDoc
+ \inheaderfile TestCPP
+ \inmodule TestCPP
+ \brief A namespace.
+
+ \section1 Usage
+ This namespace is for testing QDoc output.
+*/
+
+/*!
+ \class TestQDoc::Test
+ \inmodule TestCPP
+ \brief A class in a namespace.
+
+\if defined(test_ignoresince)
+ //! omitted by ignoresince
+ \since 1.1
+\endif
+ \ingroup testgroup
+ \ingroup cpptypes
+ \reentrant
+*/
+
+/*!
+ \class TestQDoc::TestDerived
+ \inmodule TestCPP
+ \brief A class in a namespace, derived from \l [CPP] Test.
+*/
+
+/*!
+ \macro QDOCTEST_MACRO
+ \relates TestQDoc
+\if defined(test_ignoresince)
+ //! omitted by ignoresince.Test
+ \since Test 0.9
+\endif
+*/
+
+/*!
+ \macro QDOCTEST_MACRO2(int &x)
+ \relates TestQDoc::Test
+ \since Test 1.1
+ \brief A macro with argument \a x.
+ \ingroup testgroup
+*/
+
+/*!
+\if defined(test_properties)
+ \property Test::id
+\else
+ \nothing
+\endif
+*/
+
+/*!
+ \deprecated [6.0] Use someFunction() instead.
+*/
+void Test::deprecatedMember()
+{
+ return;
+}
+
+/*!
+ \obsolete
+
+ Use someFunction() instead.
+*/
+void Test::obsoleteMember()
+{
+ return;
+}
+
+/*!
+ \obsolete Use obsoleteMember() instead.
+*/
+void Test::anotherObsoleteMember()
+{
+ return;
+}
+
+/*!
+ \nonreentrant
+ Function that takes a parameter \a i and \a b.
+\if defined(test_ignoresince)
+ \since 2.0
+\endif
+ \ingroup testgroup
+*/
+void Test::someFunctionDefaultArg(int i, bool b = false) const
+{
+ return;
+}
+
+/*!
+ \fn void Test::func(bool)
+ \internal
+*/
+
+/*!
+ \fn [funcPtr] void (*funcPtr(bool b, const char *s))(bool)
+
+ Returns a pointer to a function that takes a boolean. Uses \a b and \a s.
+*/
+
+/*!
+ \fn [op-inc] Test::operator++()
+ \fn [op-dec] Test::operator--()
+ \deprecated
+*/
+
+/*!
+ This method has en dashes in its documentation -- as you'll find
+ represented by \c{--} in the sources -- here and there. The important bit
+ to note is that when passed e.g. to the \\c command, the two hyphens are
+ processed as input to the command and not replaced by an en dash. This also
+ applies to code blocks, where otherwise, the decrement operator would get
+ completely borked:
+
+ \code
+ for (int i = 42; i > 0; --i)
+ // Do something cool during countdown.
+ \endcode
+
+ ...as it would be silly if this would output --i instead of \c {--i}.
+
+ -----------------------------------------------------------------------
+
+ It still allows people to add a bunch of dashes, though, without replacing
+ them all with a series of en dashes. Of course, they might want to use the
+ \\hr command instead, like this:
+ \hr
+
+ -- You can also start a new paragraph with an en dash, if you want to.
+
+ //! Self-referencing \sa-command for tests.
+ \sa methodWithEnDashInItsDocs
+*/
+void Test::methodWithEnDashInItsDocs()
+{
+ // Nothing to see here.
+}
+
+/*!
+ This method has em dashes in its documentation---as you'll find
+ represented by \c{---} in the sources---here and there. The important bit
+ to note is that when passed e.g. to the \\c command, the three hyphens are
+ processed as input to the command and not replaced by an em dash.
+
+ -----------------------------------------------------------------------
+
+ People can still add a bunch of dashes, though, without QDoc replacing
+ them all with a series of em dashes.
+
+ ---You can also start a new paragraph with an em dash, if you want to.
+
+ \sa methodWithEnDashInItsDocs
+
+*/
+void Test::methodWithEmDashInItsDocs()
+{
+ // Woah! Look at that!
+}
+
+// Documented below with an \fn command. Unnecessary but we support it, and it's used.
+int Test::someFunction(int, int v)
+{
+ return v;
+}
+
+/*!
+ \fn void TestQDoc::Test::inlineFunction()
+
+ \brief An inline function, documented using the \CMDFN QDoc command.
+*/
+
+/*!
+ \fn int Test::someFunction(int, int v = 0)
+
+ Function that takes a parameter \a v.
+ Also returns the value of \a v.
+\if defined(test_ignoresince)
+ \since Test 1.0
+\endif
+*/
+
+/*!
+ Function that must be reimplemented.
+*/
+void Test::virtualFun()
+{
+ return;
+}
+
+/*!
+ \fn bool Test::operator==(const Test &lhs, const Test &rhs)
+
+ Returns true if \a lhs and \a rhs are equal.
+*/
+
+/*!
+ \typedef TestQDoc::Test::SomeType
+ \brief A typedef.
+*/
+
+/*!
+ \reimp
+*/
+void TestDerived::virtualFun()
+{
+ return;
+}
+
+/*!
+ \fn TestQDoc::Test::overload()
+ \fn Test::overload(bool b)
+ //! The second overload should match even without the fully qualified path
+
+ Overloads that share a documentation comment, optionally taking
+ a parameter \a b.
+*/
+
+/*!
+ \fn Test::overload(bool b)
+ \since Test 1.2
+*/
+
+/*!
+ \typealias TestQDoc::TestDerived::DerivedType
+ An aliased typedef.
+*/
+
+/*!
+ \typedef TestQDoc::TestDerived::NotTypedef
+ I'm an alias, not a typedef.
+*/
+
+/*!
+ \obsolete
+
+ Static obsolete method.
+*/
+void TestDerived::staticObsoleteMember()
+{
+ return;
+}
+
+/*!
+\if defined(test_properties)
+ \fn void TestDerived::emitSomething()
+ Emitted when things happen.
+\else
+ \nothing
+\endif
+*/
+
+/*!
+\if defined(test_properties)
+ \reimp
+\else
+ \nothing
+\endif
+*/
+int TestDerived::id()
+{
+ return 1;
+}
+
+/*!
+ Returns a value using an aliases type.
+*/
+TestDerived::NotTypedef TestDerived::someValue()
+{
+ return 0;
+}
+
+/*!
+\if defined(test_template)
+ \fn template <typename T1, typename T2> void TestQDoc::Test::funcTemplate(T1 a, T2 b)
+ \brief Function template with two parameters, \a a and \a b.
+\else
+ \nothing
+\endif
+*/
+
+/*!
+\if defined(test_template)
+ \struct TestQDoc::Test::Struct
+ \inmodule TestCPP
+ \brief Templated struct.
+\else
+ \nothing
+\endif
+*/
+
+/*!
+\if defined(test_template)
+ \typealias TestQDoc::Test::Specialized
+\else
+ \nothing
+\endif
+*/
+
+/*!
+\if defined(test_template)
+ \class TestQDoc::Vec
+ \inmodule TestCPP
+ \brief Type alias that has its own reference.
+\else
+ \nothing
+\endif
+*/
+
+/*!
+\if defined(test_template)
+ \macro Q_INVOKABLE
+ \relates TestQDoc::Test
+
+ This is a mock Q_INVOKABLE for the purpose of ensuring QDoc autolink to it
+ as expected.
+\else
+ \nothing
+\endif
+*/
+
+} // namespace TestQDoc
+
+
+/*!
+ \namespace CrossModuleRef
+ \inmodule TestCPP
+ \brief Namespace that has documented functions in multiple modules.
+ \since 3.0
+*/
+namespace CrossModuleRef {
+
+/*!
+ Document me!
+*/
+void documentMe()
+{
+}
+
+} // namespace CrossModuleRef
+
+/*!
+ \class DontLinkToMe
+ \inmodule TestCPP
+ \brief Class that does not generate documentation.
+*/
+
+/*!
+ \dontdocument (DontLinkToMe)
+*/
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/scopedenum/src/testcpp.h b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/scopedenum/src/testcpp.h
new file mode 100644
index 000000000..9b29eff25
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/scopedenum/src/testcpp.h
@@ -0,0 +1,139 @@
+// Copyright (C) 2019 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+#pragma once
+
+#ifdef test_properties
+#include <QtCore/qmetaobject.h>
+#include <QtCore/qproperty.h>
+#include <QtCore/qstring.h>
+#endif
+
+#define QDOCTEST_MACRO test
+#define QDOCTEST_MACRO2(x) (x) < 0 ? 0 : (x))
+
+namespace TestQDoc {
+
+class Test {
+#ifdef test_properties
+ Q_OBJECT
+ Q_PROPERTY(int id READ id)
+#endif
+public:
+
+#ifdef test_template
+template<typename D, typename T> struct Struct {};
+template<typename T>
+using Specialized = Struct<int, T>;
+#endif
+
+#ifdef test_template
+# define Q_INVOKABLE void foo() {};
+#endif
+
+#ifdef test_scopedenum
+ enum ClassicEnum { Yee, Haw, Howdy, Partner };
+
+ enum class ScopedEnum : unsigned char {
+ This = 0x01,
+ That = 0x02,
+ All = This | That,
+ OmittedValue = 99,
+ UselessValue,
+ VeryLastValue
+ };
+#endif
+ typedef struct {
+ int data;
+ } SomeType;
+ int someFunction(int, int v = 0);
+ void someFunctionDefaultArg(int i, bool b) const;
+ void obsoleteMember();
+ void anotherObsoleteMember();
+ void deprecatedMember();
+ void methodWithEnDashInItsDocs();
+ void methodWithEmDashInItsDocs();
+ void func(bool) {};
+ //! [funcPtr]
+ void (*funcPtr(bool b, const char *s))(bool) {
+ return func;
+ }
+ //! [op-inc]
+ Test &operator++() { return *this; }
+ //! [op-dec]
+ Test &operator--() { return *this; }
+
+ void anotherFunc() {};
+ inline void inlineFunction() {};
+ virtual void virtualFun();
+
+ friend bool operator==(const Test &lhs, const Test &rhs) { return false; }
+
+ Test() = delete;
+
+protected:
+ void overload() {}
+ void overload(bool b) { if (!b) return; }
+#ifdef test_template
+ template <typename T1, typename T2> void funcTemplate(T1 a, T2 b) {
+ a = b;
+ }
+#endif
+#ifdef test_properties
+ virtual int id() { return 0; }
+#endif
+};
+
+class TestDerived : public Test {
+#ifdef test_properties
+ Q_OBJECT
+
+ Q_PROPERTY(QString bindableProp READ bindableProp WRITE setBindableProp NOTIFY bindablePropChanged BINDABLE bindableProp)
+ Q_PROPERTY(QString someProp READ someProp BINDABLE somBindableProp)
+ Q_PROPERTY(int *intProp READ getInt STORED false CONSTANT FINAL)
+ Q_PROPERTY(const QString *name READ name)
+ QDOC_PROPERTY(bool boolProp READ boolProp WRITE setBoolProp NOTIFY boolPropChanged RESET resetBoolProp REVISION 1)
+#endif
+
+public:
+ using DerivedType = Test::SomeType;
+ using NotTypedef = int;
+ void virtualFun() override;
+ static void staticObsoleteMember();
+ NotTypedef someValue();
+#ifdef test_properties
+ QBindable<QString> bindableProp();
+ QBindable<QString> someBindableProp();
+ const QString &someProp();
+ int *getInt();
+ bool boolProp();
+ const QString *name() const;
+
+ Q_INVOKABLE void invokeMe() const {}
+ int id() override;
+
+Q_SIGNALS:
+ void emitSomething(QPrivateSignal);
+ void bindablePropChanged();
+ Q_REVISION(1) void boolPropChanged();
+
+public Q_SLOTS:
+ void setBindableProp(const QString &s);
+ void setBoolProp(bool b);
+ void resetBoolProp();
+#endif
+};
+
+#ifdef test_template
+template <typename T>
+struct BaseVec {};
+template <typename T>
+using Vec = BaseVec<T>;
+#endif
+
+} // namespace TestQDoc
+
+namespace CrossModuleRef {
+ void documentMe();
+}
+
+class DontLinkToMe {};
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/tableaftervalue/expected/docbok/tableaftervalue.xml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/tableaftervalue/expected/docbok/tableaftervalue.xml
new file mode 100644
index 000000000..99b8fe13a
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/tableaftervalue/expected/docbok/tableaftervalue.xml
@@ -0,0 +1,60 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<db:article xmlns:db="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" version="5.2" xml:lang="en">
+<db:info>
+<db:title>TableAfterValue Class</db:title>
+<db:productname>TableAfterValue</db:productname>
+<db:titleabbrev>TableAfterValue Reference Documentation</db:titleabbrev>
+<db:abstract>
+<db:para>Test that the \table command can follow a \value command.</db:para>
+</db:abstract>
+</db:info>
+<db:variablelist>
+<db:varlistentry>
+<db:term>Header</db:term>
+<db:listitem>
+<db:para>TableAfterValue</db:para>
+</db:listitem>
+</db:varlistentry>
+</db:variablelist>
+<db:section xml:id="details">
+<db:title>Detailed Description</db:title>
+<db:section xml:id="background-for-this-test-content">
+<db:title>Background for this test content</db:title>
+<db:para>According to QTBUG-115720, whenever a \value is followed by \table, the contents of the two commands end up being merged. This is likely surprising. The bug report suggests a workaround, which is adding \br between \value and \table.</db:para>
+</db:section>
+<db:section>
+<db:title>See Also</db:title>
+<db:para><db:emphasis>See also </db:emphasis>
+<db:simplelist type="vert" role="see-also">
+<db:member><db:link xlink:href="tableaftervalue.xml#Reproduces-enum">TableAfterValue::Reproduces</db:link></db:member>
+</db:simplelist>
+</db:para>
+</db:section>
+</db:section>
+<db:section xml:id="member-type-documentation">
+<db:title>Member Type Documentation</db:title>
+<db:section xml:id="Reproduces-enum">
+<db:title>enum TableAfterValue::Reproduces</db:title>
+<db:informaltable>
+<db:thead>
+<db:tr>
+<db:th>Constant</db:th>
+</db:tr>
+</db:thead>
+<db:tr>
+<db:td>
+<db:para><db:code><db:emphasis role="bold"><db:link xlink:href="tableaftervalue.xml">TableAfterValue</db:link></db:emphasis>::Problem</db:code></db:para>
+</db:td>
+<db:td><db:code>0</db:code></db:td>
+</db:tr>
+</db:informaltable>
+<db:informaltable style="generic">
+<db:tr valign="top">
+<db:td>
+<db:para>This table shouldn't be mangled by the previous \value command.</db:para>
+</db:td>
+</db:tr>
+</db:informaltable>
+</db:section>
+</db:section>
+</db:article>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/tableaftervalue/expected/html/tableaftervalue-members.html b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/tableaftervalue/expected/html/tableaftervalue-members.html
new file mode 100644
index 000000000..328848c33
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/tableaftervalue/expected/html/tableaftervalue-members.html
@@ -0,0 +1,18 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+<!-- table-after-value.cpp -->
+ <meta name="description" content="Test that the \table command can follow a \value command.">
+ <title>List of All Members for TableAfterValue | TableAfterValue</title>
+</head>
+<body>
+<li>TableAfterValue</li>
+<div class="sidebar"><div class="sidebar-content" id="sidebar-content"></div></div>
+<h1 class="title" translate="no">List of All Members for TableAfterValue</h1>
+<p>This is the complete list of members for <a href="tableaftervalue.html">TableAfterValue</a>, including inherited members.</p>
+<ul>
+<li class="fn" translate="no">enum <span class="name"><b><a href="tableaftervalue.html#Reproduces-enum" translate="no">Reproduces</a></b></span></li>
+</ul>
+</body>
+</html>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/tableaftervalue/expected/html/tableaftervalue.html b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/tableaftervalue/expected/html/tableaftervalue.html
new file mode 100644
index 000000000..d27d77ef6
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/tableaftervalue/expected/html/tableaftervalue.html
@@ -0,0 +1,56 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+<!-- table-after-value.cpp -->
+ <meta name="description" content="Test that the \table command can follow a \value command.">
+ <title>TableAfterValue Class | TableAfterValue</title>
+</head>
+<body>
+<li>TableAfterValue</li>
+<div class="sidebar">
+<div class="toc">
+<h3 id="toc">Contents</h3>
+<ul>
+<li class="level1"><a href="#public-types">Public Types</a></li>
+<li class="level1"><a href="#details">Detailed Description</a></li>
+<li class="level2"><a href="#background-for-this-test-content">Background for this test content</a></li>
+</ul>
+</div>
+<div class="sidebar-content" id="sidebar-content"></div></div>
+<h1 class="title" translate="no">TableAfterValue Class</h1>
+<!-- $$$TableAfterValue-brief -->
+<p>Test that the \table command can follow a \value command. <a href="#details">More...</a></p>
+<!-- @@@TableAfterValue -->
+<div class="table"><table class="alignedsummary" translate="no">
+<tr><td class="memItemLeft rightAlign topAlign"> Header:</td><td class="memItemRight bottomAlign"> <span class="preprocessor">#include &lt;TableAfterValue&gt;</span></td></tr>
+</table></div>
+<ul>
+<li><a href="tableaftervalue-members.html">List of all members, including inherited members</a></li>
+</ul>
+<h2 id="public-types">Public Types</h2>
+<div class="table"><table class="alignedsummary" translate="no">
+<tr><td class="memItemLeft rightAlign topAlign"> enum </td><td class="memItemRight bottomAlign"><b><a href="tableaftervalue.html#Reproduces-enum" translate="no">Reproduces</a></b> { Problem }</td></tr>
+</table></div>
+<!-- $$$TableAfterValue-description -->
+<div class="descr">
+<h2 id="details">Detailed Description</h2>
+<h3 id="background-for-this-test-content">Background for this test content</h3>
+<p>According to QTBUG-115720, whenever a \value is followed by \table, the contents of the two commands end up being merged. This is likely surprising. The bug report suggests a workaround, which is adding \br between \value and \table.</p>
+</div>
+<p><b>See also </b><a href="tableaftervalue.html#Reproduces-enum" translate="no">TableAfterValue::Reproduces</a>.</p>
+<!-- @@@TableAfterValue -->
+<div class="types">
+<h2>Member Type Documentation</h2>
+<!-- $$$Reproduces$$$Problem -->
+<h3 class="fn" translate="no" id="Reproduces-enum">enum TableAfterValue::<span class="name">Reproduces</span></h3>
+<div class="table"><table class="valuelist"><tr><th class="tblConst">Constant</th><th class="tblVal">Value</th></tr>
+<tr><td class="topAlign"><code translate="no">TableAfterValue::Problem</code></td><td class="topAlign tblval"><code translate="no">0</code></td></tr>
+</table></div>
+<div class="table"><table class="generic">
+ <tr valign="top" class="odd"><td >This table shouldn't be mangled by the previous \value command.</td></tr>
+</table></div>
+<!-- @@@Reproduces -->
+</div>
+</body>
+</html>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/tableaftervalue/expected/html/tableaftervalue.index b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/tableaftervalue/expected/html/tableaftervalue.index
new file mode 100644
index 000000000..4f3164508
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/tableaftervalue/expected/html/tableaftervalue.index
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE QDOCINDEX>
+<INDEX url="" title="TableAfterValue Reference Documentation" version="" project="TableAfterValue">
+ <namespace name="" status="active" access="public" module="tableaftervalue">
+ <class name="TableAfterValue" href="tableaftervalue.html" status="active" access="public" location="table-after-value.h" documented="true" module="Test" brief="Test that the \table command can follow a \value command">
+ <contents name="background-for-this-test-content" title="Background for this test content" level="1"/>
+ <enum name="Reproduces" fullname="TableAfterValue::Reproduces" href="tableaftervalue.html#Reproduces-enum" status="active" access="public" location="table-after-value.h" documented="true">
+ <value name="Problem" value="0"/>
+ </enum>
+ </class>
+ <module name="Test" href="test-module.html" status="internal" seen="false" title=""/>
+ </namespace>
+</INDEX>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/tableaftervalue/expected/webxml/tableaftervalue.index b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/tableaftervalue/expected/webxml/tableaftervalue.index
new file mode 100644
index 000000000..4f3164508
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/tableaftervalue/expected/webxml/tableaftervalue.index
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE QDOCINDEX>
+<INDEX url="" title="TableAfterValue Reference Documentation" version="" project="TableAfterValue">
+ <namespace name="" status="active" access="public" module="tableaftervalue">
+ <class name="TableAfterValue" href="tableaftervalue.html" status="active" access="public" location="table-after-value.h" documented="true" module="Test" brief="Test that the \table command can follow a \value command">
+ <contents name="background-for-this-test-content" title="Background for this test content" level="1"/>
+ <enum name="Reproduces" fullname="TableAfterValue::Reproduces" href="tableaftervalue.html#Reproduces-enum" status="active" access="public" location="table-after-value.h" documented="true">
+ <value name="Problem" value="0"/>
+ </enum>
+ </class>
+ <module name="Test" href="test-module.html" status="internal" seen="false" title=""/>
+ </namespace>
+</INDEX>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/tableaftervalue/expected/webxml/tableaftervalue.webxml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/tableaftervalue/expected/webxml/tableaftervalue.webxml
new file mode 100644
index 000000000..9f7f5e3bb
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/tableaftervalue/expected/webxml/tableaftervalue.webxml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<WebXML>
+ <document>
+ <class name="TableAfterValue" href="tableaftervalue.html" status="active" access="public" location="table-after-value.h" documented="true" module="Test" brief="Test that the \table command can follow a \value command">
+ <contents name="background-for-this-test-content" title="Background for this test content" level="1"/>
+ <description>
+ <brief>Test that the \table command can follow a \value command.</brief>
+ <section id="background-for-this-test-content">
+ <heading level="1">Background for this test content</heading>
+ <para>According to QTBUG-115720, whenever a \value is followed by \table, the contents of the two commands end up being merged. This is likely surprising. The bug report suggests a workaround, which is adding \br between \value and \table.</para>
+ </section>
+ <see-also>
+ <link raw="TableAfterValue::Reproduces" href="tableaftervalue.html#Reproduces-enum" type="enum" enum="TableAfterValue::Reproduces">TableAfterValue::Reproduces</link>
+ </see-also>
+ </description>
+ <enum name="Reproduces" fullname="TableAfterValue::Reproduces" href="tableaftervalue.html#Reproduces-enum" status="active" access="public" location="table-after-value.h" documented="true">
+ <value name="Problem" value="0"/>
+ <description>
+ <list type="enum">
+ <definition>
+ <term>TableAfterValue::Problem</term>Problem</definition>
+ <item/>
+ </list>
+ <table>
+ <row>
+ <item>
+ <para>This table shouldn't be mangled by the previous \value command.</para>
+ </item>
+ </row>
+ </table>
+ </description>
+ </enum>
+ </class>
+ </document>
+</WebXML>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/tableaftervalue/src/table-after-value.cpp b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/tableaftervalue/src/table-after-value.cpp
new file mode 100644
index 000000000..4ad9932d5
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/tableaftervalue/src/table-after-value.cpp
@@ -0,0 +1,28 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+#include "tableaftervalue.h"
+
+/*!
+ \class TableAfterValue
+ \inmodule Test
+ \brief Test that the \\table command can follow a \\value command.
+
+ \section1 Background for this test content
+ According to QTBUG-115720, whenever a \\value is followed by \\table, the
+ contents of the two commands end up being merged. This is likely surprising.
+ The bug report suggests a workaround, which is adding \\br between \\value
+ and \\table.
+
+ \sa TableAfterValue::Reproduces
+*/
+
+/*!
+ \enum TableAfterValue::Reproduces
+
+ \value Problem
+
+ \table
+ \row
+ \li This table shouldn't be mangled by the previous \\value command.
+ \endtable
+*/
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/tableaftervalue/src/table-after-value.h b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/tableaftervalue/src/table-after-value.h
new file mode 100644
index 000000000..466242775
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/tableaftervalue/src/table-after-value.h
@@ -0,0 +1,8 @@
+// Copyright (C) 2020 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+class TableAfterValue
+{
+public:
+ enum Reproduces { Problem };
+};
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/tableaftervalue/tableaftervalue.qdocconf b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/tableaftervalue/tableaftervalue.qdocconf
new file mode 100644
index 000000000..fb8daa132
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/tableaftervalue/tableaftervalue.qdocconf
@@ -0,0 +1,29 @@
+project = TableAfterValue
+
+headerdirs = ./src
+sourcedirs = ./src
+exampledirs = ./src
+
+outputformats = WebXML HTML DocBook
+WebXML.quotinginformation = true
+WebXML.nosubdirs = true
+WebXML.outputsubdir = webxml
+
+HTML.nosubdirs = true
+HTML.outputsubdir = html
+
+DocBook.nosubdirs = true
+DocBook.outputsubdir = docbok
+
+sources.fileextensions = "*.qml *.cpp *.qdoc"
+headers.fileextensions = "*.h"
+
+# images
+imagedirs = ./src/images
+
+# zero warning policy
+warninglimit = 0
+warninglimit.enabled = true
+
+# don't write host system-specific paths to index files
+locationinfo = false
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/templatedcallables/expected/docbook/templated-callables-h.xml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/templatedcallables/expected/docbook/templated-callables-h.xml
new file mode 100644
index 000000000..2a7204642
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/templatedcallables/expected/docbook/templated-callables-h.xml
@@ -0,0 +1,65 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<db:article xmlns:db="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" version="5.2" xml:lang="en">
+<db:info>
+<db:title>&lt;templated_callables.h&gt;</db:title>
+<db:productname>templatedcallables</db:productname>
+<db:titleabbrev>templatedcallables Reference Documentation</db:titleabbrev>
+<db:abstract>
+<db:para>templatedcallables Reference Documentation.</db:para></db:abstract>
+</db:info>
+<db:variablelist>
+<db:varlistentry>
+<db:term>Header</db:term>
+<db:listitem>
+<db:para>templated_callables.h</db:para>
+</db:listitem>
+</db:varlistentry>
+</db:variablelist>
+<db:section xml:id="details">
+<db:title>Detailed Description</db:title>
+<db:para>Containing headers for the tested free functions.</db:para>
+</db:section>
+<db:section xml:id="function-documentation">
+<db:title>Function Documentation</db:title>
+<db:section xml:id="templated_function_with_defaulted_non_type_template_parameter">
+<db:title>void templated_function_with_defaulted_non_type_template_parameter()</db:title>
+<db:para>A templated function with a defaulted non type template parameter.</db:para>
+</db:section>
+<db:section xml:id="templated_function_with_defaulted_template_template_parameter">
+<db:title>void templated_function_with_defaulted_template_template_parameter(<db:emphasis>Container&lt;T, size&gt;</db:emphasis>)</db:title>
+<db:para>A templated function with a defaulted template template parameter.</db:para>
+</db:section>
+<db:section xml:id="templated_function_with_defaulted_type_template_parameter">
+<db:title>void templated_function_with_defaulted_type_template_parameter(<db:emphasis>T</db:emphasis>)</db:title>
+<db:para>A templated function with a defaulted type template parameter.</db:para>
+</db:section>
+<db:section xml:id="templated_function_with_non_type_template_parameter">
+<db:title>void templated_function_with_non_type_template_parameter()</db:title>
+<db:para>A templated function with a non-defaulted non type template parameter.</db:para>
+</db:section>
+<db:section xml:id="templated_function_with_non_type_template_parameter_pack">
+<db:title>void templated_function_with_non_type_template_parameter_pack()</db:title>
+<db:para>A templated function with a non type template parameter pack.</db:para>
+</db:section>
+<db:section xml:id="templated_function_with_placeholder_non_type_template_parameter">
+<db:title>void templated_function_with_placeholder_non_type_template_parameter()</db:title>
+<db:para>A templated function with a placeholder non type template parameter.</db:para>
+</db:section>
+<db:section xml:id="templated_function_with_template_template_parameter">
+<db:title>void templated_function_with_template_template_parameter(<db:emphasis>K&lt;T&gt;</db:emphasis>)</db:title>
+<db:para>A templated function with a non-defaulted template template parameter.</db:para>
+</db:section>
+<db:section xml:id="templated_function_with_template_template_parameter_pack">
+<db:title>void templated_function_with_template_template_parameter_pack(<db:emphasis>Container&lt;T&gt;...</db:emphasis>)</db:title>
+<db:para>A templated function with a template template parameter pack.</db:para>
+</db:section>
+<db:section xml:id="templated_function_with_type_template_parameter">
+<db:title>void templated_function_with_type_template_parameter(<db:emphasis>T</db:emphasis>)</db:title>
+<db:para>A templated function with a non-defaulted type template parameter.</db:para>
+</db:section>
+<db:section xml:id="templated_function_with_type_template_parameter_pack">
+<db:title>void templated_function_with_type_template_parameter_pack(<db:emphasis>Ts...</db:emphasis>)</db:title>
+<db:para>A templated function with a type template parameter pack.</db:para>
+</db:section>
+</db:section>
+</db:article>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/templatedcallables/expected/docbook/templatedclass.xml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/templatedcallables/expected/docbook/templatedclass.xml
new file mode 100644
index 000000000..427f1cab3
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/templatedcallables/expected/docbook/templatedclass.xml
@@ -0,0 +1,66 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<db:article xmlns:db="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" version="5.2" xml:lang="en">
+<db:info>
+<db:title>TemplatedClass Class</db:title>
+<db:subtitle>template &lt;typename T&gt; class TemplatedClass</db:subtitle>
+<db:productname>templatedcallables</db:productname>
+<db:titleabbrev>templatedcallables Reference Documentation</db:titleabbrev>
+<db:abstract>
+<db:para>templatedcallables Reference Documentation.</db:para></db:abstract>
+</db:info>
+<db:variablelist>
+<db:varlistentry>
+<db:term>Header</db:term>
+<db:listitem>
+<db:para>TemplatedClass</db:para>
+</db:listitem>
+</db:varlistentry>
+</db:variablelist>
+<db:section xml:id="details">
+<db:title>Detailed Description</db:title>
+<db:para>Containing record for the tested methods.</db:para>
+</db:section>
+<db:section xml:id="member-function-documentation">
+<db:title>Member Function Documentation</db:title>
+<db:section xml:id="templated_method_with_defaulted_non_type_template_parameter">
+<db:title>void TemplatedClass::templated_method_with_defaulted_non_type_template_parameter()</db:title>
+<db:para>A templated method under a templated class with a defaulted non type template parameter.</db:para>
+</db:section>
+<db:section xml:id="templated_method_with_defaulted_template_template_parameter">
+<db:title>void TemplatedClass::templated_method_with_defaulted_template_template_parameter(<db:emphasis>Container&lt;U, size&gt;</db:emphasis>)</db:title>
+<db:para>A templated method under a templated class with a defaulted template template parameter.</db:para>
+</db:section>
+<db:section xml:id="templated_method_with_defaulted_type_template_parameter">
+<db:title>void TemplatedClass::templated_method_with_defaulted_type_template_parameter(<db:emphasis>U</db:emphasis>)</db:title>
+<db:para>A templated method under a templated class with a defaulted type template parameter.</db:para>
+</db:section>
+<db:section xml:id="templated_method_with_non_type_template_parameter">
+<db:title>void TemplatedClass::templated_method_with_non_type_template_parameter()</db:title>
+<db:para>A templated method under a templated class with a non-defaulted non type template parameter.</db:para>
+</db:section>
+<db:section xml:id="templated_method_with_non_type_template_parameter_pack">
+<db:title>void TemplatedClass::templated_method_with_non_type_template_parameter_pack()</db:title>
+<db:para>A templated method under a templated class with a non type template parameter pack.</db:para>
+</db:section>
+<db:section xml:id="templated_method_with_placeholder_non_type_template_parameter">
+<db:title>void TemplatedClass::templated_method_with_placeholder_non_type_template_parameter()</db:title>
+<db:para>A templated method under a templated class with a placeholder non type template parameter.</db:para>
+</db:section>
+<db:section xml:id="templated_method_with_template_template_parameter">
+<db:title>void TemplatedClass::templated_method_with_template_template_parameter(<db:emphasis>X&lt;U&gt;</db:emphasis>)</db:title>
+<db:para>A templated method under a templated class with a non-defaulted template template parameter.</db:para>
+</db:section>
+<db:section xml:id="templated_method_with_template_template_parameter_pack">
+<db:title>void TemplatedClass::templated_method_with_template_template_parameter_pack(<db:emphasis>Container&lt;U&gt;...</db:emphasis>)</db:title>
+<db:para>A templated method under a templated class with a template template parameter pack.</db:para>
+</db:section>
+<db:section xml:id="templated_method_with_type_template_parameter">
+<db:title>void TemplatedClass::templated_method_with_type_template_parameter(<db:emphasis>U</db:emphasis>)</db:title>
+<db:para>A templated method under a templated class with a non-defaulted type template parameter.</db:para>
+</db:section>
+<db:section xml:id="templated_method_with_type_template_parameter_pack">
+<db:title>void TemplatedClass::templated_method_with_type_template_parameter_pack(<db:emphasis>Ts...</db:emphasis>)</db:title>
+<db:para>A templated method under a templated class with a type template parameter pack.</db:para>
+</db:section>
+</db:section>
+</db:article>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/templatedcallables/expected/html/templated-callables-h.html b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/templatedcallables/expected/html/templated-callables-h.html
new file mode 100644
index 000000000..4c8bd89c8
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/templatedcallables/expected/html/templated-callables-h.html
@@ -0,0 +1,81 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+<!-- templated_callables.cpp -->
+ <title>&lt;templated_callables.h&gt; | templatedcallables</title>
+</head>
+<body>
+<div class="sidebar">
+<div class="toc">
+<h3 id="toc">Contents</h3>
+</div>
+<div class="sidebar-content" id="sidebar-content"></div></div>
+<h1 class="title" translate="no">&lt;templated_callables.h&gt;</h1>
+<div class="table"><table class="alignedsummary" translate="no">
+<tr><td class="memItemLeft rightAlign topAlign"> Header:</td><td class="memItemRight bottomAlign"> <span class="preprocessor">#include &lt;templated_callables.h&gt;</span></td></tr>
+</table></div>
+<h2 id="functions">Functions</h2>
+<div class="table"><table class="alignedsummary" translate="no">
+<tr><td class="memItemLeft rightAlign topAlign"> void </td><td class="memItemRight bottomAlign"><b><a href="templated-callables-h.html#templated_function_with_defaulted_non_type_template_parameter" translate="no">templated_function_with_defaulted_non_type_template_parameter</a></b>()</td></tr>
+<tr><td class="memItemLeft rightAlign topAlign"> void </td><td class="memItemRight bottomAlign"><b><a href="templated-callables-h.html#templated_function_with_defaulted_template_template_parameter" translate="no">templated_function_with_defaulted_template_template_parameter</a></b>(Container&lt;T, size&gt;)</td></tr>
+<tr><td class="memItemLeft rightAlign topAlign"> void </td><td class="memItemRight bottomAlign"><b><a href="templated-callables-h.html#templated_function_with_defaulted_type_template_parameter" translate="no">templated_function_with_defaulted_type_template_parameter</a></b>(T)</td></tr>
+<tr><td class="memItemLeft rightAlign topAlign"> void </td><td class="memItemRight bottomAlign"><b><a href="templated-callables-h.html#templated_function_with_non_type_template_parameter" translate="no">templated_function_with_non_type_template_parameter</a></b>()</td></tr>
+<tr><td class="memItemLeft rightAlign topAlign"> void </td><td class="memItemRight bottomAlign"><b><a href="templated-callables-h.html#templated_function_with_non_type_template_parameter_pack" translate="no">templated_function_with_non_type_template_parameter_pack</a></b>()</td></tr>
+<tr><td class="memItemLeft rightAlign topAlign"> void </td><td class="memItemRight bottomAlign"><b><a href="templated-callables-h.html#templated_function_with_placeholder_non_type_template_parameter" translate="no">templated_function_with_placeholder_non_type_template_parameter</a></b>()</td></tr>
+<tr><td class="memItemLeft rightAlign topAlign"> void </td><td class="memItemRight bottomAlign"><b><a href="templated-callables-h.html#templated_function_with_template_template_parameter" translate="no">templated_function_with_template_template_parameter</a></b>(K&lt;T&gt;)</td></tr>
+<tr><td class="memItemLeft rightAlign topAlign"> void </td><td class="memItemRight bottomAlign"><b><a href="templated-callables-h.html#templated_function_with_template_template_parameter_pack" translate="no">templated_function_with_template_template_parameter_pack</a></b>(Container&lt;T&gt;...)</td></tr>
+<tr><td class="memItemLeft rightAlign topAlign"> void </td><td class="memItemRight bottomAlign"><b><a href="templated-callables-h.html#templated_function_with_type_template_parameter" translate="no">templated_function_with_type_template_parameter</a></b>(T)</td></tr>
+<tr><td class="memItemLeft rightAlign topAlign"> void </td><td class="memItemRight bottomAlign"><b><a href="templated-callables-h.html#templated_function_with_type_template_parameter_pack" translate="no">templated_function_with_type_template_parameter_pack</a></b>(Ts...)</td></tr>
+</table></div>
+<!-- $$$<templated_callables.h>-description -->
+<div class="descr">
+<h2 id="details">Detailed Description</h2>
+<p>Containing headers for the tested free functions.</p>
+</div>
+<!-- @@@<templated_callables.h> -->
+<div class="func">
+<h2>Function Documentation</h2>
+<!-- $$$templated_function_with_defaulted_non_type_template_parameter[overload1]$$$templated_function_with_defaulted_non_type_template_parameter -->
+<h3 class="fn" translate="no" id="templated_function_with_defaulted_non_type_template_parameter">template &lt;char Category = 'A'&gt; <span class="type">void</span> <span class="name">templated_function_with_defaulted_non_type_template_parameter</span>()</h3>
+<p>A templated function with a defaulted non type template parameter.</p>
+<!-- @@@templated_function_with_defaulted_non_type_template_parameter -->
+<!-- $$$templated_function_with_defaulted_template_template_parameter[overload1]$$$templated_function_with_defaulted_template_template_parameterContainer<T,size> -->
+<h3 class="fn" translate="no" id="templated_function_with_defaulted_template_template_parameter">template &lt;typename T, int size, template &lt;typename, int &gt; typename Container = std::array&gt; <span class="type">void</span> <span class="name">templated_function_with_defaulted_template_template_parameter</span>(<span class="type">Container</span>&lt;<span class="type">T</span>, <span class="type">size</span>&gt;)</h3>
+<p>A templated function with a defaulted template template parameter.</p>
+<!-- @@@templated_function_with_defaulted_template_template_parameter -->
+<!-- $$$templated_function_with_defaulted_type_template_parameter[overload1]$$$templated_function_with_defaulted_type_template_parameterT -->
+<h3 class="fn" translate="no" id="templated_function_with_defaulted_type_template_parameter">template &lt;typename T = char&gt; <span class="type">void</span> <span class="name">templated_function_with_defaulted_type_template_parameter</span>(<span class="type">T</span>)</h3>
+<p>A templated function with a defaulted type template parameter.</p>
+<!-- @@@templated_function_with_defaulted_type_template_parameter -->
+<!-- $$$templated_function_with_non_type_template_parameter[overload1]$$$templated_function_with_non_type_template_parameter -->
+<h3 class="fn" translate="no" id="templated_function_with_non_type_template_parameter">template &lt;char Category&gt; <span class="type">void</span> <span class="name">templated_function_with_non_type_template_parameter</span>()</h3>
+<p>A templated function with a non-defaulted non type template parameter.</p>
+<!-- @@@templated_function_with_non_type_template_parameter -->
+<!-- $$$templated_function_with_non_type_template_parameter_pack[overload1]$$$templated_function_with_non_type_template_parameter_pack -->
+<h3 class="fn" translate="no" id="templated_function_with_non_type_template_parameter_pack">template &lt;unsigned int... Weights&gt; <span class="type">void</span> <span class="name">templated_function_with_non_type_template_parameter_pack</span>()</h3>
+<p>A templated function with a non type template parameter pack.</p>
+<!-- @@@templated_function_with_non_type_template_parameter_pack -->
+<!-- $$$templated_function_with_placeholder_non_type_template_parameter[overload1]$$$templated_function_with_placeholder_non_type_template_parameter -->
+<h3 class="fn" translate="no" id="templated_function_with_placeholder_non_type_template_parameter">template &lt;auto Iterator&gt; <span class="type">void</span> <span class="name">templated_function_with_placeholder_non_type_template_parameter</span>()</h3>
+<p>A templated function with a placeholder non type template parameter.</p>
+<!-- @@@templated_function_with_placeholder_non_type_template_parameter -->
+<!-- $$$templated_function_with_template_template_parameter[overload1]$$$templated_function_with_template_template_parameterK<T> -->
+<h3 class="fn" translate="no" id="templated_function_with_template_template_parameter">template &lt;typename T, template &lt;typename&gt; typename K&gt; <span class="type">void</span> <span class="name">templated_function_with_template_template_parameter</span>(<span class="type">K</span>&lt;<span class="type">T</span>&gt;)</h3>
+<p>A templated function with a non-defaulted template template parameter.</p>
+<!-- @@@templated_function_with_template_template_parameter -->
+<!-- $$$templated_function_with_template_template_parameter_pack[overload1]$$$templated_function_with_template_template_parameter_packContainer<T>... -->
+<h3 class="fn" translate="no" id="templated_function_with_template_template_parameter_pack">template &lt;typename T, template &lt;typename&gt; typename... Container&gt; <span class="type">void</span> <span class="name">templated_function_with_template_template_parameter_pack</span>(<span class="type">Container</span>&lt;<span class="type">T</span>&gt;...)</h3>
+<p>A templated function with a template template parameter pack.</p>
+<!-- @@@templated_function_with_template_template_parameter_pack -->
+<!-- $$$templated_function_with_type_template_parameter[overload1]$$$templated_function_with_type_template_parameterT -->
+<h3 class="fn" translate="no" id="templated_function_with_type_template_parameter">template &lt;typename T&gt; <span class="type">void</span> <span class="name">templated_function_with_type_template_parameter</span>(<span class="type">T</span>)</h3>
+<p>A templated function with a non-defaulted type template parameter.</p>
+<!-- @@@templated_function_with_type_template_parameter -->
+<!-- $$$templated_function_with_type_template_parameter_pack[overload1]$$$templated_function_with_type_template_parameter_packTs... -->
+<h3 class="fn" translate="no" id="templated_function_with_type_template_parameter_pack">template &lt;typename... Ts&gt; <span class="type">void</span> <span class="name">templated_function_with_type_template_parameter_pack</span>(<span class="type">Ts</span>...)</h3>
+<p>A templated function with a type template parameter pack.</p>
+<!-- @@@templated_function_with_type_template_parameter_pack -->
+</div>
+</body>
+</html>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/templatedcallables/expected/html/templatedcallables.index b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/templatedcallables/expected/html/templatedcallables.index
new file mode 100644
index 000000000..91245c7f8
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/templatedcallables/expected/html/templatedcallables.index
@@ -0,0 +1,77 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE QDOCINDEX>
+<INDEX url="" title="templatedcallables Reference Documentation" version="" project="templatedcallables">
+ <namespace name="" status="active" access="public" module="templatedcallables">
+ <function name="templated_function_with_defaulted_non_type_template_parameter" href="templated-callables-h.html#templated_function_with_defaulted_non_type_template_parameter" status="active" access="public" location="templated_callables.h" documented="true" related="0" meta="plain" type="void" signature="void templated_function_with_defaulted_non_type_template_parameter()"/>
+ <function name="templated_function_with_defaulted_template_template_parameter" href="templated-callables-h.html#templated_function_with_defaulted_template_template_parameter" status="active" access="public" location="templated_callables.h" documented="true" related="1" meta="plain" type="void" signature="void templated_function_with_defaulted_template_template_parameter(Container&lt;T, size&gt;)">
+ <parameter type="Container&lt;T, size&gt;" name="" default=""/>
+ </function>
+ <function name="templated_function_with_defaulted_type_template_parameter" href="templated-callables-h.html#templated_function_with_defaulted_type_template_parameter" status="active" access="public" location="templated_callables.h" documented="true" related="2" meta="plain" type="void" signature="void templated_function_with_defaulted_type_template_parameter(T)">
+ <parameter type="T" name="" default=""/>
+ </function>
+ <function name="templated_function_with_non_type_template_parameter" href="templated-callables-h.html#templated_function_with_non_type_template_parameter" status="active" access="public" location="templated_callables.h" documented="true" related="3" meta="plain" type="void" signature="void templated_function_with_non_type_template_parameter()"/>
+ <function name="templated_function_with_non_type_template_parameter_pack" href="templated-callables-h.html#templated_function_with_non_type_template_parameter_pack" status="active" access="public" location="templated_callables.h" documented="true" related="4" meta="plain" type="void" signature="void templated_function_with_non_type_template_parameter_pack()"/>
+ <function name="templated_function_with_placeholder_non_type_template_parameter" href="templated-callables-h.html#templated_function_with_placeholder_non_type_template_parameter" status="active" access="public" location="templated_callables.h" documented="true" related="5" meta="plain" type="void" signature="void templated_function_with_placeholder_non_type_template_parameter()"/>
+ <function name="templated_function_with_template_template_parameter" href="templated-callables-h.html#templated_function_with_template_template_parameter" status="active" access="public" location="templated_callables.h" documented="true" related="6" meta="plain" type="void" signature="void templated_function_with_template_template_parameter(K&lt;T&gt;)">
+ <parameter type="K&lt;T&gt;" name="" default=""/>
+ </function>
+ <function name="templated_function_with_template_template_parameter_pack" href="templated-callables-h.html#templated_function_with_template_template_parameter_pack" status="active" access="public" location="templated_callables.h" documented="true" related="7" meta="plain" type="void" signature="void templated_function_with_template_template_parameter_pack(Container&lt;T&gt;...)">
+ <parameter type="Container&lt;T&gt;..." name="" default=""/>
+ </function>
+ <function name="templated_function_with_type_template_parameter" href="templated-callables-h.html#templated_function_with_type_template_parameter" status="active" access="public" location="templated_callables.h" documented="true" related="8" meta="plain" type="void" signature="void templated_function_with_type_template_parameter(T)">
+ <parameter type="T" name="" default=""/>
+ </function>
+ <function name="templated_function_with_type_template_parameter_pack" href="templated-callables-h.html#templated_function_with_type_template_parameter_pack" status="active" access="public" location="templated_callables.h" documented="true" related="9" meta="plain" type="void" signature="void templated_function_with_type_template_parameter_pack(Ts...)">
+ <parameter type="Ts..." name="" default=""/>
+ </function>
+ <header name="&lt;templated_callables.h&gt;" href="templated-callables-h.html" status="active" documented="true" module="TestQDoc" title="&lt;templated_callables.h&gt;" fulltitle="&lt;templated_callables.h&gt;" subtitle="">
+ <function name="templated_function_with_defaulted_non_type_template_parameter" href="templated-callables-h.html#templated_function_with_defaulted_non_type_template_parameter" status="active" access="public" location="templated_callables.h" documented="true" related="0" meta="plain" type="void" signature="void templated_function_with_defaulted_non_type_template_parameter()"/>
+ <function name="templated_function_with_defaulted_template_template_parameter" href="templated-callables-h.html#templated_function_with_defaulted_template_template_parameter" status="active" access="public" location="templated_callables.h" documented="true" related="1" meta="plain" type="void" signature="void templated_function_with_defaulted_template_template_parameter(Container&lt;T, size&gt;)">
+ <parameter type="Container&lt;T, size&gt;" name="" default=""/>
+ </function>
+ <function name="templated_function_with_defaulted_type_template_parameter" href="templated-callables-h.html#templated_function_with_defaulted_type_template_parameter" status="active" access="public" location="templated_callables.h" documented="true" related="2" meta="plain" type="void" signature="void templated_function_with_defaulted_type_template_parameter(T)">
+ <parameter type="T" name="" default=""/>
+ </function>
+ <function name="templated_function_with_non_type_template_parameter" href="templated-callables-h.html#templated_function_with_non_type_template_parameter" status="active" access="public" location="templated_callables.h" documented="true" related="3" meta="plain" type="void" signature="void templated_function_with_non_type_template_parameter()"/>
+ <function name="templated_function_with_non_type_template_parameter_pack" href="templated-callables-h.html#templated_function_with_non_type_template_parameter_pack" status="active" access="public" location="templated_callables.h" documented="true" related="4" meta="plain" type="void" signature="void templated_function_with_non_type_template_parameter_pack()"/>
+ <function name="templated_function_with_placeholder_non_type_template_parameter" href="templated-callables-h.html#templated_function_with_placeholder_non_type_template_parameter" status="active" access="public" location="templated_callables.h" documented="true" related="5" meta="plain" type="void" signature="void templated_function_with_placeholder_non_type_template_parameter()"/>
+ <function name="templated_function_with_template_template_parameter" href="templated-callables-h.html#templated_function_with_template_template_parameter" status="active" access="public" location="templated_callables.h" documented="true" related="6" meta="plain" type="void" signature="void templated_function_with_template_template_parameter(K&lt;T&gt;)">
+ <parameter type="K&lt;T&gt;" name="" default=""/>
+ </function>
+ <function name="templated_function_with_template_template_parameter_pack" href="templated-callables-h.html#templated_function_with_template_template_parameter_pack" status="active" access="public" location="templated_callables.h" documented="true" related="7" meta="plain" type="void" signature="void templated_function_with_template_template_parameter_pack(Container&lt;T&gt;...)">
+ <parameter type="Container&lt;T&gt;..." name="" default=""/>
+ </function>
+ <function name="templated_function_with_type_template_parameter" href="templated-callables-h.html#templated_function_with_type_template_parameter" status="active" access="public" location="templated_callables.h" documented="true" related="8" meta="plain" type="void" signature="void templated_function_with_type_template_parameter(T)">
+ <parameter type="T" name="" default=""/>
+ </function>
+ <function name="templated_function_with_type_template_parameter_pack" href="templated-callables-h.html#templated_function_with_type_template_parameter_pack" status="active" access="public" location="templated_callables.h" documented="true" related="9" meta="plain" type="void" signature="void templated_function_with_type_template_parameter_pack(Ts...)">
+ <parameter type="Ts..." name="" default=""/>
+ </function>
+ </header>
+ <class name="TemplatedClass" href="templatedclass.html" status="active" access="public" location="templated_callables.h" documented="true" module="TestQDoc">
+ <function name="templated_method_with_defaulted_non_type_template_parameter" fullname="TemplatedClass::templated_method_with_defaulted_non_type_template_parameter" href="templatedclass.html#templated_method_with_defaulted_non_type_template_parameter" status="active" access="public" location="templated_callables.h" documented="true" meta="plain" type="void" signature="void templated_method_with_defaulted_non_type_template_parameter()"/>
+ <function name="templated_method_with_defaulted_template_template_parameter" fullname="TemplatedClass::templated_method_with_defaulted_template_template_parameter" href="templatedclass.html#templated_method_with_defaulted_template_template_parameter" status="active" access="public" location="templated_callables.h" documented="true" meta="plain" type="void" signature="void templated_method_with_defaulted_template_template_parameter(Container&lt;U, size&gt;)">
+ <parameter type="Container&lt;U, size&gt;" name="" default=""/>
+ </function>
+ <function name="templated_method_with_defaulted_type_template_parameter" fullname="TemplatedClass::templated_method_with_defaulted_type_template_parameter" href="templatedclass.html#templated_method_with_defaulted_type_template_parameter" status="active" access="public" location="templated_callables.h" documented="true" meta="plain" type="void" signature="void templated_method_with_defaulted_type_template_parameter(U)">
+ <parameter type="U" name="" default=""/>
+ </function>
+ <function name="templated_method_with_non_type_template_parameter" fullname="TemplatedClass::templated_method_with_non_type_template_parameter" href="templatedclass.html#templated_method_with_non_type_template_parameter" status="active" access="public" location="templated_callables.h" documented="true" meta="plain" type="void" signature="void templated_method_with_non_type_template_parameter()"/>
+ <function name="templated_method_with_non_type_template_parameter_pack" fullname="TemplatedClass::templated_method_with_non_type_template_parameter_pack" href="templatedclass.html#templated_method_with_non_type_template_parameter_pack" status="active" access="public" location="templated_callables.h" documented="true" meta="plain" type="void" signature="void templated_method_with_non_type_template_parameter_pack()"/>
+ <function name="templated_method_with_placeholder_non_type_template_parameter" fullname="TemplatedClass::templated_method_with_placeholder_non_type_template_parameter" href="templatedclass.html#templated_method_with_placeholder_non_type_template_parameter" status="active" access="public" location="templated_callables.h" documented="true" meta="plain" type="void" signature="void templated_method_with_placeholder_non_type_template_parameter()"/>
+ <function name="templated_method_with_template_template_parameter" fullname="TemplatedClass::templated_method_with_template_template_parameter" href="templatedclass.html#templated_method_with_template_template_parameter" status="active" access="public" location="templated_callables.h" documented="true" meta="plain" type="void" signature="void templated_method_with_template_template_parameter(X&lt;U&gt;)">
+ <parameter type="X&lt;U&gt;" name="" default=""/>
+ </function>
+ <function name="templated_method_with_template_template_parameter_pack" fullname="TemplatedClass::templated_method_with_template_template_parameter_pack" href="templatedclass.html#templated_method_with_template_template_parameter_pack" status="active" access="public" location="templated_callables.h" documented="true" meta="plain" type="void" signature="void templated_method_with_template_template_parameter_pack(Container&lt;U&gt;...)">
+ <parameter type="Container&lt;U&gt;..." name="" default=""/>
+ </function>
+ <function name="templated_method_with_type_template_parameter" fullname="TemplatedClass::templated_method_with_type_template_parameter" href="templatedclass.html#templated_method_with_type_template_parameter" status="active" access="public" location="templated_callables.h" documented="true" meta="plain" type="void" signature="void templated_method_with_type_template_parameter(U)">
+ <parameter type="U" name="" default=""/>
+ </function>
+ <function name="templated_method_with_type_template_parameter_pack" fullname="TemplatedClass::templated_method_with_type_template_parameter_pack" href="templatedclass.html#templated_method_with_type_template_parameter_pack" status="active" access="public" location="templated_callables.h" documented="true" meta="plain" type="void" signature="void templated_method_with_type_template_parameter_pack(Ts...)">
+ <parameter type="Ts..." name="" default=""/>
+ </function>
+ </class>
+ <module name="TestQDoc" href="testqdoc-module.html" status="internal" seen="false" title=""/>
+ </namespace>
+</INDEX>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/templatedcallables/expected/html/templatedclass-members.html b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/templatedcallables/expected/html/templatedclass-members.html
new file mode 100644
index 000000000..8ab0482da
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/templatedcallables/expected/html/templatedclass-members.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+<!-- templated_callables.cpp -->
+ <title>List of All Members for TemplatedClass | templatedcallables</title>
+</head>
+<body>
+<li>TemplatedClass</li>
+<div class="sidebar"><div class="sidebar-content" id="sidebar-content"></div></div>
+<h1 class="title" translate="no">List of All Members for TemplatedClass</h1>
+<p>This is the complete list of members for <a href="templatedclass.html">TemplatedClass</a>, including inherited members.</p>
+<ul>
+<li class="fn" translate="no"><span class="name"><b><a href="templatedclass.html#templated_method_with_defaulted_non_type_template_parameter" translate="no">templated_method_with_defaulted_non_type_template_parameter</a></b></span>()</li>
+<li class="fn" translate="no"><span class="name"><b><a href="templatedclass.html#templated_method_with_defaulted_template_template_parameter" translate="no">templated_method_with_defaulted_template_template_parameter</a></b></span>(Container&lt;U, size&gt;)</li>
+<li class="fn" translate="no"><span class="name"><b><a href="templatedclass.html#templated_method_with_defaulted_type_template_parameter" translate="no">templated_method_with_defaulted_type_template_parameter</a></b></span>(U)</li>
+<li class="fn" translate="no"><span class="name"><b><a href="templatedclass.html#templated_method_with_non_type_template_parameter" translate="no">templated_method_with_non_type_template_parameter</a></b></span>()</li>
+<li class="fn" translate="no"><span class="name"><b><a href="templatedclass.html#templated_method_with_non_type_template_parameter_pack" translate="no">templated_method_with_non_type_template_parameter_pack</a></b></span>()</li>
+<li class="fn" translate="no"><span class="name"><b><a href="templatedclass.html#templated_method_with_placeholder_non_type_template_parameter" translate="no">templated_method_with_placeholder_non_type_template_parameter</a></b></span>()</li>
+<li class="fn" translate="no"><span class="name"><b><a href="templatedclass.html#templated_method_with_template_template_parameter" translate="no">templated_method_with_template_template_parameter</a></b></span>(X&lt;U&gt;)</li>
+<li class="fn" translate="no"><span class="name"><b><a href="templatedclass.html#templated_method_with_template_template_parameter_pack" translate="no">templated_method_with_template_template_parameter_pack</a></b></span>(Container&lt;U&gt;...)</li>
+<li class="fn" translate="no"><span class="name"><b><a href="templatedclass.html#templated_method_with_type_template_parameter" translate="no">templated_method_with_type_template_parameter</a></b></span>(U)</li>
+<li class="fn" translate="no"><span class="name"><b><a href="templatedclass.html#templated_method_with_type_template_parameter_pack" translate="no">templated_method_with_type_template_parameter_pack</a></b></span>(Ts...)</li>
+</ul>
+</body>
+</html>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/templatedcallables/expected/html/templatedclass.html b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/templatedcallables/expected/html/templatedclass.html
new file mode 100644
index 000000000..21066cf52
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/templatedcallables/expected/html/templatedclass.html
@@ -0,0 +1,90 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+<!-- templated_callables.cpp -->
+ <title>TemplatedClass Class | templatedcallables</title>
+</head>
+<body>
+<li>TemplatedClass</li>
+<div class="sidebar">
+<div class="toc">
+<h3 id="toc">Contents</h3>
+<ul>
+<li class="level1"><a href="#public-functions">Public Functions</a></li>
+<li class="level1"><a href="#details">Detailed Description</a></li>
+</ul>
+</div>
+<div class="sidebar-content" id="sidebar-content"></div></div>
+<h1 class="title" translate="no">TemplatedClass Class</h1>
+<span class="small-subtitle" translate="no">template &lt;typename T&gt; class TemplatedClass</span>
+<div class="table"><table class="alignedsummary" translate="no">
+<tr><td class="memItemLeft rightAlign topAlign"> Header:</td><td class="memItemRight bottomAlign"> <span class="preprocessor">#include &lt;TemplatedClass&gt;</span></td></tr>
+</table></div>
+<ul>
+<li><a href="templatedclass-members.html">List of all members, including inherited members</a></li>
+</ul>
+<h2 id="public-functions">Public Functions</h2>
+<div class="table"><table class="alignedsummary" translate="no">
+<tr><td class="memItemLeft rightAlign topAlign"> void </td><td class="memItemRight bottomAlign"><b><a href="templatedclass.html#templated_method_with_defaulted_non_type_template_parameter" translate="no">templated_method_with_defaulted_non_type_template_parameter</a></b>()</td></tr>
+<tr><td class="memItemLeft rightAlign topAlign"> void </td><td class="memItemRight bottomAlign"><b><a href="templatedclass.html#templated_method_with_defaulted_template_template_parameter" translate="no">templated_method_with_defaulted_template_template_parameter</a></b>(Container&lt;U, size&gt;)</td></tr>
+<tr><td class="memItemLeft rightAlign topAlign"> void </td><td class="memItemRight bottomAlign"><b><a href="templatedclass.html#templated_method_with_defaulted_type_template_parameter" translate="no">templated_method_with_defaulted_type_template_parameter</a></b>(U)</td></tr>
+<tr><td class="memItemLeft rightAlign topAlign"> void </td><td class="memItemRight bottomAlign"><b><a href="templatedclass.html#templated_method_with_non_type_template_parameter" translate="no">templated_method_with_non_type_template_parameter</a></b>()</td></tr>
+<tr><td class="memItemLeft rightAlign topAlign"> void </td><td class="memItemRight bottomAlign"><b><a href="templatedclass.html#templated_method_with_non_type_template_parameter_pack" translate="no">templated_method_with_non_type_template_parameter_pack</a></b>()</td></tr>
+<tr><td class="memItemLeft rightAlign topAlign"> void </td><td class="memItemRight bottomAlign"><b><a href="templatedclass.html#templated_method_with_placeholder_non_type_template_parameter" translate="no">templated_method_with_placeholder_non_type_template_parameter</a></b>()</td></tr>
+<tr><td class="memItemLeft rightAlign topAlign"> void </td><td class="memItemRight bottomAlign"><b><a href="templatedclass.html#templated_method_with_template_template_parameter" translate="no">templated_method_with_template_template_parameter</a></b>(X&lt;U&gt;)</td></tr>
+<tr><td class="memItemLeft rightAlign topAlign"> void </td><td class="memItemRight bottomAlign"><b><a href="templatedclass.html#templated_method_with_template_template_parameter_pack" translate="no">templated_method_with_template_template_parameter_pack</a></b>(Container&lt;U&gt;...)</td></tr>
+<tr><td class="memItemLeft rightAlign topAlign"> void </td><td class="memItemRight bottomAlign"><b><a href="templatedclass.html#templated_method_with_type_template_parameter" translate="no">templated_method_with_type_template_parameter</a></b>(U)</td></tr>
+<tr><td class="memItemLeft rightAlign topAlign"> void </td><td class="memItemRight bottomAlign"><b><a href="templatedclass.html#templated_method_with_type_template_parameter_pack" translate="no">templated_method_with_type_template_parameter_pack</a></b>(Ts...)</td></tr>
+</table></div>
+<!-- $$$TemplatedClass-description -->
+<div class="descr">
+<h2 id="details">Detailed Description</h2>
+<p>Containing record for the tested methods.</p>
+</div>
+<!-- @@@TemplatedClass -->
+<div class="func">
+<h2>Member Function Documentation</h2>
+<!-- $$$templated_method_with_defaulted_non_type_template_parameter[overload1]$$$templated_method_with_defaulted_non_type_template_parameter -->
+<h3 class="fn" translate="no" id="templated_method_with_defaulted_non_type_template_parameter">template &lt;int Size = 10&gt; <span class="type">void</span> TemplatedClass::<span class="name">templated_method_with_defaulted_non_type_template_parameter</span>()</h3>
+<p>A templated method under a templated class with a defaulted non type template parameter.</p>
+<!-- @@@templated_method_with_defaulted_non_type_template_parameter -->
+<!-- $$$templated_method_with_defaulted_template_template_parameter[overload1]$$$templated_method_with_defaulted_template_template_parameterContainer<U,size> -->
+<h3 class="fn" translate="no" id="templated_method_with_defaulted_template_template_parameter">template &lt;typename U, int size, template &lt;typename, int &gt; typename Container = std::array&gt; <span class="type">void</span> TemplatedClass::<span class="name">templated_method_with_defaulted_template_template_parameter</span>(<span class="type">Container</span>&lt;<span class="type">U</span>, <span class="type">size</span>&gt;)</h3>
+<p>A templated method under a templated class with a defaulted template template parameter.</p>
+<!-- @@@templated_method_with_defaulted_template_template_parameter -->
+<!-- $$$templated_method_with_defaulted_type_template_parameter[overload1]$$$templated_method_with_defaulted_type_template_parameterU -->
+<h3 class="fn" translate="no" id="templated_method_with_defaulted_type_template_parameter">template &lt;typename U = bool&gt; <span class="type">void</span> TemplatedClass::<span class="name">templated_method_with_defaulted_type_template_parameter</span>(<span class="type">U</span>)</h3>
+<p>A templated method under a templated class with a defaulted type template parameter.</p>
+<!-- @@@templated_method_with_defaulted_type_template_parameter -->
+<!-- $$$templated_method_with_non_type_template_parameter[overload1]$$$templated_method_with_non_type_template_parameter -->
+<h3 class="fn" translate="no" id="templated_method_with_non_type_template_parameter">template &lt;int Size&gt; <span class="type">void</span> TemplatedClass::<span class="name">templated_method_with_non_type_template_parameter</span>()</h3>
+<p>A templated method under a templated class with a non-defaulted non type template parameter.</p>
+<!-- @@@templated_method_with_non_type_template_parameter -->
+<!-- $$$templated_method_with_non_type_template_parameter_pack[overload1]$$$templated_method_with_non_type_template_parameter_pack -->
+<h3 class="fn" translate="no" id="templated_method_with_non_type_template_parameter_pack">template &lt;int... Dimensions&gt; <span class="type">void</span> TemplatedClass::<span class="name">templated_method_with_non_type_template_parameter_pack</span>()</h3>
+<p>A templated method under a templated class with a non type template parameter pack.</p>
+<!-- @@@templated_method_with_non_type_template_parameter_pack -->
+<!-- $$$templated_method_with_placeholder_non_type_template_parameter[overload1]$$$templated_method_with_placeholder_non_type_template_parameter -->
+<h3 class="fn" translate="no" id="templated_method_with_placeholder_non_type_template_parameter">template &lt;auto Predicate&gt; <span class="type">void</span> TemplatedClass::<span class="name">templated_method_with_placeholder_non_type_template_parameter</span>()</h3>
+<p>A templated method under a templated class with a placeholder non type template parameter.</p>
+<!-- @@@templated_method_with_placeholder_non_type_template_parameter -->
+<!-- $$$templated_method_with_template_template_parameter[overload1]$$$templated_method_with_template_template_parameterX<U> -->
+<h3 class="fn" translate="no" id="templated_method_with_template_template_parameter">template &lt;typename U, template &lt;typename&gt; typename X&gt; <span class="type">void</span> TemplatedClass::<span class="name">templated_method_with_template_template_parameter</span>(<span class="type">X</span>&lt;<span class="type">U</span>&gt;)</h3>
+<p>A templated method under a templated class with a non-defaulted template template parameter.</p>
+<!-- @@@templated_method_with_template_template_parameter -->
+<!-- $$$templated_method_with_template_template_parameter_pack[overload1]$$$templated_method_with_template_template_parameter_packContainer<U>... -->
+<h3 class="fn" translate="no" id="templated_method_with_template_template_parameter_pack">template &lt;typename U, template &lt;typename&gt; typename... Container&gt; <span class="type">void</span> TemplatedClass::<span class="name">templated_method_with_template_template_parameter_pack</span>(<span class="type">Container</span>&lt;<span class="type">U</span>&gt;...)</h3>
+<p>A templated method under a templated class with a template template parameter pack.</p>
+<!-- @@@templated_method_with_template_template_parameter_pack -->
+<!-- $$$templated_method_with_type_template_parameter[overload1]$$$templated_method_with_type_template_parameterU -->
+<h3 class="fn" translate="no" id="templated_method_with_type_template_parameter">template &lt;typename U&gt; <span class="type">void</span> TemplatedClass::<span class="name">templated_method_with_type_template_parameter</span>(<span class="type">U</span>)</h3>
+<p>A templated method under a templated class with a non-defaulted type template parameter.</p>
+<!-- @@@templated_method_with_type_template_parameter -->
+<!-- $$$templated_method_with_type_template_parameter_pack[overload1]$$$templated_method_with_type_template_parameter_packTs... -->
+<h3 class="fn" translate="no" id="templated_method_with_type_template_parameter_pack">template &lt;typename... Ts&gt; <span class="type">void</span> TemplatedClass::<span class="name">templated_method_with_type_template_parameter_pack</span>(<span class="type">Ts</span>...)</h3>
+<p>A templated method under a templated class with a type template parameter pack.</p>
+<!-- @@@templated_method_with_type_template_parameter_pack -->
+</div>
+</body>
+</html>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/templatedcallables/expected/webxml/templated-callables-h.webxml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/templatedcallables/expected/webxml/templated-callables-h.webxml
new file mode 100644
index 000000000..8ae7ad257
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/templatedcallables/expected/webxml/templated-callables-h.webxml
@@ -0,0 +1,66 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<WebXML>
+ <document>
+ <header name="&lt;templated_callables.h&gt;" href="templated-callables-h.html" status="active" documented="true" module="TestQDoc" title="&lt;templated_callables.h&gt;" fulltitle="&lt;templated_callables.h&gt;" subtitle="">
+ <description>
+ <para>Containing headers for the tested free functions.</para>
+ </description>
+ <function name="templated_function_with_defaulted_non_type_template_parameter" href="templated-callables-h.html#templated_function_with_defaulted_non_type_template_parameter" status="active" access="public" location="templated_callables.h" documented="true" related="0" meta="plain" type="void" signature="void templated_function_with_defaulted_non_type_template_parameter()">
+ <description>
+ <para>A templated function with a defaulted non type template parameter.</para>
+ </description>
+ </function>
+ <function name="templated_function_with_defaulted_template_template_parameter" href="templated-callables-h.html#templated_function_with_defaulted_template_template_parameter" status="active" access="public" location="templated_callables.h" documented="true" related="1" meta="plain" type="void" signature="void templated_function_with_defaulted_template_template_parameter(Container&lt;T, size&gt;)">
+ <parameter type="Container&lt;T, size&gt;" name="" default=""/>
+ <description>
+ <para>A templated function with a defaulted template template parameter.</para>
+ </description>
+ </function>
+ <function name="templated_function_with_defaulted_type_template_parameter" href="templated-callables-h.html#templated_function_with_defaulted_type_template_parameter" status="active" access="public" location="templated_callables.h" documented="true" related="2" meta="plain" type="void" signature="void templated_function_with_defaulted_type_template_parameter(T)">
+ <parameter type="T" name="" default=""/>
+ <description>
+ <para>A templated function with a defaulted type template parameter.</para>
+ </description>
+ </function>
+ <function name="templated_function_with_non_type_template_parameter" href="templated-callables-h.html#templated_function_with_non_type_template_parameter" status="active" access="public" location="templated_callables.h" documented="true" related="3" meta="plain" type="void" signature="void templated_function_with_non_type_template_parameter()">
+ <description>
+ <para>A templated function with a non-defaulted non type template parameter.</para>
+ </description>
+ </function>
+ <function name="templated_function_with_non_type_template_parameter_pack" href="templated-callables-h.html#templated_function_with_non_type_template_parameter_pack" status="active" access="public" location="templated_callables.h" documented="true" related="4" meta="plain" type="void" signature="void templated_function_with_non_type_template_parameter_pack()">
+ <description>
+ <para>A templated function with a non type template parameter pack.</para>
+ </description>
+ </function>
+ <function name="templated_function_with_placeholder_non_type_template_parameter" href="templated-callables-h.html#templated_function_with_placeholder_non_type_template_parameter" status="active" access="public" location="templated_callables.h" documented="true" related="5" meta="plain" type="void" signature="void templated_function_with_placeholder_non_type_template_parameter()">
+ <description>
+ <para>A templated function with a placeholder non type template parameter.</para>
+ </description>
+ </function>
+ <function name="templated_function_with_template_template_parameter" href="templated-callables-h.html#templated_function_with_template_template_parameter" status="active" access="public" location="templated_callables.h" documented="true" related="6" meta="plain" type="void" signature="void templated_function_with_template_template_parameter(K&lt;T&gt;)">
+ <parameter type="K&lt;T&gt;" name="" default=""/>
+ <description>
+ <para>A templated function with a non-defaulted template template parameter.</para>
+ </description>
+ </function>
+ <function name="templated_function_with_template_template_parameter_pack" href="templated-callables-h.html#templated_function_with_template_template_parameter_pack" status="active" access="public" location="templated_callables.h" documented="true" related="7" meta="plain" type="void" signature="void templated_function_with_template_template_parameter_pack(Container&lt;T&gt;...)">
+ <parameter type="Container&lt;T&gt;..." name="" default=""/>
+ <description>
+ <para>A templated function with a template template parameter pack.</para>
+ </description>
+ </function>
+ <function name="templated_function_with_type_template_parameter" href="templated-callables-h.html#templated_function_with_type_template_parameter" status="active" access="public" location="templated_callables.h" documented="true" related="8" meta="plain" type="void" signature="void templated_function_with_type_template_parameter(T)">
+ <parameter type="T" name="" default=""/>
+ <description>
+ <para>A templated function with a non-defaulted type template parameter.</para>
+ </description>
+ </function>
+ <function name="templated_function_with_type_template_parameter_pack" href="templated-callables-h.html#templated_function_with_type_template_parameter_pack" status="active" access="public" location="templated_callables.h" documented="true" related="9" meta="plain" type="void" signature="void templated_function_with_type_template_parameter_pack(Ts...)">
+ <parameter type="Ts..." name="" default=""/>
+ <description>
+ <para>A templated function with a type template parameter pack.</para>
+ </description>
+ </function>
+ </header>
+ </document>
+</WebXML>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/templatedcallables/expected/webxml/templatedcallables.index b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/templatedcallables/expected/webxml/templatedcallables.index
new file mode 100644
index 000000000..91245c7f8
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/templatedcallables/expected/webxml/templatedcallables.index
@@ -0,0 +1,77 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE QDOCINDEX>
+<INDEX url="" title="templatedcallables Reference Documentation" version="" project="templatedcallables">
+ <namespace name="" status="active" access="public" module="templatedcallables">
+ <function name="templated_function_with_defaulted_non_type_template_parameter" href="templated-callables-h.html#templated_function_with_defaulted_non_type_template_parameter" status="active" access="public" location="templated_callables.h" documented="true" related="0" meta="plain" type="void" signature="void templated_function_with_defaulted_non_type_template_parameter()"/>
+ <function name="templated_function_with_defaulted_template_template_parameter" href="templated-callables-h.html#templated_function_with_defaulted_template_template_parameter" status="active" access="public" location="templated_callables.h" documented="true" related="1" meta="plain" type="void" signature="void templated_function_with_defaulted_template_template_parameter(Container&lt;T, size&gt;)">
+ <parameter type="Container&lt;T, size&gt;" name="" default=""/>
+ </function>
+ <function name="templated_function_with_defaulted_type_template_parameter" href="templated-callables-h.html#templated_function_with_defaulted_type_template_parameter" status="active" access="public" location="templated_callables.h" documented="true" related="2" meta="plain" type="void" signature="void templated_function_with_defaulted_type_template_parameter(T)">
+ <parameter type="T" name="" default=""/>
+ </function>
+ <function name="templated_function_with_non_type_template_parameter" href="templated-callables-h.html#templated_function_with_non_type_template_parameter" status="active" access="public" location="templated_callables.h" documented="true" related="3" meta="plain" type="void" signature="void templated_function_with_non_type_template_parameter()"/>
+ <function name="templated_function_with_non_type_template_parameter_pack" href="templated-callables-h.html#templated_function_with_non_type_template_parameter_pack" status="active" access="public" location="templated_callables.h" documented="true" related="4" meta="plain" type="void" signature="void templated_function_with_non_type_template_parameter_pack()"/>
+ <function name="templated_function_with_placeholder_non_type_template_parameter" href="templated-callables-h.html#templated_function_with_placeholder_non_type_template_parameter" status="active" access="public" location="templated_callables.h" documented="true" related="5" meta="plain" type="void" signature="void templated_function_with_placeholder_non_type_template_parameter()"/>
+ <function name="templated_function_with_template_template_parameter" href="templated-callables-h.html#templated_function_with_template_template_parameter" status="active" access="public" location="templated_callables.h" documented="true" related="6" meta="plain" type="void" signature="void templated_function_with_template_template_parameter(K&lt;T&gt;)">
+ <parameter type="K&lt;T&gt;" name="" default=""/>
+ </function>
+ <function name="templated_function_with_template_template_parameter_pack" href="templated-callables-h.html#templated_function_with_template_template_parameter_pack" status="active" access="public" location="templated_callables.h" documented="true" related="7" meta="plain" type="void" signature="void templated_function_with_template_template_parameter_pack(Container&lt;T&gt;...)">
+ <parameter type="Container&lt;T&gt;..." name="" default=""/>
+ </function>
+ <function name="templated_function_with_type_template_parameter" href="templated-callables-h.html#templated_function_with_type_template_parameter" status="active" access="public" location="templated_callables.h" documented="true" related="8" meta="plain" type="void" signature="void templated_function_with_type_template_parameter(T)">
+ <parameter type="T" name="" default=""/>
+ </function>
+ <function name="templated_function_with_type_template_parameter_pack" href="templated-callables-h.html#templated_function_with_type_template_parameter_pack" status="active" access="public" location="templated_callables.h" documented="true" related="9" meta="plain" type="void" signature="void templated_function_with_type_template_parameter_pack(Ts...)">
+ <parameter type="Ts..." name="" default=""/>
+ </function>
+ <header name="&lt;templated_callables.h&gt;" href="templated-callables-h.html" status="active" documented="true" module="TestQDoc" title="&lt;templated_callables.h&gt;" fulltitle="&lt;templated_callables.h&gt;" subtitle="">
+ <function name="templated_function_with_defaulted_non_type_template_parameter" href="templated-callables-h.html#templated_function_with_defaulted_non_type_template_parameter" status="active" access="public" location="templated_callables.h" documented="true" related="0" meta="plain" type="void" signature="void templated_function_with_defaulted_non_type_template_parameter()"/>
+ <function name="templated_function_with_defaulted_template_template_parameter" href="templated-callables-h.html#templated_function_with_defaulted_template_template_parameter" status="active" access="public" location="templated_callables.h" documented="true" related="1" meta="plain" type="void" signature="void templated_function_with_defaulted_template_template_parameter(Container&lt;T, size&gt;)">
+ <parameter type="Container&lt;T, size&gt;" name="" default=""/>
+ </function>
+ <function name="templated_function_with_defaulted_type_template_parameter" href="templated-callables-h.html#templated_function_with_defaulted_type_template_parameter" status="active" access="public" location="templated_callables.h" documented="true" related="2" meta="plain" type="void" signature="void templated_function_with_defaulted_type_template_parameter(T)">
+ <parameter type="T" name="" default=""/>
+ </function>
+ <function name="templated_function_with_non_type_template_parameter" href="templated-callables-h.html#templated_function_with_non_type_template_parameter" status="active" access="public" location="templated_callables.h" documented="true" related="3" meta="plain" type="void" signature="void templated_function_with_non_type_template_parameter()"/>
+ <function name="templated_function_with_non_type_template_parameter_pack" href="templated-callables-h.html#templated_function_with_non_type_template_parameter_pack" status="active" access="public" location="templated_callables.h" documented="true" related="4" meta="plain" type="void" signature="void templated_function_with_non_type_template_parameter_pack()"/>
+ <function name="templated_function_with_placeholder_non_type_template_parameter" href="templated-callables-h.html#templated_function_with_placeholder_non_type_template_parameter" status="active" access="public" location="templated_callables.h" documented="true" related="5" meta="plain" type="void" signature="void templated_function_with_placeholder_non_type_template_parameter()"/>
+ <function name="templated_function_with_template_template_parameter" href="templated-callables-h.html#templated_function_with_template_template_parameter" status="active" access="public" location="templated_callables.h" documented="true" related="6" meta="plain" type="void" signature="void templated_function_with_template_template_parameter(K&lt;T&gt;)">
+ <parameter type="K&lt;T&gt;" name="" default=""/>
+ </function>
+ <function name="templated_function_with_template_template_parameter_pack" href="templated-callables-h.html#templated_function_with_template_template_parameter_pack" status="active" access="public" location="templated_callables.h" documented="true" related="7" meta="plain" type="void" signature="void templated_function_with_template_template_parameter_pack(Container&lt;T&gt;...)">
+ <parameter type="Container&lt;T&gt;..." name="" default=""/>
+ </function>
+ <function name="templated_function_with_type_template_parameter" href="templated-callables-h.html#templated_function_with_type_template_parameter" status="active" access="public" location="templated_callables.h" documented="true" related="8" meta="plain" type="void" signature="void templated_function_with_type_template_parameter(T)">
+ <parameter type="T" name="" default=""/>
+ </function>
+ <function name="templated_function_with_type_template_parameter_pack" href="templated-callables-h.html#templated_function_with_type_template_parameter_pack" status="active" access="public" location="templated_callables.h" documented="true" related="9" meta="plain" type="void" signature="void templated_function_with_type_template_parameter_pack(Ts...)">
+ <parameter type="Ts..." name="" default=""/>
+ </function>
+ </header>
+ <class name="TemplatedClass" href="templatedclass.html" status="active" access="public" location="templated_callables.h" documented="true" module="TestQDoc">
+ <function name="templated_method_with_defaulted_non_type_template_parameter" fullname="TemplatedClass::templated_method_with_defaulted_non_type_template_parameter" href="templatedclass.html#templated_method_with_defaulted_non_type_template_parameter" status="active" access="public" location="templated_callables.h" documented="true" meta="plain" type="void" signature="void templated_method_with_defaulted_non_type_template_parameter()"/>
+ <function name="templated_method_with_defaulted_template_template_parameter" fullname="TemplatedClass::templated_method_with_defaulted_template_template_parameter" href="templatedclass.html#templated_method_with_defaulted_template_template_parameter" status="active" access="public" location="templated_callables.h" documented="true" meta="plain" type="void" signature="void templated_method_with_defaulted_template_template_parameter(Container&lt;U, size&gt;)">
+ <parameter type="Container&lt;U, size&gt;" name="" default=""/>
+ </function>
+ <function name="templated_method_with_defaulted_type_template_parameter" fullname="TemplatedClass::templated_method_with_defaulted_type_template_parameter" href="templatedclass.html#templated_method_with_defaulted_type_template_parameter" status="active" access="public" location="templated_callables.h" documented="true" meta="plain" type="void" signature="void templated_method_with_defaulted_type_template_parameter(U)">
+ <parameter type="U" name="" default=""/>
+ </function>
+ <function name="templated_method_with_non_type_template_parameter" fullname="TemplatedClass::templated_method_with_non_type_template_parameter" href="templatedclass.html#templated_method_with_non_type_template_parameter" status="active" access="public" location="templated_callables.h" documented="true" meta="plain" type="void" signature="void templated_method_with_non_type_template_parameter()"/>
+ <function name="templated_method_with_non_type_template_parameter_pack" fullname="TemplatedClass::templated_method_with_non_type_template_parameter_pack" href="templatedclass.html#templated_method_with_non_type_template_parameter_pack" status="active" access="public" location="templated_callables.h" documented="true" meta="plain" type="void" signature="void templated_method_with_non_type_template_parameter_pack()"/>
+ <function name="templated_method_with_placeholder_non_type_template_parameter" fullname="TemplatedClass::templated_method_with_placeholder_non_type_template_parameter" href="templatedclass.html#templated_method_with_placeholder_non_type_template_parameter" status="active" access="public" location="templated_callables.h" documented="true" meta="plain" type="void" signature="void templated_method_with_placeholder_non_type_template_parameter()"/>
+ <function name="templated_method_with_template_template_parameter" fullname="TemplatedClass::templated_method_with_template_template_parameter" href="templatedclass.html#templated_method_with_template_template_parameter" status="active" access="public" location="templated_callables.h" documented="true" meta="plain" type="void" signature="void templated_method_with_template_template_parameter(X&lt;U&gt;)">
+ <parameter type="X&lt;U&gt;" name="" default=""/>
+ </function>
+ <function name="templated_method_with_template_template_parameter_pack" fullname="TemplatedClass::templated_method_with_template_template_parameter_pack" href="templatedclass.html#templated_method_with_template_template_parameter_pack" status="active" access="public" location="templated_callables.h" documented="true" meta="plain" type="void" signature="void templated_method_with_template_template_parameter_pack(Container&lt;U&gt;...)">
+ <parameter type="Container&lt;U&gt;..." name="" default=""/>
+ </function>
+ <function name="templated_method_with_type_template_parameter" fullname="TemplatedClass::templated_method_with_type_template_parameter" href="templatedclass.html#templated_method_with_type_template_parameter" status="active" access="public" location="templated_callables.h" documented="true" meta="plain" type="void" signature="void templated_method_with_type_template_parameter(U)">
+ <parameter type="U" name="" default=""/>
+ </function>
+ <function name="templated_method_with_type_template_parameter_pack" fullname="TemplatedClass::templated_method_with_type_template_parameter_pack" href="templatedclass.html#templated_method_with_type_template_parameter_pack" status="active" access="public" location="templated_callables.h" documented="true" meta="plain" type="void" signature="void templated_method_with_type_template_parameter_pack(Ts...)">
+ <parameter type="Ts..." name="" default=""/>
+ </function>
+ </class>
+ <module name="TestQDoc" href="testqdoc-module.html" status="internal" seen="false" title=""/>
+ </namespace>
+</INDEX>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/templatedcallables/expected/webxml/templatedclass.webxml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/templatedcallables/expected/webxml/templatedclass.webxml
new file mode 100644
index 000000000..29c0bc661
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/templatedcallables/expected/webxml/templatedclass.webxml
@@ -0,0 +1,66 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<WebXML>
+ <document>
+ <class name="TemplatedClass" href="templatedclass.html" status="active" access="public" location="templated_callables.h" documented="true" module="TestQDoc">
+ <description>
+ <para>Containing record for the tested methods.</para>
+ </description>
+ <function name="templated_method_with_defaulted_non_type_template_parameter" fullname="TemplatedClass::templated_method_with_defaulted_non_type_template_parameter" href="templatedclass.html#templated_method_with_defaulted_non_type_template_parameter" status="active" access="public" location="templated_callables.h" documented="true" meta="plain" type="void" signature="void templated_method_with_defaulted_non_type_template_parameter()">
+ <description>
+ <para>A templated method under a templated class with a defaulted non type template parameter.</para>
+ </description>
+ </function>
+ <function name="templated_method_with_defaulted_template_template_parameter" fullname="TemplatedClass::templated_method_with_defaulted_template_template_parameter" href="templatedclass.html#templated_method_with_defaulted_template_template_parameter" status="active" access="public" location="templated_callables.h" documented="true" meta="plain" type="void" signature="void templated_method_with_defaulted_template_template_parameter(Container&lt;U, size&gt;)">
+ <parameter type="Container&lt;U, size&gt;" name="" default=""/>
+ <description>
+ <para>A templated method under a templated class with a defaulted template template parameter.</para>
+ </description>
+ </function>
+ <function name="templated_method_with_defaulted_type_template_parameter" fullname="TemplatedClass::templated_method_with_defaulted_type_template_parameter" href="templatedclass.html#templated_method_with_defaulted_type_template_parameter" status="active" access="public" location="templated_callables.h" documented="true" meta="plain" type="void" signature="void templated_method_with_defaulted_type_template_parameter(U)">
+ <parameter type="U" name="" default=""/>
+ <description>
+ <para>A templated method under a templated class with a defaulted type template parameter.</para>
+ </description>
+ </function>
+ <function name="templated_method_with_non_type_template_parameter" fullname="TemplatedClass::templated_method_with_non_type_template_parameter" href="templatedclass.html#templated_method_with_non_type_template_parameter" status="active" access="public" location="templated_callables.h" documented="true" meta="plain" type="void" signature="void templated_method_with_non_type_template_parameter()">
+ <description>
+ <para>A templated method under a templated class with a non-defaulted non type template parameter.</para>
+ </description>
+ </function>
+ <function name="templated_method_with_non_type_template_parameter_pack" fullname="TemplatedClass::templated_method_with_non_type_template_parameter_pack" href="templatedclass.html#templated_method_with_non_type_template_parameter_pack" status="active" access="public" location="templated_callables.h" documented="true" meta="plain" type="void" signature="void templated_method_with_non_type_template_parameter_pack()">
+ <description>
+ <para>A templated method under a templated class with a non type template parameter pack.</para>
+ </description>
+ </function>
+ <function name="templated_method_with_placeholder_non_type_template_parameter" fullname="TemplatedClass::templated_method_with_placeholder_non_type_template_parameter" href="templatedclass.html#templated_method_with_placeholder_non_type_template_parameter" status="active" access="public" location="templated_callables.h" documented="true" meta="plain" type="void" signature="void templated_method_with_placeholder_non_type_template_parameter()">
+ <description>
+ <para>A templated method under a templated class with a placeholder non type template parameter.</para>
+ </description>
+ </function>
+ <function name="templated_method_with_template_template_parameter" fullname="TemplatedClass::templated_method_with_template_template_parameter" href="templatedclass.html#templated_method_with_template_template_parameter" status="active" access="public" location="templated_callables.h" documented="true" meta="plain" type="void" signature="void templated_method_with_template_template_parameter(X&lt;U&gt;)">
+ <parameter type="X&lt;U&gt;" name="" default=""/>
+ <description>
+ <para>A templated method under a templated class with a non-defaulted template template parameter.</para>
+ </description>
+ </function>
+ <function name="templated_method_with_template_template_parameter_pack" fullname="TemplatedClass::templated_method_with_template_template_parameter_pack" href="templatedclass.html#templated_method_with_template_template_parameter_pack" status="active" access="public" location="templated_callables.h" documented="true" meta="plain" type="void" signature="void templated_method_with_template_template_parameter_pack(Container&lt;U&gt;...)">
+ <parameter type="Container&lt;U&gt;..." name="" default=""/>
+ <description>
+ <para>A templated method under a templated class with a template template parameter pack.</para>
+ </description>
+ </function>
+ <function name="templated_method_with_type_template_parameter" fullname="TemplatedClass::templated_method_with_type_template_parameter" href="templatedclass.html#templated_method_with_type_template_parameter" status="active" access="public" location="templated_callables.h" documented="true" meta="plain" type="void" signature="void templated_method_with_type_template_parameter(U)">
+ <parameter type="U" name="" default=""/>
+ <description>
+ <para>A templated method under a templated class with a non-defaulted type template parameter.</para>
+ </description>
+ </function>
+ <function name="templated_method_with_type_template_parameter_pack" fullname="TemplatedClass::templated_method_with_type_template_parameter_pack" href="templatedclass.html#templated_method_with_type_template_parameter_pack" status="active" access="public" location="templated_callables.h" documented="true" meta="plain" type="void" signature="void templated_method_with_type_template_parameter_pack(Ts...)">
+ <parameter type="Ts..." name="" default=""/>
+ <description>
+ <para>A templated method under a templated class with a type template parameter pack.</para>
+ </description>
+ </function>
+ </class>
+ </document>
+</WebXML>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/templatedcallables/src/templated_callables.cpp b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/templatedcallables/src/templated_callables.cpp
new file mode 100644
index 000000000..9e91242e3
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/templatedcallables/src/templated_callables.cpp
@@ -0,0 +1,149 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+#pragma once
+
+#include "templated_callables.h"
+
+/*!
+ * \headerfile <templated_callables.h>
+ * \inmodule TestQDoc
+ *
+ * Containing headers for the tested free functions.
+ */
+
+/*!
+ * \class TemplatedClass
+ * \inmodule TestQDoc
+ *
+ * Containing record for the tested methods.
+ */
+
+/*!
+ * \fn template<typename T> template <typename U> void TemplatedClass<T>::templated_method_with_type_template_parameter(U)
+ *
+ * A templated method under a templated class with a non-defaulted type template parameter.
+ */
+
+/*!
+ * \fn template<typename T> void templated_function_with_type_template_parameter(T)
+ * \relates <templated_callables.h>
+ *
+ * A templated function with a non-defaulted type template parameter.
+ */
+
+/*!
+ * \fn template<typename T> template <typename U> void TemplatedClass<T>::templated_method_with_defaulted_type_template_parameter(U)
+ *
+ * A templated method under a templated class with a defaulted type template parameter.
+ */
+
+/*!
+ * \fn template<typename T> void templated_function_with_defaulted_type_template_parameter(T)
+ * \relates <templated_callables.h>
+ *
+ * A templated function with a defaulted type template parameter.
+ */
+
+/*!
+ * \fn template<typename T> template <typename... Ts> void TemplatedClass<T>::templated_method_with_type_template_parameter_pack(Ts...)
+ *
+ * A templated method under a templated class with a type template parameter pack.
+ */
+
+/*!
+ * \fn template<typename... Ts> void templated_function_with_type_template_parameter_pack(Ts...)
+ * \relates <templated_callables.h>
+ *
+ * A templated function with a type template parameter pack.
+ */
+
+/*!
+ * \fn template<typename T> template <int Size> void TemplatedClass<T>::templated_method_with_non_type_template_parameter()
+ *
+ * A templated method under a templated class with a non-defaulted non type template parameter.
+ */
+
+/*!
+ * \fn template<char Category> void templated_function_with_non_type_template_parameter()
+ * \relates <templated_callables.h>
+ *
+ * A templated function with a non-defaulted non type template parameter.
+ */
+
+/*!
+ * \fn template<typename T> template <int Size = 10> void TemplatedClass<T>::templated_method_with_defaulted_non_type_template_parameter()
+ *
+ * A templated method under a templated class with a defaulted non type template parameter.
+ */
+
+/*!
+ * \fn template<char Category = 'A'> void templated_function_with_defaulted_non_type_template_parameter()
+ * \relates <templated_callables.h>
+ *
+ * A templated function with a defaulted non type template parameter.
+ */
+
+/*!
+ * \fn template<typename T> template <int... Dimensions> void TemplatedClass<T>::templated_method_with_non_type_template_parameter_pack()
+ *
+ * A templated method under a templated class with a non type template parameter pack.
+ */
+
+/*!
+ * \fn template<unsigned... Weights> void templated_function_with_non_type_template_parameter_pack()
+ * \relates <templated_callables.h>
+ *
+ * A templated function with a non type template parameter pack.
+ */
+
+/*!
+ * \fn template<typename T> template <auto Predicate> void TemplatedClass<T>::templated_method_with_placeholder_non_type_template_parameter()
+ *
+ * A templated method under a templated class with a placeholder non type template parameter.
+ */
+
+/*!
+ * \fn template<auto Iterator> void templated_function_with_placeholder_non_type_template_parameter()
+ * \relates <templated_callables.h>
+ *
+ * A templated function with a placeholder non type template parameter.
+ */
+
+/*!
+ * \fn template<typename T> template <typename U, template <typename> typename X> void TemplatedClass<T>::templated_method_with_template_template_parameter(X<U>)
+ *
+ * A templated method under a templated class with a non-defaulted template template parameter.
+ */
+
+/*!
+ * \fn template <typename T, template <typename> typename K> void templated_function_with_template_template_parameter(K<T>)
+ * \relates <templated_callables.h>
+ *
+ * A templated function with a non-defaulted template template parameter.
+ */
+
+/*!
+ * \fn template<typename T> template <typename U, int size, template <typename, int> typename Container = std::array> void TemplatedClass<T>::templated_method_with_defaulted_template_template_parameter(Container<U, size>)
+ *
+ * A templated method under a templated class with a defaulted template template parameter.
+ */
+
+/*!
+ * \fn template <typename T, int size, template <typename, int> typename Container = std::array> void templated_function_with_defaulted_template_template_parameter(Container<T, size>)
+ * \relates <templated_callables.h>
+ *
+ * A templated function with a defaulted template template parameter.
+ */
+
+/*!
+ * \fn template<typename T> template <typename U, template <typename> typename... Container> void TemplatedClass<T>::templated_method_with_template_template_parameter_pack(Container<U>...)
+ *
+ * A templated method under a templated class with a template template parameter pack.
+ */
+
+/*!
+ * \fn template <typename T, template <typename> typename... Container> void templated_function_with_template_template_parameter_pack(Container<T>...)
+ * \relates <templated_callables.h>
+ *
+ * A templated function with a template template parameter pack.
+ */
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/templatedcallables/src/templated_callables.h b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/templatedcallables/src/templated_callables.h
new file mode 100644
index 000000000..c745ccad2
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/templatedcallables/src/templated_callables.h
@@ -0,0 +1,73 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+#pragma once
+
+// dummy declaration
+namespace std {
+ template <class T, int N> struct array;
+}
+
+template <typename T>
+class TemplatedClass
+{
+public:
+ template <typename U>
+ void templated_method_with_type_template_parameter(U);
+
+ template <typename U = bool>
+ void templated_method_with_defaulted_type_template_parameter(U);
+
+ template <typename... Ts>
+ void templated_method_with_type_template_parameter_pack(Ts...);
+
+ template <int Size>
+ void templated_method_with_non_type_template_parameter();
+
+ template <int Size = 10>
+ void templated_method_with_defaulted_non_type_template_parameter();
+
+ template <int... Dimensions>
+ void templated_method_with_non_type_template_parameter_pack();
+
+ template <auto Predicate>
+ void templated_method_with_placeholder_non_type_template_parameter();
+
+ template <typename U, template <typename> typename X>
+ void templated_method_with_template_template_parameter(X<U>);
+
+ template <typename U, int size, template <typename, int> typename Container = std::array>
+ void templated_method_with_defaulted_template_template_parameter(Container<U, size>);
+
+ template <typename U, template <typename> typename... Container>
+ void templated_method_with_template_template_parameter_pack(Container<U>...);
+};
+
+template <typename T>
+void templated_function_with_type_template_parameter(T);
+
+template <typename T = char>
+void templated_function_with_defaulted_type_template_parameter(T);
+
+template <typename... Ts>
+void templated_function_with_type_template_parameter_pack(Ts...);
+
+template <char Category>
+void templated_function_with_non_type_template_parameter();
+
+template <char Category = 'A'>
+void templated_function_with_defaulted_non_type_template_parameter();
+
+template <unsigned... Weights>
+void templated_function_with_non_type_template_parameter_pack();
+
+template <auto Iterator>
+void templated_function_with_placeholder_non_type_template_parameter();
+
+template <typename T, template <typename> typename K>
+void templated_function_with_template_template_parameter(K<T>);
+
+template <typename T, int size, template <typename, int> typename Container = std::array>
+void templated_function_with_defaulted_template_template_parameter(Container<T, size>);
+
+template <typename T, template <typename> typename... Container>
+void templated_function_with_template_template_parameter_pack(Container<T>...);
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/templatedcallables/templatedcallables.qdocconf b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/templatedcallables/templatedcallables.qdocconf
new file mode 100644
index 000000000..d7a7c46ef
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/templatedcallables/templatedcallables.qdocconf
@@ -0,0 +1,29 @@
+project = templatedcallables
+
+headerdirs = ./src
+sourcedirs = ./src
+exampledirs = ./src
+outputformats = WebXML HTML DocBook
+
+WebXML.quotinginformation = true
+WebXML.nosubdirs = true
+WebXML.outputsubdir = webxml
+
+HTML.nosubdirs = true
+HTML.outputsubdir = html
+
+DocBook.nosubdirs = true
+DocBook.outputsubdir = docbook
+
+sources.fileextensions = "*.qml *.cpp *.qdoc"
+headers.fileextensions = "*.h"
+
+# images
+imagedirs = ./src/images
+
+# zero warning policy
+warninglimit = 0
+warninglimit.enabled = true
+
+# don't write host system-specific paths to index files
+locationinfo = false
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/testtemplate/expected/docbook/autolinking.xml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/testtemplate/expected/docbook/autolinking.xml
new file mode 100644
index 000000000..b9e9b7eb0
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/testtemplate/expected/docbook/autolinking.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<db:article xmlns:db="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" version="5.2" xml:lang="en">
+<db:info>
+<db:title>Autolinking</db:title>
+<db:productname>TestTemplate</db:productname>
+<db:titleabbrev>TestTemplate Reference Documentation</db:titleabbrev>
+<db:abstract>
+<db:para>TestTemplate Reference Documentation.</db:para></db:abstract>
+</db:info>
+<db:section xml:id="testqdoc">
+<db:title>TestQDoc</db:title>
+<db:para>The string <db:link xlink:href="testqdoc.xml">TestQDoc</db:link> links to the C++ namespace unless linking explicitly, <db:link xlink:href="autolinking.xml#testqdoc">like this</db:link>, or <db:link xlink:href="testqdoc.xml">this</db:link>. Also,</db:para>
+<db:para>Autolinks:</db:para>
+<db:itemizedlist>
+<db:listitem>
+<db:para><db:link xlink:href="testqdoc-testderived.xml">TestQDoc::TestDerived</db:link></db:para>
+</db:listitem>
+</db:itemizedlist>
+<db:para>Explicit links:</db:para>
+<db:itemizedlist>
+<db:listitem>
+<db:para><db:link xlink:href="testqdoc-testderived.xml">TestQDoc::TestDerived</db:link></db:para>
+</db:listitem>
+<db:listitem>
+<db:para><db:link xlink:href="obsolete-classes.xml#testqdoc">Obsolete Classes#TestQDoc</db:link></db:para>
+</db:listitem>
+</db:itemizedlist>
+</db:section>
+<db:section xml:id="someprop">
+<db:title>someProp</db:title>
+</db:section>
+</db:article>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/testtemplate/expected/docbook/bar.xml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/testtemplate/expected/docbook/bar.xml
new file mode 100644
index 000000000..9c54fe914
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/testtemplate/expected/docbook/bar.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<db:article xmlns:db="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" version="5.2" xml:lang="en">
+<db:info>
+<db:title>Bar Class</db:title>
+<db:subtitle>template &lt;typename T, typename D&gt; class Bar</db:subtitle>
+<db:productname>TestTemplate</db:productname>
+<db:titleabbrev>TestTemplate Reference Documentation</db:titleabbrev>
+<db:abstract>
+<db:para>Another class template.</db:para>
+<db:para>This class was introduced in Qt 2.0.</db:para>
+</db:abstract>
+</db:info>
+<db:variablelist>
+<db:varlistentry>
+<db:term>Header</db:term>
+<db:listitem>
+<db:para>Bar</db:para>
+</db:listitem>
+</db:varlistentry>
+<db:varlistentry>
+<db:term>Since</db:term>
+<db:listitem>
+<db:para>Qt 2.0</db:para>
+</db:listitem>
+</db:varlistentry>
+<db:varlistentry>
+<db:term>CMake</db:term>
+<db:listitem>
+<db:para>find_package(Qt6 REQUIRED COMPONENTS QDocTest)</db:para>
+<db:para>target_link_libraries(mytarget PRIVATE Qt6::QDocTest)</db:para>
+</db:listitem>
+</db:varlistentry>
+<db:varlistentry>
+<db:term>qmake</db:term>
+<db:listitem>
+<db:para>QT += testcpp</db:para>
+</db:listitem>
+</db:varlistentry>
+</db:variablelist>
+<db:section xml:id="details">
+<db:title>Detailed Description</db:title>
+</db:section>
+</db:article>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/testtemplate/expected/docbook/baz.xml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/testtemplate/expected/docbook/baz.xml
new file mode 100644
index 000000000..cf36a35b1
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/testtemplate/expected/docbook/baz.xml
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<db:article xmlns:db="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" version="5.2" xml:lang="en">
+<db:info>
+<db:title></db:title>
+<db:productname>TestTemplate</db:productname>
+<db:titleabbrev>TestTemplate Reference Documentation</db:titleabbrev>
+<db:abstract>
+<db:para>Class template template.</db:para>
+<db:para>This struct was introduced in Qt 2.0.</db:para>
+</db:abstract>
+</db:info>
+<db:variablelist>
+<db:varlistentry>
+<db:term>Header</db:term>
+<db:listitem>
+<db:para>Baz</db:para>
+</db:listitem>
+</db:varlistentry>
+<db:varlistentry>
+<db:term>Since</db:term>
+<db:listitem>
+<db:para>Qt 2.0</db:para>
+</db:listitem>
+</db:varlistentry>
+<db:varlistentry>
+<db:term>CMake</db:term>
+<db:listitem>
+<db:para>find_package(Qt6 REQUIRED COMPONENTS QDocTest)</db:para>
+<db:para>target_link_libraries(mytarget PRIVATE Qt6::QDocTest)</db:para>
+</db:listitem>
+</db:varlistentry>
+<db:varlistentry>
+<db:term>qmake</db:term>
+<db:listitem>
+<db:para>QT += testcpp</db:para>
+</db:listitem>
+</db:varlistentry>
+</db:variablelist>
+<db:section xml:id="details">
+<db:title>Detailed Description</db:title>
+</db:section>
+</db:article>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/testtemplate/expected/docbook/cpptypes.xml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/testtemplate/expected/docbook/cpptypes.xml
new file mode 100644
index 000000000..8be7809ec
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/testtemplate/expected/docbook/cpptypes.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<db:article xmlns:db="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" version="5.2" xml:lang="en">
+<db:info>
+<db:title>Test C++ Types</db:title>
+<db:productname>TestTemplate</db:productname>
+<db:titleabbrev>TestTemplate Reference Documentation</db:titleabbrev>
+<db:abstract>
+<db:para>TestTemplate Reference Documentation.</db:para></db:abstract>
+</db:info>
+<db:anchor xml:id="details"/>
+<db:itemizedlist role="testgroup">
+<db:listitem>
+<db:para><db:link xlink:href="testqdoc-test.xml" xlink:role="class">TestQDoc::Test</db:link></db:para>
+</db:listitem>
+<db:listitem>
+<db:para><db:link xlink:href="testqdoc-test.xml#QDOCTEST_MACRO2" xlink:role="function">TestQDoc::Test::QDOCTEST_MACRO2</db:link></db:para>
+</db:listitem>
+<db:listitem>
+<db:para><db:link xlink:href="testqdoc-test.xml#someFunctionDefaultArg" xlink:role="function">TestQDoc::Test::someFunctionDefaultArg()</db:link></db:para>
+</db:listitem>
+</db:itemizedlist>
+<db:variablelist role="members">
+<db:varlistentry>
+<db:term><db:link xlink:href="testqdoc-test.xml" xlink:role="class">TestQDoc::Test</db:link></db:term>
+<db:listitem>
+<db:para>A class in a namespace.</db:para>
+</db:listitem>
+</db:varlistentry>
+</db:variablelist>
+</db:article>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/testtemplate/expected/docbook/crossmoduleref.xml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/testtemplate/expected/docbook/crossmoduleref.xml
new file mode 100644
index 000000000..b86d8f01b
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/testtemplate/expected/docbook/crossmoduleref.xml
@@ -0,0 +1,49 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<db:article xmlns:db="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" version="5.2" xml:lang="en">
+<db:info>
+<db:title>CrossModuleRef Namespace</db:title>
+<db:productname>TestTemplate</db:productname>
+<db:titleabbrev>TestTemplate Reference Documentation</db:titleabbrev>
+<db:abstract>
+<db:para>Namespace that has documented functions in multiple modules.</db:para>
+<db:para>This namespace was introduced in Qt 3.0.</db:para>
+</db:abstract>
+</db:info>
+<db:variablelist>
+<db:varlistentry>
+<db:term>Header</db:term>
+<db:listitem>
+<db:para>CrossModuleRef</db:para>
+</db:listitem>
+</db:varlistentry>
+<db:varlistentry>
+<db:term>Since</db:term>
+<db:listitem>
+<db:para>Qt 3.0</db:para>
+</db:listitem>
+</db:varlistentry>
+<db:varlistentry>
+<db:term>CMake</db:term>
+<db:listitem>
+<db:para>find_package(Qt6 REQUIRED COMPONENTS QDocTest)</db:para>
+<db:para>target_link_libraries(mytarget PRIVATE Qt6::QDocTest)</db:para>
+</db:listitem>
+</db:varlistentry>
+<db:varlistentry>
+<db:term>qmake</db:term>
+<db:listitem>
+<db:para>QT += testcpp</db:para>
+</db:listitem>
+</db:varlistentry>
+</db:variablelist>
+<db:section xml:id="details">
+<db:title>Detailed Description</db:title>
+</db:section>
+<db:section xml:id="function-documentation">
+<db:title>Function Documentation</db:title>
+<db:section xml:id="documentMe">
+<db:title>void CrossModuleRef::documentMe()</db:title>
+<db:para>Document me!</db:para>
+</db:section>
+</db:section>
+</db:article>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/testtemplate/expected/docbook/foo.xml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/testtemplate/expected/docbook/foo.xml
new file mode 100644
index 000000000..da6867876
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/testtemplate/expected/docbook/foo.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<db:article xmlns:db="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" version="5.2" xml:lang="en">
+<db:info>
+<db:title>Foo Class</db:title>
+<db:subtitle>template &lt;typename T&gt; class Foo</db:subtitle>
+<db:productname>TestTemplate</db:productname>
+<db:titleabbrev>TestTemplate Reference Documentation</db:titleabbrev>
+<db:abstract>
+<db:para>Class template.</db:para>
+<db:para>This class was introduced in Qt 2.0.</db:para>
+</db:abstract>
+</db:info>
+<db:variablelist>
+<db:varlistentry>
+<db:term>Header</db:term>
+<db:listitem>
+<db:para>Foo</db:para>
+</db:listitem>
+</db:varlistentry>
+<db:varlistentry>
+<db:term>Since</db:term>
+<db:listitem>
+<db:para>Qt 2.0</db:para>
+</db:listitem>
+</db:varlistentry>
+<db:varlistentry>
+<db:term>CMake</db:term>
+<db:listitem>
+<db:para>find_package(Qt6 REQUIRED COMPONENTS QDocTest)</db:para>
+<db:para>target_link_libraries(mytarget PRIVATE Qt6::QDocTest)</db:para>
+</db:listitem>
+</db:varlistentry>
+<db:varlistentry>
+<db:term>qmake</db:term>
+<db:listitem>
+<db:para>QT += testcpp</db:para>
+</db:listitem>
+</db:varlistentry>
+</db:variablelist>
+<db:section xml:id="details">
+<db:title>Detailed Description</db:title>
+</db:section>
+</db:article>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/testtemplate/expected/docbook/obsolete-classes.xml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/testtemplate/expected/docbook/obsolete-classes.xml
new file mode 100644
index 000000000..50090db37
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/testtemplate/expected/docbook/obsolete-classes.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<db:article xmlns:db="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" version="5.2" xml:lang="en">
+<db:info>
+<db:title>Obsolete Classes</db:title>
+<db:productname>TestTemplate</db:productname>
+<db:titleabbrev>TestTemplate Reference Documentation</db:titleabbrev>
+<db:abstract>
+<db:para>TestTemplate Reference Documentation.</db:para></db:abstract>
+</db:info>
+<db:section xml:id="classes-with-obsolete-members">
+<db:title>Classes with obsolete members</db:title>
+<db:variablelist role="obsoletecppmembers">
+<db:varlistentry>
+<db:term><db:emphasis role="bold">T</db:emphasis></db:term>
+<db:listitem>
+<db:itemizedlist>
+<db:listitem>
+<db:para><db:link xlink:href="testqdoc-test.xml" role="class">Test</db:link> (<db:link xlink:href="testqdoc.xml" xlink:role="namespace">TestQDoc</db:link>)</db:para>
+</db:listitem>
+<db:listitem>
+<db:para><db:link xlink:href="testqdoc-testderived.xml" role="class">TestDerived</db:link> (<db:link xlink:href="testqdoc.xml" xlink:role="namespace">TestQDoc</db:link>)</db:para>
+</db:listitem>
+</db:itemizedlist>
+</db:listitem>
+</db:varlistentry>
+</db:variablelist>
+<db:section xml:id="testqdoc">
+<db:title>TestQDoc</db:title>
+</db:section>
+</db:section>
+</db:article>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/testtemplate/expected/docbook/testcpp-module.xml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/testtemplate/expected/docbook/testcpp-module.xml
new file mode 100644
index 000000000..d7a8ea674
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/testtemplate/expected/docbook/testcpp-module.xml
@@ -0,0 +1,107 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<db:article xmlns:db="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" version="5.2" xml:lang="en">
+<db:info>
+<db:title>QDoc Test C++ Classes</db:title>
+<db:productname>TestTemplate</db:productname>
+<db:titleabbrev>TestTemplate Reference Documentation</db:titleabbrev>
+<db:abstract>
+<db:para>A test module page.</db:para>
+<db:para>This module was introduced in Qt 2.0.</db:para>
+</db:abstract>
+</db:info>
+<db:para>A test module page.</db:para>
+<db:para>This module was introduced in Qt 2.0.</db:para>
+<db:section xml:id="namespaces">
+<db:title>Namespaces</db:title>
+<db:variablelist role="namespaces">
+<db:varlistentry>
+<db:term><db:link xlink:href="crossmoduleref.xml" xlink:role="namespace">CrossModuleRef</db:link></db:term>
+<db:listitem>
+<db:para>Namespace that has documented functions in multiple modules.</db:para>
+</db:listitem>
+</db:varlistentry>
+<db:varlistentry>
+<db:term><db:link xlink:href="testqdoc.xml" xlink:role="namespace">TestQDoc</db:link></db:term>
+<db:listitem>
+<db:para>A namespace.</db:para>
+</db:listitem>
+</db:varlistentry>
+</db:variablelist>
+</db:section>
+<db:section xml:id="classes">
+<db:title>Classes</db:title>
+<db:variablelist role="classes">
+<db:varlistentry>
+<db:term><db:link xlink:href="bar.xml" xlink:role="class">Bar</db:link></db:term>
+<db:listitem>
+<db:para>Another class template.</db:para>
+</db:listitem>
+</db:varlistentry>
+<db:varlistentry>
+<db:term><db:link xlink:href="baz.xml" xlink:role="class">Baz</db:link></db:term>
+<db:listitem>
+<db:para>Class template template.</db:para>
+</db:listitem>
+</db:varlistentry>
+<db:varlistentry>
+<db:term><db:link xlink:href="foo.xml" xlink:role="class">Foo</db:link></db:term>
+<db:listitem>
+<db:para>Class template.</db:para>
+</db:listitem>
+</db:varlistentry>
+<db:varlistentry>
+<db:term><db:link xlink:href="testqdoc-test.xml" xlink:role="class">TestQDoc::Test</db:link></db:term>
+<db:listitem>
+<db:para>A class in a namespace.</db:para>
+</db:listitem>
+</db:varlistentry>
+<db:varlistentry>
+<db:term><db:link xlink:href="testqdoc-test-struct.xml" xlink:role="class">TestQDoc::Test::Struct</db:link></db:term>
+<db:listitem>
+<db:para>Templated struct.</db:para>
+</db:listitem>
+</db:varlistentry>
+<db:varlistentry>
+<db:term><db:link xlink:href="testqdoc-testderived.xml" xlink:role="class">TestQDoc::TestDerived</db:link></db:term>
+<db:listitem>
+<db:para>A class in a namespace, derived from Test.</db:para>
+</db:listitem>
+</db:varlistentry>
+<db:varlistentry>
+<db:term><db:link xlink:href="testqdoc-vec.xml" xlink:role="class">TestQDoc::Vec</db:link></db:term>
+<db:listitem>
+<db:para>Type alias that has its own reference.</db:para>
+</db:listitem>
+</db:varlistentry>
+</db:variablelist>
+</db:section>
+<db:section xml:id="details">
+<db:title>Detailed Description</db:title>
+<db:note>
+<db:para>This is just a test. /* Look, Ma! {I'm made of arguments!} */</db:para>
+</db:note>
+<db:section xml:id="linking-to-function-like-things">
+<db:title>Linking to function-like things</db:title>
+<db:itemizedlist>
+<db:listitem>
+<db:para><db:link xlink:href="testqdoc-test.xml#someFunctionDefaultArg">someFunctionDefaultArg</db:link>()</db:para>
+</db:listitem>
+<db:listitem>
+<db:para><db:link xlink:href="testqdoc-test.xml#QDOCTEST_MACRO2">QDOCTEST_MACRO2</db:link>()</db:para>
+</db:listitem>
+<db:listitem>
+<db:para><db:link xlink:href="testqdoc-test.xml#QDOCTEST_MACRO2">QDOCTEST_MACRO2</db:link>(int &amp;x)</db:para>
+</db:listitem>
+<db:listitem>
+<db:para><db:link xlink:href="testcpp-module.xml#section">section()</db:link></db:para>
+</db:listitem>
+<db:listitem>
+<db:para><db:link xlink:href="testcpp-module.xml#section">section() is a section title</db:link></db:para>
+</db:listitem>
+</db:itemizedlist>
+<db:section xml:id="section">
+<db:title>section()</db:title>
+</db:section>
+</db:section>
+</db:section>
+</db:article>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/testtemplate/expected/docbook/testqdoc-test-struct.xml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/testtemplate/expected/docbook/testqdoc-test-struct.xml
new file mode 100644
index 000000000..67962de28
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/testtemplate/expected/docbook/testqdoc-test-struct.xml
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<db:article xmlns:db="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" version="5.2" xml:lang="en">
+<db:info>
+<db:title></db:title>
+<db:productname>TestTemplate</db:productname>
+<db:titleabbrev>TestTemplate Reference Documentation</db:titleabbrev>
+<db:abstract>
+<db:para>Templated struct.</db:para>
+<db:para>This struct was introduced in Qt 2.0.</db:para>
+</db:abstract>
+</db:info>
+<db:variablelist>
+<db:varlistentry>
+<db:term>Header</db:term>
+<db:listitem>
+<db:para>Struct</db:para>
+</db:listitem>
+</db:varlistentry>
+<db:varlistentry>
+<db:term>Since</db:term>
+<db:listitem>
+<db:para>Qt 2.0</db:para>
+</db:listitem>
+</db:varlistentry>
+<db:varlistentry>
+<db:term>CMake</db:term>
+<db:listitem>
+<db:para>find_package(Qt6 REQUIRED COMPONENTS QDocTest)</db:para>
+<db:para>target_link_libraries(mytarget PRIVATE Qt6::QDocTest)</db:para>
+</db:listitem>
+</db:varlistentry>
+<db:varlistentry>
+<db:term>qmake</db:term>
+<db:listitem>
+<db:para>QT += testcpp</db:para>
+</db:listitem>
+</db:varlistentry>
+</db:variablelist>
+<db:section xml:id="details">
+<db:title>Detailed Description</db:title>
+</db:section>
+</db:article>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/testtemplate/expected/docbook/testqdoc-test.xml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/testtemplate/expected/docbook/testqdoc-test.xml
new file mode 100644
index 000000000..bf8c99483
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/testtemplate/expected/docbook/testqdoc-test.xml
@@ -0,0 +1,189 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<db:article xmlns:db="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" version="5.2" xml:lang="en">
+<db:info>
+<db:title>Test Class</db:title>
+<db:subtitle>TestQDoc::Test</db:subtitle>
+<db:productname>TestTemplate</db:productname>
+<db:titleabbrev>TestTemplate Reference Documentation</db:titleabbrev>
+<db:abstract>
+<db:para>A class in a namespace.</db:para>
+<db:para>This class was introduced in Qt 2.0.</db:para>
+<db:note>
+<db:para>All functions in this class are <db:link xlink:href="https://doc.qt.io/qt/17-qdoc-commands-thread.html#reentrant-command">reentrant</db:link> with the following exceptions:</db:para>
+<db:para>These functions are not <db:link xlink:href="https://doc.qt.io/qt/17-qdoc-commands-thread.html#reentrant-command">reentrant</db:link>:</db:para>
+<db:itemizedlist>
+<db:listitem>
+<db:para><db:link xlink:href="testqdoc-test.xml#someFunctionDefaultArg">someFunctionDefaultArg(int i, bool b) const</db:link></db:para>
+</db:listitem>
+</db:itemizedlist>
+</db:note>
+</db:abstract>
+</db:info>
+<db:variablelist>
+<db:varlistentry>
+<db:term>Header</db:term>
+<db:listitem>
+<db:para>Test</db:para>
+</db:listitem>
+</db:varlistentry>
+<db:varlistentry>
+<db:term>Since</db:term>
+<db:listitem>
+<db:para>Qt 2.0</db:para>
+</db:listitem>
+</db:varlistentry>
+<db:varlistentry>
+<db:term>CMake</db:term>
+<db:listitem>
+<db:para>find_package(Qt6 REQUIRED COMPONENTS QDocTest)</db:para>
+<db:para>target_link_libraries(mytarget PRIVATE Qt6::QDocTest)</db:para>
+</db:listitem>
+</db:varlistentry>
+<db:varlistentry>
+<db:term>qmake</db:term>
+<db:listitem>
+<db:para>QT += testcpp</db:para>
+</db:listitem>
+</db:varlistentry>
+<db:varlistentry>
+<db:term>Inherited By</db:term>
+<db:listitem>
+<db:para><db:link xlink:href="testqdoc-testderived.xml" xlink:role="class">TestQDoc::TestDerived</db:link></db:para>
+</db:listitem>
+</db:varlistentry>
+<db:varlistentry>
+<db:term>Group</db:term>
+<db:listitem>
+<db:para>Test is part of <db:simplelist><db:member>testgroup</db:member><db:member><db:link xlink:href="cpptypes.xml">Test C++ Types</db:link></db:member></db:simplelist>
+</db:para>
+</db:listitem>
+</db:varlistentry>
+</db:variablelist>
+<db:section xml:id="details">
+<db:title>Detailed Description</db:title>
+</db:section>
+<db:section xml:id="member-type-documentation">
+<db:title>Member Type Documentation</db:title>
+<db:section xml:id="SomeType-typedef">
+<db:title>Test::SomeType</db:title>
+<db:para>A typedef.</db:para>
+</db:section>
+<db:section xml:id="Specialized-typedef">
+<db:title>[alias] template &lt;typename T&gt; Test::Specialized</db:title>
+</db:section>
+</db:section>
+<db:section xml:id="member-function-documentation">
+<db:title>Member Function Documentation</db:title>
+<db:section xml:id="overload">
+<db:title>[protected] void Test::overload()</db:title>
+<db:bridgehead renderas="sect2" xml:id="overload-1">[protected, since Test 1.2] void Test::overload(bool <db:emphasis>b</db:emphasis>)</db:bridgehead>
+<db:para>Overloads that share a documentation comment, optionally taking a parameter <db:code role="parameter">b</db:code>.</db:para>
+</db:section>
+<db:section xml:id="funcPtr">
+<db:title>void (*)(bool) Test::funcPtr(bool <db:emphasis>b</db:emphasis>, const char *<db:emphasis>s</db:emphasis>)</db:title>
+<db:para>Returns a pointer to a function that takes a boolean. Uses <db:code role="parameter">b</db:code> and <db:code role="parameter">s</db:code>.</db:para>
+</db:section>
+<db:section xml:id="funcTemplate">
+<db:title>[protected] void Test::funcTemplate(T1 <db:emphasis>a</db:emphasis>, T2 <db:emphasis>b</db:emphasis>)</db:title>
+<db:para>Function template with two parameters, <db:code role="parameter">a</db:code> and <db:code role="parameter">b</db:code>.</db:para>
+</db:section>
+<db:section xml:id="inlineFunction">
+<db:title>void Test::inlineFunction()</db:title>
+<db:para>An inline function, documented using the \fn QDoc command.</db:para>
+</db:section>
+<db:section xml:id="methodWithEmDashInItsDocs">
+<db:title>void Test::methodWithEmDashInItsDocs()</db:title>
+<db:para>This method has em dashes in its documentation—as you'll find represented by <db:code>---</db:code> in the sources—here and there. The important bit to note is that when passed e.g. to the \c command, the three hyphens are processed as input to the command and not replaced by an em dash.</db:para>
+<db:para>-----------------------------------------------------------------------</db:para>
+<db:para>People can still add a bunch of dashes, though, without QDoc replacing them all with a series of em dashes.</db:para>
+<db:para>—You can also start a new paragraph with an em dash, if you want to.</db:para>
+<db:section>
+<db:title>See Also</db:title>
+<db:para><db:emphasis>See also </db:emphasis>
+<db:simplelist type="vert" role="see-also">
+<db:member><db:link xlink:href="testqdoc-test.xml#methodWithEnDashInItsDocs">methodWithEnDashInItsDocs</db:link></db:member>
+</db:simplelist>
+</db:para>
+</db:section>
+</db:section>
+<db:section xml:id="methodWithEnDashInItsDocs">
+<db:title>void Test::methodWithEnDashInItsDocs()</db:title>
+<db:para>This method has en dashes in its documentation – as you'll find represented by <db:code>--</db:code> in the sources – here and there. The important bit to note is that when passed e.g. to the \c command, the two hyphens are processed as input to the command and not replaced by an en dash. This also applies to code blocks, where otherwise, the decrement operator would get completely borked:</db:para>
+<db:programlisting language="cpp">for (int i = 42; i &amp;gt; 0; --i)
+ // Do something cool during countdown.
+</db:programlisting>
+<db:para>...as it would be silly if this would output –i instead of <db:code>--i</db:code>.</db:para>
+<db:para>-----------------------------------------------------------------------</db:para>
+<db:para>It still allows people to add a bunch of dashes, though, without replacing them all with a series of en dashes. Of course, they might want to use the \hr command instead, like this:</db:para>
+<db:para>– You can also start a new paragraph with an en dash, if you want to.</db:para>
+<db:section>
+<db:title>See Also</db:title>
+<db:para><db:emphasis>See also </db:emphasis>
+<db:simplelist type="vert" role="see-also">
+<db:member><db:link xlink:href="">methodWithEnDashInItsDocs</db:link></db:member>
+</db:simplelist>
+</db:para>
+</db:section>
+</db:section>
+<db:section xml:id="someFunction">
+<db:title>int Test::someFunction(<db:emphasis>int</db:emphasis>, int <db:emphasis>v</db:emphasis> = 0)</db:title>
+<db:para>Function that takes a parameter <db:code role="parameter">v</db:code>. Also returns the value of <db:code role="parameter">v</db:code>.</db:para>
+</db:section>
+<db:section xml:id="someFunctionDefaultArg">
+<db:title>void Test::someFunctionDefaultArg(int <db:emphasis>i</db:emphasis>, bool <db:emphasis>b</db:emphasis> = false) const</db:title>
+<db:para>Function that takes a parameter <db:code role="parameter">i</db:code> and <db:code role="parameter">b</db:code>.</db:para>
+<db:warning>
+<db:para>This function is not <db:link xlink:href="https://doc.qt.io/qt/17-qdoc-commands-thread.html#reentrant-command">reentrant</db:link>.</db:para>
+</db:warning></db:section>
+<db:section xml:id="virtualFun">
+<db:title>[virtual] void Test::virtualFun()</db:title>
+<db:para>Function that must be reimplemented.</db:para>
+</db:section>
+</db:section>
+<db:section xml:id="related-non-members">
+<db:title>Related Non-Members</db:title>
+<db:section xml:id="operator-eq-eq">
+<db:title>bool operator==(const TestQDoc::Test &amp;<db:emphasis>lhs</db:emphasis>, const TestQDoc::Test &amp;<db:emphasis>rhs</db:emphasis>)</db:title>
+<db:para>Returns true if <db:code role="parameter">lhs</db:code> and <db:code role="parameter">rhs</db:code> are equal.</db:para>
+</db:section>
+</db:section>
+<db:section xml:id="macro-documentation">
+<db:title>Macro Documentation</db:title>
+<db:section xml:id="QDOCTEST_MACRO2">
+<db:title>[since Test 1.1] QDOCTEST_MACRO2(int &amp;<db:emphasis>x</db:emphasis>)</db:title>
+<db:para>A macro with argument <db:code role="parameter">x</db:code>.</db:para>
+<db:para>This macro was introduced in Test 1.1.</db:para>
+</db:section>
+<db:section xml:id="Q_INVOKABLE">
+<db:title>Q_INVOKABLE</db:title>
+<db:para>This is a mock Q_INVOKABLE for the purpose of ensuring QDoc autolink to it as expected.</db:para>
+</db:section>
+</db:section>
+<db:section xml:id="obsolete">
+<db:title>Obsolete Members for Test</db:title>
+<db:para><db:emphasis role="bold">The following members of class <db:link xlink:href="testqdoc-test.xml">Test</db:link> are deprecated.</db:emphasis> We strongly advise against using them in new code.</db:para>
+<db:section xml:id="obsolete-member-function-documentation">
+<db:title>Obsolete Member Function Documentation</db:title>
+<db:section xml:id="operator-2b-2b">
+<db:title>[deprecated] TestQDoc::Test &amp;Test::operator++()</db:title>
+<db:bridgehead renderas="sect2" xml:id="operator--">[deprecated] TestQDoc::Test &amp;Test::operator--()</db:bridgehead>
+<db:para>This function is deprecated. We strongly advise against using it in new code.</db:para>
+</db:section>
+<db:section xml:id="anotherObsoleteMember">
+<db:title>[deprecated] void Test::anotherObsoleteMember()</db:title>
+<db:para>This function is deprecated. We strongly advise against using it in new code.</db:para>
+<db:para>Use <db:link xlink:href="testqdoc-test.xml#obsoleteMember">obsoleteMember</db:link>() instead.</db:para>
+</db:section>
+<db:section xml:id="deprecatedMember">
+<db:title>[deprecated in 6.0] void Test::deprecatedMember()</db:title>
+<db:para>This function is deprecated since 6.0. We strongly advise against using it in new code.</db:para>
+<db:para>Use <db:link xlink:href="testqdoc-test.xml#someFunction">someFunction</db:link>() instead.</db:para>
+</db:section>
+<db:section xml:id="obsoleteMember">
+<db:title>[deprecated] void Test::obsoleteMember()</db:title>
+<db:para>This function is deprecated. We strongly advise against using it in new code.</db:para>
+<db:para>Use <db:link xlink:href="testqdoc-test.xml#someFunction">someFunction</db:link>() instead.</db:para>
+</db:section>
+</db:section>
+</db:section>
+</db:article>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/testtemplate/expected/docbook/testqdoc-testderived.xml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/testtemplate/expected/docbook/testqdoc-testderived.xml
new file mode 100644
index 000000000..602faea3b
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/testtemplate/expected/docbook/testqdoc-testderived.xml
@@ -0,0 +1,83 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<db:article xmlns:db="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" version="5.2" xml:lang="en">
+<db:info>
+<db:title>TestDerived Class</db:title>
+<db:subtitle>TestQDoc::TestDerived</db:subtitle>
+<db:productname>TestTemplate</db:productname>
+<db:titleabbrev>TestTemplate Reference Documentation</db:titleabbrev>
+<db:abstract>
+<db:para>A class in a namespace, derived from <db:link xlink:href="testqdoc-test.xml">Test</db:link>.</db:para>
+<db:para>This class was introduced in Qt 2.0.</db:para>
+</db:abstract>
+</db:info>
+<db:variablelist>
+<db:varlistentry>
+<db:term>Header</db:term>
+<db:listitem>
+<db:para>TestDerived</db:para>
+</db:listitem>
+</db:varlistentry>
+<db:varlistentry>
+<db:term>Since</db:term>
+<db:listitem>
+<db:para>Qt 2.0</db:para>
+</db:listitem>
+</db:varlistentry>
+<db:varlistentry>
+<db:term>CMake</db:term>
+<db:listitem>
+<db:para>find_package(Qt6 REQUIRED COMPONENTS QDocTest)</db:para>
+<db:para>target_link_libraries(mytarget PRIVATE Qt6::QDocTest)</db:para>
+</db:listitem>
+</db:varlistentry>
+<db:varlistentry>
+<db:term>qmake</db:term>
+<db:listitem>
+<db:para>QT += testcpp</db:para>
+</db:listitem>
+</db:varlistentry>
+<db:varlistentry>
+<db:term>Inherits</db:term>
+<db:listitem>
+<db:para><db:link xlink:href="testqdoc-test.xml" xlink:role="class">TestQDoc::Test</db:link></db:para>
+</db:listitem>
+</db:varlistentry>
+</db:variablelist>
+<db:section xml:id="details">
+<db:title>Detailed Description</db:title>
+</db:section>
+<db:section xml:id="member-type-documentation">
+<db:title>Member Type Documentation</db:title>
+<db:section xml:id="DerivedType-typedef">
+<db:title>[alias] TestDerived::DerivedType</db:title>
+<db:para>An aliased typedef.</db:para>
+</db:section>
+<db:section xml:id="NotTypedef-typedef">
+<db:title>[alias] TestDerived::NotTypedef</db:title>
+<db:para>I'm an alias, not a typedef.</db:para>
+</db:section>
+</db:section>
+<db:section xml:id="member-function-documentation">
+<db:title>Member Function Documentation</db:title>
+<db:section xml:id="someValue">
+<db:title>TestQDoc::TestDerived::NotTypedef TestDerived::someValue()</db:title>
+<db:para>Returns a value using an aliases type.</db:para>
+</db:section>
+<db:section xml:id="virtualFun">
+<db:title>[override virtual] void TestDerived::virtualFun()</db:title>
+<db:para>Reimplements: <db:link xlink:href="testqdoc-test.xml#virtualFun" role="function">Test::virtualFun()</db:link>.</db:para>
+</db:section>
+</db:section>
+<db:section xml:id="obsolete">
+<db:title>Obsolete Members for TestDerived</db:title>
+<db:para><db:emphasis role="bold">The following members of class <db:link xlink:href="testqdoc-testderived.xml">TestDerived</db:link> are deprecated.</db:emphasis> We strongly advise against using them in new code.</db:para>
+<db:section xml:id="obsolete-member-function-documentation">
+<db:title>Obsolete Member Function Documentation</db:title>
+<db:section xml:id="staticObsoleteMember">
+<db:title>[static, deprecated] void TestDerived::staticObsoleteMember()</db:title>
+<db:para>This function is deprecated. We strongly advise against using it in new code.</db:para>
+<db:para>Static obsolete method.</db:para>
+</db:section>
+</db:section>
+</db:section>
+</db:article>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/testtemplate/expected/docbook/testqdoc-vec.xml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/testtemplate/expected/docbook/testqdoc-vec.xml
new file mode 100644
index 000000000..d276da7af
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/testtemplate/expected/docbook/testqdoc-vec.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<db:article xmlns:db="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" version="5.2" xml:lang="en">
+<db:info>
+<db:title>Vec Class</db:title>
+<db:subtitle>template &lt;typename T&gt; class TestQDoc::Vec</db:subtitle>
+<db:productname>TestTemplate</db:productname>
+<db:titleabbrev>TestTemplate Reference Documentation</db:titleabbrev>
+<db:abstract>
+<db:para>Type alias that has its own reference.</db:para>
+<db:para>This class was introduced in Qt 2.0.</db:para>
+</db:abstract>
+</db:info>
+<db:variablelist>
+<db:varlistentry>
+<db:term>Header</db:term>
+<db:listitem>
+<db:para>Vec</db:para>
+</db:listitem>
+</db:varlistentry>
+<db:varlistentry>
+<db:term>Since</db:term>
+<db:listitem>
+<db:para>Qt 2.0</db:para>
+</db:listitem>
+</db:varlistentry>
+<db:varlistentry>
+<db:term>CMake</db:term>
+<db:listitem>
+<db:para>find_package(Qt6 REQUIRED COMPONENTS QDocTest)</db:para>
+<db:para>target_link_libraries(mytarget PRIVATE Qt6::QDocTest)</db:para>
+</db:listitem>
+</db:varlistentry>
+<db:varlistentry>
+<db:term>qmake</db:term>
+<db:listitem>
+<db:para>QT += testcpp</db:para>
+</db:listitem>
+</db:varlistentry>
+</db:variablelist>
+<db:section xml:id="details">
+<db:title>Detailed Description</db:title>
+</db:section>
+</db:article>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/testtemplate/expected/docbook/testqdoc.xml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/testtemplate/expected/docbook/testqdoc.xml
new file mode 100644
index 000000000..cd575b1e8
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/testtemplate/expected/docbook/testqdoc.xml
@@ -0,0 +1,67 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<db:article xmlns:db="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" version="5.2" xml:lang="en">
+<db:info>
+<db:title>TestQDoc Namespace</db:title>
+<db:productname>TestTemplate</db:productname>
+<db:titleabbrev>TestTemplate Reference Documentation</db:titleabbrev>
+<db:abstract>
+<db:para>A namespace.</db:para>
+<db:para>This namespace was introduced in Qt 2.0.</db:para>
+</db:abstract>
+</db:info>
+<db:variablelist>
+<db:varlistentry>
+<db:term>Header</db:term>
+<db:listitem>
+<db:para>TestCPP</db:para>
+</db:listitem>
+</db:varlistentry>
+<db:varlistentry>
+<db:term>Since</db:term>
+<db:listitem>
+<db:para>Qt 2.0</db:para>
+</db:listitem>
+</db:varlistentry>
+<db:varlistentry>
+<db:term>CMake</db:term>
+<db:listitem>
+<db:para>find_package(Qt6 REQUIRED COMPONENTS QDocTest)</db:para>
+<db:para>target_link_libraries(mytarget PRIVATE Qt6::QDocTest)</db:para>
+</db:listitem>
+</db:varlistentry>
+<db:varlistentry>
+<db:term>qmake</db:term>
+<db:listitem>
+<db:para>QT += testcpp</db:para>
+</db:listitem>
+</db:varlistentry>
+</db:variablelist>
+<db:section xml:id="details">
+<db:title>Detailed Description</db:title>
+<db:section xml:id="usage">
+<db:title>Usage</db:title>
+<db:para>This namespace is for testing QDoc output.</db:para>
+</db:section>
+</db:section>
+<db:section xml:id="classes">
+<db:title>Classes</db:title>
+<db:section>
+<db:title>class <db:link xlink:href="testqdoc-test.xml" xlink:role="class">Test</db:link></db:title>
+<db:para>A class in a namespace.</db:para>
+</db:section>
+<db:section>
+<db:title>class <db:link xlink:href="testqdoc-testderived.xml" xlink:role="class">TestDerived</db:link></db:title>
+<db:para>A class in a namespace, derived from <db:link xlink:href="testqdoc-test.xml">Test</db:link>.</db:para>
+</db:section>
+<db:section>
+<db:title>class <db:link xlink:href="testqdoc-vec.xml" xlink:role="class">Vec</db:link></db:title>
+<db:para>Type alias that has its own reference.</db:para>
+</db:section>
+</db:section>
+<db:section xml:id="macro-documentation">
+<db:title>Macro Documentation</db:title>
+<db:section xml:id="QDOCTEST_MACRO">
+<db:title>QDOCTEST_MACRO</db:title>
+</db:section>
+</db:section>
+</db:article>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/testtemplate/expected/html/autolinking.html b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/testtemplate/expected/html/autolinking.html
new file mode 100644
index 000000000..689afdfb9
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/testtemplate/expected/html/autolinking.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+<!-- classlists.qdoc -->
+ <title>Autolinking | TestTemplate</title>
+</head>
+<body>
+<div class="sidebar">
+<div class="toc">
+<h3 id="toc">Contents</h3>
+<ul>
+<li class="level1"><a href="#testqdoc">TestQDoc</a></li>
+<li class="level1"><a href="#someprop">someProp</a></li>
+</ul>
+</div>
+<div class="sidebar-content" id="sidebar-content"></div></div>
+<h1 class="title">Autolinking</h1>
+<!-- $$$autolinking.html-description -->
+<div class="descr" id="details">
+<h2 id="testqdoc">TestQDoc</h2>
+<p>The string <a href="testqdoc.html" translate="no">TestQDoc</a> links to the C++ namespace unless linking explicitly, <a href="autolinking.html#testqdoc">like this</a>, or <a href="testqdoc.html" translate="no">this</a>. Also,</p>
+<p>Autolinks:</p>
+<ul>
+<li><a href="testqdoc-testderived.html" translate="no">TestQDoc::TestDerived</a></li>
+</ul>
+<p>Explicit links:</p>
+<ul>
+<li><a href="testqdoc-testderived.html" translate="no">TestQDoc::TestDerived</a></li>
+<li><a href="obsolete-classes.html#testqdoc">Obsolete Classes#TestQDoc</a></li>
+</ul>
+<h2 id="someprop">someProp</h2>
+</div>
+<!-- @@@autolinking.html -->
+</body>
+</html>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/testtemplate/expected/html/bar.html b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/testtemplate/expected/html/bar.html
new file mode 100644
index 000000000..3ce7052c1
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/testtemplate/expected/html/bar.html
@@ -0,0 +1,38 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+<!-- testtemplate.cpp -->
+ <meta name="description" content="Another class template.">
+ <title>Bar Class | TestTemplate</title>
+</head>
+<body>
+<li><a href="testcpp-module.html" translate="no">C++ Classes</a></li>
+<li>Bar</li>
+<div class="sidebar">
+<div class="toc">
+<h3 id="toc">Contents</h3>
+<ul>
+<li class="level1"><a href="#details">Detailed Description</a></li>
+</ul>
+</div>
+<div class="sidebar-content" id="sidebar-content"></div></div>
+<h1 class="title" translate="no">Bar Class</h1>
+<span class="small-subtitle" translate="no">template &lt;typename T, typename D&gt; class Bar</span>
+<!-- $$$Bar-brief -->
+<p>Another class template. <a href="#details">More...</a></p>
+<!-- @@@Bar -->
+<div class="table"><table class="alignedsummary" translate="no">
+<tr><td class="memItemLeft rightAlign topAlign"> Header:</td><td class="memItemRight bottomAlign"> <span class="preprocessor">#include &lt;Bar&gt;</span></td></tr>
+<tr><td class="memItemLeft rightAlign topAlign"> CMake:</td><td class="memItemRight bottomAlign"> find_package(Qt6 REQUIRED COMPONENTS QDocTest) <br/>
+target_link_libraries(mytarget PRIVATE Qt6::QDocTest)</td></tr>
+<tr><td class="memItemLeft rightAlign topAlign"> qmake:</td><td class="memItemRight bottomAlign"> QT += testcpp</td></tr>
+<tr><td class="memItemLeft rightAlign topAlign"> Since:</td><td class="memItemRight bottomAlign"> Qt 2.0</td></tr>
+</table></div>
+<!-- $$$Bar-description -->
+<div class="descr">
+<h2 id="details">Detailed Description</h2>
+</div>
+<!-- @@@Bar -->
+</body>
+</html>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/testtemplate/expected/html/baz.html b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/testtemplate/expected/html/baz.html
new file mode 100644
index 000000000..03738ff89
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/testtemplate/expected/html/baz.html
@@ -0,0 +1,38 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+<!-- testtemplate.cpp -->
+ <meta name="description" content="Class template template.">
+ <title>Baz Struct | TestTemplate</title>
+</head>
+<body>
+<li><a href="testcpp-module.html" translate="no">C++ Classes</a></li>
+<li>Baz</li>
+<div class="sidebar">
+<div class="toc">
+<h3 id="toc">Contents</h3>
+<ul>
+<li class="level1"><a href="#details">Detailed Description</a></li>
+</ul>
+</div>
+<div class="sidebar-content" id="sidebar-content"></div></div>
+<h1 class="title" translate="no">Baz Struct</h1>
+<span class="small-subtitle" translate="no">template &lt;template &lt;typename&gt; typename X, typename Y&gt; struct Baz</span>
+<!-- $$$Baz-brief -->
+<p>Class template template. <a href="#details">More...</a></p>
+<!-- @@@Baz -->
+<div class="table"><table class="alignedsummary" translate="no">
+<tr><td class="memItemLeft rightAlign topAlign"> Header:</td><td class="memItemRight bottomAlign"> <span class="preprocessor">#include &lt;Baz&gt;</span></td></tr>
+<tr><td class="memItemLeft rightAlign topAlign"> CMake:</td><td class="memItemRight bottomAlign"> find_package(Qt6 REQUIRED COMPONENTS QDocTest) <br/>
+target_link_libraries(mytarget PRIVATE Qt6::QDocTest)</td></tr>
+<tr><td class="memItemLeft rightAlign topAlign"> qmake:</td><td class="memItemRight bottomAlign"> QT += testcpp</td></tr>
+<tr><td class="memItemLeft rightAlign topAlign"> Since:</td><td class="memItemRight bottomAlign"> Qt 2.0</td></tr>
+</table></div>
+<!-- $$$Baz-description -->
+<div class="descr">
+<h2 id="details">Detailed Description</h2>
+</div>
+<!-- @@@Baz -->
+</body>
+</html>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/testtemplate/expected/html/cpptypes.html b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/testtemplate/expected/html/cpptypes.html
new file mode 100644
index 000000000..f33885f8f
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/testtemplate/expected/html/cpptypes.html
@@ -0,0 +1,24 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+<!-- classlists.qdoc -->
+ <title>Test C++ Types | TestTemplate</title>
+</head>
+<body>
+<div class="sidebar"><div class="sidebar-content" id="sidebar-content"></div></div>
+<h1 class="title">Test C++ Types</h1>
+<!-- $$$cpptypes-description -->
+<div class="descr" id="details">
+<ul>
+<li translate="no"><a href="testqdoc-test.html">TestQDoc::Test</a></li>
+<li translate="no"><a href="testqdoc-test.html#QDOCTEST_MACRO2">TestQDoc::Test::QDOCTEST_MACRO2</a></li>
+<li translate="no"><a href="testqdoc-test.html#someFunctionDefaultArg">TestQDoc::Test::someFunctionDefaultArg()</a></li>
+</ul>
+</div>
+<!-- @@@cpptypes -->
+<div class="table"><table class="annotated">
+<tr class="odd topAlign"><td class="tblName" translate="no"><p><a href="testqdoc-test.html">TestQDoc::Test</a></p></td><td class="tblDescr"><p>A class in a namespace</p></td></tr>
+</table></div>
+</body>
+</html>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/testtemplate/expected/html/crossmoduleref.html b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/testtemplate/expected/html/crossmoduleref.html
new file mode 100644
index 000000000..551b86690
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/testtemplate/expected/html/crossmoduleref.html
@@ -0,0 +1,47 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+<!-- testcpp.cpp -->
+ <meta name="description" content="Namespace that has documented functions in multiple modules.">
+ <title>CrossModuleRef Namespace | TestTemplate</title>
+</head>
+<body>
+<div class="sidebar">
+<div class="toc">
+<h3 id="toc">Contents</h3>
+<ul>
+<li class="level1"><a href="#functions">Functions</a></li>
+<li class="level1"><a href="#details">Detailed Description</a></li>
+</ul>
+</div>
+<div class="sidebar-content" id="sidebar-content"></div></div>
+<h1 class="title" translate="no">CrossModuleRef Namespace</h1>
+<!-- $$$CrossModuleRef-brief -->
+<p>Namespace that has documented functions in multiple modules. <a href="#details">More...</a></p>
+<!-- @@@CrossModuleRef -->
+<div class="table"><table class="alignedsummary" translate="no">
+<tr><td class="memItemLeft rightAlign topAlign"> Header:</td><td class="memItemRight bottomAlign"> <span class="preprocessor">#include &lt;CrossModuleRef&gt;</span></td></tr>
+<tr><td class="memItemLeft rightAlign topAlign"> CMake:</td><td class="memItemRight bottomAlign"> find_package(Qt6 REQUIRED COMPONENTS QDocTest) <br/>
+target_link_libraries(mytarget PRIVATE Qt6::QDocTest)</td></tr>
+<tr><td class="memItemLeft rightAlign topAlign"> qmake:</td><td class="memItemRight bottomAlign"> QT += testcpp</td></tr>
+<tr><td class="memItemLeft rightAlign topAlign"> Since:</td><td class="memItemRight bottomAlign"> Qt 3.0</td></tr>
+</table></div>
+<h2 id="functions">Functions</h2>
+<div class="table"><table class="alignedsummary" translate="no">
+<tr><td class="memItemLeft rightAlign topAlign"> void </td><td class="memItemRight bottomAlign"><b><a href="crossmoduleref.html#documentMe" translate="no">documentMe</a></b>()</td></tr>
+</table></div>
+<!-- $$$CrossModuleRef-description -->
+<div class="descr">
+<h2 id="details">Detailed Description</h2>
+</div>
+<!-- @@@CrossModuleRef -->
+<div class="func">
+<h2>Function Documentation</h2>
+<!-- $$$documentMe[overload1]$$$documentMe -->
+<h3 class="fn" translate="no" id="documentMe"><span class="type">void</span> CrossModuleRef::<span class="name">documentMe</span>()</h3>
+<p>Document me!</p>
+<!-- @@@documentMe -->
+</div>
+</body>
+</html>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/testtemplate/expected/html/foo.html b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/testtemplate/expected/html/foo.html
new file mode 100644
index 000000000..56e5bdd34
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/testtemplate/expected/html/foo.html
@@ -0,0 +1,38 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+<!-- testtemplate.cpp -->
+ <meta name="description" content="Class template.">
+ <title>Foo Class | TestTemplate</title>
+</head>
+<body>
+<li><a href="testcpp-module.html" translate="no">C++ Classes</a></li>
+<li>Foo</li>
+<div class="sidebar">
+<div class="toc">
+<h3 id="toc">Contents</h3>
+<ul>
+<li class="level1"><a href="#details">Detailed Description</a></li>
+</ul>
+</div>
+<div class="sidebar-content" id="sidebar-content"></div></div>
+<h1 class="title" translate="no">Foo Class</h1>
+<span class="small-subtitle" translate="no">template &lt;typename T&gt; class Foo</span>
+<!-- $$$Foo-brief -->
+<p>Class template. <a href="#details">More...</a></p>
+<!-- @@@Foo -->
+<div class="table"><table class="alignedsummary" translate="no">
+<tr><td class="memItemLeft rightAlign topAlign"> Header:</td><td class="memItemRight bottomAlign"> <span class="preprocessor">#include &lt;Foo&gt;</span></td></tr>
+<tr><td class="memItemLeft rightAlign topAlign"> CMake:</td><td class="memItemRight bottomAlign"> find_package(Qt6 REQUIRED COMPONENTS QDocTest) <br/>
+target_link_libraries(mytarget PRIVATE Qt6::QDocTest)</td></tr>
+<tr><td class="memItemLeft rightAlign topAlign"> qmake:</td><td class="memItemRight bottomAlign"> QT += testcpp</td></tr>
+<tr><td class="memItemLeft rightAlign topAlign"> Since:</td><td class="memItemRight bottomAlign"> Qt 2.0</td></tr>
+</table></div>
+<!-- $$$Foo-description -->
+<div class="descr">
+<h2 id="details">Detailed Description</h2>
+</div>
+<!-- @@@Foo -->
+</body>
+</html>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/testtemplate/expected/html/obsolete-classes.html b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/testtemplate/expected/html/obsolete-classes.html
new file mode 100644
index 000000000..28e5842e7
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/testtemplate/expected/html/obsolete-classes.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+<!-- classlists.qdoc -->
+ <title>Obsolete Classes | TestTemplate</title>
+</head>
+<body>
+<div class="sidebar">
+<div class="toc">
+<h3 id="toc">Contents</h3>
+<ul>
+<li class="level1"><a href="#classes-with-obsolete-members">Classes with obsolete members</a></li>
+<li class="level2"><a href="#testqdoc">TestQDoc</a></li>
+</ul>
+</div>
+<div class="sidebar-content" id="sidebar-content"></div></div>
+<h1 class="title">Obsolete Classes</h1>
+<!-- $$$obsolete-classes.html-description -->
+<div class="descr" id="details">
+<h2 id="classes-with-obsolete-members">Classes with obsolete members</h2>
+<div class="flowListDiv" translate="no">
+<dl class="flowList odd"><dt class="alphaChar"><b>T</b></dt>
+<dd><a href="testqdoc-test-obsolete.html">Test</a> (<a href="testqdoc.html">TestQDoc</a>)</dd>
+<dd><a href="testqdoc-testderived-obsolete.html">TestDerived</a> (<a href="testqdoc.html">TestQDoc</a>)</dd>
+</dl>
+</div>
+<h3 id="testqdoc">TestQDoc</h3>
+</div>
+<!-- @@@obsolete-classes.html -->
+</body>
+</html>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/testtemplate/expected/html/testcpp-module.html b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/testtemplate/expected/html/testcpp-module.html
new file mode 100644
index 000000000..8734780e6
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/testtemplate/expected/html/testcpp-module.html
@@ -0,0 +1,60 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+<!-- testcpp.cpp -->
+ <meta name="description" content="A test module page.">
+ <title>QDoc Test C++ Classes | TestTemplate</title>
+</head>
+<body>
+<div class="sidebar">
+<div class="toc">
+<h3 id="toc">Contents</h3>
+<ul>
+<li class="level1"><a href="#namespaces">Namespaces</a></li>
+<li class="level1"><a href="#classes">Classes</a></li>
+<li class="level1"><a href="#details">Detailed Description</a></li>
+<li class="level2"><a href="#linking-to-function-like-things">Linking to function-like things</a></li>
+<li class="level3"><a href="#section">section()</a></li>
+</ul>
+</div>
+<div class="sidebar-content" id="sidebar-content"></div></div>
+<h1 class="title" translate="no">QDoc Test C++ Classes</h1>
+<!-- $$$TestCPP-brief -->
+<p>A test module page. <a href="#details">More...</a></p>
+<!-- @@@TestCPP -->
+<p>This module was introduced in Qt 2.0.</p>
+<h2 id="namespaces">Namespaces</h2>
+<div class="table"><table class="annotated">
+<tr class="odd topAlign"><td class="tblName" translate="no"><p><a href="crossmoduleref.html">CrossModuleRef</a></p></td><td class="tblDescr"><p>Namespace that has documented functions in multiple modules</p></td></tr>
+<tr class="even topAlign"><td class="tblName" translate="no"><p><a href="testqdoc.html">TestQDoc</a></p></td><td class="tblDescr"><p>A namespace</p></td></tr>
+</table></div>
+<h2 id="classes">Classes</h2>
+<div class="table"><table class="annotated">
+<tr class="odd topAlign"><td class="tblName" translate="no"><p><a href="bar.html">Bar</a></p></td><td class="tblDescr"><p>Another class template</p></td></tr>
+<tr class="even topAlign"><td class="tblName" translate="no"><p><a href="baz.html">Baz</a></p></td><td class="tblDescr"><p>Class template template</p></td></tr>
+<tr class="odd topAlign"><td class="tblName" translate="no"><p><a href="foo.html">Foo</a></p></td><td class="tblDescr"><p>Class template</p></td></tr>
+<tr class="even topAlign"><td class="tblName" translate="no"><p><a href="testqdoc-test.html">TestQDoc::Test</a></p></td><td class="tblDescr"><p>A class in a namespace</p></td></tr>
+<tr class="odd topAlign"><td class="tblName" translate="no"><p><a href="testqdoc-test-struct.html">TestQDoc::Test::Struct</a></p></td><td class="tblDescr"><p>Templated struct</p></td></tr>
+<tr class="even topAlign"><td class="tblName" translate="no"><p><a href="testqdoc-testderived.html">TestQDoc::TestDerived</a></p></td><td class="tblDescr"><p>A class in a namespace, derived from Test</p></td></tr>
+<tr class="odd topAlign"><td class="tblName" translate="no"><p><a href="testqdoc-vec.html">TestQDoc::Vec</a></p></td><td class="tblDescr"><p>Type alias that has its own reference</p></td></tr>
+</table></div>
+<!-- $$$TestCPP-description -->
+<div class="descr">
+<h2 id="details">Detailed Description</h2>
+<div class="admonition note">
+<p><b>Note: </b>This is just a test. /* Look, Ma! {I'm made of arguments!} */</p>
+</div>
+<h3 id="linking-to-function-like-things">Linking to function-like things</h3>
+<ul>
+<li><a href="testqdoc-test.html#someFunctionDefaultArg" translate="no">someFunctionDefaultArg</a>()</li>
+<li><a href="testqdoc-test.html#QDOCTEST_MACRO2" translate="no">QDOCTEST_MACRO2</a>()</li>
+<li><a href="testqdoc-test.html#QDOCTEST_MACRO2" translate="no">QDOCTEST_MACRO2</a>(int &amp;x)</li>
+<li><a href="testcpp-module.html#section" translate="no">section()</a></li>
+<li><a href="testcpp-module.html#section" translate="no">section() is a section title</a></li>
+</ul>
+<h4 id="section">section()</h4>
+</div>
+<!-- @@@TestCPP -->
+</body>
+</html>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/testtemplate/expected/html/testqdoc-test-members.html b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/testtemplate/expected/html/testqdoc-test-members.html
new file mode 100644
index 000000000..2bd243006
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/testtemplate/expected/html/testqdoc-test-members.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+<!-- testcpp.cpp -->
+ <meta name="description" content="A class in a namespace.">
+ <title>List of All Members for Test | TestTemplate</title>
+</head>
+<body>
+<li><a href="testcpp-module.html" translate="no">C++ Classes</a></li>
+<li>Test</li>
+<div class="sidebar"><div class="sidebar-content" id="sidebar-content"></div></div>
+<h1 class="title" translate="no">List of All Members for Test</h1>
+<p>This is the complete list of members for <a href="testqdoc-test.html">TestQDoc::Test</a>, including inherited members.</p>
+<ul>
+<li class="fn" translate="no">struct <span class="name"><b><a href="testqdoc-test-struct.html" translate="no">Struct</a></b></span></li>
+<li class="fn" translate="no"><span class="name"><b><a href="testqdoc-test.html#SomeType-typedef" translate="no">SomeType</a></b></span></li>
+<li class="fn" translate="no"><span class="name"><b><a href="testqdoc-test.html#Specialized-typedef" translate="no">Specialized</a></b></span></li>
+<li class="fn" translate="no"><span class="name"><b><a href="testqdoc-test.html#funcPtr" translate="no">funcPtr</a></b></span>(bool, const char *) : void (*)(bool)</li>
+<li class="fn" translate="no"><span class="name"><b><a href="testqdoc-test.html#funcTemplate" translate="no">funcTemplate</a></b></span>(T1, T2)</li>
+<li class="fn" translate="no"><span class="name"><b><a href="testqdoc-test.html#inlineFunction" translate="no">inlineFunction</a></b></span>()</li>
+<li class="fn" translate="no"><span class="name"><b><a href="testqdoc-test.html#methodWithEmDashInItsDocs" translate="no">methodWithEmDashInItsDocs</a></b></span>()</li>
+<li class="fn" translate="no"><span class="name"><b><a href="testqdoc-test.html#methodWithEnDashInItsDocs" translate="no">methodWithEnDashInItsDocs</a></b></span>()</li>
+<li class="fn" translate="no"><span class="name"><b><a href="testqdoc-test.html#overload" translate="no">overload</a></b></span>()</li>
+<li class="fn" translate="no"><span class="name"><b><a href="testqdoc-test.html#overload-1" translate="no">overload</a></b></span>(bool)</li>
+<li class="fn" translate="no"><span class="name"><b><a href="testqdoc-test.html#someFunction" translate="no">someFunction</a></b></span>(int, int) : int</li>
+<li class="fn" translate="no"><span class="name"><b><a href="testqdoc-test.html#someFunctionDefaultArg" translate="no">someFunctionDefaultArg</a></b></span>(int, bool) const</li>
+<li class="fn" translate="no"><span class="name"><b><a href="testqdoc-test.html#virtualFun" translate="no">virtualFun</a></b></span>()</li>
+</ul>
+</body>
+</html>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/testtemplate/expected/html/testqdoc-test-obsolete.html b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/testtemplate/expected/html/testqdoc-test-obsolete.html
new file mode 100644
index 000000000..e70edce76
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/testtemplate/expected/html/testqdoc-test-obsolete.html
@@ -0,0 +1,45 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+<!-- testcpp.cpp -->
+ <meta name="description" content="A class in a namespace.">
+ <title>Obsolete Members for Test | TestTemplate</title>
+</head>
+<body>
+<li><a href="testcpp-module.html" translate="no">C++ Classes</a></li>
+<li>Test</li>
+<div class="sidebar"><div class="sidebar-content" id="sidebar-content"></div></div>
+<h1 class="title" translate="no">Obsolete Members for Test</h1>
+<p><b>The following members of class <a href="testqdoc-test.html" translate="no">Test</a> are deprecated.</b> They are provided to keep old source code working. We strongly advise against using them in new code.</p>
+<h2>Public Functions</h2>
+<div class="table"><table class="alignedsummary" translate="no">
+<tr><td class="memItemLeft topAlign rightAlign"> <code class="summary extra" translate="no">(deprecated)</code> void </td><td class="memItemRight bottomAlign"><b><a href="testqdoc-test-obsolete.html#anotherObsoleteMember" translate="no">anotherObsoleteMember</a></b>()</td></tr>
+<tr><td class="memItemLeft topAlign rightAlign"> <code class="summary extra" translate="no">(deprecated in 6.0)</code> void </td><td class="memItemRight bottomAlign"><b><a href="testqdoc-test-obsolete.html#deprecatedMember" translate="no">deprecatedMember</a></b>()</td></tr>
+<tr><td class="memItemLeft topAlign rightAlign"> <code class="summary extra" translate="no">(deprecated)</code> void </td><td class="memItemRight bottomAlign"><b><a href="testqdoc-test-obsolete.html#obsoleteMember" translate="no">obsoleteMember</a></b>()</td></tr>
+<tr><td class="memItemLeft topAlign rightAlign"> <code class="summary extra" translate="no">(deprecated)</code> TestQDoc::Test &amp;</td><td class="memItemRight bottomAlign"><b><a href="testqdoc-test-obsolete.html#operator-2b-2b" translate="no">operator++</a></b>()</td></tr>
+<tr><td class="memItemLeft topAlign rightAlign"> <code class="summary extra" translate="no">(deprecated)</code> TestQDoc::Test &amp;</td><td class="memItemRight bottomAlign"><b><a href="testqdoc-test-obsolete.html#operator--" translate="no">operator--</a></b>()</td></tr>
+</table></div>
+<h2>Member Function Documentation</h2>
+<!-- $$$ -->
+<div class="fngroup">
+<h3 class="fn fngroupitem" translate="no" id="operator-2b-2b"><code class="details extra" translate="no">[deprecated]</code> <span class="type"><a href="testqdoc-test.html" translate="no">TestQDoc::Test</a></span> &amp;Test::<span class="name">operator++</span>()</h3><h3 class="fn fngroupitem" translate="no" id="operator--"><code class="details extra" translate="no">[deprecated]</code> <span class="type"><a href="testqdoc-test.html" translate="no">TestQDoc::Test</a></span> &amp;Test::<span class="name">operator--</span>()</h3></div>
+<p>This function is deprecated. We strongly advise against using it in new code.</p>
+<!-- @@@ -->
+<!-- $$$anotherObsoleteMember[overload1]$$$anotherObsoleteMember -->
+<h3 class="fn" translate="no" id="anotherObsoleteMember"><code class="details extra" translate="no">[deprecated]</code> <span class="type">void</span> Test::<span class="name">anotherObsoleteMember</span>()</h3>
+<p>This function is deprecated. We strongly advise against using it in new code.</p>
+<p>Use <a href="testqdoc-test-obsolete.html#obsoleteMember" translate="no">obsoleteMember</a>() instead.</p>
+<!-- @@@anotherObsoleteMember -->
+<!-- $$$deprecatedMember[overload1]$$$deprecatedMember -->
+<h3 class="fn" translate="no" id="deprecatedMember"><code class="details extra" translate="no">[deprecated in 6.0]</code> <span class="type">void</span> Test::<span class="name">deprecatedMember</span>()</h3>
+<p>This function is deprecated since 6.0. We strongly advise against using it in new code.</p>
+<p>Use <a href="testqdoc-test.html#someFunction" translate="no">someFunction</a>() instead.</p>
+<!-- @@@deprecatedMember -->
+<!-- $$$obsoleteMember[overload1]$$$obsoleteMember -->
+<h3 class="fn" translate="no" id="obsoleteMember"><code class="details extra" translate="no">[deprecated]</code> <span class="type">void</span> Test::<span class="name">obsoleteMember</span>()</h3>
+<p>This function is deprecated. We strongly advise against using it in new code.</p>
+<p>Use <a href="testqdoc-test.html#someFunction" translate="no">someFunction</a>() instead.</p>
+<!-- @@@obsoleteMember -->
+</body>
+</html>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/testtemplate/expected/html/testqdoc-test-struct.html b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/testtemplate/expected/html/testqdoc-test-struct.html
new file mode 100644
index 000000000..442f1341c
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/testtemplate/expected/html/testqdoc-test-struct.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+<!-- testcpp.cpp -->
+ <meta name="description" content="Templated struct.">
+ <title>Struct Struct | TestTemplate</title>
+</head>
+<body>
+<li><a href="testcpp-module.html" translate="no">C++ Classes</a></li>
+<li>Struct</li>
+<div class="sidebar">
+<div class="toc">
+<h3 id="toc">Contents</h3>
+<ul>
+<li class="level1"><a href="#details">Detailed Description</a></li>
+</ul>
+</div>
+<div class="sidebar-content" id="sidebar-content"></div></div>
+<h1 class="title" translate="no">Struct Struct</h1>
+<span class="small-subtitle" translate="no">template &lt;typename D, typename T&gt; struct <a href="testqdoc.html" translate="no">TestQDoc</a>::<a href="testqdoc-test.html" translate="no">Test</a>::Struct</span>
+<!-- $$$Struct-brief -->
+<p>Templated struct. <a href="#details">More...</a></p>
+<!-- @@@Struct -->
+<p>This struct was introduced in Qt 2.0.</p>
+<!-- $$$Struct-description -->
+<div class="descr">
+<h2 id="details">Detailed Description</h2>
+</div>
+<!-- @@@Struct -->
+</body>
+</html>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/testtemplate/expected/html/testqdoc-test.html b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/testtemplate/expected/html/testqdoc-test.html
new file mode 100644
index 000000000..0f25a69d4
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/testtemplate/expected/html/testqdoc-test.html
@@ -0,0 +1,167 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+<!-- testcpp.cpp -->
+ <meta name="description" content="A class in a namespace.">
+ <title>Test Class | TestTemplate</title>
+</head>
+<body>
+<li><a href="testcpp-module.html" translate="no">C++ Classes</a></li>
+<li>Test</li>
+<div class="sidebar">
+<div class="toc">
+<h3 id="toc">Contents</h3>
+<ul>
+<li class="level1"><a href="#public-types">Public Types</a></li>
+<li class="level1"><a href="#public-functions">Public Functions</a></li>
+<li class="level1"><a href="#protected-functions">Protected Functions</a></li>
+<li class="level1"><a href="#related-non-members">Related Non-Members</a></li>
+<li class="level1"><a href="#macros">Macros</a></li>
+<li class="level1"><a href="#details">Detailed Description</a></li>
+</ul>
+</div>
+<div class="sidebar-content" id="sidebar-content"></div></div>
+<h1 class="title" translate="no">Test Class</h1>
+<span class="small-subtitle" translate="no">class <a href="testqdoc.html" translate="no">TestQDoc</a>::Test</span>
+<!-- $$$Test-brief -->
+<p>A class in a namespace. <a href="#details">More...</a></p>
+<!-- @@@Test -->
+<div class="table"><table class="alignedsummary" translate="no">
+<tr><td class="memItemLeft rightAlign topAlign"> Header:</td><td class="memItemRight bottomAlign"> <span class="preprocessor">#include &lt;Test&gt;</span></td></tr>
+<tr><td class="memItemLeft rightAlign topAlign"> CMake:</td><td class="memItemRight bottomAlign"> find_package(Qt6 REQUIRED COMPONENTS QDocTest) <br/>
+target_link_libraries(mytarget PRIVATE Qt6::QDocTest)</td></tr>
+<tr><td class="memItemLeft rightAlign topAlign"> qmake:</td><td class="memItemRight bottomAlign"> QT += testcpp</td></tr>
+<tr><td class="memItemLeft rightAlign topAlign"> Since:</td><td class="memItemRight bottomAlign"> Qt 2.0</td></tr>
+<tr><td class="memItemLeft rightAlign topAlign"> Inherited By:</td><td class="memItemRight bottomAlign"> <p><a href="testqdoc-testderived.html" translate="no">TestQDoc::TestDerived</a></p>
+</td></tr>
+</table></div>
+<ul>
+<li><a href="testqdoc-test-members.html">List of all members, including inherited members</a></li>
+<li><a href="testqdoc-test-obsolete.html">Deprecated members</a></li>
+<li>Test is part of <a href="cpptypes.html">Test C++ Types</a>.</li>
+</ul>
+<p><b>Note:</b> All functions in this class are <a href="https://doc.qt.io/qt/17-qdoc-commands-thread.html#reentrant-command">reentrant</a> with the following exceptions:</p>
+<ul>
+<li><a href="testqdoc-test.html#someFunctionDefaultArg" translate="no">someFunctionDefaultArg</a>(int i, bool b) const</li>
+</ul>
+<h2 id="public-types">Public Types</h2>
+<div class="table"><table class="alignedsummary" translate="no">
+<tr><td class="memItemLeft rightAlign topAlign"> <code class="summary extra" translate="no">(since 2.0)</code> struct </td><td class="memItemRight bottomAlign"><b><a href="testqdoc-test-struct.html" translate="no">Struct</a></b></td></tr>
+<tr><td class="memItemLeft rightAlign topAlign"> </td><td class="memItemRight bottomAlign"><b><a href="testqdoc-test.html#SomeType-typedef" translate="no">SomeType</a></b></td></tr>
+<tr><td class="memItemLeft rightAlign topAlign"> </td><td class="memItemRight bottomAlign"><b><a href="testqdoc-test.html#Specialized-typedef" translate="no">Specialized</a></b></td></tr>
+</table></div>
+<h2 id="public-functions">Public Functions</h2>
+<div class="table"><table class="alignedsummary" translate="no">
+<tr><td class="memItemLeft rightAlign topAlign"> void (*)(bool) </td><td class="memItemRight bottomAlign"><b><a href="testqdoc-test.html#funcPtr" translate="no">funcPtr</a></b>(bool <i>b</i>, const char *<i>s</i>)</td></tr>
+<tr><td class="memItemLeft rightAlign topAlign"> void </td><td class="memItemRight bottomAlign"><b><a href="testqdoc-test.html#inlineFunction" translate="no">inlineFunction</a></b>()</td></tr>
+<tr><td class="memItemLeft rightAlign topAlign"> void </td><td class="memItemRight bottomAlign"><b><a href="testqdoc-test.html#methodWithEmDashInItsDocs" translate="no">methodWithEmDashInItsDocs</a></b>()</td></tr>
+<tr><td class="memItemLeft rightAlign topAlign"> void </td><td class="memItemRight bottomAlign"><b><a href="testqdoc-test.html#methodWithEnDashInItsDocs" translate="no">methodWithEnDashInItsDocs</a></b>()</td></tr>
+<tr><td class="memItemLeft rightAlign topAlign"> int </td><td class="memItemRight bottomAlign"><b><a href="testqdoc-test.html#someFunction" translate="no">someFunction</a></b>(int, int <i>v</i> = 0)</td></tr>
+<tr><td class="memItemLeft rightAlign topAlign"> void </td><td class="memItemRight bottomAlign"><b><a href="testqdoc-test.html#someFunctionDefaultArg" translate="no">someFunctionDefaultArg</a></b>(int <i>i</i>, bool <i>b</i> = false) const</td></tr>
+<tr><td class="memItemLeft rightAlign topAlign"> virtual void </td><td class="memItemRight bottomAlign"><b><a href="testqdoc-test.html#virtualFun" translate="no">virtualFun</a></b>()</td></tr>
+</table></div>
+<h2 id="protected-functions">Protected Functions</h2>
+<div class="table"><table class="alignedsummary" translate="no">
+<tr><td class="memItemLeft rightAlign topAlign"> void </td><td class="memItemRight bottomAlign"><b><a href="testqdoc-test.html#funcTemplate" translate="no">funcTemplate</a></b>(T1 <i>a</i>, T2 <i>b</i>)</td></tr>
+<tr><td class="memItemLeft rightAlign topAlign"> void </td><td class="memItemRight bottomAlign"><b><a href="testqdoc-test.html#overload" translate="no">overload</a></b>()</td></tr>
+<tr><td class="memItemLeft rightAlign topAlign"> <code class="summary extra" translate="no">(since Test 1.2)</code> void </td><td class="memItemRight bottomAlign"><b><a href="testqdoc-test.html#overload-1" translate="no">overload</a></b>(bool <i>b</i>)</td></tr>
+</table></div>
+<h2 id="related-non-members">Related Non-Members</h2>
+<div class="table"><table class="alignedsummary" translate="no">
+<tr><td class="memItemLeft rightAlign topAlign"> bool </td><td class="memItemRight bottomAlign"><b><a href="testqdoc-test.html#operator-eq-eq" translate="no">operator==</a></b>(const TestQDoc::Test &amp;<i>lhs</i>, const TestQDoc::Test &amp;<i>rhs</i>)</td></tr>
+</table></div>
+<h2 id="macros">Macros</h2>
+<div class="table"><table class="alignedsummary" translate="no">
+<tr><td class="memItemLeft rightAlign topAlign"> <code class="summary extra" translate="no">(since Test 1.1)</code> </td><td class="memItemRight bottomAlign"><b><a href="testqdoc-test.html#QDOCTEST_MACRO2" translate="no">QDOCTEST_MACRO2</a></b>(int &amp;<i>x</i>)</td></tr>
+<tr><td class="memItemLeft rightAlign topAlign"> </td><td class="memItemRight bottomAlign"><b><a href="testqdoc-test.html#Q_INVOKABLE" translate="no">Q_INVOKABLE</a></b></td></tr>
+</table></div>
+<!-- $$$Test-description -->
+<div class="descr">
+<h2 id="details">Detailed Description</h2>
+</div>
+<!-- @@@Test -->
+<div class="types">
+<h2>Member Type Documentation</h2>
+<!-- $$$SomeType -->
+<h3 class="fn" translate="no" id="SomeType-typedef">Test::<span class="name">SomeType</span></h3>
+<p>A typedef.</p>
+<!-- @@@SomeType -->
+<!-- $$$Specialized -->
+<h3 class="fn" translate="no" id="Specialized-typedef"><code class="details extra" translate="no">[alias]</code> template &lt;typename T&gt; Test::<span class="name">Specialized</span></h3>
+<!-- @@@Specialized -->
+</div>
+<div class="func">
+<h2>Member Function Documentation</h2>
+<!-- $$$ -->
+<div class="fngroup">
+<h3 class="fn fngroupitem" translate="no" id="overload"><code class="details extra" translate="no">[protected]</code> <span class="type">void</span> Test::<span class="name">overload</span>()</h3><h3 class="fn fngroupitem" translate="no" id="overload-1"><code class="details extra" translate="no">[protected, since Test 1.2]</code> <span class="type">void</span> Test::<span class="name">overload</span>(<span class="type">bool</span> <i>b</i>)</h3></div>
+<p>Overloads that share a documentation comment, optionally taking a parameter <i translate="no">b</i>.</p>
+<!-- @@@ -->
+<!-- $$$funcPtr[overload1]$$$funcPtrboolconstchar* -->
+<h3 class="fn" translate="no" id="funcPtr"><span class="type">void</span> (*)(<span class="type">bool</span>) Test::<span class="name">funcPtr</span>(<span class="type">bool</span> <i>b</i>, const <span class="type">char</span> *<i>s</i>)</h3>
+<p>Returns a pointer to a function that takes a boolean. Uses <i translate="no">b</i> and <i translate="no">s</i>.</p>
+<!-- @@@funcPtr -->
+<!-- $$$funcTemplate[overload1]$$$funcTemplateT1T2 -->
+<h3 class="fn" translate="no" id="funcTemplate"><code class="details extra" translate="no">[protected]</code> template &lt;typename T1, typename T2&gt; <span class="type">void</span> Test::<span class="name">funcTemplate</span>(<span class="type">T1</span> <i>a</i>, <span class="type">T2</span> <i>b</i>)</h3>
+<p>Function template with two parameters, <i translate="no">a</i> and <i translate="no">b</i>.</p>
+<!-- @@@funcTemplate -->
+<!-- $$$inlineFunction[overload1]$$$inlineFunction -->
+<h3 class="fn" translate="no" id="inlineFunction"><span class="type">void</span> Test::<span class="name">inlineFunction</span>()</h3>
+<p>An inline function, documented using the \fn QDoc command.</p>
+<!-- @@@inlineFunction -->
+<!-- $$$methodWithEmDashInItsDocs[overload1]$$$methodWithEmDashInItsDocs -->
+<h3 class="fn" translate="no" id="methodWithEmDashInItsDocs"><span class="type">void</span> Test::<span class="name">methodWithEmDashInItsDocs</span>()</h3>
+<p>This method has em dashes in its documentation&mdash;as you'll find represented by <code translate="no">---</code> in the sources&mdash;here and there. The important bit to note is that when passed e.g. to the \c command, the three hyphens are processed as input to the command and not replaced by an em dash.</p>
+<p>-----------------------------------------------------------------------</p>
+<p>People can still add a bunch of dashes, though, without QDoc replacing them all with a series of em dashes.</p>
+<p>&mdash;You can also start a new paragraph with an em dash, if you want to.</p>
+<p><b>See also </b><a href="testqdoc-test.html#methodWithEnDashInItsDocs" translate="no">methodWithEnDashInItsDocs</a>.</p>
+<!-- @@@methodWithEmDashInItsDocs -->
+<!-- $$$methodWithEnDashInItsDocs[overload1]$$$methodWithEnDashInItsDocs -->
+<h3 class="fn" translate="no" id="methodWithEnDashInItsDocs"><span class="type">void</span> Test::<span class="name">methodWithEnDashInItsDocs</span>()</h3>
+<p>This method has en dashes in its documentation &ndash; as you'll find represented by <code translate="no">--</code> in the sources &ndash; here and there. The important bit to note is that when passed e.g. to the \c command, the two hyphens are processed as input to the command and not replaced by an en dash. This also applies to code blocks, where otherwise, the decrement operator would get completely borked:</p>
+<pre class="cpp" translate="no"><span class="keyword">for</span> (<span class="type">int</span> i <span class="operator">=</span> <span class="number">42</span>; i <span class="operator">&gt;</span> <span class="number">0</span>; <span class="operator">-</span><span class="operator">-</span>i)
+ <span class="comment">// Do something cool during countdown.</span></pre>
+<p>...as it would be silly if this would output &ndash;i instead of <code translate="no">--i</code>.</p>
+<p>-----------------------------------------------------------------------</p>
+<p>It still allows people to add a bunch of dashes, though, without replacing them all with a series of en dashes. Of course, they might want to use the \hr command instead, like this:</p>
+<hr />
+<p>&ndash; You can also start a new paragraph with an en dash, if you want to.</p>
+<p><b>See also </b>methodWithEnDashInItsDocs.</p>
+<!-- @@@methodWithEnDashInItsDocs -->
+<!-- $$$someFunction[overload1]$$$someFunctionintint -->
+<h3 class="fn" translate="no" id="someFunction"><span class="type">int</span> Test::<span class="name">someFunction</span>(<span class="type">int</span>, <span class="type">int</span> <i>v</i> = 0)</h3>
+<p>Function that takes a parameter <i translate="no">v</i>. Also returns the value of <i translate="no">v</i>.</p>
+<!-- @@@someFunction -->
+<!-- $$$someFunctionDefaultArg[overload1]$$$someFunctionDefaultArgintbool -->
+<h3 class="fn" translate="no" id="someFunctionDefaultArg"><span class="type">void</span> Test::<span class="name">someFunctionDefaultArg</span>(<span class="type">int</span> <i>i</i>, <span class="type">bool</span> <i>b</i> = false) const</h3>
+<p>Function that takes a parameter <i translate="no">i</i> and <i translate="no">b</i>.</p>
+<p><b>Warning:</b> This function is not <a href="https://doc.qt.io/qt/17-qdoc-commands-thread.html#reentrant-command">reentrant</a>.</p>
+<!-- @@@someFunctionDefaultArg -->
+<!-- $$$virtualFun[overload1]$$$virtualFun -->
+<h3 class="fn" translate="no" id="virtualFun"><code class="details extra" translate="no">[virtual]</code> <span class="type">void</span> Test::<span class="name">virtualFun</span>()</h3>
+<p>Function that must be reimplemented.</p>
+<!-- @@@virtualFun -->
+</div>
+<div class="relnonmem">
+<h2>Related Non-Members</h2>
+<!-- $$$operator==[overload1]$$$operator==constTestQDoc::Test&constTestQDoc::Test& -->
+<h3 class="fn" translate="no" id="operator-eq-eq"><span class="type">bool</span> <span class="name">operator==</span>(const <span class="type"><a href="testqdoc-test.html" translate="no">TestQDoc::Test</a></span> &amp;<i>lhs</i>, const <span class="type"><a href="testqdoc-test.html" translate="no">TestQDoc::Test</a></span> &amp;<i>rhs</i>)</h3>
+<p>Returns true if <i translate="no">lhs</i> and <i translate="no">rhs</i> are equal.</p>
+<!-- @@@operator== -->
+</div>
+<div class="macros">
+<h2>Macro Documentation</h2>
+<!-- $$$QDOCTEST_MACRO2[overload1]$$$QDOCTEST_MACRO2int& -->
+<h3 class="fn" translate="no" id="QDOCTEST_MACRO2"><code class="details extra" translate="no">[since Test 1.1]</code> <span class="name">QDOCTEST_MACRO2</span>(<span class="type">int</span> &amp;<i>x</i>)</h3>
+<p>A macro with argument <i translate="no">x</i>.</p>
+<p>This macro was introduced in Test 1.1.</p>
+<!-- @@@QDOCTEST_MACRO2 -->
+<!-- $$$Q_INVOKABLE[overload1]$$$Q_INVOKABLE -->
+<h3 class="fn" translate="no" id="Q_INVOKABLE"><span class="name">Q_INVOKABLE</span></h3>
+<p>This is a mock Q_INVOKABLE for the purpose of ensuring QDoc autolink to it as expected.</p>
+<!-- @@@Q_INVOKABLE -->
+</div>
+</body>
+</html>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/testtemplate/expected/html/testqdoc-testderived-members.html b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/testtemplate/expected/html/testqdoc-testderived-members.html
new file mode 100644
index 000000000..cd251011c
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/testtemplate/expected/html/testqdoc-testderived-members.html
@@ -0,0 +1,39 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+<!-- testcpp.cpp -->
+ <meta name="description" content="A class in a namespace, derived from Test.">
+ <title>List of All Members for TestDerived | TestTemplate</title>
+</head>
+<body>
+<li><a href="testcpp-module.html" translate="no">C++ Classes</a></li>
+<li>TestDerived</li>
+<div class="sidebar"><div class="sidebar-content" id="sidebar-content"></div></div>
+<h1 class="title" translate="no">List of All Members for TestDerived</h1>
+<p>This is the complete list of members for <a href="testqdoc-testderived.html">TestQDoc::TestDerived</a>, including inherited members.</p>
+<div class="table"><table class="propsummary" translate="no">
+<tr><td class="topAlign"><ul>
+<li class="fn" translate="no">struct <span class="name"><b><a href="testqdoc-test-struct.html" translate="no">Struct</a></b></span></li>
+<li class="fn" translate="no"><span class="name"><b><a href="testqdoc-testderived.html#DerivedType-typedef" translate="no">DerivedType</a></b></span></li>
+<li class="fn" translate="no"><span class="name"><b><a href="testqdoc-testderived.html#NotTypedef-typedef" translate="no">NotTypedef</a></b></span></li>
+<li class="fn" translate="no"><span class="name"><b><a href="testqdoc-test.html#SomeType-typedef" translate="no">SomeType</a></b></span></li>
+<li class="fn" translate="no"><span class="name"><b><a href="testqdoc-test.html#Specialized-typedef" translate="no">Specialized</a></b></span></li>
+<li class="fn" translate="no"><span class="name"><b><a href="testqdoc-test.html#funcPtr" translate="no">funcPtr</a></b></span>(bool, const char *) : void (*)(bool)</li>
+<li class="fn" translate="no"><span class="name"><b><a href="testqdoc-test.html#funcTemplate" translate="no">funcTemplate</a></b></span>(T1, T2)</li>
+<li class="fn" translate="no"><span class="name"><b><a href="testqdoc-test.html#inlineFunction" translate="no">inlineFunction</a></b></span>()</li>
+<li class="fn" translate="no"><span class="name"><b><a href="testqdoc-test.html#methodWithEmDashInItsDocs" translate="no">methodWithEmDashInItsDocs</a></b></span>()</li>
+</ul></td><td class="topAlign"><ul>
+<li class="fn" translate="no"><span class="name"><b><a href="testqdoc-test.html#methodWithEnDashInItsDocs" translate="no">methodWithEnDashInItsDocs</a></b></span>()</li>
+<li class="fn" translate="no"><span class="name"><b><a href="testqdoc-test.html#overload" translate="no">overload</a></b></span>()</li>
+<li class="fn" translate="no"><span class="name"><b><a href="testqdoc-test.html#overload-1" translate="no">overload</a></b></span>(bool)</li>
+<li class="fn" translate="no"><span class="name"><b><a href="testqdoc-test.html#someFunction" translate="no">someFunction</a></b></span>(int, int) : int</li>
+<li class="fn" translate="no"><span class="name"><b><a href="testqdoc-test.html#someFunctionDefaultArg" translate="no">someFunctionDefaultArg</a></b></span>(int, bool) const</li>
+<li class="fn" translate="no"><span class="name"><b><a href="testqdoc-testderived.html#someValue" translate="no">someValue</a></b></span>() : TestQDoc::TestDerived::NotTypedef</li>
+<li class="fn" translate="no"><span class="name"><b><a href="testqdoc-testderived.html#virtualFun" translate="no">virtualFun</a></b></span>()</li>
+<li class="fn" translate="no"><span class="name"><b><a href="testqdoc-test.html#virtualFun" translate="no">virtualFun</a></b></span>()</li>
+</ul>
+</td></tr>
+</table></div>
+</body>
+</html>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/testtemplate/expected/html/testqdoc-testderived-obsolete.html b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/testtemplate/expected/html/testqdoc-testderived-obsolete.html
new file mode 100644
index 000000000..3eda5f832
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/testtemplate/expected/html/testqdoc-testderived-obsolete.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+<!-- testcpp.cpp -->
+ <meta name="description" content="A class in a namespace, derived from Test.">
+ <title>Obsolete Members for TestDerived | TestTemplate</title>
+</head>
+<body>
+<li><a href="testcpp-module.html" translate="no">C++ Classes</a></li>
+<li>TestDerived</li>
+<div class="sidebar"><div class="sidebar-content" id="sidebar-content"></div></div>
+<h1 class="title" translate="no">Obsolete Members for TestDerived</h1>
+<p><b>The following members of class <a href="testqdoc-testderived.html" translate="no">TestDerived</a> are deprecated.</b> They are provided to keep old source code working. We strongly advise against using them in new code.</p>
+<h2>Static Public Members</h2>
+<div class="table"><table class="alignedsummary" translate="no">
+<tr><td class="memItemLeft topAlign rightAlign"> <code class="summary extra" translate="no">(deprecated)</code> void </td><td class="memItemRight bottomAlign"><b><a href="testqdoc-testderived-obsolete.html#staticObsoleteMember" translate="no">staticObsoleteMember</a></b>()</td></tr>
+</table></div>
+<h2>Member Function Documentation</h2>
+<!-- $$$staticObsoleteMember[overload1]$$$staticObsoleteMember -->
+<h3 class="fn" translate="no" id="staticObsoleteMember"><code class="details extra" translate="no">[static, deprecated]</code> <span class="type">void</span> TestDerived::<span class="name">staticObsoleteMember</span>()</h3>
+<p>This function is deprecated. We strongly advise against using it in new code.</p>
+<p>Static obsolete method.</p>
+<!-- @@@staticObsoleteMember -->
+</body>
+</html>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/testtemplate/expected/html/testqdoc-testderived.html b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/testtemplate/expected/html/testqdoc-testderived.html
new file mode 100644
index 000000000..b96fefe51
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/testtemplate/expected/html/testqdoc-testderived.html
@@ -0,0 +1,81 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+<!-- testcpp.cpp -->
+ <meta name="description" content="A class in a namespace, derived from Test.">
+ <title>TestDerived Class | TestTemplate</title>
+</head>
+<body>
+<li><a href="testcpp-module.html" translate="no">C++ Classes</a></li>
+<li>TestDerived</li>
+<div class="sidebar">
+<div class="toc">
+<h3 id="toc">Contents</h3>
+<ul>
+<li class="level1"><a href="#public-types">Public Types</a></li>
+<li class="level1"><a href="#public-functions">Public Functions</a></li>
+<li class="level1"><a href="#reimplemented-public-functions">Reimplemented Public Functions</a></li>
+<li class="level1"><a href="#details">Detailed Description</a></li>
+</ul>
+</div>
+<div class="sidebar-content" id="sidebar-content"></div></div>
+<h1 class="title" translate="no">TestDerived Class</h1>
+<span class="small-subtitle" translate="no">class <a href="testqdoc.html" translate="no">TestQDoc</a>::TestDerived</span>
+<!-- $$$TestDerived-brief -->
+<p>A class in a namespace, derived from <a href="testqdoc-test.html" translate="no">Test</a>. <a href="#details">More...</a></p>
+<!-- @@@TestDerived -->
+<div class="table"><table class="alignedsummary" translate="no">
+<tr><td class="memItemLeft rightAlign topAlign"> Header:</td><td class="memItemRight bottomAlign"> <span class="preprocessor">#include &lt;TestDerived&gt;</span></td></tr>
+<tr><td class="memItemLeft rightAlign topAlign"> CMake:</td><td class="memItemRight bottomAlign"> find_package(Qt6 REQUIRED COMPONENTS QDocTest) <br/>
+target_link_libraries(mytarget PRIVATE Qt6::QDocTest)</td></tr>
+<tr><td class="memItemLeft rightAlign topAlign"> qmake:</td><td class="memItemRight bottomAlign"> QT += testcpp</td></tr>
+<tr><td class="memItemLeft rightAlign topAlign"> Since:</td><td class="memItemRight bottomAlign"> Qt 2.0</td></tr>
+<tr><td class="memItemLeft rightAlign topAlign"> Inherits:</td><td class="memItemRight bottomAlign"> <a href="testqdoc-test.html" translate="no">TestQDoc::Test</a></td></tr>
+</table></div>
+<ul>
+<li><a href="testqdoc-testderived-members.html">List of all members, including inherited members</a></li>
+<li><a href="testqdoc-testderived-obsolete.html">Deprecated members</a></li>
+</ul>
+<h2 id="public-types">Public Types</h2>
+<div class="table"><table class="alignedsummary" translate="no">
+<tr><td class="memItemLeft rightAlign topAlign"> </td><td class="memItemRight bottomAlign"><b><a href="testqdoc-testderived.html#DerivedType-typedef" translate="no">DerivedType</a></b></td></tr>
+<tr><td class="memItemLeft rightAlign topAlign"> </td><td class="memItemRight bottomAlign"><b><a href="testqdoc-testderived.html#NotTypedef-typedef" translate="no">NotTypedef</a></b></td></tr>
+</table></div>
+<h2 id="public-functions">Public Functions</h2>
+<div class="table"><table class="alignedsummary" translate="no">
+<tr><td class="memItemLeft rightAlign topAlign"> TestQDoc::TestDerived::NotTypedef </td><td class="memItemRight bottomAlign"><b><a href="testqdoc-testderived.html#someValue" translate="no">someValue</a></b>()</td></tr>
+</table></div>
+<h2 id="reimplemented-public-functions">Reimplemented Public Functions</h2>
+<div class="table"><table class="alignedsummary" translate="no">
+<tr><td class="memItemLeft rightAlign topAlign"> virtual void </td><td class="memItemRight bottomAlign"><b><a href="testqdoc-testderived.html#virtualFun" translate="no">virtualFun</a></b>() override</td></tr>
+</table></div>
+<!-- $$$TestDerived-description -->
+<div class="descr">
+<h2 id="details">Detailed Description</h2>
+</div>
+<!-- @@@TestDerived -->
+<div class="types">
+<h2>Member Type Documentation</h2>
+<!-- $$$DerivedType -->
+<h3 class="fn" translate="no" id="DerivedType-typedef"><code class="details extra" translate="no">[alias]</code> TestDerived::<span class="name">DerivedType</span></h3>
+<p>An aliased typedef.</p>
+<!-- @@@DerivedType -->
+<!-- $$$NotTypedef -->
+<h3 class="fn" translate="no" id="NotTypedef-typedef"><code class="details extra" translate="no">[alias]</code> TestDerived::<span class="name">NotTypedef</span></h3>
+<p>I'm an alias, not a typedef.</p>
+<!-- @@@NotTypedef -->
+</div>
+<div class="func">
+<h2>Member Function Documentation</h2>
+<!-- $$$someValue[overload1]$$$someValue -->
+<h3 class="fn" translate="no" id="someValue"><span class="type"><a href="testqdoc-testderived.html#NotTypedef-typedef" translate="no">TestQDoc::TestDerived::NotTypedef</a></span> TestDerived::<span class="name">someValue</span>()</h3>
+<p>Returns a value using an aliases type.</p>
+<!-- @@@someValue -->
+<!-- $$$virtualFun[overload1]$$$virtualFun -->
+<h3 class="fn" translate="no" id="virtualFun"><code class="details extra" translate="no">[override virtual]</code> <span class="type">void</span> TestDerived::<span class="name">virtualFun</span>()</h3>
+<p>Reimplements: <a href="testqdoc-test.html#virtualFun" translate="no">Test::virtualFun</a>().</p>
+<!-- @@@virtualFun -->
+</div>
+</body>
+</html>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/testtemplate/expected/html/testqdoc-vec.html b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/testtemplate/expected/html/testqdoc-vec.html
new file mode 100644
index 000000000..a3085f6b3
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/testtemplate/expected/html/testqdoc-vec.html
@@ -0,0 +1,38 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+<!-- testcpp.cpp -->
+ <meta name="description" content="Type alias that has its own reference.">
+ <title>Vec Class | TestTemplate</title>
+</head>
+<body>
+<li><a href="testcpp-module.html" translate="no">C++ Classes</a></li>
+<li>Vec</li>
+<div class="sidebar">
+<div class="toc">
+<h3 id="toc">Contents</h3>
+<ul>
+<li class="level1"><a href="#details">Detailed Description</a></li>
+</ul>
+</div>
+<div class="sidebar-content" id="sidebar-content"></div></div>
+<h1 class="title" translate="no">Vec Class</h1>
+<span class="small-subtitle" translate="no">template &lt;typename T&gt; class <a href="testqdoc.html" translate="no">TestQDoc</a>::Vec</span>
+<!-- $$$Vec-brief -->
+<p>Type alias that has its own reference. <a href="#details">More...</a></p>
+<!-- @@@Vec -->
+<div class="table"><table class="alignedsummary" translate="no">
+<tr><td class="memItemLeft rightAlign topAlign"> Header:</td><td class="memItemRight bottomAlign"> <span class="preprocessor">#include &lt;Vec&gt;</span></td></tr>
+<tr><td class="memItemLeft rightAlign topAlign"> CMake:</td><td class="memItemRight bottomAlign"> find_package(Qt6 REQUIRED COMPONENTS QDocTest) <br/>
+target_link_libraries(mytarget PRIVATE Qt6::QDocTest)</td></tr>
+<tr><td class="memItemLeft rightAlign topAlign"> qmake:</td><td class="memItemRight bottomAlign"> QT += testcpp</td></tr>
+<tr><td class="memItemLeft rightAlign topAlign"> Since:</td><td class="memItemRight bottomAlign"> Qt 2.0</td></tr>
+</table></div>
+<!-- $$$Vec-description -->
+<div class="descr">
+<h2 id="details">Detailed Description</h2>
+</div>
+<!-- @@@Vec -->
+</body>
+</html>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/testtemplate/expected/html/testqdoc.html b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/testtemplate/expected/html/testqdoc.html
new file mode 100644
index 000000000..516e256da
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/testtemplate/expected/html/testqdoc.html
@@ -0,0 +1,68 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+<!-- testcpp.cpp -->
+ <meta name="description" content="A namespace.">
+ <title>TestQDoc Namespace | TestTemplate</title>
+</head>
+<body>
+<div class="sidebar">
+<div class="toc">
+<h3 id="toc">Contents</h3>
+<ul>
+<li class="level1"><a href="#classes">Classes</a></li>
+<li class="level1"><a href="#macros">Macros</a></li>
+<li class="level1"><a href="#details">Detailed Description</a></li>
+<li class="level2"><a href="#usage">Usage</a></li>
+</ul>
+</div>
+<div class="sidebar-content" id="sidebar-content"></div></div>
+<h1 class="title" translate="no">TestQDoc Namespace</h1>
+<!-- $$$TestQDoc-brief -->
+<p>A namespace. <a href="#details">More...</a></p>
+<!-- @@@TestQDoc -->
+<div class="table"><table class="alignedsummary" translate="no">
+<tr><td class="memItemLeft rightAlign topAlign"> Header:</td><td class="memItemRight bottomAlign"> <span class="preprocessor">#include &lt;TestCPP&gt;</span></td></tr>
+<tr><td class="memItemLeft rightAlign topAlign"> CMake:</td><td class="memItemRight bottomAlign"> find_package(Qt6 REQUIRED COMPONENTS QDocTest) <br/>
+target_link_libraries(mytarget PRIVATE Qt6::QDocTest)</td></tr>
+<tr><td class="memItemLeft rightAlign topAlign"> qmake:</td><td class="memItemRight bottomAlign"> QT += testcpp</td></tr>
+<tr><td class="memItemLeft rightAlign topAlign"> Since:</td><td class="memItemRight bottomAlign"> Qt 2.0</td></tr>
+</table></div>
+<h2 id="classes">Classes</h2>
+<div class="table"><table class="alignedsummary" translate="no">
+<tr><td class="memItemLeft rightAlign topAlign"> <code class="summary extra" translate="no">(since 2.0)</code> class </td><td class="memItemRight bottomAlign"><b><a href="testqdoc-test.html" translate="no">Test</a></b></td></tr>
+<tr><td class="memItemLeft rightAlign topAlign"> <code class="summary extra" translate="no">(since 2.0)</code> class </td><td class="memItemRight bottomAlign"><b><a href="testqdoc-testderived.html" translate="no">TestDerived</a></b></td></tr>
+<tr><td class="memItemLeft rightAlign topAlign"> <code class="summary extra" translate="no">(since 2.0)</code> class </td><td class="memItemRight bottomAlign"><b><a href="testqdoc-vec.html" translate="no">Vec</a></b></td></tr>
+</table></div>
+<h2 id="macros">Macros</h2>
+<div class="table"><table class="alignedsummary" translate="no">
+<tr><td class="memItemLeft rightAlign topAlign"> </td><td class="memItemRight bottomAlign"><b><a href="testqdoc.html#QDOCTEST_MACRO" translate="no">QDOCTEST_MACRO</a></b></td></tr>
+</table></div>
+<!-- $$$TestQDoc-description -->
+<div class="descr">
+<h2 id="details">Detailed Description</h2>
+<h3 id="usage">Usage</h3>
+<p>This namespace is for testing QDoc output.</p>
+</div>
+<!-- @@@TestQDoc -->
+<div class="classes">
+<h2>Classes</h2>
+<h3> class <a href="testqdoc-test.html">Test</a></h3><!-- $$$Test-brief -->
+<p>A class in a namespace. <a href="testqdoc-test.html#details">More...</a></p>
+<!-- @@@Test -->
+<h3> class <a href="testqdoc-testderived.html">TestDerived</a></h3><!-- $$$TestDerived-brief -->
+<p>A class in a namespace, derived from <a href="testqdoc-test.html" translate="no">Test</a>. <a href="testqdoc-testderived.html#details">More...</a></p>
+<!-- @@@TestDerived -->
+<h3> class <a href="testqdoc-vec.html">Vec</a></h3><!-- $$$Vec-brief -->
+<p>Type alias that has its own reference. <a href="testqdoc-vec.html#details">More...</a></p>
+<!-- @@@Vec -->
+</div>
+<div class="macros">
+<h2>Macro Documentation</h2>
+<!-- $$$QDOCTEST_MACRO[overload1]$$$QDOCTEST_MACRO -->
+<h3 class="fn" translate="no" id="QDOCTEST_MACRO"><span class="name">QDOCTEST_MACRO</span></h3>
+<!-- @@@QDOCTEST_MACRO -->
+</div>
+</body>
+</html>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/testtemplate/expected/html/testtemplate.index b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/testtemplate/expected/html/testtemplate.index
new file mode 100644
index 000000000..34346db56
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/testtemplate/expected/html/testtemplate.index
@@ -0,0 +1,87 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE QDOCINDEX>
+<INDEX url="" title="TestTemplate Reference Documentation" version="" project="TestTemplate">
+ <namespace name="" status="active" access="public" module="testtemplate">
+ <function name="QDOCTEST_MACRO" href="testqdoc.html#QDOCTEST_MACRO" status="active" access="public" documented="true" related="0" meta="macrowithoutparams" signature="QDOCTEST_MACRO"/>
+ <function name="QDOCTEST_MACRO2" href="testqdoc-test.html#QDOCTEST_MACRO2" status="active" access="public" documented="true" related="1" since="Test 1.1" meta="macrowithparams" brief="A macro with argument x" signature="QDOCTEST_MACRO2(int &amp;x)" groups="testgroup">
+ <parameter type="int &amp;" name="x" default=""/>
+ </function>
+ <function name="Q_INVOKABLE" href="testqdoc-test.html#Q_INVOKABLE" status="active" access="public" documented="true" related="2" meta="macrowithoutparams" signature="Q_INVOKABLE"/>
+ <page name="autolinking.html" href="autolinking.html" status="active" location="classlists.qdoc" documented="true" subtype="page" title="Autolinking" fulltitle="Autolinking" subtitle="">
+ <contents name="testqdoc" title="TestQDoc" level="1"/>
+ <contents name="someprop" title="someProp" level="1"/>
+ </page>
+ <class name="Bar" href="bar.html" status="active" access="public" location="testtemplate.h" since="2.0" documented="true" module="TestCPP" brief="Another class template"/>
+ <struct name="Baz" href="baz.html" status="active" access="public" location="testtemplate.h" since="2.0" documented="true" module="TestCPP" brief="Class template template"/>
+ <namespace name="CrossModuleRef" href="crossmoduleref.html" status="active" access="public" location="testcpp.h" since="3.0" documented="true" module="TestCPP" brief="Namespace that has documented functions in multiple modules">
+ <function name="documentMe" fullname="CrossModuleRef::documentMe" href="crossmoduleref.html#documentMe" status="active" access="public" location="testcpp.h" documented="true" meta="plain" type="void" signature="void documentMe()"/>
+ </namespace>
+ <class name="DontLinkToMe" href="dontlinktome.html" status="ignored" access="public" location="testcpp.h" since="2.0" documented="true" module="TestCPP" brief="Class that does not generate documentation"/>
+ <class name="Foo" href="foo.html" status="active" access="public" location="testtemplate.h" since="2.0" documented="true" module="TestCPP" brief="Class template"/>
+ <page name="obsolete-classes.html" href="obsolete-classes.html" status="active" location="classlists.qdoc" documented="true" subtype="page" title="Obsolete Classes" fulltitle="Obsolete Classes" subtitle="">
+ <contents name="classes-with-obsolete-members" title="Classes with obsolete members" level="1"/>
+ <contents name="testqdoc" title="TestQDoc" level="2"/>
+ </page>
+ <namespace name="TestQDoc" href="testqdoc.html" status="active" access="public" location="testcpp.h" since="2.0" documented="true" module="TestCPP" brief="A namespace">
+ <contents name="usage" title="Usage" level="1"/>
+ <function name="QDOCTEST_MACRO" href="testqdoc.html#QDOCTEST_MACRO" status="active" access="public" documented="true" related="0" meta="macrowithoutparams" signature="QDOCTEST_MACRO"/>
+ <class threadsafety="reentrant" name="Test" fullname="TestQDoc::Test" href="testqdoc-test.html" status="active" access="public" location="testcpp.h" since="2.0" documented="true" groups="cpptypes,testgroup" module="TestCPP" brief="A class in a namespace">
+ <function name="QDOCTEST_MACRO2" href="testqdoc-test.html#QDOCTEST_MACRO2" status="active" access="public" documented="true" related="1" since="Test 1.1" meta="macrowithparams" brief="A macro with argument x" signature="QDOCTEST_MACRO2(int &amp;x)" groups="testgroup">
+ <parameter type="int &amp;" name="x" default=""/>
+ </function>
+ <function name="Q_INVOKABLE" href="testqdoc-test.html#Q_INVOKABLE" status="active" access="public" documented="true" related="2" meta="macrowithoutparams" signature="Q_INVOKABLE"/>
+ <function name="anotherObsoleteMember" fullname="TestQDoc::Test::anotherObsoleteMember" href="testqdoc-test-obsolete.html#anotherObsoleteMember" status="deprecated" access="public" location="testcpp.h" documented="true" meta="plain" type="void" signature="void anotherObsoleteMember()"/>
+ <function name="deprecatedMember" fullname="TestQDoc::Test::deprecatedMember" href="testqdoc-test-obsolete.html#deprecatedMember" status="deprecated" access="public" location="testcpp.h" documented="true" meta="plain" type="void" signature="void deprecatedMember()"/>
+ <function name="funcPtr" fullname="TestQDoc::Test::funcPtr" href="testqdoc-test.html#funcPtr" status="active" access="public" location="testcpp.h" documented="true" meta="plain" type="void (*)(bool)" signature="void (*)(bool) funcPtr(bool b, const char *s)">
+ <parameter type="bool" name="b" default=""/>
+ <parameter type="const char *" name="s" default=""/>
+ </function>
+ <function name="funcTemplate" fullname="TestQDoc::Test::funcTemplate" href="testqdoc-test.html#funcTemplate" status="active" access="protected" location="testcpp.h" documented="true" meta="plain" type="void" brief="Function template with two parameters, a and b" signature="void funcTemplate(T1 a, T2 b)">
+ <parameter type="T1" name="a" default=""/>
+ <parameter type="T2" name="b" default=""/>
+ </function>
+ <function name="inlineFunction" fullname="TestQDoc::Test::inlineFunction" href="testqdoc-test.html#inlineFunction" status="active" access="public" location="testcpp.h" documented="true" meta="plain" type="void" brief="An inline function, documented using the \fn QDoc command" signature="void inlineFunction()"/>
+ <function name="methodWithEmDashInItsDocs" fullname="TestQDoc::Test::methodWithEmDashInItsDocs" href="testqdoc-test.html#methodWithEmDashInItsDocs" status="active" access="public" location="testcpp.h" documented="true" meta="plain" type="void" signature="void methodWithEmDashInItsDocs()"/>
+ <function name="methodWithEnDashInItsDocs" fullname="TestQDoc::Test::methodWithEnDashInItsDocs" href="testqdoc-test.html#methodWithEnDashInItsDocs" status="active" access="public" location="testcpp.h" documented="true" meta="plain" type="void" signature="void methodWithEnDashInItsDocs()"/>
+ <function name="obsoleteMember" fullname="TestQDoc::Test::obsoleteMember" href="testqdoc-test-obsolete.html#obsoleteMember" status="deprecated" access="public" location="testcpp.h" documented="true" meta="plain" type="void" signature="void obsoleteMember()"/>
+ <function name="operator++" fullname="TestQDoc::Test::operator++" href="testqdoc-test-obsolete.html#operator-2b-2b" status="deprecated" access="public" location="testcpp.h" documented="true" meta="plain" type="TestQDoc::Test &amp;" signature="TestQDoc::Test &amp; operator++()"/>
+ <function name="operator--" fullname="TestQDoc::Test::operator--" href="testqdoc-test-obsolete.html#operator--" status="deprecated" access="public" location="testcpp.h" documented="true" meta="plain" type="TestQDoc::Test &amp;" signature="TestQDoc::Test &amp; operator--()"/>
+ <function name="operator==" href="testqdoc-test.html#operator-eq-eq" status="active" access="public" location="testcpp.h" documented="true" related="3" meta="plain" type="bool" signature="bool operator==(const TestQDoc::Test &amp;lhs, const TestQDoc::Test &amp;rhs)">
+ <parameter type="const TestQDoc::Test &amp;" name="lhs" default=""/>
+ <parameter type="const TestQDoc::Test &amp;" name="rhs" default=""/>
+ </function>
+ <function name="overload" fullname="TestQDoc::Test::overload" href="testqdoc-test.html#overload" status="active" access="protected" location="testcpp.h" documented="true" meta="plain" type="void" signature="void overload()"/>
+ <function name="overload" fullname="TestQDoc::Test::overload" href="testqdoc-test.html#overload-1" status="active" access="protected" location="testcpp.h" documented="true" since="Test 1.2" meta="plain" overload="true" overload-number="1" type="void" signature="void overload(bool b)">
+ <parameter type="bool" name="b" default=""/>
+ </function>
+ <function name="someFunction" fullname="TestQDoc::Test::someFunction" href="testqdoc-test.html#someFunction" status="active" access="public" location="testcpp.h" documented="true" meta="plain" type="int" signature="int someFunction(int, int v)">
+ <parameter type="int" name="" default=""/>
+ <parameter type="int" name="v" default="0"/>
+ </function>
+ <function name="someFunctionDefaultArg" fullname="TestQDoc::Test::someFunctionDefaultArg" href="testqdoc-test.html#someFunctionDefaultArg" threadsafety="non-reentrant" status="active" access="public" location="testcpp.h" documented="true" meta="plain" const="true" type="void" signature="void someFunctionDefaultArg(int i, bool b) const" groups="testgroup">
+ <parameter type="int" name="i" default=""/>
+ <parameter type="bool" name="b" default="false"/>
+ </function>
+ <function name="virtualFun" fullname="TestQDoc::Test::virtualFun" href="testqdoc-test.html#virtualFun" status="active" access="public" location="testcpp.h" documented="true" meta="plain" virtual="virtual" type="void" signature="void virtualFun()"/>
+ <struct name="Struct" fullname="TestQDoc::Test::Struct" href="testqdoc-test-struct.html" status="active" access="public" location="testcpp.h" since="2.0" documented="true" module="TestCPP" brief="Templated struct"/>
+ <typedef name="SomeType" fullname="TestQDoc::Test::SomeType" href="testqdoc-test.html#SomeType-typedef" status="active" access="public" location="testcpp.h" documented="true"/>
+ <typedef name="Specialized" fullname="TestQDoc::Test::Specialized" href="testqdoc-test.html#Specialized-typedef" status="active" access="public" location="testcpp.h" documented="true" aliasedtype="Struct&lt;int, T&gt;"/>
+ </class>
+ <class name="TestDerived" fullname="TestQDoc::TestDerived" href="testqdoc-testderived.html" status="active" access="public" location="testcpp.h" since="2.0" documented="true" bases="TestQDoc::Test" module="TestCPP" brief="A class in a namespace, derived from Test">
+ <function name="someValue" fullname="TestQDoc::TestDerived::someValue" href="testqdoc-testderived.html#someValue" status="active" access="public" location="testcpp.h" documented="true" meta="plain" type="TestQDoc::TestDerived::NotTypedef" signature="TestQDoc::TestDerived::NotTypedef someValue()"/>
+ <function name="staticObsoleteMember" fullname="TestQDoc::TestDerived::staticObsoleteMember" href="testqdoc-testderived-obsolete.html#staticObsoleteMember" status="deprecated" access="public" location="testcpp.h" documented="true" meta="plain" static="true" type="void" signature="void staticObsoleteMember()"/>
+ <function name="virtualFun" fullname="TestQDoc::TestDerived::virtualFun" href="testqdoc-testderived.html#virtualFun" status="active" access="public" location="testcpp.h" documented="true" meta="plain" virtual="virtual" override="true" type="void" signature="void virtualFun() override"/>
+ <typedef name="DerivedType" fullname="TestQDoc::TestDerived::DerivedType" href="testqdoc-testderived.html#DerivedType-typedef" status="active" access="public" location="testcpp.h" documented="true" aliasedtype="Test::SomeType"/>
+ <typedef name="NotTypedef" fullname="TestQDoc::TestDerived::NotTypedef" href="testqdoc-testderived.html#NotTypedef-typedef" status="active" access="public" location="testcpp.h" documented="true" aliasedtype="int"/>
+ </class>
+ <class name="Vec" fullname="TestQDoc::Vec" href="testqdoc-vec.html" status="active" access="public" location="testcpp.h" since="2.0" documented="true" module="TestCPP" brief="Type alias that has its own reference"/>
+ </namespace>
+ <page name="https://doc.qt.io/qt/17-qdoc-commands-thread.html#reentrant-command" href="https://doc.qt.io/qt/17-qdoc-commands-thread.html#reentrant-command" status="active" location="classlists.qdoc" documented="true" subtype="externalpage" title="reentrant" fulltitle="reentrant" subtitle=""/>
+ <group name="cpptypes" href="cpptypes.html" status="active" location="classlists.qdoc" documented="true" seen="true" title="Test C++ Types"/>
+ <group name="testgroup" href="testgroup.html" status="internal" seen="false" title=""/>
+ <module name="TestCPP" href="testcpp-module.html" status="active" since="2.0" documented="true" seen="true" title="QDoc Test C++ Classes" brief="A test module page">
+ <contents name="linking-to-function-like-things" title="Linking to function-like things" level="1"/>
+ <contents name="section" title="section()" level="2"/>
+ </module>
+ </namespace>
+</INDEX>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/testtemplate/expected/webxml/autolinking.webxml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/testtemplate/expected/webxml/autolinking.webxml
new file mode 100644
index 000000000..91f35d5d7
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/testtemplate/expected/webxml/autolinking.webxml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<WebXML>
+ <document>
+ <page name="autolinking.html" href="autolinking.html" status="active" location="classlists.qdoc" documented="true" subtype="page" title="Autolinking" fulltitle="Autolinking" subtitle="">
+ <contents name="testqdoc" title="TestQDoc" level="1"/>
+ <contents name="someprop" title="someProp" level="1"/>
+ <description>
+ <section id="testqdoc">
+ <heading level="1">TestQDoc</heading>
+ <para>The string <link raw="TestQDoc" href="testqdoc.html" type="namespace">TestQDoc</link> links to the C++ namespace unless linking explicitly, <link raw="#TestQDoc" href="autolinking.html#testqdoc" type="page" page="Autolinking">like this</link>, or <link raw="TestQDoc" href="testqdoc.html" type="namespace">this</link>. Also,</para>
+ <para>Autolinks:</para>
+ <list type="bullet">
+ <item>
+ <para>
+ <link raw="TestQDoc::TestDerived" href="testqdoc-testderived.html" type="class">TestQDoc::TestDerived</link></para>
+ </item>
+ </list>
+ <para>Explicit links:</para>
+ <list type="bullet">
+ <item>
+ <para>
+ <link raw="TestQDoc::TestDerived" href="testqdoc-testderived.html" type="class">TestQDoc::TestDerived</link></para>
+ </item>
+ <item>
+ <para>
+ <link raw="Obsolete Classes#TestQDoc" href="obsolete-classes.html#testqdoc" type="page" page="Obsolete Classes">Obsolete Classes#TestQDoc</link></para>
+ </item>
+ </list>
+ </section>
+ <section id="someprop">
+ <heading level="1">someProp</heading>
+ </section>
+ </description>
+ </page>
+ </document>
+</WebXML>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/testtemplate/expected/webxml/bar.webxml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/testtemplate/expected/webxml/bar.webxml
new file mode 100644
index 000000000..fd653db97
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/testtemplate/expected/webxml/bar.webxml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<WebXML>
+ <document>
+ <class name="Bar" href="bar.html" status="active" access="public" location="testtemplate.h" since="2.0" documented="true" module="TestCPP" brief="Another class template">
+ <description>
+ <brief>Another class template.</brief>
+ </description>
+ </class>
+ </document>
+</WebXML>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/testtemplate/expected/webxml/baz.webxml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/testtemplate/expected/webxml/baz.webxml
new file mode 100644
index 000000000..8184ea659
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/testtemplate/expected/webxml/baz.webxml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<WebXML>
+ <document>
+ <struct name="Baz" href="baz.html" status="active" access="public" location="testtemplate.h" since="2.0" documented="true" module="TestCPP" brief="Class template template">
+ <description>
+ <brief>Class template template.</brief>
+ </description>
+ </struct>
+ </document>
+</WebXML>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/testtemplate/expected/webxml/cpptypes.webxml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/testtemplate/expected/webxml/cpptypes.webxml
new file mode 100644
index 000000000..df7cd7024
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/testtemplate/expected/webxml/cpptypes.webxml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<WebXML>
+ <document>
+ <group name="cpptypes" href="cpptypes.html" status="active" location="classlists.qdoc" documented="true" seen="true" title="Test C++ Types">
+ <description>
+ <generatedlist contents="testgroup"/>
+ <table width="100%">
+ <row>
+ <item>
+ <para>
+ <link raw="TestQDoc::Test" href="testqdoc-test.html" type="class"/>
+ </para>
+ </item>
+ <item>
+ <para>A class in a namespace.</para>
+ </item>
+ </row>
+ </table>
+ </description>
+ </group>
+ </document>
+</WebXML>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/testtemplate/expected/webxml/crossmoduleref.webxml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/testtemplate/expected/webxml/crossmoduleref.webxml
new file mode 100644
index 000000000..682799bd8
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/testtemplate/expected/webxml/crossmoduleref.webxml
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<WebXML>
+ <document>
+ <namespace name="CrossModuleRef" href="crossmoduleref.html" status="active" access="public" location="testcpp.h" since="3.0" documented="true" module="TestCPP" brief="Namespace that has documented functions in multiple modules">
+ <description>
+ <brief>Namespace that has documented functions in multiple modules.</brief>
+ </description>
+ <function name="documentMe" fullname="CrossModuleRef::documentMe" href="crossmoduleref.html#documentMe" status="active" access="public" location="testcpp.h" documented="true" meta="plain" type="void" signature="void documentMe()">
+ <description>
+ <para>Document me!</para>
+ </description>
+ </function>
+ </namespace>
+ </document>
+</WebXML>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/testtemplate/expected/webxml/foo.webxml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/testtemplate/expected/webxml/foo.webxml
new file mode 100644
index 000000000..6135e5c4e
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/testtemplate/expected/webxml/foo.webxml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<WebXML>
+ <document>
+ <class name="Foo" href="foo.html" status="active" access="public" location="testtemplate.h" since="2.0" documented="true" module="TestCPP" brief="Class template">
+ <description>
+ <brief>Class template.</brief>
+ </description>
+ </class>
+ </document>
+</WebXML>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/testtemplate/expected/webxml/obsolete-classes.webxml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/testtemplate/expected/webxml/obsolete-classes.webxml
new file mode 100644
index 000000000..dda841458
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/testtemplate/expected/webxml/obsolete-classes.webxml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<WebXML>
+ <document>
+ <page name="obsolete-classes.html" href="obsolete-classes.html" status="active" location="classlists.qdoc" documented="true" subtype="page" title="Obsolete Classes" fulltitle="Obsolete Classes" subtitle="">
+ <contents name="classes-with-obsolete-members" title="Classes with obsolete members" level="1"/>
+ <contents name="testqdoc" title="TestQDoc" level="2"/>
+ <description>
+ <section id="classes-with-obsolete-members">
+ <heading level="1">Classes with obsolete members</heading>
+ <generatedlist contents="obsoletecppmembers"/>
+ </section>
+ <section id="testqdoc">
+ <heading level="2">TestQDoc</heading>
+ </section>
+ </description>
+ </page>
+ </document>
+</WebXML>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/testtemplate/expected/webxml/testcpp-module.webxml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/testtemplate/expected/webxml/testcpp-module.webxml
new file mode 100644
index 000000000..5d24b3077
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/testtemplate/expected/webxml/testcpp-module.webxml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<WebXML>
+ <document/>
+</WebXML>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/testtemplate/expected/webxml/testqdoc-test-struct.webxml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/testtemplate/expected/webxml/testqdoc-test-struct.webxml
new file mode 100644
index 000000000..f883d421e
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/testtemplate/expected/webxml/testqdoc-test-struct.webxml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<WebXML>
+ <document>
+ <struct name="Struct" fullname="TestQDoc::Test::Struct" href="testqdoc-test-struct.html" status="active" access="public" location="testcpp.h" since="2.0" documented="true" module="TestCPP" brief="Templated struct">
+ <description>
+ <brief>Templated struct.</brief>
+ </description>
+ </struct>
+ </document>
+</WebXML>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/testtemplate/expected/webxml/testqdoc-test.webxml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/testtemplate/expected/webxml/testqdoc-test.webxml
new file mode 100644
index 000000000..d6dfec6f1
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/testtemplate/expected/webxml/testqdoc-test.webxml
@@ -0,0 +1,130 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<WebXML>
+ <document>
+ <class threadsafety="reentrant" name="Test" fullname="TestQDoc::Test" href="testqdoc-test.html" status="active" access="public" location="testcpp.h" since="2.0" documented="true" groups="cpptypes,testgroup" module="TestCPP" brief="A class in a namespace">
+ <description>
+ <brief>A class in a namespace.</brief>
+ </description>
+ <function name="QDOCTEST_MACRO2" href="testqdoc-test.html#QDOCTEST_MACRO2" status="active" access="public" documented="true" related="1" since="Test 1.1" meta="macrowithparams" brief="A macro with argument x" signature="QDOCTEST_MACRO2(int &amp;x)" groups="testgroup">
+ <parameter type="int &amp;" name="x" default=""/>
+ <description>
+ <brief>A macro with argument <argument>x</argument>.</brief>
+ </description>
+ </function>
+ <function name="Q_INVOKABLE" href="testqdoc-test.html#Q_INVOKABLE" status="active" access="public" documented="true" related="2" meta="macrowithoutparams" signature="Q_INVOKABLE">
+ <description>
+ <para>This is a mock Q_INVOKABLE for the purpose of ensuring QDoc autolink to it as expected.</para>
+ </description>
+ </function>
+ <function name="anotherObsoleteMember" fullname="TestQDoc::Test::anotherObsoleteMember" href="testqdoc-test-obsolete.html#anotherObsoleteMember" status="deprecated" access="public" location="testcpp.h" documented="true" meta="plain" type="void" signature="void anotherObsoleteMember()">
+ <description>
+ <para>Use <link raw="obsoleteMember()" href="testqdoc-test.html#obsoleteMember" type="function">obsoleteMember()</link> instead.</para>
+ </description>
+ </function>
+ <function name="deprecatedMember" fullname="TestQDoc::Test::deprecatedMember" href="testqdoc-test-obsolete.html#deprecatedMember" status="deprecated" access="public" location="testcpp.h" documented="true" meta="plain" type="void" signature="void deprecatedMember()">
+ <description>
+ <para>Use <link raw="someFunction()" href="testqdoc-test.html#someFunction" type="function">someFunction()</link> instead.</para>
+ </description>
+ </function>
+ <function name="funcPtr" fullname="TestQDoc::Test::funcPtr" href="testqdoc-test.html#funcPtr" status="active" access="public" location="testcpp.h" documented="true" meta="plain" type="void (*)(bool)" signature="void (*)(bool) funcPtr(bool b, const char *s)">
+ <parameter type="bool" name="b" default=""/>
+ <parameter type="const char *" name="s" default=""/>
+ <description>
+ <para>Returns a pointer to a function that takes a boolean. Uses <argument>b</argument> and <argument>s</argument>.</para>
+ </description>
+ </function>
+ <function name="funcTemplate" fullname="TestQDoc::Test::funcTemplate" href="testqdoc-test.html#funcTemplate" status="active" access="protected" location="testcpp.h" documented="true" meta="plain" type="void" brief="Function template with two parameters, a and b" signature="void funcTemplate(T1 a, T2 b)">
+ <parameter type="T1" name="a" default=""/>
+ <parameter type="T2" name="b" default=""/>
+ <description>
+ <brief>Function template with two parameters, <argument>a</argument> and <argument>b</argument>.</brief>
+ </description>
+ </function>
+ <function name="inlineFunction" fullname="TestQDoc::Test::inlineFunction" href="testqdoc-test.html#inlineFunction" status="active" access="public" location="testcpp.h" documented="true" meta="plain" type="void" brief="An inline function, documented using the \fn QDoc command" signature="void inlineFunction()">
+ <description>
+ <brief>An inline function, documented using the \fn QDoc command.</brief>
+ </description>
+ </function>
+ <function name="methodWithEmDashInItsDocs" fullname="TestQDoc::Test::methodWithEmDashInItsDocs" href="testqdoc-test.html#methodWithEmDashInItsDocs" status="active" access="public" location="testcpp.h" documented="true" meta="plain" type="void" signature="void methodWithEmDashInItsDocs()">
+ <description>
+ <para>This method has em dashes in its documentation—as you'll find represented by <teletype type="highlighted">---</teletype> in the sources—here and there. The important bit to note is that when passed e.g. to the \c command, the three hyphens are processed as input to the command and not replaced by an em dash.</para>
+ <para>-----------------------------------------------------------------------</para>
+ <para>People can still add a bunch of dashes, though, without QDoc replacing them all with a series of em dashes.</para>
+ <para>—You can also start a new paragraph with an em dash, if you want to.</para>
+ <see-also>
+ <link raw="methodWithEnDashInItsDocs" href="testqdoc-test.html#methodWithEnDashInItsDocs" type="function">methodWithEnDashInItsDocs</link>
+ </see-also>
+ </description>
+ </function>
+ <function name="methodWithEnDashInItsDocs" fullname="TestQDoc::Test::methodWithEnDashInItsDocs" href="testqdoc-test.html#methodWithEnDashInItsDocs" status="active" access="public" location="testcpp.h" documented="true" meta="plain" type="void" signature="void methodWithEnDashInItsDocs()">
+ <description>
+ <para>This method has en dashes in its documentation – as you'll find represented by <teletype type="highlighted">--</teletype> in the sources – here and there. The important bit to note is that when passed e.g. to the \c command, the two hyphens are processed as input to the command and not replaced by an en dash. This also applies to code blocks, where otherwise, the decrement operator would get completely borked:</para>
+ <code>for (int i = 42; i &gt; 0; --i)
+ // Do something cool during countdown.</code>
+ <para>...as it would be silly if this would output –i instead of <teletype type="highlighted">--i</teletype>.</para>
+ <para>-----------------------------------------------------------------------</para>
+ <para>It still allows people to add a bunch of dashes, though, without replacing them all with a series of en dashes. Of course, they might want to use the \hr command instead, like this:</para>
+ <para>– You can also start a new paragraph with an en dash, if you want to.</para>
+ <see-also>methodWithEnDashInItsDocs</see-also>
+ </description>
+ </function>
+ <function name="obsoleteMember" fullname="TestQDoc::Test::obsoleteMember" href="testqdoc-test-obsolete.html#obsoleteMember" status="deprecated" access="public" location="testcpp.h" documented="true" meta="plain" type="void" signature="void obsoleteMember()">
+ <description>
+ <para>Use <link raw="someFunction()" href="testqdoc-test.html#someFunction" type="function">someFunction()</link> instead.</para>
+ </description>
+ </function>
+ <function name="operator++" fullname="TestQDoc::Test::operator++" href="testqdoc-test-obsolete.html#operator-2b-2b" status="deprecated" access="public" location="testcpp.h" documented="true" meta="plain" type="TestQDoc::Test &amp;" signature="TestQDoc::Test &amp; operator++()">
+ <description/>
+ </function>
+ <function name="operator--" fullname="TestQDoc::Test::operator--" href="testqdoc-test-obsolete.html#operator--" status="deprecated" access="public" location="testcpp.h" documented="true" meta="plain" type="TestQDoc::Test &amp;" signature="TestQDoc::Test &amp; operator--()">
+ <description/>
+ </function>
+ <function name="operator==" href="testqdoc-test.html#operator-eq-eq" status="active" access="public" location="testcpp.h" documented="true" related="3" meta="plain" type="bool" signature="bool operator==(const TestQDoc::Test &amp;lhs, const TestQDoc::Test &amp;rhs)">
+ <parameter type="const TestQDoc::Test &amp;" name="lhs" default=""/>
+ <parameter type="const TestQDoc::Test &amp;" name="rhs" default=""/>
+ <description>
+ <para>Returns true if <argument>lhs</argument> and <argument>rhs</argument> are equal.</para>
+ </description>
+ </function>
+ <function name="overload" fullname="TestQDoc::Test::overload" href="testqdoc-test.html#overload" status="active" access="protected" location="testcpp.h" documented="true" meta="plain" type="void" signature="void overload()">
+ <description/>
+ </function>
+ <function name="overload" fullname="TestQDoc::Test::overload" href="testqdoc-test.html#overload-1" status="active" access="protected" location="testcpp.h" documented="true" since="Test 1.2" meta="plain" overload="true" overload-number="1" type="void" signature="void overload(bool b)">
+ <parameter type="bool" name="b" default=""/>
+ <description/>
+ </function>
+ <function name="someFunction" fullname="TestQDoc::Test::someFunction" href="testqdoc-test.html#someFunction" status="active" access="public" location="testcpp.h" documented="true" meta="plain" type="int" signature="int someFunction(int, int v)">
+ <parameter type="int" name="" default=""/>
+ <parameter type="int" name="v" default="0"/>
+ <description>
+ <para>Function that takes a parameter <argument>v</argument>. Also returns the value of <argument>v</argument>.</para>
+ </description>
+ </function>
+ <function name="someFunctionDefaultArg" fullname="TestQDoc::Test::someFunctionDefaultArg" href="testqdoc-test.html#someFunctionDefaultArg" threadsafety="non-reentrant" status="active" access="public" location="testcpp.h" documented="true" meta="plain" const="true" type="void" signature="void someFunctionDefaultArg(int i, bool b) const" groups="testgroup">
+ <parameter type="int" name="i" default=""/>
+ <parameter type="bool" name="b" default="false"/>
+ <description>
+ <para>Function that takes a parameter <argument>i</argument> and <argument>b</argument>.</para>
+ </description>
+ </function>
+ <function name="virtualFun" fullname="TestQDoc::Test::virtualFun" href="testqdoc-test.html#virtualFun" status="active" access="public" location="testcpp.h" documented="true" meta="plain" virtual="virtual" type="void" signature="void virtualFun()">
+ <description>
+ <para>Function that must be reimplemented.</para>
+ </description>
+ </function>
+ <struct name="Struct" fullname="TestQDoc::Test::Struct" href="testqdoc-test-struct.html" status="active" access="public" location="testcpp.h" since="2.0" documented="true" module="TestCPP" brief="Templated struct">
+ <description>
+ <brief>Templated struct.</brief>
+ </description>
+ </struct>
+ <typedef name="SomeType" fullname="TestQDoc::Test::SomeType" href="testqdoc-test.html#SomeType-typedef" status="active" access="public" location="testcpp.h" documented="true">
+ <description>
+ <brief>A typedef.</brief>
+ </description>
+ </typedef>
+ <typedef name="Specialized" fullname="TestQDoc::Test::Specialized" href="testqdoc-test.html#Specialized-typedef" status="active" access="public" location="testcpp.h" documented="true" aliasedtype="Struct&lt;int, T&gt;">
+ <description/>
+ </typedef>
+ </class>
+ </document>
+</WebXML>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/testtemplate/expected/webxml/testqdoc-testderived.webxml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/testtemplate/expected/webxml/testqdoc-testderived.webxml
new file mode 100644
index 000000000..3752e9950
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/testtemplate/expected/webxml/testqdoc-testderived.webxml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<WebXML>
+ <document>
+ <class name="TestDerived" fullname="TestQDoc::TestDerived" href="testqdoc-testderived.html" status="active" access="public" location="testcpp.h" since="2.0" documented="true" bases="TestQDoc::Test" module="TestCPP" brief="A class in a namespace, derived from Test">
+ <description>
+ <brief>A class in a namespace, derived from <link raw="Test" href="testqdoc-test.html" type="class">Test</link>.</brief>
+ </description>
+ <function name="someValue" fullname="TestQDoc::TestDerived::someValue" href="testqdoc-testderived.html#someValue" status="active" access="public" location="testcpp.h" documented="true" meta="plain" type="TestQDoc::TestDerived::NotTypedef" signature="TestQDoc::TestDerived::NotTypedef someValue()">
+ <description>
+ <para>Returns a value using an aliases type.</para>
+ </description>
+ </function>
+ <function name="staticObsoleteMember" fullname="TestQDoc::TestDerived::staticObsoleteMember" href="testqdoc-testderived-obsolete.html#staticObsoleteMember" status="deprecated" access="public" location="testcpp.h" documented="true" meta="plain" static="true" type="void" signature="void staticObsoleteMember()">
+ <description>
+ <para>Static obsolete method.</para>
+ </description>
+ </function>
+ <function name="virtualFun" fullname="TestQDoc::TestDerived::virtualFun" href="testqdoc-testderived.html#virtualFun" status="active" access="public" location="testcpp.h" documented="true" meta="plain" virtual="virtual" override="true" type="void" signature="void virtualFun() override">
+ <description/>
+ </function>
+ <typedef name="DerivedType" fullname="TestQDoc::TestDerived::DerivedType" href="testqdoc-testderived.html#DerivedType-typedef" status="active" access="public" location="testcpp.h" documented="true" aliasedtype="Test::SomeType">
+ <description>
+ <para>An aliased typedef.</para>
+ </description>
+ </typedef>
+ <typedef name="NotTypedef" fullname="TestQDoc::TestDerived::NotTypedef" href="testqdoc-testderived.html#NotTypedef-typedef" status="active" access="public" location="testcpp.h" documented="true" aliasedtype="int">
+ <description>
+ <para>I'm an alias, not a typedef.</para>
+ </description>
+ </typedef>
+ </class>
+ </document>
+</WebXML>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/testtemplate/expected/webxml/testqdoc-vec.webxml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/testtemplate/expected/webxml/testqdoc-vec.webxml
new file mode 100644
index 000000000..9e3104404
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/testtemplate/expected/webxml/testqdoc-vec.webxml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<WebXML>
+ <document>
+ <class name="Vec" fullname="TestQDoc::Vec" href="testqdoc-vec.html" status="active" access="public" location="testcpp.h" since="2.0" documented="true" module="TestCPP" brief="Type alias that has its own reference">
+ <description>
+ <brief>Type alias that has its own reference.</brief>
+ </description>
+ </class>
+ </document>
+</WebXML>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/testtemplate/expected/webxml/testqdoc.webxml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/testtemplate/expected/webxml/testqdoc.webxml
new file mode 100644
index 000000000..de87a3e86
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/testtemplate/expected/webxml/testqdoc.webxml
@@ -0,0 +1,176 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<WebXML>
+ <document>
+ <namespace name="TestQDoc" href="testqdoc.html" status="active" access="public" location="testcpp.h" since="2.0" documented="true" module="TestCPP" brief="A namespace">
+ <contents name="usage" title="Usage" level="1"/>
+ <description>
+ <brief>A namespace.</brief>
+ <section id="usage">
+ <heading level="1">Usage</heading>
+ <para>This namespace is for testing QDoc output.</para>
+ </section>
+ </description>
+ <function name="QDOCTEST_MACRO" href="testqdoc.html#QDOCTEST_MACRO" status="active" access="public" documented="true" related="0" meta="macrowithoutparams" signature="QDOCTEST_MACRO">
+ <description/>
+ </function>
+ <class threadsafety="reentrant" name="Test" fullname="TestQDoc::Test" href="testqdoc-test.html" status="active" access="public" location="testcpp.h" since="2.0" documented="true" groups="cpptypes,testgroup" module="TestCPP" brief="A class in a namespace">
+ <description>
+ <brief>A class in a namespace.</brief>
+ </description>
+ <function name="QDOCTEST_MACRO2" href="testqdoc-test.html#QDOCTEST_MACRO2" status="active" access="public" documented="true" related="1" since="Test 1.1" meta="macrowithparams" brief="A macro with argument x" signature="QDOCTEST_MACRO2(int &amp;x)" groups="testgroup">
+ <parameter type="int &amp;" name="x" default=""/>
+ <description>
+ <brief>A macro with argument <argument>x</argument>.</brief>
+ </description>
+ </function>
+ <function name="Q_INVOKABLE" href="testqdoc-test.html#Q_INVOKABLE" status="active" access="public" documented="true" related="2" meta="macrowithoutparams" signature="Q_INVOKABLE">
+ <description>
+ <para>This is a mock Q_INVOKABLE for the purpose of ensuring QDoc autolink to it as expected.</para>
+ </description>
+ </function>
+ <function name="anotherObsoleteMember" fullname="TestQDoc::Test::anotherObsoleteMember" href="testqdoc-test-obsolete.html#anotherObsoleteMember" status="deprecated" access="public" location="testcpp.h" documented="true" meta="plain" type="void" signature="void anotherObsoleteMember()">
+ <description>
+ <para>Use <link raw="obsoleteMember()" href="testqdoc-test.html#obsoleteMember" type="function">obsoleteMember()</link> instead.</para>
+ </description>
+ </function>
+ <function name="deprecatedMember" fullname="TestQDoc::Test::deprecatedMember" href="testqdoc-test-obsolete.html#deprecatedMember" status="deprecated" access="public" location="testcpp.h" documented="true" meta="plain" type="void" signature="void deprecatedMember()">
+ <description>
+ <para>Use <link raw="someFunction()" href="testqdoc-test.html#someFunction" type="function">someFunction()</link> instead.</para>
+ </description>
+ </function>
+ <function name="funcPtr" fullname="TestQDoc::Test::funcPtr" href="testqdoc-test.html#funcPtr" status="active" access="public" location="testcpp.h" documented="true" meta="plain" type="void (*)(bool)" signature="void (*)(bool) funcPtr(bool b, const char *s)">
+ <parameter type="bool" name="b" default=""/>
+ <parameter type="const char *" name="s" default=""/>
+ <description>
+ <para>Returns a pointer to a function that takes a boolean. Uses <argument>b</argument> and <argument>s</argument>.</para>
+ </description>
+ </function>
+ <function name="funcTemplate" fullname="TestQDoc::Test::funcTemplate" href="testqdoc-test.html#funcTemplate" status="active" access="protected" location="testcpp.h" documented="true" meta="plain" type="void" brief="Function template with two parameters, a and b" signature="void funcTemplate(T1 a, T2 b)">
+ <parameter type="T1" name="a" default=""/>
+ <parameter type="T2" name="b" default=""/>
+ <description>
+ <brief>Function template with two parameters, <argument>a</argument> and <argument>b</argument>.</brief>
+ </description>
+ </function>
+ <function name="inlineFunction" fullname="TestQDoc::Test::inlineFunction" href="testqdoc-test.html#inlineFunction" status="active" access="public" location="testcpp.h" documented="true" meta="plain" type="void" brief="An inline function, documented using the \fn QDoc command" signature="void inlineFunction()">
+ <description>
+ <brief>An inline function, documented using the \fn QDoc command.</brief>
+ </description>
+ </function>
+ <function name="methodWithEmDashInItsDocs" fullname="TestQDoc::Test::methodWithEmDashInItsDocs" href="testqdoc-test.html#methodWithEmDashInItsDocs" status="active" access="public" location="testcpp.h" documented="true" meta="plain" type="void" signature="void methodWithEmDashInItsDocs()">
+ <description>
+ <para>This method has em dashes in its documentation—as you'll find represented by <teletype type="highlighted">---</teletype> in the sources—here and there. The important bit to note is that when passed e.g. to the \c command, the three hyphens are processed as input to the command and not replaced by an em dash.</para>
+ <para>-----------------------------------------------------------------------</para>
+ <para>People can still add a bunch of dashes, though, without QDoc replacing them all with a series of em dashes.</para>
+ <para>—You can also start a new paragraph with an em dash, if you want to.</para>
+ <see-also>
+ <link raw="methodWithEnDashInItsDocs" href="testqdoc-test.html#methodWithEnDashInItsDocs" type="function">methodWithEnDashInItsDocs</link>
+ </see-also>
+ </description>
+ </function>
+ <function name="methodWithEnDashInItsDocs" fullname="TestQDoc::Test::methodWithEnDashInItsDocs" href="testqdoc-test.html#methodWithEnDashInItsDocs" status="active" access="public" location="testcpp.h" documented="true" meta="plain" type="void" signature="void methodWithEnDashInItsDocs()">
+ <description>
+ <para>This method has en dashes in its documentation – as you'll find represented by <teletype type="highlighted">--</teletype> in the sources – here and there. The important bit to note is that when passed e.g. to the \c command, the two hyphens are processed as input to the command and not replaced by an en dash. This also applies to code blocks, where otherwise, the decrement operator would get completely borked:</para>
+ <code>for (int i = 42; i &gt; 0; --i)
+ // Do something cool during countdown.</code>
+ <para>...as it would be silly if this would output –i instead of <teletype type="highlighted">--i</teletype>.</para>
+ <para>-----------------------------------------------------------------------</para>
+ <para>It still allows people to add a bunch of dashes, though, without replacing them all with a series of en dashes. Of course, they might want to use the \hr command instead, like this:</para>
+ <para>– You can also start a new paragraph with an en dash, if you want to.</para>
+ <see-also>methodWithEnDashInItsDocs</see-also>
+ </description>
+ </function>
+ <function name="obsoleteMember" fullname="TestQDoc::Test::obsoleteMember" href="testqdoc-test-obsolete.html#obsoleteMember" status="deprecated" access="public" location="testcpp.h" documented="true" meta="plain" type="void" signature="void obsoleteMember()">
+ <description>
+ <para>Use <link raw="someFunction()" href="testqdoc-test.html#someFunction" type="function">someFunction()</link> instead.</para>
+ </description>
+ </function>
+ <function name="operator++" fullname="TestQDoc::Test::operator++" href="testqdoc-test-obsolete.html#operator-2b-2b" status="deprecated" access="public" location="testcpp.h" documented="true" meta="plain" type="TestQDoc::Test &amp;" signature="TestQDoc::Test &amp; operator++()">
+ <description/>
+ </function>
+ <function name="operator--" fullname="TestQDoc::Test::operator--" href="testqdoc-test-obsolete.html#operator--" status="deprecated" access="public" location="testcpp.h" documented="true" meta="plain" type="TestQDoc::Test &amp;" signature="TestQDoc::Test &amp; operator--()">
+ <description/>
+ </function>
+ <function name="operator==" href="testqdoc-test.html#operator-eq-eq" status="active" access="public" location="testcpp.h" documented="true" related="3" meta="plain" type="bool" signature="bool operator==(const TestQDoc::Test &amp;lhs, const TestQDoc::Test &amp;rhs)">
+ <parameter type="const TestQDoc::Test &amp;" name="lhs" default=""/>
+ <parameter type="const TestQDoc::Test &amp;" name="rhs" default=""/>
+ <description>
+ <para>Returns true if <argument>lhs</argument> and <argument>rhs</argument> are equal.</para>
+ </description>
+ </function>
+ <function name="overload" fullname="TestQDoc::Test::overload" href="testqdoc-test.html#overload" status="active" access="protected" location="testcpp.h" documented="true" meta="plain" type="void" signature="void overload()">
+ <description/>
+ </function>
+ <function name="overload" fullname="TestQDoc::Test::overload" href="testqdoc-test.html#overload-1" status="active" access="protected" location="testcpp.h" documented="true" since="Test 1.2" meta="plain" overload="true" overload-number="1" type="void" signature="void overload(bool b)">
+ <parameter type="bool" name="b" default=""/>
+ <description/>
+ </function>
+ <function name="someFunction" fullname="TestQDoc::Test::someFunction" href="testqdoc-test.html#someFunction" status="active" access="public" location="testcpp.h" documented="true" meta="plain" type="int" signature="int someFunction(int, int v)">
+ <parameter type="int" name="" default=""/>
+ <parameter type="int" name="v" default="0"/>
+ <description>
+ <para>Function that takes a parameter <argument>v</argument>. Also returns the value of <argument>v</argument>.</para>
+ </description>
+ </function>
+ <function name="someFunctionDefaultArg" fullname="TestQDoc::Test::someFunctionDefaultArg" href="testqdoc-test.html#someFunctionDefaultArg" threadsafety="non-reentrant" status="active" access="public" location="testcpp.h" documented="true" meta="plain" const="true" type="void" signature="void someFunctionDefaultArg(int i, bool b) const" groups="testgroup">
+ <parameter type="int" name="i" default=""/>
+ <parameter type="bool" name="b" default="false"/>
+ <description>
+ <para>Function that takes a parameter <argument>i</argument> and <argument>b</argument>.</para>
+ </description>
+ </function>
+ <function name="virtualFun" fullname="TestQDoc::Test::virtualFun" href="testqdoc-test.html#virtualFun" status="active" access="public" location="testcpp.h" documented="true" meta="plain" virtual="virtual" type="void" signature="void virtualFun()">
+ <description>
+ <para>Function that must be reimplemented.</para>
+ </description>
+ </function>
+ <struct name="Struct" fullname="TestQDoc::Test::Struct" href="testqdoc-test-struct.html" status="active" access="public" location="testcpp.h" since="2.0" documented="true" module="TestCPP" brief="Templated struct">
+ <description>
+ <brief>Templated struct.</brief>
+ </description>
+ </struct>
+ <typedef name="SomeType" fullname="TestQDoc::Test::SomeType" href="testqdoc-test.html#SomeType-typedef" status="active" access="public" location="testcpp.h" documented="true">
+ <description>
+ <brief>A typedef.</brief>
+ </description>
+ </typedef>
+ <typedef name="Specialized" fullname="TestQDoc::Test::Specialized" href="testqdoc-test.html#Specialized-typedef" status="active" access="public" location="testcpp.h" documented="true" aliasedtype="Struct&lt;int, T&gt;">
+ <description/>
+ </typedef>
+ </class>
+ <class name="TestDerived" fullname="TestQDoc::TestDerived" href="testqdoc-testderived.html" status="active" access="public" location="testcpp.h" since="2.0" documented="true" bases="TestQDoc::Test" module="TestCPP" brief="A class in a namespace, derived from Test">
+ <description>
+ <brief>A class in a namespace, derived from <link raw="Test" href="testqdoc-test.html" type="class">Test</link>.</brief>
+ </description>
+ <function name="someValue" fullname="TestQDoc::TestDerived::someValue" href="testqdoc-testderived.html#someValue" status="active" access="public" location="testcpp.h" documented="true" meta="plain" type="TestQDoc::TestDerived::NotTypedef" signature="TestQDoc::TestDerived::NotTypedef someValue()">
+ <description>
+ <para>Returns a value using an aliases type.</para>
+ </description>
+ </function>
+ <function name="staticObsoleteMember" fullname="TestQDoc::TestDerived::staticObsoleteMember" href="testqdoc-testderived-obsolete.html#staticObsoleteMember" status="deprecated" access="public" location="testcpp.h" documented="true" meta="plain" static="true" type="void" signature="void staticObsoleteMember()">
+ <description>
+ <para>Static obsolete method.</para>
+ </description>
+ </function>
+ <function name="virtualFun" fullname="TestQDoc::TestDerived::virtualFun" href="testqdoc-testderived.html#virtualFun" status="active" access="public" location="testcpp.h" documented="true" meta="plain" virtual="virtual" override="true" type="void" signature="void virtualFun() override">
+ <description/>
+ </function>
+ <typedef name="DerivedType" fullname="TestQDoc::TestDerived::DerivedType" href="testqdoc-testderived.html#DerivedType-typedef" status="active" access="public" location="testcpp.h" documented="true" aliasedtype="Test::SomeType">
+ <description>
+ <para>An aliased typedef.</para>
+ </description>
+ </typedef>
+ <typedef name="NotTypedef" fullname="TestQDoc::TestDerived::NotTypedef" href="testqdoc-testderived.html#NotTypedef-typedef" status="active" access="public" location="testcpp.h" documented="true" aliasedtype="int">
+ <description>
+ <para>I'm an alias, not a typedef.</para>
+ </description>
+ </typedef>
+ </class>
+ <class name="Vec" fullname="TestQDoc::Vec" href="testqdoc-vec.html" status="active" access="public" location="testcpp.h" since="2.0" documented="true" module="TestCPP" brief="Type alias that has its own reference">
+ <description>
+ <brief>Type alias that has its own reference.</brief>
+ </description>
+ </class>
+ </namespace>
+ </document>
+</WebXML>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/testtemplate/expected/webxml/testtemplate.index b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/testtemplate/expected/webxml/testtemplate.index
new file mode 100644
index 000000000..34346db56
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/testtemplate/expected/webxml/testtemplate.index
@@ -0,0 +1,87 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE QDOCINDEX>
+<INDEX url="" title="TestTemplate Reference Documentation" version="" project="TestTemplate">
+ <namespace name="" status="active" access="public" module="testtemplate">
+ <function name="QDOCTEST_MACRO" href="testqdoc.html#QDOCTEST_MACRO" status="active" access="public" documented="true" related="0" meta="macrowithoutparams" signature="QDOCTEST_MACRO"/>
+ <function name="QDOCTEST_MACRO2" href="testqdoc-test.html#QDOCTEST_MACRO2" status="active" access="public" documented="true" related="1" since="Test 1.1" meta="macrowithparams" brief="A macro with argument x" signature="QDOCTEST_MACRO2(int &amp;x)" groups="testgroup">
+ <parameter type="int &amp;" name="x" default=""/>
+ </function>
+ <function name="Q_INVOKABLE" href="testqdoc-test.html#Q_INVOKABLE" status="active" access="public" documented="true" related="2" meta="macrowithoutparams" signature="Q_INVOKABLE"/>
+ <page name="autolinking.html" href="autolinking.html" status="active" location="classlists.qdoc" documented="true" subtype="page" title="Autolinking" fulltitle="Autolinking" subtitle="">
+ <contents name="testqdoc" title="TestQDoc" level="1"/>
+ <contents name="someprop" title="someProp" level="1"/>
+ </page>
+ <class name="Bar" href="bar.html" status="active" access="public" location="testtemplate.h" since="2.0" documented="true" module="TestCPP" brief="Another class template"/>
+ <struct name="Baz" href="baz.html" status="active" access="public" location="testtemplate.h" since="2.0" documented="true" module="TestCPP" brief="Class template template"/>
+ <namespace name="CrossModuleRef" href="crossmoduleref.html" status="active" access="public" location="testcpp.h" since="3.0" documented="true" module="TestCPP" brief="Namespace that has documented functions in multiple modules">
+ <function name="documentMe" fullname="CrossModuleRef::documentMe" href="crossmoduleref.html#documentMe" status="active" access="public" location="testcpp.h" documented="true" meta="plain" type="void" signature="void documentMe()"/>
+ </namespace>
+ <class name="DontLinkToMe" href="dontlinktome.html" status="ignored" access="public" location="testcpp.h" since="2.0" documented="true" module="TestCPP" brief="Class that does not generate documentation"/>
+ <class name="Foo" href="foo.html" status="active" access="public" location="testtemplate.h" since="2.0" documented="true" module="TestCPP" brief="Class template"/>
+ <page name="obsolete-classes.html" href="obsolete-classes.html" status="active" location="classlists.qdoc" documented="true" subtype="page" title="Obsolete Classes" fulltitle="Obsolete Classes" subtitle="">
+ <contents name="classes-with-obsolete-members" title="Classes with obsolete members" level="1"/>
+ <contents name="testqdoc" title="TestQDoc" level="2"/>
+ </page>
+ <namespace name="TestQDoc" href="testqdoc.html" status="active" access="public" location="testcpp.h" since="2.0" documented="true" module="TestCPP" brief="A namespace">
+ <contents name="usage" title="Usage" level="1"/>
+ <function name="QDOCTEST_MACRO" href="testqdoc.html#QDOCTEST_MACRO" status="active" access="public" documented="true" related="0" meta="macrowithoutparams" signature="QDOCTEST_MACRO"/>
+ <class threadsafety="reentrant" name="Test" fullname="TestQDoc::Test" href="testqdoc-test.html" status="active" access="public" location="testcpp.h" since="2.0" documented="true" groups="cpptypes,testgroup" module="TestCPP" brief="A class in a namespace">
+ <function name="QDOCTEST_MACRO2" href="testqdoc-test.html#QDOCTEST_MACRO2" status="active" access="public" documented="true" related="1" since="Test 1.1" meta="macrowithparams" brief="A macro with argument x" signature="QDOCTEST_MACRO2(int &amp;x)" groups="testgroup">
+ <parameter type="int &amp;" name="x" default=""/>
+ </function>
+ <function name="Q_INVOKABLE" href="testqdoc-test.html#Q_INVOKABLE" status="active" access="public" documented="true" related="2" meta="macrowithoutparams" signature="Q_INVOKABLE"/>
+ <function name="anotherObsoleteMember" fullname="TestQDoc::Test::anotherObsoleteMember" href="testqdoc-test-obsolete.html#anotherObsoleteMember" status="deprecated" access="public" location="testcpp.h" documented="true" meta="plain" type="void" signature="void anotherObsoleteMember()"/>
+ <function name="deprecatedMember" fullname="TestQDoc::Test::deprecatedMember" href="testqdoc-test-obsolete.html#deprecatedMember" status="deprecated" access="public" location="testcpp.h" documented="true" meta="plain" type="void" signature="void deprecatedMember()"/>
+ <function name="funcPtr" fullname="TestQDoc::Test::funcPtr" href="testqdoc-test.html#funcPtr" status="active" access="public" location="testcpp.h" documented="true" meta="plain" type="void (*)(bool)" signature="void (*)(bool) funcPtr(bool b, const char *s)">
+ <parameter type="bool" name="b" default=""/>
+ <parameter type="const char *" name="s" default=""/>
+ </function>
+ <function name="funcTemplate" fullname="TestQDoc::Test::funcTemplate" href="testqdoc-test.html#funcTemplate" status="active" access="protected" location="testcpp.h" documented="true" meta="plain" type="void" brief="Function template with two parameters, a and b" signature="void funcTemplate(T1 a, T2 b)">
+ <parameter type="T1" name="a" default=""/>
+ <parameter type="T2" name="b" default=""/>
+ </function>
+ <function name="inlineFunction" fullname="TestQDoc::Test::inlineFunction" href="testqdoc-test.html#inlineFunction" status="active" access="public" location="testcpp.h" documented="true" meta="plain" type="void" brief="An inline function, documented using the \fn QDoc command" signature="void inlineFunction()"/>
+ <function name="methodWithEmDashInItsDocs" fullname="TestQDoc::Test::methodWithEmDashInItsDocs" href="testqdoc-test.html#methodWithEmDashInItsDocs" status="active" access="public" location="testcpp.h" documented="true" meta="plain" type="void" signature="void methodWithEmDashInItsDocs()"/>
+ <function name="methodWithEnDashInItsDocs" fullname="TestQDoc::Test::methodWithEnDashInItsDocs" href="testqdoc-test.html#methodWithEnDashInItsDocs" status="active" access="public" location="testcpp.h" documented="true" meta="plain" type="void" signature="void methodWithEnDashInItsDocs()"/>
+ <function name="obsoleteMember" fullname="TestQDoc::Test::obsoleteMember" href="testqdoc-test-obsolete.html#obsoleteMember" status="deprecated" access="public" location="testcpp.h" documented="true" meta="plain" type="void" signature="void obsoleteMember()"/>
+ <function name="operator++" fullname="TestQDoc::Test::operator++" href="testqdoc-test-obsolete.html#operator-2b-2b" status="deprecated" access="public" location="testcpp.h" documented="true" meta="plain" type="TestQDoc::Test &amp;" signature="TestQDoc::Test &amp; operator++()"/>
+ <function name="operator--" fullname="TestQDoc::Test::operator--" href="testqdoc-test-obsolete.html#operator--" status="deprecated" access="public" location="testcpp.h" documented="true" meta="plain" type="TestQDoc::Test &amp;" signature="TestQDoc::Test &amp; operator--()"/>
+ <function name="operator==" href="testqdoc-test.html#operator-eq-eq" status="active" access="public" location="testcpp.h" documented="true" related="3" meta="plain" type="bool" signature="bool operator==(const TestQDoc::Test &amp;lhs, const TestQDoc::Test &amp;rhs)">
+ <parameter type="const TestQDoc::Test &amp;" name="lhs" default=""/>
+ <parameter type="const TestQDoc::Test &amp;" name="rhs" default=""/>
+ </function>
+ <function name="overload" fullname="TestQDoc::Test::overload" href="testqdoc-test.html#overload" status="active" access="protected" location="testcpp.h" documented="true" meta="plain" type="void" signature="void overload()"/>
+ <function name="overload" fullname="TestQDoc::Test::overload" href="testqdoc-test.html#overload-1" status="active" access="protected" location="testcpp.h" documented="true" since="Test 1.2" meta="plain" overload="true" overload-number="1" type="void" signature="void overload(bool b)">
+ <parameter type="bool" name="b" default=""/>
+ </function>
+ <function name="someFunction" fullname="TestQDoc::Test::someFunction" href="testqdoc-test.html#someFunction" status="active" access="public" location="testcpp.h" documented="true" meta="plain" type="int" signature="int someFunction(int, int v)">
+ <parameter type="int" name="" default=""/>
+ <parameter type="int" name="v" default="0"/>
+ </function>
+ <function name="someFunctionDefaultArg" fullname="TestQDoc::Test::someFunctionDefaultArg" href="testqdoc-test.html#someFunctionDefaultArg" threadsafety="non-reentrant" status="active" access="public" location="testcpp.h" documented="true" meta="plain" const="true" type="void" signature="void someFunctionDefaultArg(int i, bool b) const" groups="testgroup">
+ <parameter type="int" name="i" default=""/>
+ <parameter type="bool" name="b" default="false"/>
+ </function>
+ <function name="virtualFun" fullname="TestQDoc::Test::virtualFun" href="testqdoc-test.html#virtualFun" status="active" access="public" location="testcpp.h" documented="true" meta="plain" virtual="virtual" type="void" signature="void virtualFun()"/>
+ <struct name="Struct" fullname="TestQDoc::Test::Struct" href="testqdoc-test-struct.html" status="active" access="public" location="testcpp.h" since="2.0" documented="true" module="TestCPP" brief="Templated struct"/>
+ <typedef name="SomeType" fullname="TestQDoc::Test::SomeType" href="testqdoc-test.html#SomeType-typedef" status="active" access="public" location="testcpp.h" documented="true"/>
+ <typedef name="Specialized" fullname="TestQDoc::Test::Specialized" href="testqdoc-test.html#Specialized-typedef" status="active" access="public" location="testcpp.h" documented="true" aliasedtype="Struct&lt;int, T&gt;"/>
+ </class>
+ <class name="TestDerived" fullname="TestQDoc::TestDerived" href="testqdoc-testderived.html" status="active" access="public" location="testcpp.h" since="2.0" documented="true" bases="TestQDoc::Test" module="TestCPP" brief="A class in a namespace, derived from Test">
+ <function name="someValue" fullname="TestQDoc::TestDerived::someValue" href="testqdoc-testderived.html#someValue" status="active" access="public" location="testcpp.h" documented="true" meta="plain" type="TestQDoc::TestDerived::NotTypedef" signature="TestQDoc::TestDerived::NotTypedef someValue()"/>
+ <function name="staticObsoleteMember" fullname="TestQDoc::TestDerived::staticObsoleteMember" href="testqdoc-testderived-obsolete.html#staticObsoleteMember" status="deprecated" access="public" location="testcpp.h" documented="true" meta="plain" static="true" type="void" signature="void staticObsoleteMember()"/>
+ <function name="virtualFun" fullname="TestQDoc::TestDerived::virtualFun" href="testqdoc-testderived.html#virtualFun" status="active" access="public" location="testcpp.h" documented="true" meta="plain" virtual="virtual" override="true" type="void" signature="void virtualFun() override"/>
+ <typedef name="DerivedType" fullname="TestQDoc::TestDerived::DerivedType" href="testqdoc-testderived.html#DerivedType-typedef" status="active" access="public" location="testcpp.h" documented="true" aliasedtype="Test::SomeType"/>
+ <typedef name="NotTypedef" fullname="TestQDoc::TestDerived::NotTypedef" href="testqdoc-testderived.html#NotTypedef-typedef" status="active" access="public" location="testcpp.h" documented="true" aliasedtype="int"/>
+ </class>
+ <class name="Vec" fullname="TestQDoc::Vec" href="testqdoc-vec.html" status="active" access="public" location="testcpp.h" since="2.0" documented="true" module="TestCPP" brief="Type alias that has its own reference"/>
+ </namespace>
+ <page name="https://doc.qt.io/qt/17-qdoc-commands-thread.html#reentrant-command" href="https://doc.qt.io/qt/17-qdoc-commands-thread.html#reentrant-command" status="active" location="classlists.qdoc" documented="true" subtype="externalpage" title="reentrant" fulltitle="reentrant" subtitle=""/>
+ <group name="cpptypes" href="cpptypes.html" status="active" location="classlists.qdoc" documented="true" seen="true" title="Test C++ Types"/>
+ <group name="testgroup" href="testgroup.html" status="internal" seen="false" title=""/>
+ <module name="TestCPP" href="testcpp-module.html" status="active" since="2.0" documented="true" seen="true" title="QDoc Test C++ Classes" brief="A test module page">
+ <contents name="linking-to-function-like-things" title="Linking to function-like things" level="1"/>
+ <contents name="section" title="section()" level="2"/>
+ </module>
+ </namespace>
+</INDEX>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/testtemplate/src/classlists.qdoc b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/testtemplate/src/classlists.qdoc
new file mode 100644
index 000000000..2954e5beb
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/testtemplate/src/classlists.qdoc
@@ -0,0 +1,51 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+/*!
+ \page obsolete-classes.html
+ \title Obsolete Classes
+
+ \section1 Classes with obsolete members
+ \generatelist obsoletecppmembers
+
+ \section2 TestQDoc
+*/
+
+/*!
+ \page autolinking.html
+ \title Autolinking
+
+ //! a section title that qualifies for autolinking
+ \section1 TestQDoc
+
+ The string TestQDoc links to the C++ namespace unless linking explicitly,
+ \l {#TestQDoc}{like this}, or \l {TestQDoc}{this}. Also,
+
+ Autolinks:
+
+ \list
+ \li TestQDoc::TestDerived
+ \endlist
+
+ Explicit links:
+
+ \list
+ \li \l [CPP] {TestQDoc::TestDerived}
+ \li \l {Obsolete Classes#TestQDoc}
+ \endlist
+
+ //! a section title shadowing a known property name
+ \section1 someProp
+*/
+
+/*!
+ \group cpptypes
+ \title Test C++ Types
+
+ \generatelist testgroup
+*/
+
+/*!
+ \externalpage https://doc.qt.io/qt/17-qdoc-commands-thread.html#reentrant-command
+ \title reentrant
+*/
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/testtemplate/src/snippets/snippet_testcpp.cpp b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/testtemplate/src/snippets/snippet_testcpp.cpp
new file mode 100644
index 000000000..1660fbc2b
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/testtemplate/src/snippets/snippet_testcpp.cpp
@@ -0,0 +1,3 @@
+//! [random tag]
+You're not supposed to see this.
+//! [random tag]
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/testtemplate/src/testcpp.cpp b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/testtemplate/src/testcpp.cpp
new file mode 100644
index 000000000..25dc960ff
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/testtemplate/src/testcpp.cpp
@@ -0,0 +1,402 @@
+// Copyright (C) 2019 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+#include "testcpp.h"
+
+namespace TestQDoc {
+
+/*
+//! [random tag]
+\note This is just a test.
+//! [random tag]
+
+//! [args]
+\1\2 \3 \2\1
+//! [args]
+*/
+
+/*!
+ \module TestCPP
+ \qtvariable testcpp
+ \qtcmakepackage QDocTest
+ \title QDoc Test C++ Classes
+ \brief A test module page.
+ \since 2.0
+
+ \testnoautolist
+
+ \include testcpp.cpp random tag
+ \include testcpp.cpp {args} {/} {*} {Look, Ma! {I'm made of arguments!}}
+
+\if defined(test_nestedmacro)
+ \versionnote {module} {\ver 5.15.0}
+ \ver 1.0.0
+\endif
+
+ \section1 Linking to function-like things
+
+ \list
+ \li \l {TestQDoc::Test::someFunctionDefaultArg}
+ {someFunctionDefaultArg()}
+ \li QDOCTEST_MACRO2()
+ \li \l {TestQDoc::Test::}{QDOCTEST_MACRO2(int &x)}
+ \li \l {section()}
+ \li \l {section()} {section() is a section title}
+ \endlist
+
+ \section2 section()
+*/
+
+/*!
+ \namespace TestQDoc
+ \inheaderfile TestCPP
+ \inmodule TestCPP
+ \brief A namespace.
+
+ \section1 Usage
+ This namespace is for testing QDoc output.
+*/
+
+/*!
+ \class TestQDoc::Test
+ \inmodule TestCPP
+ \brief A class in a namespace.
+
+\if defined(test_ignoresince)
+ //! omitted by ignoresince
+ \since 1.1
+\endif
+ \ingroup testgroup
+ \ingroup cpptypes
+ \reentrant
+*/
+
+/*!
+ \class TestQDoc::TestDerived
+ \inmodule TestCPP
+ \brief A class in a namespace, derived from \l [CPP] Test.
+*/
+
+/*!
+ \macro QDOCTEST_MACRO
+ \relates TestQDoc
+\if defined(test_ignoresince)
+ //! omitted by ignoresince.Test
+ \since Test 0.9
+\endif
+*/
+
+/*!
+ \macro QDOCTEST_MACRO2(int &x)
+ \relates TestQDoc::Test
+ \since Test 1.1
+ \brief A macro with argument \a x.
+ \ingroup testgroup
+*/
+
+/*!
+\if defined(test_properties)
+ \property Test::id
+\else
+ \nothing
+\endif
+*/
+
+/*!
+ \deprecated [6.0] Use someFunction() instead.
+*/
+void Test::deprecatedMember()
+{
+ return;
+}
+
+/*!
+ \obsolete
+
+ Use someFunction() instead.
+*/
+void Test::obsoleteMember()
+{
+ return;
+}
+
+/*!
+ \obsolete Use obsoleteMember() instead.
+*/
+void Test::anotherObsoleteMember()
+{
+ return;
+}
+
+/*!
+ \nonreentrant
+ Function that takes a parameter \a i and \a b.
+\if defined(test_ignoresince)
+ \since 2.0
+\endif
+ \ingroup testgroup
+*/
+void Test::someFunctionDefaultArg(int i, bool b = false) const
+{
+ return;
+}
+
+/*!
+ \fn void Test::func(bool)
+ \internal
+*/
+
+/*!
+ \fn [funcPtr] void (*funcPtr(bool b, const char *s))(bool)
+
+ Returns a pointer to a function that takes a boolean. Uses \a b and \a s.
+*/
+
+/*!
+ \fn [op-inc] Test::operator++()
+ \fn [op-dec] Test::operator--()
+ \deprecated
+*/
+
+/*!
+ This method has en dashes in its documentation -- as you'll find
+ represented by \c{--} in the sources -- here and there. The important bit
+ to note is that when passed e.g. to the \\c command, the two hyphens are
+ processed as input to the command and not replaced by an en dash. This also
+ applies to code blocks, where otherwise, the decrement operator would get
+ completely borked:
+
+ \code
+ for (int i = 42; i > 0; --i)
+ // Do something cool during countdown.
+ \endcode
+
+ ...as it would be silly if this would output --i instead of \c {--i}.
+
+ -----------------------------------------------------------------------
+
+ It still allows people to add a bunch of dashes, though, without replacing
+ them all with a series of en dashes. Of course, they might want to use the
+ \\hr command instead, like this:
+ \hr
+
+ -- You can also start a new paragraph with an en dash, if you want to.
+
+ //! Self-referencing \sa-command for tests.
+ \sa methodWithEnDashInItsDocs
+*/
+void Test::methodWithEnDashInItsDocs()
+{
+ // Nothing to see here.
+}
+
+/*!
+ This method has em dashes in its documentation---as you'll find
+ represented by \c{---} in the sources---here and there. The important bit
+ to note is that when passed e.g. to the \\c command, the three hyphens are
+ processed as input to the command and not replaced by an em dash.
+
+ -----------------------------------------------------------------------
+
+ People can still add a bunch of dashes, though, without QDoc replacing
+ them all with a series of em dashes.
+
+ ---You can also start a new paragraph with an em dash, if you want to.
+
+ \sa methodWithEnDashInItsDocs
+
+*/
+void Test::methodWithEmDashInItsDocs()
+{
+ // Woah! Look at that!
+}
+
+// Documented below with an \fn command. Unnecessary but we support it, and it's used.
+int Test::someFunction(int, int v)
+{
+ return v;
+}
+
+/*!
+ \fn void TestQDoc::Test::inlineFunction()
+
+ \brief An inline function, documented using the \CMDFN QDoc command.
+*/
+
+/*!
+ \fn int Test::someFunction(int, int v = 0)
+
+ Function that takes a parameter \a v.
+ Also returns the value of \a v.
+\if defined(test_ignoresince)
+ \since Test 1.0
+\endif
+*/
+
+/*!
+ Function that must be reimplemented.
+*/
+void Test::virtualFun()
+{
+ return;
+}
+
+/*!
+ \fn bool Test::operator==(const Test &lhs, const Test &rhs)
+
+ Returns true if \a lhs and \a rhs are equal.
+*/
+
+/*!
+ \typedef TestQDoc::Test::SomeType
+ \brief A typedef.
+*/
+
+/*!
+ \reimp
+*/
+void TestDerived::virtualFun()
+{
+ return;
+}
+
+/*!
+ \fn TestQDoc::Test::overload()
+ \fn Test::overload(bool b)
+ //! The second overload should match even without the fully qualified path
+
+ Overloads that share a documentation comment, optionally taking
+ a parameter \a b.
+*/
+
+/*!
+ \fn Test::overload(bool b)
+ \since Test 1.2
+*/
+
+/*!
+ \typealias TestQDoc::TestDerived::DerivedType
+ An aliased typedef.
+*/
+
+/*!
+ \typedef TestQDoc::TestDerived::NotTypedef
+ I'm an alias, not a typedef.
+*/
+
+/*!
+ \obsolete
+
+ Static obsolete method.
+*/
+void TestDerived::staticObsoleteMember()
+{
+ return;
+}
+
+/*!
+\if defined(test_properties)
+ \fn void TestDerived::emitSomething()
+ Emitted when things happen.
+\else
+ \nothing
+\endif
+*/
+
+/*!
+\if defined(test_properties)
+ \reimp
+\else
+ \nothing
+\endif
+*/
+int TestDerived::id()
+{
+ return 1;
+}
+
+/*!
+ Returns a value using an aliases type.
+*/
+TestDerived::NotTypedef TestDerived::someValue()
+{
+ return 0;
+}
+
+/*!
+\if defined(test_template)
+ \fn template <typename T1, typename T2> void TestQDoc::Test::funcTemplate(T1 a, T2 b)
+ \brief Function template with two parameters, \a a and \a b.
+\else
+ \nothing
+\endif
+*/
+
+/*!
+\if defined(test_template)
+ \struct TestQDoc::Test::Struct
+ \inmodule TestCPP
+ \brief Templated struct.
+\else
+ \nothing
+\endif
+*/
+
+/*!
+\if defined(test_template)
+ \typealias TestQDoc::Test::Specialized
+\else
+ \nothing
+\endif
+*/
+
+/*!
+\if defined(test_template)
+ \class TestQDoc::Vec
+ \inmodule TestCPP
+ \brief Type alias that has its own reference.
+\else
+ \nothing
+\endif
+*/
+
+/*!
+\if defined(test_template)
+ \macro Q_INVOKABLE
+ \relates TestQDoc::Test
+
+ This is a mock Q_INVOKABLE for the purpose of ensuring QDoc autolink to it
+ as expected.
+\else
+ \nothing
+\endif
+*/
+
+} // namespace TestQDoc
+
+
+/*!
+ \namespace CrossModuleRef
+ \inmodule TestCPP
+ \brief Namespace that has documented functions in multiple modules.
+ \since 3.0
+*/
+namespace CrossModuleRef {
+
+/*!
+ Document me!
+*/
+void documentMe()
+{
+}
+
+} // namespace CrossModuleRef
+
+/*!
+ \class DontLinkToMe
+ \inmodule TestCPP
+ \brief Class that does not generate documentation.
+*/
+
+/*!
+ \dontdocument (DontLinkToMe)
+*/
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/testtemplate/src/testcpp.h b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/testtemplate/src/testcpp.h
new file mode 100644
index 000000000..06126494a
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/testtemplate/src/testcpp.h
@@ -0,0 +1,137 @@
+// Copyright (C) 2019 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+#pragma once
+
+#ifdef test_properties
+#include <QtCore/qmetaobject.h>
+#include <QtCore/qproperty.h>
+#include <QtCore/qstring.h>
+#endif
+
+#define QDOCTEST_MACRO test
+#define QDOCTEST_MACRO2(x) (x) < 0 ? 0 : (x))
+
+namespace TestQDoc {
+
+class Test {
+#ifdef test_properties
+ Q_OBJECT
+ Q_PROPERTY(int id READ id)
+#endif
+public:
+
+#ifdef test_template
+template<typename D, typename T> struct Struct {};
+template<typename T>
+using Specialized = Struct<int, T>;
+#endif
+
+#ifdef test_template
+# define Q_INVOKABLE void foo() {};
+#endif
+
+#ifdef test_scopedenum
+ enum ClassicEnum { Yee, Haw, Howdy, Partner };
+
+ enum class ScopedEnum : unsigned char {
+ This = 0x01,
+ That = 0x02,
+ All = This | That,
+ OmittedValue = 99,
+ UselessValue,
+ VeryLastValue
+ };
+#endif
+ typedef struct {
+ int data;
+ } SomeType;
+ int someFunction(int, int v = 0);
+ void someFunctionDefaultArg(int i, bool b) const;
+ void obsoleteMember();
+ void anotherObsoleteMember();
+ void deprecatedMember();
+ void methodWithEnDashInItsDocs();
+ void methodWithEmDashInItsDocs();
+ void func(bool) {};
+ //! [funcPtr]
+ void (*funcPtr(bool b, const char *s))(bool) {
+ return func;
+ }
+ //! [op-inc]
+ Test &operator++() { return *this; }
+ //! [op-dec]
+ Test &operator--() { return *this; }
+
+ void anotherFunc() {};
+ inline void inlineFunction() {};
+ virtual void virtualFun();
+
+ friend bool operator==(const Test &lhs, const Test &rhs) { return false; }
+
+protected:
+ void overload() {}
+ void overload(bool b) { if (!b) return; }
+#ifdef test_template
+ template <typename T1, typename T2> void funcTemplate(T1 a, T2 b) {
+ a = b;
+ }
+#endif
+#ifdef test_properties
+ virtual int id() { return 0; }
+#endif
+};
+
+class TestDerived : public Test {
+#ifdef test_properties
+ Q_OBJECT
+
+ Q_PROPERTY(QString bindableProp READ bindableProp WRITE setBindableProp NOTIFY bindablePropChanged BINDABLE bindableProp)
+ Q_PROPERTY(QString someProp READ someProp BINDABLE somBindableProp)
+ Q_PROPERTY(int *intProp READ getInt STORED false CONSTANT FINAL)
+ Q_PROPERTY(const QString *name READ name)
+ QDOC_PROPERTY(bool boolProp READ boolProp WRITE setBoolProp NOTIFY boolPropChanged RESET resetBoolProp REVISION 1)
+#endif
+
+public:
+ using DerivedType = Test::SomeType;
+ using NotTypedef = int;
+ void virtualFun() override;
+ static void staticObsoleteMember();
+ NotTypedef someValue();
+#ifdef test_properties
+ QBindable<QString> bindableProp();
+ QBindable<QString> someBindableProp();
+ const QString &someProp();
+ int *getInt();
+ bool boolProp();
+ const QString *name() const;
+
+ Q_INVOKABLE void invokeMe() const {}
+ int id() override;
+
+Q_SIGNALS:
+ void emitSomething(QPrivateSignal);
+ void bindablePropChanged();
+ Q_REVISION(1) void boolPropChanged();
+
+public Q_SLOTS:
+ void setBindableProp(const QString &s);
+ void setBoolProp(bool b);
+ void resetBoolProp();
+#endif
+};
+
+#ifdef test_template
+template <typename T>
+struct BaseVec {};
+template <typename T>
+using Vec = BaseVec<T>;
+#endif
+
+} // namespace TestQDoc
+
+namespace CrossModuleRef {
+ void documentMe();
+}
+
+class DontLinkToMe {};
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/testtemplate/src/testtemplate.cpp b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/testtemplate/src/testtemplate.cpp
new file mode 100644
index 000000000..04be329ea
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/testtemplate/src/testtemplate.cpp
@@ -0,0 +1,23 @@
+// Copyright (C) 2020 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "testtemplate.h"
+
+/*!
+ \class Foo
+ \inmodule TestCPP
+ \brief Class template.
+*/
+
+/*!
+ \class Bar
+ \inmodule TestCPP
+ \brief Another class template.
+*/
+
+/*!
+ //! Baz is a struct, QDoc auto-converts this to the correct type
+ \class Baz
+ \inmodule TestCPP
+ \brief Class template template.
+*/
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/testtemplate/src/testtemplate.h b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/testtemplate/src/testtemplate.h
new file mode 100644
index 000000000..68c164e25
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/testtemplate/src/testtemplate.h
@@ -0,0 +1,28 @@
+// Copyright (C) 2020 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#pragma once
+
+template <typename T>
+class Foo {
+public:
+ Foo() {}
+private:
+ T t;
+};
+
+template <typename T, typename D>
+class Bar {
+public:
+ Bar() {}
+private:
+ T t;
+ D d;
+};
+
+template<template<typename> class X, typename Y>
+struct Baz
+{
+ X<Y> z;
+ Baz() : z() {}
+};
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/testtemplate/testtemplate.qdocconf b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/testtemplate/testtemplate.qdocconf
new file mode 100644
index 000000000..188c268bd
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/testtemplate/testtemplate.qdocconf
@@ -0,0 +1,38 @@
+sources.fileextensions = "*.qml *.cpp *.qdoc"
+headers.fileextensions = "*.h"
+
+# images
+imagedirs = ./src/images
+
+# zero warning policy
+warninglimit = 0
+warninglimit.enabled = true
+
+# don't write host system-specific paths to index files
+locationinfo = false
+
+project = TestTemplate
+includepaths += -I./src
+
+headers = ./src/testcpp.h \
+ ./src/testtemplate.h
+sources = ./src/testcpp.cpp \
+ ./src/testtemplate.cpp \
+ ./src/classlists.qdoc
+exampledirs = ./src/snippets
+
+macro.CMDFN = \\\\fn
+macro.nothing = \\dontdocument ()
+macro.testnoautolist = \\if defined(test_noautolist)\n\\noautolist\n\\endif
+
+navigation.cppclassespage = "QDoc Test C++ Classes"
+
+defines += test_template
+
+outputformats = HTML DocBook WebXML
+HTML.nosubdirs = true
+HTML.outputsubdir = html
+DocBook.nosubdirs = true
+DocBook.outputsubdir = docbook
+WebXML.nosubdirs = true
+WebXML.outputsubdir = webxml
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/tocnavigation/expected/docbook/crash.xml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/tocnavigation/expected/docbook/crash.xml
new file mode 100644
index 000000000..542b9ce59
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/tocnavigation/expected/docbook/crash.xml
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<db:article xmlns:db="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" version="5.2" xml:lang="en">
+<db:info>
+<db:title></db:title>
+<db:productname>OutputFromQDocFiles</db:productname>
+<db:edition>OutputFromQDocFiles - A test project for QDoc build artifacts</db:edition>
+<db:titleabbrev>A test project for QDoc build artifacts</db:titleabbrev>
+<db:extendedlink xlink:type="extended"><db:link xlink:to="qdoctests-qdocfileoutput-linking.xml" xlink:type="arc" xlink:arcrole="prev" xlink:title="QDoc Linking Test"/></db:extendedlink>
+<db:extendedlink xlink:type="extended"><db:link xlink:to="toc.xml" xlink:type="arc" xlink:arcrole="next" xlink:title="Table of Contents"/></db:extendedlink>
+<db:abstract>
+<db:para>A test project for QDoc build artifacts.</db:para></db:abstract>
+</db:info>
+<db:para><db:link xlink:href=""></db:link></db:para>
+</db:article>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/tocnavigation/expected/docbook/images/01.png b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/tocnavigation/expected/docbook/images/01.png
new file mode 100644
index 000000000..d73ab969b
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/tocnavigation/expected/docbook/images/01.png
Binary files differ
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/tocnavigation/expected/docbook/images/leonardo-da-vinci.png b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/tocnavigation/expected/docbook/images/leonardo-da-vinci.png
new file mode 100644
index 000000000..854acb4ca
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/tocnavigation/expected/docbook/images/leonardo-da-vinci.png
Binary files differ
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/tocnavigation/expected/docbook/qdoctests-qdocfileoutput-exhaustive.xml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/tocnavigation/expected/docbook/qdoctests-qdocfileoutput-exhaustive.xml
new file mode 100644
index 000000000..f7aff1e27
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/tocnavigation/expected/docbook/qdoctests-qdocfileoutput-exhaustive.xml
@@ -0,0 +1,131 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<db:article xmlns:db="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" version="5.2" xml:lang="en">
+<db:info>
+<db:title>Exhaustive testing of QDoc commands</db:title>
+<db:productname>OutputFromQDocFiles</db:productname>
+<db:edition>OutputFromQDocFiles - A test project for QDoc build artifacts</db:edition>
+<db:titleabbrev>A test project for QDoc build artifacts</db:titleabbrev>
+<db:extendedlink xlink:type="extended"><db:link xlink:to="qdoctests-qdocfileoutput.xml" xlink:type="arc" xlink:arcrole="prev" xlink:title="QDoc Testing"/></db:extendedlink>
+<db:extendedlink xlink:type="extended"><db:link xlink:to="qdoctests-qdocfileoutput-linking.xml" xlink:type="arc" xlink:arcrole="next" xlink:title="QDoc Linking Test"/></db:extendedlink>
+<db:abstract>
+<db:para>This page is a dumping ground for QDoc commands under test.</db:para>
+</db:abstract>
+</db:info>
+<db:section xml:id="this-is-a-section1">
+<db:title>This is a section1</db:title>
+<db:section xml:id="this-is-a-section2">
+<db:title>This is a section2</db:title>
+<db:section xml:id="this-is-a-section3">
+<db:title>This is a section3</db:title>
+<db:section xml:id="this-is-a-section4">
+<db:title>This is a section4</db:title>
+<db:programlisting language="cpp" role="bad">This is bad code
+</db:programlisting>
+<db:para>This text should have a line break riiiiight noooow.</db:para>
+<db:para><db:emphasis role="bold">All your text belong to bold</db:emphasis> ...And this is an examble of only <db:emphasis role="bold">bold</db:emphasis> being, well, bold.</db:para>
+<db:programlisting language="cpp"> ...
+</db:programlisting>
+<db:title>This a caption</db:title>
+<db:para>Lorem legal ipsum</db:para>
+<db:blockquote><db:para>This is a quotation.</db:para>
+</db:blockquote>
+<db:sidebar><db:para>Look, ma! I made a sidebar!</db:para>
+</db:sidebar>
+<db:informaltable style="generic">
+<db:tr valign="top">
+<db:td>
+<db:para>Table item in a table row</db:para>
+</db:td>
+</db:tr>
+<db:tr valign="top">
+<db:td>
+<db:para>Another item in a different row</db:para>
+</db:td>
+</db:tr>
+</db:informaltable>
+<db:important>
+<db:para>This is really important.</db:para>
+</db:important>
+<db:note>
+<db:para>The code above doesn't compile</db:para>
+</db:note>
+</db:section>
+</db:section>
+</db:section>
+</db:section>
+<db:section xml:id="images">
+<db:title>Images</db:title>
+<db:para>An image without any text:</db:para>
+<db:mediaobject>
+<db:imageobject>
+<db:imagedata fileref="images/leonardo-da-vinci.png"/>
+</db:imageobject>
+</db:mediaobject>
+<db:para>An image with just an alternative text:</db:para>
+<db:mediaobject>
+<db:alt>Image alt</db:alt>
+<db:imageobject>
+<db:imagedata fileref="images/leonardo-da-vinci.png"/>
+</db:imageobject>
+</db:mediaobject>
+<db:para>An image with alternative text and 1-atom caption:</db:para>
+<db:figure>
+<db:title>Image caption</db:title>
+<db:mediaobject>
+<db:alt>Image alt</db:alt>
+<db:imageobject>
+<db:imagedata fileref="images/leonardo-da-vinci.png"/>
+</db:imageobject>
+</db:mediaobject>
+</db:figure>
+<db:para>An image with alternative text and 2-atom caption:</db:para>
+<db:figure>
+<db:title>Image caption with <db:emphasis role="bold">bold</db:emphasis> text</db:title>
+<db:mediaobject>
+<db:alt>Image alt</db:alt>
+<db:imageobject>
+<db:imagedata fileref="images/leonardo-da-vinci.png"/>
+</db:imageobject>
+</db:mediaobject>
+</db:figure>
+<db:para>A bordered image:</db:para>
+<db:mediaobject>
+<db:imageobject>
+<db:imagedata fileref="images/leonardo-da-vinci.png"/>
+</db:imageobject>
+</db:mediaobject>
+<db:para>A bordered image with a caption:</db:para>
+<db:figure>
+<db:title>Screenshot of the System Tray Icon</db:title>
+<db:mediaobject>
+<db:imageobject>
+<db:imagedata fileref="images/leonardo-da-vinci.png"/>
+</db:imageobject>
+</db:mediaobject>
+</db:figure>
+<db:para>An inline image:</db:para>
+<db:para>The is a paragraph containing an <db:inlinemediaobject>
+<db:imageobject>
+<db:imagedata fileref="images/01.png"/>
+</db:imageobject>
+</db:inlinemediaobject> inline image to test if qdoc handles them properly, without considering rest of the line as alt text for the image.</db:para>
+<db:para>An inline image with alt text:</db:para>
+<db:para>Here is another example of <db:inlinemediaobject>
+<db:alt>No. 1</db:alt>
+<db:imageobject>
+<db:imagedata fileref="images/01.png"/>
+</db:imageobject>
+</db:inlinemediaobject> inline image with alternative text, which should be added as an attribute to the inline image.</db:para>
+<db:para>File quoting:</db:para>
+<db:programlisting language="cpp"> if (false) {
+ return 1;
+ }
+</db:programlisting>
+</db:section>
+<db:section xml:id="commands-not-yet-tested">
+<db:title>Commands not yet tested</db:title>
+<db:warning>
+<db:para>The following commands have yet to be tested: footnote link sincelist header index topicref // or just don’t care, remove it inlineimage printline printto quotefile skipline skipuntil span snippet codeline overload sub sup tableofcontents tt uicontrol endmapref endomit underline unicode</db:para>
+</db:warning>
+</db:section>
+</db:article>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/tocnavigation/expected/docbook/qdoctests-qdocfileoutput-linking.xml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/tocnavigation/expected/docbook/qdoctests-qdocfileoutput-linking.xml
new file mode 100644
index 000000000..f7a8c6f59
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/tocnavigation/expected/docbook/qdoctests-qdocfileoutput-linking.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<db:article xmlns:db="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" version="5.2" xml:lang="en">
+<db:info>
+<db:title>Testing QDoc's link command</db:title>
+<db:productname>OutputFromQDocFiles</db:productname>
+<db:edition>OutputFromQDocFiles - A test project for QDoc build artifacts</db:edition>
+<db:titleabbrev>A test project for QDoc build artifacts</db:titleabbrev>
+<db:extendedlink xlink:type="extended"><db:link xlink:to="qdoctests-qdocfileoutput-exhaustive.xml" xlink:type="arc" xlink:arcrole="prev" xlink:title="Exhaustive testing of QDoc commands"/></db:extendedlink>
+<db:extendedlink xlink:type="extended"><db:link xlink:to="" xlink:type="arc" xlink:arcrole="next" xlink:title="Random page"/></db:extendedlink>
+<db:abstract>
+<db:para>This is a page for testing QDoc's link command.</db:para>
+</db:abstract>
+</db:info>
+<db:anchor xml:id="link-test-target"/>
+<db:section xml:id="link-targets">
+<db:title>Link targets</db:title>
+<db:para>Valid parameters for the link command (<db:code>\l</db:code>) are page and section titles, targets defined with \target or \keyword commands, and API reference keywords (types, methods, namespaces, and so on).</db:para>
+</db:section>
+</db:article>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/tocnavigation/expected/docbook/qdoctests-qdocfileoutput.xml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/tocnavigation/expected/docbook/qdoctests-qdocfileoutput.xml
new file mode 100644
index 000000000..81b07bf82
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/tocnavigation/expected/docbook/qdoctests-qdocfileoutput.xml
@@ -0,0 +1,80 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<db:article xmlns:db="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" version="5.2" xml:lang="en">
+<db:info>
+<db:title>Testing QDoc output from .qdoc files</db:title>
+<db:productname>OutputFromQDocFiles</db:productname>
+<db:edition>OutputFromQDocFiles - A test project for QDoc build artifacts</db:edition>
+<db:titleabbrev>A test project for QDoc build artifacts</db:titleabbrev>
+<db:extendedlink xlink:type="extended"><db:link xlink:to="qdoctests-qdocfileoutput-exhaustive.xml" xlink:type="arc" xlink:arcrole="next" xlink:title="Exhaustive testing of QDoc commands"/></db:extendedlink>
+<db:abstract>
+<db:para>This is a simple page for testing purposes only.</db:para>
+</db:abstract>
+</db:info>
+<db:para>QDoc generates documentation for software projects. It does this by extracting <db:emphasis>QDoc comments</db:emphasis> from project source files. QDoc comments are signified by a C-style-like comment tag followed by an exclamation point, like this: <db:code>/*!</db:code> <db:code>This text is contained within QDoc comment tags.</db:code> <db:code>*/</db:code>.</db:para>
+<db:section xml:id="supported-file-types">
+<db:title>Supported file types</db:title>
+<db:para>QDoc parses <db:code>.cpp</db:code> and <db:code>.qdoc</db:code> files. It does extract comments from header (<db:code>.h</db:code>) files.</db:para>
+</db:section>
+<db:section xml:id="further-information">
+<db:title>Further information</db:title>
+<db:para>This test document is written with the purpose of testing the output QDoc generates when parsing <db:code>.qdoc</db:code> files. It is fairly simple and makes use of a limited subset of QDoc's command. Those commands are:</db:para>
+<db:itemizedlist>
+<db:listitem>
+<db:para><db:code>\page</db:code></db:para>
+</db:listitem>
+<db:listitem>
+<db:para><db:code>\title</db:code></db:para>
+</db:listitem>
+<db:listitem>
+<db:para><db:code>\brief</db:code></db:para>
+</db:listitem>
+<db:listitem>
+<db:para><db:code>\e</db:code> (for emphasizing &quot;QDoc comments&quot;)</db:para>
+</db:listitem>
+<db:listitem>
+<db:para><db:code>\c</db:code> (for multiple monospace-formatted entries)</db:para>
+</db:listitem>
+<db:listitem>
+<db:para><db:code>\section1</db:code></db:para>
+</db:listitem>
+<db:listitem>
+<db:para><db:code>\list</db:code></db:para>
+</db:listitem>
+<db:listitem>
+<db:para><db:code>\li</db:code></db:para>
+</db:listitem>
+<db:listitem>
+<db:para><db:code>\endlist</db:code></db:para>
+</db:listitem>
+</db:itemizedlist>
+</db:section>
+<db:section xml:id="linking">
+<db:title>Linking</db:title>
+<db:para>There are multiple ways to create hyperlinks to other topics:</db:para>
+<db:itemizedlist>
+<db:listitem>
+<db:para><db:link xlink:href="qdoctests-qdocfileoutput-linking.xml">Linking to a page title</db:link></db:para>
+</db:listitem>
+<db:listitem>
+<db:para><db:link xlink:href="qdoctests-qdocfileoutput-linking.xml#link-targets">Linking to a section title</db:link></db:para>
+</db:listitem>
+<db:listitem>
+<db:para><db:link xlink:href="qdoctests-qdocfileoutput-linking.xml#link-test-target">Linking using a \target string</db:link></db:para>
+</db:listitem>
+<db:listitem>
+<db:para><db:link xlink:href="qdoctests-qdocfileoutput-linking.xml">Linking using a \keyword string</db:link></db:para>
+</db:listitem>
+</db:itemizedlist>
+</db:section>
+<db:section xml:id="qdoc-linking-test">
+<db:title>QDoc Linking Test</db:title>
+<db:para>This section title is overridden by another target which takes precedence.</db:para>
+</db:section>
+<db:section xml:id="linking-to-something-in-a-section-title">
+<db:title>Linking to <db:link xlink:href="qdoctests-qdocfileoutput.xml#further-information">something</db:link> in a section title</db:title>
+<db:para>This is allowed but a questionable practice.</db:para>
+<db:note>
+<db:para>You're looking at detailed information.</db:para>
+</db:note>
+</db:section>
+</db:article>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/tocnavigation/expected/docbook/qdoctests-qdocmanuallikefileoutput.xml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/tocnavigation/expected/docbook/qdoctests-qdocmanuallikefileoutput.xml
new file mode 100644
index 000000000..38e3887ef
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/tocnavigation/expected/docbook/qdoctests-qdocmanuallikefileoutput.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<db:article xmlns:db="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" version="5.2" xml:lang="en">
+<db:info>
+<db:title>Document Navigation</db:title>
+<db:productname>OutputFromQDocFiles</db:productname>
+<db:edition>OutputFromQDocFiles - A test project for QDoc build artifacts</db:edition>
+<db:titleabbrev>A test project for QDoc build artifacts</db:titleabbrev>
+<db:abstract>
+<db:para>A test project for QDoc build artifacts.</db:para></db:abstract>
+</db:info>
+<db:para>The navigation commands...</db:para>
+<db:blockquote/>
+<db:programlisting language="cpp">&amp;lt;head&amp;gt;
+ ...
+ &amp;lt;link rel=&amp;quot;start&amp;quot; href=&amp;quot;basicqt.html&amp;quot; /&amp;gt;
+ ...
+&amp;lt;/head&amp;gt;
+</db:programlisting>
+<db:section xml:id="commands">
+<db:title>Commands</db:title>
+<db:anchor xml:id="previouspage-command"/>
+<db:section xml:id="previouspage">
+<db:title>\previouspage</db:title>
+<db:para>The \previouspage command...</db:para>
+</db:section>
+</db:section>
+</db:article>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/tocnavigation/expected/docbook/toc-test.xml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/tocnavigation/expected/docbook/toc-test.xml
new file mode 100644
index 000000000..6230326b7
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/tocnavigation/expected/docbook/toc-test.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<db:article xmlns:db="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" version="5.2" xml:lang="en">
+<db:info>
+<db:title>TOC</db:title>
+<db:productname>OutputFromQDocFiles</db:productname>
+<db:edition>OutputFromQDocFiles - A test project for QDoc build artifacts</db:edition>
+<db:titleabbrev>A test project for QDoc build artifacts</db:titleabbrev>
+<db:abstract>
+<db:para>A test project for QDoc build artifacts.</db:para></db:abstract>
+</db:info>
+<db:itemizedlist>
+<db:listitem>
+<db:para><db:link xlink:href="qdoctests-qdocfileoutput.xml">QDoc Testing</db:link></db:para>
+<db:itemizedlist>
+<db:listitem>
+<db:para><db:link xlink:href="qdoctests-qdocfileoutput-exhaustive.xml">Exhaustive testing of QDoc commands</db:link></db:para>
+</db:listitem>
+</db:itemizedlist>
+</db:listitem>
+<db:listitem>
+<db:para>Linking</db:para>
+<db:itemizedlist>
+<db:listitem>
+<db:para><db:link xlink:href="qdoctests-qdocfileoutput-linking.xml">QDoc Linking Test</db:link></db:para>
+<db:itemizedlist>
+<db:listitem>
+<db:para><db:link xlink:href="qdoctests-qdocfileoutput-linking.xml#link-targets">Link targets</db:link></db:para>
+</db:listitem>
+</db:itemizedlist>
+</db:listitem>
+<db:listitem>
+<db:para><db:link xlink:href="crash.xml">Random page</db:link></db:para>
+</db:listitem>
+<db:listitem>
+<db:para><db:link xlink:href="toc.xml">Table of Contents</db:link></db:para>
+</db:listitem>
+</db:itemizedlist>
+</db:listitem>
+</db:itemizedlist>
+</db:article>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/tocnavigation/expected/docbook/toc.xml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/tocnavigation/expected/docbook/toc.xml
new file mode 100644
index 000000000..693673259
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/tocnavigation/expected/docbook/toc.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<db:article xmlns:db="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" version="5.2" xml:lang="en">
+<db:info>
+<db:title>Table of Contents</db:title>
+<db:productname>OutputFromQDocFiles</db:productname>
+<db:edition>OutputFromQDocFiles - A test project for QDoc build artifacts</db:edition>
+<db:titleabbrev>A test project for QDoc build artifacts</db:titleabbrev>
+<db:extendedlink xlink:type="extended"><db:link xlink:to="" xlink:type="arc" xlink:arcrole="prev" xlink:title="Random page"/></db:extendedlink>
+<db:abstract>
+<db:para>A test project for QDoc build artifacts.</db:para></db:abstract>
+</db:info>
+<db:itemizedlist>
+<db:listitem>
+<db:para><db:link xlink:href="qdoctests-qdocfileoutput.xml">QDoc Testing</db:link></db:para>
+</db:listitem>
+<db:listitem>
+<db:para><db:link xlink:href="qdoctests-qdocfileoutput-linking.xml">QDoc Linking Test</db:link></db:para>
+</db:listitem>
+<db:listitem>
+<db:para><db:link xlink:href="toc.xml">Table of Contents</db:link></db:para>
+</db:listitem>
+</db:itemizedlist>
+</db:article>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/tocnavigation/expected/html/crash.html b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/tocnavigation/expected/html/crash.html
new file mode 100644
index 000000000..3c502d3fc
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/tocnavigation/expected/html/crash.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+<!-- qdoctests-outputfromqdocfiles.qdoc -->
+ <title>OutputFromQDocFiles</title>
+</head>
+<body>
+ <link rel="prev" href="qdoctests-qdocfileoutput-linking.html" />
+ <link rel="next" href="toc.html" />
+<p class="naviNextPrevious headerNavi">
+<a class="prevPage" href="qdoctests-qdocfileoutput-linking.html">QDoc Linking Test</a>
+<a class="nextPage" href="toc.html">Table of Contents</a>
+</p>
+<div class="sidebar"><div class="sidebar-content" id="sidebar-content"></div></div>
+<!-- $$$crash.html-description -->
+<div class="descr" id="details">
+<p></p>
+</div>
+<!-- @@@crash.html -->
+<p class="naviNextPrevious footerNavi">
+<a class="prevPage" href="qdoctests-qdocfileoutput-linking.html">QDoc Linking Test</a>
+<a class="nextPage" href="toc.html">Table of Contents</a>
+</p>
+</body>
+</html>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/tocnavigation/expected/html/images/01.png b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/tocnavigation/expected/html/images/01.png
new file mode 100644
index 000000000..d73ab969b
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/tocnavigation/expected/html/images/01.png
Binary files differ
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/tocnavigation/expected/html/images/leonardo-da-vinci.png b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/tocnavigation/expected/html/images/leonardo-da-vinci.png
new file mode 100644
index 000000000..854acb4ca
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/tocnavigation/expected/html/images/leonardo-da-vinci.png
Binary files differ
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/tocnavigation/expected/html/outputfromqdocfiles.index b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/tocnavigation/expected/html/outputfromqdocfiles.index
new file mode 100644
index 000000000..c158f9fac
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/tocnavigation/expected/html/outputfromqdocfiles.index
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE QDOCINDEX>
+<INDEX url="" title="A test project for QDoc build artifacts" version="" project="OutputFromQDocFiles">
+ <namespace name="" status="active" access="public" module="outputfromqdocfiles">
+ <page name="qdoctests-qdocmanuallikefileoutput.html" href="qdoctests-qdocmanuallikefileoutput.html" status="active" location="qdoctests-outputfromqdocmanuallikefiles.qdoc" documented="true" subtype="page" title="Document Navigation" fulltitle="Document Navigation" subtitle="">
+ <target name="previouspage-command"/>
+ <contents name="commands" title="Commands" level="1"/>
+ <contents name="previouspage" title="\previouspage" level="2"/>
+ </page>
+ <page name="qdoctests-qdocfileoutput-exhaustive.html" href="qdoctests-qdocfileoutput-exhaustive.html" status="active" location="qdoctests-outputfromqdocfiles.qdoc" documented="true" subtype="page" title="Exhaustive testing of QDoc commands" fulltitle="Exhaustive testing of QDoc commands" subtitle="" brief="This page is a dumping ground for QDoc commands under test">
+ <contents name="this-is-a-section1" title="This is a section1" level="1"/>
+ <contents name="this-is-a-section2" title="This is a section2" level="2"/>
+ <contents name="this-is-a-section3" title="This is a section3" level="3"/>
+ <contents name="this-is-a-section4" title="This is a section4" level="4"/>
+ <contents name="images" title="Images" level="1"/>
+ <contents name="commands-not-yet-tested" title="Commands not yet tested" level="1"/>
+ </page>
+ <page name="toc-test.html" href="toc-test.html" status="active" location="toc.qdoc" documented="true" subtype="page" title="TOC" fulltitle="TOC" subtitle=""/>
+ <page name="toc.html" href="toc.html" status="active" location="qdoctests-outputfromqdocfiles.qdoc" documented="true" subtype="page" title="Table of Contents" fulltitle="Table of Contents" subtitle=""/>
+ <page name="qdoctests-qdocfileoutput.html" href="qdoctests-qdocfileoutput.html" status="active" location="qdoctests-outputfromqdocfiles.qdoc" documented="true" subtype="page" title="Testing QDoc output from .qdoc files" fulltitle="Testing QDoc output from .qdoc files" subtitle="" brief="This is a simple page for testing purposes only">
+ <contents name="supported-file-types" title="Supported file types" level="1"/>
+ <contents name="further-information" title="Further information" level="1"/>
+ <contents name="linking" title="Linking" level="1"/>
+ <contents name="qdoc-linking-test" title="QDoc Linking Test" level="1"/>
+ <contents name="linking-to-something-in-a-section-title" title="Linking to something in a section title" level="1"/>
+ </page>
+ <page name="qdoctests-qdocfileoutput-linking.html" href="qdoctests-qdocfileoutput-linking.html" status="active" location="qdoctests-outputfromqdocfiles.qdoc" documented="true" subtype="page" title="Testing QDoc's link command" fulltitle="Testing QDoc's link command" subtitle="" brief="This is a page for testing QDoc's link command">
+ <target name="link-test-target"/>
+ <keyword name="qdoc-linking-test" title="QDoc Linking Test"/>
+ <contents name="link-targets" title="Link targets" level="1"/>
+ </page>
+ <page name="crash.html" href="crash.html" status="active" location="qdoctests-outputfromqdocfiles.qdoc" documented="true" subtype="page" title="" fulltitle="" subtitle=""/>
+ </namespace>
+</INDEX>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/tocnavigation/expected/html/qdoctests-qdocfileoutput-exhaustive.html b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/tocnavigation/expected/html/qdoctests-qdocfileoutput-exhaustive.html
new file mode 100644
index 000000000..4c707b186
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/tocnavigation/expected/html/qdoctests-qdocfileoutput-exhaustive.html
@@ -0,0 +1,91 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+<!-- qdoctests-outputfromqdocfiles.qdoc -->
+ <meta name="description" content="This page is a dumping ground for QDoc commands under test.">
+ <title>Exhaustive testing of QDoc commands | OutputFromQDocFiles</title>
+</head>
+<body>
+<li><a href="toc-test.html">TOC</a></li>
+<li><a href="qdoctests-qdocfileoutput.html">Testing QDoc output from .qdoc files</a></li>
+<li>Exhaustive testing of QDoc commands</li>
+<li id="buildversion">OutputFromQDocFiles - A test project for QDoc build artifacts</li>
+ <link rel="prev" href="qdoctests-qdocfileoutput.html" />
+ <link rel="next" href="qdoctests-qdocfileoutput-linking.html" />
+<p class="naviNextPrevious headerNavi">
+<a class="prevPage" href="qdoctests-qdocfileoutput.html">QDoc Testing</a>
+<a class="nextPage" href="qdoctests-qdocfileoutput-linking.html">QDoc Linking Test</a>
+</p>
+<div class="sidebar">
+<div class="toc">
+<h3 id="toc">Contents</h3>
+<ul>
+<li class="level1"><a href="#this-is-a-section1">This is a section1</a></li>
+<li class="level2"><a href="#this-is-a-section2">This is a section2</a></li>
+<li class="level3"><a href="#this-is-a-section3">This is a section3</a></li>
+<li class="level4"><a href="#this-is-a-section4">This is a section4</a></li>
+<li class="level1"><a href="#images">Images</a></li>
+<li class="level1"><a href="#commands-not-yet-tested">Commands not yet tested</a></li>
+</ul>
+</div>
+<div class="sidebar-content" id="sidebar-content"></div></div>
+<h1 class="title">Exhaustive testing of QDoc commands</h1>
+<!-- $$$qdoctests-qdocfileoutput-exhaustive.html-description -->
+<div class="descr" id="details">
+<h2 id="this-is-a-section1">This is a section1</h2>
+<h3 id="this-is-a-section2">This is a section2</h3>
+<h4 id="this-is-a-section3">This is a section3</h4>
+<h5 id="this-is-a-section4">This is a section4</h5>
+<pre class="cpp plain" translate="no">This is bad code</pre>
+<p>This text should have a line break riiiiight <br />
+ noooow.</p>
+<p><b>All your text belong to bold</b> ...And this is an examble of only <b>bold</b> being, well, bold.</p>
+<pre class="cpp" translate="no"> ...</pre>
+<p class="figCaption">This a caption</p>
+<div class="LegaleseLeft"><p>Lorem legal ipsum</p>
+</div><blockquote><p>This is a quotation.</p>
+</blockquote>
+ <html><body>This is <b>raw</b>. Like the <h1>Eddie Murphy</h1> movie. Just not as funny.</body></html>
+ <p>Look, ma! I made a sidebar!</p>
+<div class="table"><table class="generic">
+ <tr valign="top" class="odd"><td >Table item in a table row</td></tr>
+<tr valign="top" class="even"><td >Another item in a different row</td></tr>
+</table></div>
+<div class="admonition important">
+<p><b>Important: </b>This is really important.</p>
+</div>
+<div class="admonition note">
+<p><b>Note: </b>The code above doesn't compile</p>
+</div>
+<hr />
+<h2 id="images">Images</h2>
+<p>An image without any text:</p>
+<p class="centerAlign"><img src="images/leonardo-da-vinci.png" alt="" /></p><p>An image with just an alternative text:</p>
+<p class="centerAlign"><img src="images/leonardo-da-vinci.png" alt="Image alt" /></p><p>An image with alternative text and 1-atom caption:</p>
+<p class="centerAlign"><img src="images/leonardo-da-vinci.png" alt="Image alt" /></p><p class="figCaption">Image caption</p>
+<p>An image with alternative text and 2-atom caption:</p>
+<p class="centerAlign"><img src="images/leonardo-da-vinci.png" alt="Image alt" /></p><p class="figCaption">Image caption with <b>bold</b> text</p>
+<p>A bordered image:</p>
+<div class="border"><p class="centerAlign"><img src="images/leonardo-da-vinci.png" alt="" /></p></div><p>A bordered image with a caption:</p>
+<div class="border"><p class="centerAlign"><img src="images/leonardo-da-vinci.png" alt="" /></p></div><p class="figCaption">Screenshot of the System Tray Icon</p>
+<p>An inline image:</p>
+<p>The is a paragraph containing an <img src="images/01.png" alt="" /> inline image to test if qdoc handles them properly, without considering rest of the line as alt text for the image.</p>
+<p>An inline image with alt text:</p>
+<p>Here is another example of <img src="images/01.png" alt="No. 1" /> inline image with alternative text, which should be added as an attribute to the inline image.</p>
+<p>File quoting:</p>
+<pre class="cpp" translate="no"> <span class="keyword">if</span> (<span class="keyword">false</span>) {
+ <span class="keyword">return</span> <span class="number">1</span>;
+ }</pre>
+<h2 id="commands-not-yet-tested">Commands not yet tested</h2>
+<div class="admonition warning">
+<p><b>Warning: </b>The following commands have yet to be tested: footnote link sincelist header index topicref // or just don’t care, remove it inlineimage printline printto quotefile skipline skipuntil span snippet codeline overload sub sup tableofcontents tt uicontrol endmapref endomit underline unicode</p>
+</div>
+</div>
+<!-- @@@qdoctests-qdocfileoutput-exhaustive.html -->
+<p class="naviNextPrevious footerNavi">
+<a class="prevPage" href="qdoctests-qdocfileoutput.html">QDoc Testing</a>
+<a class="nextPage" href="qdoctests-qdocfileoutput-linking.html">QDoc Linking Test</a>
+</p>
+</body>
+</html>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/tocnavigation/expected/html/qdoctests-qdocfileoutput-linking.html b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/tocnavigation/expected/html/qdoctests-qdocfileoutput-linking.html
new file mode 100644
index 000000000..137707017
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/tocnavigation/expected/html/qdoctests-qdocfileoutput-linking.html
@@ -0,0 +1,39 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+<!-- qdoctests-outputfromqdocfiles.qdoc -->
+ <meta name="description" content="This is a page for testing QDoc's link command.">
+ <title>Testing QDoc's link command | OutputFromQDocFiles</title>
+</head>
+<body>
+<li><a href="toc-test.html">TOC</a></li>
+<li>Testing QDoc's link command</li>
+<li id="buildversion">OutputFromQDocFiles - A test project for QDoc build artifacts</li>
+ <link rel="prev" href="qdoctests-qdocfileoutput-exhaustive.html" />
+ <link rel="next" href="" />
+<p class="naviNextPrevious headerNavi">
+<a class="prevPage" href="qdoctests-qdocfileoutput-exhaustive.html">Exhaustive testing of QDoc commands</a>
+<a class="nextPage" href="">Random page</a>
+</p>
+<div class="sidebar">
+<div class="toc">
+<h3 id="toc">Contents</h3>
+<ul>
+<li class="level1"><a href="#link-targets">Link targets</a></li>
+</ul>
+</div>
+<div class="sidebar-content" id="sidebar-content"></div></div>
+<h1 class="title">Testing QDoc's link command</h1>
+<!-- $$$qdoctests-qdocfileoutput-linking.html-description -->
+<div class="descr" id="details">
+<span id="link-test-target"></span><h2 id="link-targets">Link targets</h2>
+<p>Valid parameters for the link command (<code translate="no">\l</code>) are page and section titles, targets defined with \target or \keyword commands, and API reference keywords (types, methods, namespaces, and so on).</p>
+</div>
+<!-- @@@qdoctests-qdocfileoutput-linking.html -->
+<p class="naviNextPrevious footerNavi">
+<a class="prevPage" href="qdoctests-qdocfileoutput-exhaustive.html">Exhaustive testing of QDoc commands</a>
+<a class="nextPage" href="">Random page</a>
+</p>
+</body>
+</html>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/tocnavigation/expected/html/qdoctests-qdocfileoutput.html b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/tocnavigation/expected/html/qdoctests-qdocfileoutput.html
new file mode 100644
index 000000000..9b7578fc3
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/tocnavigation/expected/html/qdoctests-qdocfileoutput.html
@@ -0,0 +1,72 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+<!-- qdoctests-outputfromqdocfiles.qdoc -->
+ <meta name="description" content="This is a simple page for testing purposes only.">
+ <title>Testing QDoc output from .qdoc files | OutputFromQDocFiles</title>
+</head>
+<body>
+<li><a href="toc-test.html">TOC</a></li>
+<li>Testing QDoc output from .qdoc files</li>
+<li id="buildversion">OutputFromQDocFiles - A test project for QDoc build artifacts</li>
+ <link rel="next" href="qdoctests-qdocfileoutput-exhaustive.html" />
+<p class="naviNextPrevious headerNavi">
+<a class="nextPage" href="qdoctests-qdocfileoutput-exhaustive.html">Exhaustive testing of QDoc commands</a>
+</p>
+<div class="sidebar">
+<div class="toc">
+<h3 id="toc">Contents</h3>
+<ul>
+<li class="level1"><a href="#supported-file-types">Supported file types</a></li>
+<li class="level1"><a href="#further-information">Further information</a></li>
+<li class="level1"><a href="#linking">Linking</a></li>
+<li class="level1"><a href="#qdoc-linking-test">QDoc Linking Test</a></li>
+<li class="level1"><a href="#linking-to-something-in-a-section-title">Linking to something in a section title</a></li>
+</ul>
+</div>
+<div class="sidebar-content" id="sidebar-content"></div></div>
+<h1 class="title">Testing QDoc output from .qdoc files</h1>
+<!-- $$$qdoctests-qdocfileoutput.html-description -->
+<div class="descr" id="details">
+<p>QDoc generates documentation for software projects. It does this by extracting <i>QDoc comments</i> from project source files. QDoc comments are signified by a C-style-like comment tag followed by an exclamation point, like this: <code translate="no">/*!</code> <code translate="no">This text is contained within QDoc comment tags.</code> <code translate="no">*/</code>.</p>
+<h2 id="supported-file-types">Supported file types</h2>
+<p>QDoc parses <code translate="no">.cpp</code> and <code translate="no">.qdoc</code> files. It does extract comments from header (<code translate="no">.h</code>) files.</p>
+<h2 id="further-information">Further information</h2>
+<p>This test document is written with the purpose of testing the output QDoc generates when parsing <code translate="no">.qdoc</code> files. It is fairly simple and makes use of a limited subset of QDoc's command. Those commands are:</p>
+<ul>
+<li><code translate="no">\page</code></li>
+<li><code translate="no">\title</code></li>
+<li><code translate="no">\brief</code></li>
+<li><code translate="no">\e</code> (for emphasizing &quot;QDoc comments&quot;)</li>
+<li><code translate="no">\c</code> (for multiple monospace-formatted entries)</li>
+<li><code translate="no">\section1</code></li>
+<li><code translate="no">\list</code></li>
+<li><code translate="no">\li</code></li>
+<li><code translate="no">\endlist</code></li>
+</ul>
+<h2 id="linking">Linking</h2>
+<p>There are multiple ways to create hyperlinks to other topics:</p>
+<ul>
+<li><a href="qdoctests-qdocfileoutput-linking.html">Linking to a page title</a></li>
+<li><a href="qdoctests-qdocfileoutput-linking.html#link-targets">Linking to a section title</a></li>
+<li><a href="qdoctests-qdocfileoutput-linking.html#link-test-target">Linking using a \target string</a></li>
+<li><a href="qdoctests-qdocfileoutput-linking.html">Linking using a \keyword string</a></li>
+</ul>
+<h2 id="qdoc-linking-test">QDoc Linking Test</h2>
+<p>This section title is overridden by another target which takes precedence.</p>
+<h2 id="linking-to-something-in-a-section-title">Linking to <a href="qdoctests-qdocfileoutput.html#further-information">something</a> in a section title</h2>
+<p>This is allowed but a questionable practice.</p>
+<details>
+<summary>QDoc details</summary>
+<div class="admonition note">
+<p><b>Note: </b>You're looking at detailed information.</p>
+</div>
+</details>
+</div>
+<!-- @@@qdoctests-qdocfileoutput.html -->
+<p class="naviNextPrevious footerNavi">
+<a class="nextPage" href="qdoctests-qdocfileoutput-exhaustive.html">Exhaustive testing of QDoc commands</a>
+</p>
+</body>
+</html>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/tocnavigation/expected/html/qdoctests-qdocmanuallikefileoutput.html b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/tocnavigation/expected/html/qdoctests-qdocmanuallikefileoutput.html
new file mode 100644
index 000000000..e4b120bd0
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/tocnavigation/expected/html/qdoctests-qdocmanuallikefileoutput.html
@@ -0,0 +1,63 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+<!-- qdoctests-outputfromqdocmanuallikefiles.qdoc -->
+ <title>Document Navigation | OutputFromQDocFiles</title>
+</head>
+<body>
+<li id="buildversion">OutputFromQDocFiles - A test project for QDoc build artifacts</li>
+<div class="sidebar">
+<div class="toc">
+<h3 id="toc">Contents</h3>
+<ul>
+<li class="level1"><a href="#commands">Commands</a></li>
+<li class="level2"><a href="#previouspage">\previouspage</a></li>
+</ul>
+</div>
+<div class="sidebar-content" id="sidebar-content"></div></div>
+<h1 class="title">Document Navigation</h1>
+<!-- $$$qdoctests-qdocmanuallikefileoutput.html-description -->
+<div class="descr" id="details">
+<p>The navigation commands...</p>
+<blockquote> <table border="0" cellpadding="0" cellspacing="5" width="100%">
+
+ <tr>
+ <p>
+ [Previous: <a href="15-qdoc-commands-navigation.html#deadlink">
+ Basic Qt</a>]
+ [<a href="15-qdoc-commands-navigation.html#deadlink">Contents</a>]
+ [Next: <a href="15-qdoc-commands-navigation.html#deadlink">
+ Creating Dialogs</a>]
+ </p>
+
+ <h1 align="center">Getting Started<br /></h1>
+
+ <p>
+ This chapter shows how to combine basic C++ with the
+ functionality provided by Qt to create a few small graphical
+ interface (GUI) applications.
+ </p>
+
+ <p>
+ [Previous: <a href="15-qdoc-commands-navigation.html#deadlink">
+ Basic Qt</a>]
+ [<a href="15-qdoc-commands-navigation.html#deadlink">Contents</a>]
+ [Next: <a href="15-qdoc-commands-navigation.html#deadlink">
+ Creating Dialogs</a>]
+ </p>
+
+ </table>
+ </blockquote>
+<pre class="cpp" translate="no"><span class="operator">&lt;</span>head<span class="operator">&gt;</span>
+ <span class="operator">.</span><span class="operator">.</span><span class="operator">.</span>
+ <span class="operator">&lt;</span>link rel<span class="operator">=</span><span class="string">&quot;start&quot;</span> href<span class="operator">=</span><span class="string">&quot;basicqt.html&quot;</span> <span class="operator">/</span><span class="operator">&gt;</span>
+ <span class="operator">.</span><span class="operator">.</span><span class="operator">.</span>
+<span class="operator">&lt;</span><span class="operator">/</span>head<span class="operator">&gt;</span></pre>
+<h2 id="commands">Commands</h2>
+<span id="previouspage-command"></span><h3 id="previouspage">\previouspage</h3>
+<p>The \previouspage command...</p>
+</div>
+<!-- @@@qdoctests-qdocmanuallikefileoutput.html -->
+</body>
+</html>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/tocnavigation/expected/html/toc-test.html b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/tocnavigation/expected/html/toc-test.html
new file mode 100644
index 000000000..08ec52c10
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/tocnavigation/expected/html/toc-test.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+<!-- toc.qdoc -->
+ <title>TOC | OutputFromQDocFiles</title>
+</head>
+<body>
+<li id="buildversion">OutputFromQDocFiles - A test project for QDoc build artifacts</li>
+<div class="sidebar"><div class="sidebar-content" id="sidebar-content"></div></div>
+<h1 class="title">TOC</h1>
+<!-- $$$toc-test.html-description -->
+<div class="descr" id="details">
+<ul>
+<li><a href="qdoctests-qdocfileoutput.html">QDoc Testing</a><ul>
+<li><a href="qdoctests-qdocfileoutput-exhaustive.html">Exhaustive testing of QDoc commands</a></li>
+</ul>
+</li>
+<li>Linking<ul>
+<li><a href="qdoctests-qdocfileoutput-linking.html">QDoc Linking Test</a><ul>
+<li><a href="qdoctests-qdocfileoutput-linking.html#link-targets">Link targets</a></li>
+</ul>
+</li>
+<li><a href="crash.html">Random page</a></li>
+<li><a href="toc.html">Table of Contents</a></li>
+</ul>
+</li>
+</ul>
+</div>
+<!-- @@@toc-test.html -->
+</body>
+</html>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/tocnavigation/expected/html/toc.html b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/tocnavigation/expected/html/toc.html
new file mode 100644
index 000000000..d7220d013
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/tocnavigation/expected/html/toc.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+<!-- qdoctests-outputfromqdocfiles.qdoc -->
+ <title>Table of Contents | OutputFromQDocFiles</title>
+</head>
+<body>
+<li><a href="toc-test.html">TOC</a></li>
+<li>Table of Contents</li>
+<li id="buildversion">OutputFromQDocFiles - A test project for QDoc build artifacts</li>
+ <link rel="prev" href="" />
+<p class="naviNextPrevious headerNavi">
+<a class="prevPage" href="">Random page</a>
+</p>
+<div class="sidebar"><div class="sidebar-content" id="sidebar-content"></div></div>
+<h1 class="title">Table of Contents</h1>
+<!-- $$$toc.html-description -->
+<div class="descr" id="details">
+<ul>
+<li><a href="qdoctests-qdocfileoutput.html">QDoc Testing</a></li>
+<li><a href="qdoctests-qdocfileoutput-linking.html">QDoc Linking Test</a></li>
+<li><a href="toc.html">Table of Contents</a></li>
+</ul>
+</div>
+<!-- @@@toc.html -->
+<p class="naviNextPrevious footerNavi">
+<a class="prevPage" href="">Random page</a>
+</p>
+</body>
+</html>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/tocnavigation/expected/webxml/crash.webxml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/tocnavigation/expected/webxml/crash.webxml
new file mode 100644
index 000000000..6ffdbcba5
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/tocnavigation/expected/webxml/crash.webxml
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<WebXML>
+ <document>
+ <page name="crash.html" href="crash.html" status="active" location="qdoctests-outputfromqdocfiles.qdoc" documented="true" subtype="page" title="" fulltitle="" subtitle="">
+ <description>
+ <relation href="toc.html" type="page" meta="next" description="Table of Contents"/>
+ <relation href="qdoctests-qdocfileoutput-linking.html" type="page" meta="previous" description="Testing QDoc's link command"/>
+ <para></para>
+ </description>
+ </page>
+ </document>
+</WebXML>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/tocnavigation/expected/webxml/images/01.png b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/tocnavigation/expected/webxml/images/01.png
new file mode 100644
index 000000000..d73ab969b
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/tocnavigation/expected/webxml/images/01.png
Binary files differ
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/tocnavigation/expected/webxml/images/leonardo-da-vinci.png b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/tocnavigation/expected/webxml/images/leonardo-da-vinci.png
new file mode 100644
index 000000000..854acb4ca
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/tocnavigation/expected/webxml/images/leonardo-da-vinci.png
Binary files differ
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/tocnavigation/expected/webxml/outputfromqdocfiles.index b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/tocnavigation/expected/webxml/outputfromqdocfiles.index
new file mode 100644
index 000000000..c158f9fac
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/tocnavigation/expected/webxml/outputfromqdocfiles.index
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE QDOCINDEX>
+<INDEX url="" title="A test project for QDoc build artifacts" version="" project="OutputFromQDocFiles">
+ <namespace name="" status="active" access="public" module="outputfromqdocfiles">
+ <page name="qdoctests-qdocmanuallikefileoutput.html" href="qdoctests-qdocmanuallikefileoutput.html" status="active" location="qdoctests-outputfromqdocmanuallikefiles.qdoc" documented="true" subtype="page" title="Document Navigation" fulltitle="Document Navigation" subtitle="">
+ <target name="previouspage-command"/>
+ <contents name="commands" title="Commands" level="1"/>
+ <contents name="previouspage" title="\previouspage" level="2"/>
+ </page>
+ <page name="qdoctests-qdocfileoutput-exhaustive.html" href="qdoctests-qdocfileoutput-exhaustive.html" status="active" location="qdoctests-outputfromqdocfiles.qdoc" documented="true" subtype="page" title="Exhaustive testing of QDoc commands" fulltitle="Exhaustive testing of QDoc commands" subtitle="" brief="This page is a dumping ground for QDoc commands under test">
+ <contents name="this-is-a-section1" title="This is a section1" level="1"/>
+ <contents name="this-is-a-section2" title="This is a section2" level="2"/>
+ <contents name="this-is-a-section3" title="This is a section3" level="3"/>
+ <contents name="this-is-a-section4" title="This is a section4" level="4"/>
+ <contents name="images" title="Images" level="1"/>
+ <contents name="commands-not-yet-tested" title="Commands not yet tested" level="1"/>
+ </page>
+ <page name="toc-test.html" href="toc-test.html" status="active" location="toc.qdoc" documented="true" subtype="page" title="TOC" fulltitle="TOC" subtitle=""/>
+ <page name="toc.html" href="toc.html" status="active" location="qdoctests-outputfromqdocfiles.qdoc" documented="true" subtype="page" title="Table of Contents" fulltitle="Table of Contents" subtitle=""/>
+ <page name="qdoctests-qdocfileoutput.html" href="qdoctests-qdocfileoutput.html" status="active" location="qdoctests-outputfromqdocfiles.qdoc" documented="true" subtype="page" title="Testing QDoc output from .qdoc files" fulltitle="Testing QDoc output from .qdoc files" subtitle="" brief="This is a simple page for testing purposes only">
+ <contents name="supported-file-types" title="Supported file types" level="1"/>
+ <contents name="further-information" title="Further information" level="1"/>
+ <contents name="linking" title="Linking" level="1"/>
+ <contents name="qdoc-linking-test" title="QDoc Linking Test" level="1"/>
+ <contents name="linking-to-something-in-a-section-title" title="Linking to something in a section title" level="1"/>
+ </page>
+ <page name="qdoctests-qdocfileoutput-linking.html" href="qdoctests-qdocfileoutput-linking.html" status="active" location="qdoctests-outputfromqdocfiles.qdoc" documented="true" subtype="page" title="Testing QDoc's link command" fulltitle="Testing QDoc's link command" subtitle="" brief="This is a page for testing QDoc's link command">
+ <target name="link-test-target"/>
+ <keyword name="qdoc-linking-test" title="QDoc Linking Test"/>
+ <contents name="link-targets" title="Link targets" level="1"/>
+ </page>
+ <page name="crash.html" href="crash.html" status="active" location="qdoctests-outputfromqdocfiles.qdoc" documented="true" subtype="page" title="" fulltitle="" subtitle=""/>
+ </namespace>
+</INDEX>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/tocnavigation/expected/webxml/qdoctests-qdocfileoutput-exhaustive.webxml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/tocnavigation/expected/webxml/qdoctests-qdocfileoutput-exhaustive.webxml
new file mode 100644
index 000000000..25e3dcc9e
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/tocnavigation/expected/webxml/qdoctests-qdocfileoutput-exhaustive.webxml
@@ -0,0 +1,92 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<WebXML>
+ <document>
+ <page name="qdoctests-qdocfileoutput-exhaustive.html" href="qdoctests-qdocfileoutput-exhaustive.html" status="active" location="qdoctests-outputfromqdocfiles.qdoc" documented="true" subtype="page" title="Exhaustive testing of QDoc commands" fulltitle="Exhaustive testing of QDoc commands" subtitle="" brief="This page is a dumping ground for QDoc commands under test">
+ <contents name="this-is-a-section1" title="This is a section1" level="1"/>
+ <contents name="this-is-a-section2" title="This is a section2" level="2"/>
+ <contents name="this-is-a-section3" title="This is a section3" level="3"/>
+ <contents name="this-is-a-section4" title="This is a section4" level="4"/>
+ <contents name="images" title="Images" level="1"/>
+ <contents name="commands-not-yet-tested" title="Commands not yet tested" level="1"/>
+ <description>
+ <relation href="qdoctests-qdocfileoutput-linking.html" type="page" meta="next" description="Testing QDoc's link command"/>
+ <relation href="qdoctests-qdocfileoutput.html" type="page" meta="previous" description="Testing QDoc output from .qdoc files"/>
+ <brief>This page is a dumping ground for QDoc commands under test.</brief>
+ <section id="this-is-a-section1">
+ <heading level="1">This is a section1</heading>
+ </section>
+ <section id="this-is-a-section2">
+ <heading level="2">This is a section2</heading>
+ </section>
+ <section id="this-is-a-section3">
+ <heading level="3">This is a section3</heading>
+ </section>
+ <section id="this-is-a-section4">
+ <heading level="4">This is a section4</heading>
+ </section>
+ </description>
+ </page>
+ </document>
+ <badcode>This is bad code</badcode>
+ <para>This text should have a line break riiiiight noooow.</para>
+ <para>
+ <bold>All your text belong to bold</bold> ...And this is an examble of only <bold>bold</bold> being, well, bold.</para>
+ <dots indent="4">...</dots>
+ <para>This a caption</para>
+ <legalese>
+ <para>Lorem legal ipsum</para>
+ </legalese>
+ <quote>
+ <para>This is a quotation.</para>
+ </quote>
+ <raw format="HTML"> &lt;html&gt;&lt;body&gt;This is &lt;b&gt;raw&lt;/b&gt;. Like the &lt;h1&gt;Eddie Murphy&lt;/h1&gt; movie. Just not as funny.&lt;/body&gt;&lt;/html&gt;
+ </raw>
+ <para>Look, ma! I made a sidebar!</para>
+ <table>
+ <row>
+ <item>
+ <para>Table item in a table row</para>
+ </item>
+ </row>
+ <row>
+ <item>
+ <para>Another item in a different row</para>
+ </item>
+ </row>
+ </table>
+ <para>
+ <bold>Important:</bold> This is really important.</para>
+ <para>
+ <bold>Note:</bold> The code above doesn't compile</para>
+ <section id="images">
+ <heading level="1">Images</heading>
+ <para>An image without any text:</para>
+ <image href="images/leonardo-da-vinci.png"/>
+ <para>An image with just an alternative text:</para>
+ <image href="images/leonardo-da-vinci.png"/>
+ <para>An image with alternative text and 1-atom caption:</para>
+ <image href="images/leonardo-da-vinci.png"/>
+ <para>Image caption</para>
+ <para>An image with alternative text and 2-atom caption:</para>
+ <image href="images/leonardo-da-vinci.png"/>
+ <para>Image caption with <bold>bold</bold> text</para>
+ <para>A bordered image:</para>
+ <image href="images/leonardo-da-vinci.png"/>
+ <para>A bordered image with a caption:</para>
+ <image href="images/leonardo-da-vinci.png"/>
+ <para>Screenshot of the System Tray Icon</para>
+ <para>An inline image:</para>
+ <para>The is a paragraph containing an <inlineimage href="images/01.png"/> inline image to test if qdoc handles them properly, without considering rest of the line as alt text for the image.</para>
+ <para>An inline image with alt text:</para>
+ <para>Here is another example of <inlineimage href="images/01.png"/> inline image with alternative text, which should be added as an attribute to the inline image.</para>
+ <para>File quoting:</para>
+ <quotefromfile>main.cpp</quotefromfile>
+ <skipto>/if \(/</skipto>
+ <printuntil>/^ \}/</printuntil>
+ </section>
+ <section id="commands-not-yet-tested">
+ <heading level="1">Commands not yet tested</heading>
+ <para>
+ <bold>Warning:</bold> The following commands have yet to be tested: footnote link sincelist header index topicref // or just don’t care, remove it inlineimage printline printto quotefile skipline skipuntil span snippet codeline overload sub sup tableofcontents tt uicontrol endmapref endomit underline unicode</para>
+ </section>
+</WebXML>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/tocnavigation/expected/webxml/qdoctests-qdocfileoutput-linking.webxml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/tocnavigation/expected/webxml/qdoctests-qdocfileoutput-linking.webxml
new file mode 100644
index 000000000..d2c53d965
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/tocnavigation/expected/webxml/qdoctests-qdocfileoutput-linking.webxml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<WebXML>
+ <document>
+ <page name="qdoctests-qdocfileoutput-linking.html" href="qdoctests-qdocfileoutput-linking.html" status="active" location="qdoctests-outputfromqdocfiles.qdoc" documented="true" subtype="page" title="Testing QDoc's link command" fulltitle="Testing QDoc's link command" subtitle="" brief="This is a page for testing QDoc's link command">
+ <target name="link-test-target"/>
+ <keyword name="qdoc-linking-test" title="QDoc Linking Test"/>
+ <contents name="link-targets" title="Link targets" level="1"/>
+ <description>
+ <relation href="" type="page" meta="next" description="Random page"/>
+ <relation href="qdoctests-qdocfileoutput-exhaustive.html" type="page" meta="previous" description="Exhaustive testing of QDoc commands"/>
+ <brief>This is a page for testing QDoc's link command.</brief>
+ <target name="link-test-target"/>
+ <section id="link-targets">
+ <heading level="1">Link targets</heading>
+ <para>Valid parameters for the link command (<teletype type="highlighted">\l</teletype>) are page and section titles, targets defined with \target or \keyword commands, and API reference keywords (types, methods, namespaces, and so on).</para>
+ </section>
+ </description>
+ </page>
+ </document>
+</WebXML>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/tocnavigation/expected/webxml/qdoctests-qdocfileoutput.webxml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/tocnavigation/expected/webxml/qdoctests-qdocfileoutput.webxml
new file mode 100644
index 000000000..b6b8dc6cb
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/tocnavigation/expected/webxml/qdoctests-qdocfileoutput.webxml
@@ -0,0 +1,95 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<WebXML>
+ <document>
+ <page name="qdoctests-qdocfileoutput.html" href="qdoctests-qdocfileoutput.html" status="active" location="qdoctests-outputfromqdocfiles.qdoc" documented="true" subtype="page" title="Testing QDoc output from .qdoc files" fulltitle="Testing QDoc output from .qdoc files" subtitle="" brief="This is a simple page for testing purposes only">
+ <contents name="supported-file-types" title="Supported file types" level="1"/>
+ <contents name="further-information" title="Further information" level="1"/>
+ <contents name="linking" title="Linking" level="1"/>
+ <contents name="qdoc-linking-test" title="QDoc Linking Test" level="1"/>
+ <contents name="linking-to-something-in-a-section-title" title="Linking to something in a section title" level="1"/>
+ <description>
+ <relation href="qdoctests-qdocfileoutput-exhaustive.html" type="page" meta="next" description="Exhaustive testing of QDoc commands"/>
+ <brief>This is a simple page for testing purposes only.</brief>
+ <para>QDoc generates documentation for software projects. It does this by extracting <italic>QDoc comments</italic> from project source files. QDoc comments are signified by a C-style-like comment tag followed by an exclamation point, like this: <teletype type="highlighted">/*!</teletype> <teletype type="highlighted">This text is contained within QDoc comment tags.</teletype> <teletype type="highlighted">*/</teletype>.</para>
+ <section id="supported-file-types">
+ <heading level="1">Supported file types</heading>
+ <para>QDoc parses <teletype type="highlighted">.cpp</teletype> and <teletype type="highlighted">.qdoc</teletype> files. It does extract comments from header (<teletype type="highlighted">.h</teletype>) files.</para>
+ </section>
+ <section id="further-information">
+ <heading level="1">Further information</heading>
+ <para>This test document is written with the purpose of testing the output QDoc generates when parsing <teletype type="highlighted">.qdoc</teletype> files. It is fairly simple and makes use of a limited subset of QDoc's command. Those commands are:</para>
+ <list type="bullet">
+ <item>
+ <para>
+ <teletype type="highlighted">\page</teletype></para>
+ </item>
+ <item>
+ <para>
+ <teletype type="highlighted">\title</teletype></para>
+ </item>
+ <item>
+ <para>
+ <teletype type="highlighted">\brief</teletype></para>
+ </item>
+ <item>
+ <para>
+ <teletype type="highlighted">\e</teletype> (for emphasizing &quot;QDoc comments&quot;)</para>
+ </item>
+ <item>
+ <para>
+ <teletype type="highlighted">\c</teletype> (for multiple monospace-formatted entries)</para>
+ </item>
+ <item>
+ <para>
+ <teletype type="highlighted">\section1</teletype></para>
+ </item>
+ <item>
+ <para>
+ <teletype type="highlighted">\list</teletype></para>
+ </item>
+ <item>
+ <para>
+ <teletype type="highlighted">\li</teletype></para>
+ </item>
+ <item>
+ <para>
+ <teletype type="highlighted">\endlist</teletype></para>
+ </item>
+ </list>
+ </section>
+ <section id="linking">
+ <heading level="1">Linking</heading>
+ <para>There are multiple ways to create hyperlinks to other topics:</para>
+ <list type="bullet">
+ <item>
+ <para>
+ <link raw="Testing QDoc's link command" href="qdoctests-qdocfileoutput-linking.html" type="page" page="Testing QDoc's link command">Linking to a page title</link></para>
+ </item>
+ <item>
+ <para>
+ <link raw="Link targets" href="qdoctests-qdocfileoutput-linking.html#link-targets" type="page" page="Testing QDoc's link command">Linking to a section title</link></para>
+ </item>
+ <item>
+ <para>
+ <link raw="link-test-target" href="qdoctests-qdocfileoutput-linking.html#link-test-target" type="page" page="Testing QDoc's link command">Linking using a \target string</link></para>
+ </item>
+ <item>
+ <para>
+ <link raw="QDoc Linking Test" href="qdoctests-qdocfileoutput-linking.html" type="page" page="Testing QDoc's link command">Linking using a \keyword string</link></para>
+ </item>
+ </list>
+ </section>
+ <section id="qdoc-linking-test">
+ <heading level="1">QDoc Linking Test</heading>
+ <para>This section title is overridden by another target which takes precedence.</para>
+ </section>
+ <section id="linking-to-something-in-a-section-title">
+ <heading level="1">Linking to <link raw="Further information" href="qdoctests-qdocfileoutput.html#further-information" type="page" page="Testing QDoc output from .qdoc files">something</link> in a section title</heading>
+ <para>This is allowed but a questionable practice.</para>
+ <para>
+ <bold>Note:</bold> You're looking at detailed information.</para>
+ </section>
+ </description>
+ </page>
+ </document>
+</WebXML>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/tocnavigation/expected/webxml/qdoctests-qdocmanuallikefileoutput.webxml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/tocnavigation/expected/webxml/qdoctests-qdocmanuallikefileoutput.webxml
new file mode 100644
index 000000000..4502dcf7f
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/tocnavigation/expected/webxml/qdoctests-qdocmanuallikefileoutput.webxml
@@ -0,0 +1,57 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<WebXML>
+ <document>
+ <page name="qdoctests-qdocmanuallikefileoutput.html" href="qdoctests-qdocmanuallikefileoutput.html" status="active" location="qdoctests-outputfromqdocmanuallikefiles.qdoc" documented="true" subtype="page" title="Document Navigation" fulltitle="Document Navigation" subtitle="">
+ <target name="previouspage-command"/>
+ <contents name="commands" title="Commands" level="1"/>
+ <contents name="previouspage" title="\previouspage" level="2"/>
+ <description>
+ <para>The navigation commands...</para>
+ <quote>
+ <raw format="HTML"> &lt;table border=&quot;0&quot; cellpadding=&quot;0&quot; cellspacing=&quot;5&quot; width=&quot;100%&quot;&gt;
+
+ &lt;tr&gt;
+ &lt;p&gt;
+ [Previous: &lt;a href=&quot;15-qdoc-commands-navigation.html#deadlink&quot;&gt;
+ Basic Qt&lt;/a&gt;]
+ [&lt;a href=&quot;15-qdoc-commands-navigation.html#deadlink&quot;&gt;Contents&lt;/a&gt;]
+ [Next: &lt;a href=&quot;15-qdoc-commands-navigation.html#deadlink&quot;&gt;
+ Creating Dialogs&lt;/a&gt;]
+ &lt;/p&gt;
+
+ &lt;h1 align=&quot;center&quot;&gt;Getting Started&lt;br /&gt;&lt;/h1&gt;
+
+ &lt;p&gt;
+ This chapter shows how to combine basic C++ with the
+ functionality provided by Qt to create a few small graphical
+ interface (GUI) applications.
+ &lt;/p&gt;
+
+ &lt;p&gt;
+ [Previous: &lt;a href=&quot;15-qdoc-commands-navigation.html#deadlink&quot;&gt;
+ Basic Qt&lt;/a&gt;]
+ [&lt;a href=&quot;15-qdoc-commands-navigation.html#deadlink&quot;&gt;Contents&lt;/a&gt;]
+ [Next: &lt;a href=&quot;15-qdoc-commands-navigation.html#deadlink&quot;&gt;
+ Creating Dialogs&lt;/a&gt;]
+ &lt;/p&gt;
+
+ &lt;/table&gt;
+ </raw>
+ </quote>
+ <code>&lt;head&gt;
+ ...
+ &lt;link rel=&quot;start&quot; href=&quot;basicqt.html&quot; /&gt;
+ ...
+&lt;/head&gt;</code>
+ <section id="commands">
+ <heading level="1">Commands</heading>
+ <target name="previouspage-command"/>
+ </section>
+ <section id="previouspage">
+ <heading level="2">\previouspage</heading>
+ <para>The \previouspage command...</para>
+ </section>
+ </description>
+ </page>
+ </document>
+</WebXML>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/tocnavigation/expected/webxml/toc-test.webxml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/tocnavigation/expected/webxml/toc-test.webxml
new file mode 100644
index 000000000..767973fc6
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/tocnavigation/expected/webxml/toc-test.webxml
@@ -0,0 +1,44 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<WebXML>
+ <document>
+ <page name="toc-test.html" href="toc-test.html" status="active" location="toc.qdoc" documented="true" subtype="page" title="TOC" fulltitle="TOC" subtitle="">
+ <description>
+ <list type="bullet">
+ <item>
+ <para>
+ <link raw="Testing QDoc output from .qdoc files" href="qdoctests-qdocfileoutput.html" type="page" page="Testing QDoc output from .qdoc files">QDoc Testing</link></para>
+ <list type="bullet">
+ <item>
+ <para>
+ <link raw="Exhaustive testing of QDoc commands" href="qdoctests-qdocfileoutput-exhaustive.html" type="page" page="Exhaustive testing of QDoc commands">Exhaustive testing of QDoc commands</link></para>
+ </item>
+ </list>
+ </item>
+ <item>
+ <para>Linking</para>
+ <list type="bullet">
+ <item>
+ <para>
+ <link raw="QDoc Linking Test" href="qdoctests-qdocfileoutput-linking.html" type="page" page="Testing QDoc's link command">QDoc Linking Test</link></para>
+ <list type="bullet">
+ <item>
+ <para>
+ <link raw="Link targets" href="qdoctests-qdocfileoutput-linking.html#link-targets" type="page" page="Testing QDoc's link command">Link targets</link></para>
+ </item>
+ </list>
+ </item>
+ <item>
+ <para>
+ <link raw="crash.html" href="crash.html" type="page" page="crash.html">Random page</link></para>
+ </item>
+ <item>
+ <para>
+ <link raw="Table of Contents" href="toc.html" type="page" page="Table of Contents">Table of Contents</link></para>
+ </item>
+ </list>
+ </item>
+ </list>
+ </description>
+ </page>
+ </document>
+</WebXML>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/tocnavigation/expected/webxml/toc.webxml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/tocnavigation/expected/webxml/toc.webxml
new file mode 100644
index 000000000..9718191e0
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/tocnavigation/expected/webxml/toc.webxml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<WebXML>
+ <document>
+ <page name="toc.html" href="toc.html" status="active" location="qdoctests-outputfromqdocfiles.qdoc" documented="true" subtype="page" title="Table of Contents" fulltitle="Table of Contents" subtitle="">
+ <description>
+ <relation href="" type="page" meta="previous" description="Random page"/>
+ <list type="bullet">
+ <item>
+ <para>
+ <link raw="Testing QDoc output from .qdoc files" href="qdoctests-qdocfileoutput.html" type="page" page="Testing QDoc output from .qdoc files">QDoc Testing</link></para>
+ </item>
+ <item>
+ <para>
+ <link raw="QDoc Linking Test" href="qdoctests-qdocfileoutput-linking.html" type="page" page="Testing QDoc's link command">QDoc Linking Test</link></para>
+ </item>
+ <item>
+ <para>
+ <link raw="Table of Contents" href="toc.html" type="page" page="Table of Contents">Table of Contents</link></para>
+ </item>
+ </list>
+ </description>
+ </page>
+ </document>
+</WebXML>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/tocnavigation/src/images/01.png b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/tocnavigation/src/images/01.png
new file mode 100644
index 000000000..d73ab969b
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/tocnavigation/src/images/01.png
Binary files differ
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/tocnavigation/src/images/leonardo-da-vinci.png b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/tocnavigation/src/images/leonardo-da-vinci.png
new file mode 100644
index 000000000..854acb4ca
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/tocnavigation/src/images/leonardo-da-vinci.png
Binary files differ
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/tocnavigation/src/qdoctests-outputfromqdocfiles.qdoc b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/tocnavigation/src/qdoctests-outputfromqdocfiles.qdoc
new file mode 100644
index 000000000..b19905b7e
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/tocnavigation/src/qdoctests-outputfromqdocfiles.qdoc
@@ -0,0 +1,241 @@
+// Copyright (C) 2020 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+
+/*!
+\if defined(test_navigation)
+ \nextpage {qdoctests-qdocfileoutput-linking.html}{QDoc Linking Test}
+\endif
+
+ \page qdoctests-qdocfileoutput.html
+ \title Testing \PROD output from .qdoc files
+ \brief This is a simple page for testing purposes only.
+
+ QDoc generates documentation for software projects. It does this by
+ extracting \e {QDoc comments} from project source files. QDoc comments are
+ signified by a C-style-like comment tag followed by an exclamation point,
+ like this:
+ \beginqdoc \c {This text is contained within QDoc comment tags.} \endqdoc.
+
+ \section1 Supported file types
+ QDoc parses \c .cpp and \c .qdoc files. It does extract comments from
+ header (\c {.h}) files.
+
+ \section1 Further information
+ This test document is written with the purpose of testing the output QDoc
+ generates when parsing \c .qdoc files. It is fairly simple and makes use of
+ a limited subset of QDoc's command. Those commands are:
+ \list
+ \li \c {\page}
+ \li \c {\title}
+ \li \c {\brief}
+ \li \c {\e} (for emphasizing "QDoc comments")
+ \li \c {\c} (for multiple monospace-formatted entries)
+ \li \c {\section1}
+ \li \c {\list}
+ \li \c {\li}
+ \li \c {\endlist}
+ \endlist
+
+ \section1 Linking
+
+ There are multiple ways to create hyperlinks to other topics:
+
+ \list
+ \li \l {Testing QDoc's link command}{Linking to a page title}
+ \li \l {Link targets}{Linking to a section title}
+ \li \l {link-test-target}{Linking using a \\target string}
+ \li \l {QDoc Linking Test}{Linking using a \\keyword string}
+ \endlist
+
+ \section1 QDoc Linking Test
+
+ This section title is overridden by another target which takes
+ precedence.
+
+ \section1 Linking to \l {Further information}{something} in a section title
+
+ This is allowed but a questionable practice.
+
+ \details {\PROD details}
+ \note You're looking at detailed information.
+ \enddetails
+*/
+
+/*!
+\if defined(test_navigation)
+ \previouspage qdoctests-qdocfileoutput.html \PROD Testing
+ \nextpage Table of Contents
+\endif
+
+ \keyword QDoc Linking Test
+ \page qdoctests-qdocfileoutput-linking.html
+ \title Testing QDoc's link command
+ \brief This is a page for testing QDoc's link command.
+
+ \target link-test-target
+ \section1 Link targets
+
+ Valid parameters for the link command (\c {\l}) are page and section
+ titles, targets defined with \\target or \\keyword commands, and API
+ reference keywords (types, methods, namespaces, and so on).
+*/
+
+/*!
+\if defined(test_navigation)
+ \previouspage {Testing QDoc's link command}{QDoc Linking Test}
+\endif
+
+ \page toc.html
+ \title Table of Contents
+
+ \list
+ \li \l {Testing \PROD output from .qdoc files}{\PROD Testing}
+ \li \l {QDoc Linking Test}
+ \li \l {Table of Contents}
+ \endlist
+*/
+
+/*!
+ \page qdoctests-qdocfileoutput-exhaustive.html
+ \title Exhaustive testing of QDoc commands
+ \brief This page is a dumping ground for QDoc commands under test.
+
+ \section1 This is a section1
+ \section2 This is a section2
+ \section3 This is a section3
+ \section4 This is a section4
+ \endsection4
+ \endsection3
+ \endsection2
+ \endsection1
+
+ \badcode
+ This is bad code
+ \endcode
+
+ This text should have a line break riiiiight \br noooow.
+
+ \b{All your text belong to bold}
+ ...And this is an examble of only \b bold being, well, bold.
+
+ \dots
+
+ \caption This a caption
+
+ \legalese
+ Lorem legal ipsum
+ \endlegalese
+
+ \quotation
+ This is a quotation.
+ \endquotation
+
+ \raw HTML
+ <html><body>This is <b>raw</b>. Like the <h1>Eddie Murphy</h1> movie. Just not as funny.</body></html>
+ \endraw
+
+ \sidebar
+ Look, ma! I made a sidebar!
+ \endsidebar
+
+ \table
+ \row \li Table item in a table row
+ \row \li Another item in a different row
+ \endtable
+
+ \important This is really important.
+
+ \note The code above doesn't compile
+
+ \hr
+
+ \section1 Images
+
+ An image without any text:
+
+ \image leonardo-da-vinci.png
+
+ An image with just an alternative text:
+
+ \image leonardo-da-vinci.png Image alt
+
+ An image with alternative text and 1-atom caption:
+
+ \image leonardo-da-vinci.png Image alt
+ \caption Image caption
+
+ An image with alternative text and 2-atom caption:
+
+ \image leonardo-da-vinci.png Image alt
+ \caption Image caption with \b {bold} text
+
+ A bordered image:
+
+ \borderedimage leonardo-da-vinci.png
+
+ //! A bordered image with alternative text:
+ //!
+ //! \borderedimage leonardo-da-vinci.png Screenshot of the Drill Down Example
+ //! It looks like this macro is not written to handle alternative text (no \2)
+
+ A bordered image with a caption:
+
+ \borderedimage leonardo-da-vinci.png
+ \caption Screenshot of the System Tray Icon
+
+ An inline image:
+
+ The is a paragraph containing an \inlineimage 01.png inline image to test
+ if qdoc handles them properly, without considering rest of the line as
+ alt text for the image.
+
+ An inline image with alt text:
+
+ Here is another example of \inlineimage 01.png {No. 1} inline image with
+ alternative text, which should be added as an attribute to the inline
+ image.
+
+ File quoting:
+
+ \quotefromfile main.cpp
+ \skipto /if \(/
+ \printuntil /^ \}/
+
+ \section1 Commands not yet tested
+
+ \warning The following commands have yet to be tested:
+ footnote
+ link
+ //! Check why above two (when used in this order) cause missing linefeeds on Windows/webxml
+ sincelist
+ header
+ index
+ topicref // or just don’t care, remove it
+ inlineimage
+ printline
+ printto
+ quotefile
+ skipline
+ skipuntil
+ span
+ snippet
+ codeline
+ overload
+ sub
+ sup
+ tableofcontents
+ tt
+ uicontrol
+ endmapref
+ endomit
+ underline
+ unicode
+
+*/
+
+// Empty link target that was known to assert
+/*!
+ \page crash.html
+
+ \l {}
+*/
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/tocnavigation/src/qdoctests-outputfromqdocmanuallikefiles.qdoc b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/tocnavigation/src/qdoctests-outputfromqdocmanuallikefiles.qdoc
new file mode 100644
index 000000000..23f229745
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/tocnavigation/src/qdoctests-outputfromqdocmanuallikefiles.qdoc
@@ -0,0 +1,59 @@
+// Copyright (C) 2022 Thibaut Cuvelier
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// Excerpts from src/qdoc/doc/qdoc-guide.qdoc
+
+/*!
+ \page qdoctests-qdocmanuallikefileoutput.html
+
+ \title Document Navigation
+
+ The navigation commands...
+
+ \quotation
+ \raw HTML
+ <table border="0" cellpadding="0" cellspacing="5" width="100%">
+
+ <tr>
+ <p>
+ [Previous: <a href="15-qdoc-commands-navigation.html#deadlink">
+ Basic Qt</a>]
+ [<a href="15-qdoc-commands-navigation.html#deadlink">Contents</a>]
+ [Next: <a href="15-qdoc-commands-navigation.html#deadlink">
+ Creating Dialogs</a>]
+ </p>
+
+ <h1 align="center">Getting Started<br /></h1>
+
+ <p>
+ This chapter shows how to combine basic C++ with the
+ functionality provided by Qt to create a few small graphical
+ interface (GUI) applications.
+ </p>
+
+ <p>
+ [Previous: <a href="15-qdoc-commands-navigation.html#deadlink">
+ Basic Qt</a>]
+ [<a href="15-qdoc-commands-navigation.html#deadlink">Contents</a>]
+ [Next: <a href="15-qdoc-commands-navigation.html#deadlink">
+ Creating Dialogs</a>]
+ </p>
+
+ </table>
+ \endraw
+ \endquotation
+
+ \code
+ <head>
+ ...
+ <link rel="start" href="basicqt.html" />
+ ...
+ </head>
+ \endcode
+
+ \section1 Commands
+
+ \target previouspage-command
+ \section2 \\previouspage
+
+ The \\previouspage command...
+*/ \ No newline at end of file
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/tocnavigation/src/snippets/main.cpp b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/tocnavigation/src/snippets/main.cpp
new file mode 100644
index 000000000..6993849e4
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/tocnavigation/src/snippets/main.cpp
@@ -0,0 +1,10 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+
+int main()
+{
+ if (false) {
+ return 1;
+ }
+ return 0;
+}
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/tocnavigation/src/toc.qdoc b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/tocnavigation/src/toc.qdoc
new file mode 100644
index 000000000..6ca61de64
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/tocnavigation/src/toc.qdoc
@@ -0,0 +1,23 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+/*!
+ \page toc-test.html
+ \title TOC
+
+ \list
+ \li \l {Testing \PROD output from .qdoc files}{\PROD Testing}
+ \list
+ \li \l {Exhaustive testing of QDoc commands}
+ \endlist
+ \li Linking
+ \list
+ \li \l {QDoc Linking Test}
+ \list
+ \li \l {Link targets}
+ \endlist
+ \li \l {crash.html}{Random page}
+ \li \l {Table of Contents}
+ \endlist
+ \endlist
+*/
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/tocnavigation/tocnavigation.qdocconf b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/tocnavigation/tocnavigation.qdocconf
new file mode 100644
index 000000000..b78d91169
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/tocnavigation/tocnavigation.qdocconf
@@ -0,0 +1,48 @@
+project = OutputFromQDocFiles
+description = "A test project for QDoc build artifacts"
+buildversion = "$project - $description"
+moduleheader =
+
+sources.fileextensions = "*.qml *.cpp *.qdoc"
+headers.fileextensions = "*.h"
+
+# zero warning policy -- here; allow one which is (qdoc) warning: Can't link to ''
+warninglimit = 1
+warninglimit.enabled = true
+
+# don't write host system-specific paths to index files
+locationinfo = false
+
+outputformats = HTML WebXML DocBook
+HTML.nosubdirs = true
+HTML.outputsubdir = html
+WebXML.quotinginformation = true
+WebXML.nosubdirs = true
+WebXML.outputsubdir = webxml
+DocBook.nosubdirs = true
+DocBook.outputsubdir = docbook
+DocBook.usedocbookextensions = true
+
+# images
+imagedirs = ./src/images
+
+sources = ./src/qdoctests-outputfromqdocfiles.qdoc \
+ ./src/qdoctests-outputfromqdocmanuallikefiles.qdoc \
+ ./src/toc.qdoc
+
+exampledirs = ./src/snippets
+
+macro.beginqdoc = "\\c {/*!}"
+macro.endqdoc = "\\c */"
+macro.PROD = QDoc
+
+# Macro from qtbase/doc/global/macros.qdocconf
+# The file cannot be included directly, because it requires many
+# variables to be set, like QT_VER
+macro.borderedimage = "\\div {class=\"border\"} \\image \1\n\\enddiv"
+
+defines =
+
+navigation.toctitles = TOC
+navigation.toctitles.inclusive = true
+
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/trademark_command/expected/docbook/trademark-test.xml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/trademark_command/expected/docbook/trademark-test.xml
new file mode 100644
index 000000000..4f80aa29a
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/trademark_command/expected/docbook/trademark-test.xml
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<db:article xmlns:db="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" version="5.2" xml:lang="en">
+<db:info>
+<db:title>Testing the \tm command</db:title>
+<db:productname>TrademarkCommand</db:productname>
+<db:titleabbrev>TrademarkCommand Reference Documentation</db:titleabbrev>
+<db:abstract>
+<db:para>TrademarkCommand Reference Documentation.</db:para></db:abstract>
+</db:info>
+<db:para><db:trademark>Acme</db:trademark> is a trademark.</db:para>
+<db:section xml:id="acme-corp">
+<db:title> Acme corp</db:title>
+<db:para><db:phrase>Acme</db:phrase>, the <db:trademark>Acme Anvil</db:trademark>, and <db:trademark>`It Rings Like a Bell!`</db:trademark> are trademarks of the <db:emphasis>Acme corp</db:emphasis>.</db:para>
+</db:section>
+</db:article>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/trademark_command/expected/docbook/trademarks.xml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/trademark_command/expected/docbook/trademarks.xml
new file mode 100644
index 000000000..c6a5a5953
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/trademark_command/expected/docbook/trademarks.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<db:article xmlns:db="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" version="5.2" xml:lang="en">
+<db:info>
+<db:title>Trademarks</db:title>
+<db:productname>TrademarkCommand</db:productname>
+<db:titleabbrev>TrademarkCommand Reference Documentation</db:titleabbrev>
+<db:abstract>
+<db:para>A page that documents trademarks.</db:para>
+</db:abstract>
+</db:info>
+<db:itemizedlist>
+<db:listitem>
+<db:para><db:link xlink:href="trademark-test.xml#acme-corp">Acme corp</db:link>.</db:para>
+</db:listitem>
+</db:itemizedlist>
+<db:para><db:trademark>Foo</db:trademark> should have the trademark symbol but no link as we're already on the correct page.</db:para>
+<db:para>For repeated keywords, the symbol is added only to the first instance:</db:para>
+<db:para><db:trademark>Foobar</db:trademark> <db:phrase>Foobar</db:phrase> is a trademark.</db:para>
+</db:article>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/trademark_command/expected/html/trademark-test.html b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/trademark_command/expected/html/trademark-test.html
new file mode 100644
index 000000000..ae890142c
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/trademark_command/expected/html/trademark-test.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+<!-- test.qdoc -->
+ <title>Testing the \tm command | TrademarkCommand</title>
+</head>
+<body>
+<div class="sidebar">
+<div class="toc">
+<h3 id="toc">Contents</h3>
+<ul>
+<li class="level1"><a href="#acme-corp"> Acme corp</a></li>
+</ul>
+</div>
+<div class="sidebar-content" id="sidebar-content"></div></div>
+<h1 class="title">Testing the \tm command</h1>
+<!-- $$$trademark-test.html-description -->
+<div class="descr" id="details">
+<p><span translate="no">Acme<a href="trademarks.html">&#8482;</a></span> is a trademark.</p>
+<h2 id="acme-corp"> Acme corp</h2>
+<p><span translate="no">Acme</span>, the <span translate="no">Acme Anvil<a href="trademarks.html">&#8482;</a></span>, and <span translate="no">`It Rings Like a Bell!`<a href="trademarks.html">&#8482;</a></span> are trademarks of the <i>Acme corp</i>.</p>
+</div>
+<!-- @@@trademark-test.html -->
+</body>
+</html>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/trademark_command/expected/html/trademarkcommand.index b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/trademark_command/expected/html/trademarkcommand.index
new file mode 100644
index 000000000..9b0fe807a
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/trademark_command/expected/html/trademarkcommand.index
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE QDOCINDEX>
+<INDEX url="" title="TrademarkCommand Reference Documentation" version="" project="TrademarkCommand">
+ <namespace name="" status="active" access="public" module="trademarkcommand">
+ <page name="trademark-test.html" href="trademark-test.html" status="active" location="test.qdoc" documented="true" subtype="page" title="Testing the \tm command" fulltitle="Testing the \tm command" subtitle="">
+ <contents name="acme-corp" title=" Acme corp" level="1"/>
+ </page>
+ <page name="trademarks.html" href="trademarks.html" status="active" location="test.qdoc" documented="true" subtype="page" title="Trademarks" fulltitle="Trademarks" subtitle="" brief="A page that documents trademarks">
+ <keyword name="trademark"/>
+ </page>
+ </namespace>
+</INDEX>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/trademark_command/expected/html/trademarks.html b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/trademark_command/expected/html/trademarks.html
new file mode 100644
index 000000000..46aacb969
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/trademark_command/expected/html/trademarks.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+<!-- test.qdoc -->
+ <meta name="description" content="A page that documents trademarks.">
+ <title>Trademarks | TrademarkCommand</title>
+</head>
+<body>
+<div class="sidebar"><div class="sidebar-content" id="sidebar-content"></div></div>
+<h1 class="title">Trademarks</h1>
+<!-- $$$trademarks.html-description -->
+<div class="descr" id="details">
+<ul>
+<li><a href="trademark-test.html#acme-corp">Acme corp</a>.</li>
+</ul>
+<p><span translate="no">Foo&#8482;</span> should have the trademark symbol but no link as we're already on the correct page.</p>
+<p>For repeated keywords, the symbol is added only to the first instance:</p>
+<p><span translate="no">Foobar&#8482;</span> <span translate="no">Foobar</span> is a trademark.</p>
+</div>
+<!-- @@@trademarks.html -->
+</body>
+</html>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/trademark_command/expected/webxml/trademark-test.webxml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/trademark_command/expected/webxml/trademark-test.webxml
new file mode 100644
index 000000000..09f1c5a94
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/trademark_command/expected/webxml/trademark-test.webxml
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<WebXML>
+ <document>
+ <page name="trademark-test.html" href="trademark-test.html" status="active" location="test.qdoc" documented="true" subtype="page" title="Testing the \tm command" fulltitle="Testing the \tm command" subtitle="">
+ <contents name="acme-corp" title=" Acme corp" level="1"/>
+ <description>
+ <para>Acme™ is a trademark.</para>
+ <section id="acme-corp">
+ <heading level="1"> Acme corp</heading>
+ <para>Acme, the Acme Anvil™, and `It Rings Like a Bell!`™ are trademarks of the <italic>Acme corp</italic>.</para>
+ </section>
+ </description>
+ </page>
+ </document>
+</WebXML>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/trademark_command/expected/webxml/trademarkcommand.index b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/trademark_command/expected/webxml/trademarkcommand.index
new file mode 100644
index 000000000..9b0fe807a
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/trademark_command/expected/webxml/trademarkcommand.index
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE QDOCINDEX>
+<INDEX url="" title="TrademarkCommand Reference Documentation" version="" project="TrademarkCommand">
+ <namespace name="" status="active" access="public" module="trademarkcommand">
+ <page name="trademark-test.html" href="trademark-test.html" status="active" location="test.qdoc" documented="true" subtype="page" title="Testing the \tm command" fulltitle="Testing the \tm command" subtitle="">
+ <contents name="acme-corp" title=" Acme corp" level="1"/>
+ </page>
+ <page name="trademarks.html" href="trademarks.html" status="active" location="test.qdoc" documented="true" subtype="page" title="Trademarks" fulltitle="Trademarks" subtitle="" brief="A page that documents trademarks">
+ <keyword name="trademark"/>
+ </page>
+ </namespace>
+</INDEX>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/trademark_command/expected/webxml/trademarks.webxml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/trademark_command/expected/webxml/trademarks.webxml
new file mode 100644
index 000000000..1d9350777
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/trademark_command/expected/webxml/trademarks.webxml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<WebXML>
+ <document>
+ <page name="trademarks.html" href="trademarks.html" status="active" location="test.qdoc" documented="true" subtype="page" title="Trademarks" fulltitle="Trademarks" subtitle="" brief="A page that documents trademarks">
+ <keyword name="trademark"/>
+ <description>
+ <brief>A page that documents trademarks.</brief>
+ <list type="bullet">
+ <item>
+ <para>
+ <link raw="Acme corp" href="trademark-test.html#acme-corp" type="page" page="Testing the \tm command">Acme corp</link>.</para>
+ </item>
+ </list>
+ <para>Foo™ should have the trademark symbol but no link as we're already on the correct page.</para>
+ <para>For repeated keywords, the symbol is added only to the first instance:</para>
+ <para>Foobar™ Foobar is a trademark.</para>
+ </description>
+ </page>
+ </document>
+</WebXML>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/trademark_command/src/test.qdoc b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/trademark_command/src/test.qdoc
new file mode 100644
index 000000000..ca7265253
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/trademark_command/src/test.qdoc
@@ -0,0 +1,32 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+/*!
+ \page trademark-test.html
+ \title Testing the \\tm command
+
+ \tm {Acme} is a trademark.
+
+ \section1 \tm Acme corp
+
+ \tm Acme, the \tm {Acme Anvil}, and \tm {`It Rings Like a Bell!`}
+ are trademarks of the \e {Acme corp}.
+*/
+
+/*!
+ \page trademarks.html
+ \title Trademarks
+ \keyword trademark
+ \brief A page that documents trademarks.
+
+ \list
+ \li \l {Acme corp}.
+ \endlist
+
+ \tm Foo should have the trademark symbol but no link as we're
+ already on the correct page.
+
+ For repeated keywords, the symbol is added only to the first
+ instance:
+ \trademark {\Foobar \Foobar}
+*/
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/trademark_command/trademark_command.qdocconf b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/trademark_command/trademark_command.qdocconf
new file mode 100644
index 000000000..150f9873f
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/trademark_command/trademark_command.qdocconf
@@ -0,0 +1,20 @@
+project = TrademarkCommand
+
+{sourcedirs,headerdirs} = ./src
+
+navigation.trademarkspage = Trademarks
+
+macro.trademark = "\n\n\1 is a trademark.\n\n"
+macro.Foobar = "\\tm {Foobar}"
+
+locationinfo = false
+warninglimit = 0
+warninglimit.enabled = true
+
+outputformats = WebXML HTML DocBook
+{WebXML.nosubdirs,HTML.nosubdirs,DocBook.nosubdirs} = true
+WebXML.quotinginformation = true
+
+WebXML.outputsubdir = webxml
+HTML.outputsubdir = html
+DocBook.outputsubdir = docbook
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/trailing_backslashes/expected/docbook/struct.xml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/trailing_backslashes/expected/docbook/struct.xml
new file mode 100644
index 000000000..48efdadb7
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/trailing_backslashes/expected/docbook/struct.xml
@@ -0,0 +1,57 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<db:article xmlns:db="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" version="5.2" xml:lang="en">
+<db:info>
+<db:title></db:title>
+<db:productname>TrailingBackslashes</db:productname>
+<db:titleabbrev>TrailingBackslashes Reference Documentation</db:titleabbrev>
+<db:abstract>
+<db:para>TrailingBackslashes Reference Documentation.</db:para></db:abstract>
+</db:info>
+<db:variablelist>
+<db:varlistentry>
+<db:term>Header</db:term>
+<db:listitem>
+<db:para>Struct</db:para>
+</db:listitem>
+</db:varlistentry>
+</db:variablelist>
+<db:section xml:id="details">
+<db:title>Detailed Description</db:title>
+</db:section>
+<db:section xml:id="member-function-documentation">
+<db:title>Member Function Documentation</db:title>
+<db:section xml:id="MultipleTrailingSlashes">
+<db:title>void Struct::MultipleTrailingSlashes()</db:title>
+<db:section>
+<db:title>See Also</db:title>
+<db:para><db:emphasis>See also </db:emphasis>
+<db:simplelist type="vert" role="see-also">
+<db:member><db:link xlink:href="struct.xml#oneTrailingSlash">one slash</db:link></db:member>
+</db:simplelist>
+</db:para>
+</db:section>
+</db:section>
+<db:section xml:id="multipleTrailingSlashesAndRandomWhitespace">
+<db:title>void Struct::multipleTrailingSlashesAndRandomWhitespace()</db:title>
+<db:section>
+<db:title>See Also</db:title>
+<db:para><db:emphasis>See also </db:emphasis>
+<db:simplelist type="vert" role="see-also">
+<db:member><db:link xlink:href="struct.xml#MultipleTrailingSlashes">two slashes here</db:link></db:member>
+</db:simplelist>
+</db:para>
+</db:section>
+</db:section>
+<db:section xml:id="oneTrailingSlash">
+<db:title>void Struct::oneTrailingSlash()</db:title>
+<db:section>
+<db:title>See Also</db:title>
+<db:para><db:emphasis>See also </db:emphasis>
+<db:simplelist type="vert" role="see-also">
+<db:member><db:link xlink:href="struct.xml#multipleTrailingSlashesAndRandomWhitespace">two slashes again</db:link></db:member>
+</db:simplelist>
+</db:para>
+</db:section>
+</db:section>
+</db:section>
+</db:article>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/trailing_backslashes/expected/html/struct-members.html b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/trailing_backslashes/expected/html/struct-members.html
new file mode 100644
index 000000000..7fe474e80
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/trailing_backslashes/expected/html/struct-members.html
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+<!-- trailing_backslashes.cpp -->
+ <title>List of All Members for Struct | TrailingBackslashes</title>
+</head>
+<body>
+<li>Struct</li>
+<div class="sidebar"><div class="sidebar-content" id="sidebar-content"></div></div>
+<h1 class="title" translate="no">List of All Members for Struct</h1>
+<p>This is the complete list of members for <a href="struct.html">Struct</a>, including inherited members.</p>
+<ul>
+<li class="fn" translate="no"><span class="name"><b><a href="struct.html#MultipleTrailingSlashes" translate="no">MultipleTrailingSlashes</a></b></span>()</li>
+<li class="fn" translate="no"><span class="name"><b><a href="struct.html#multipleTrailingSlashesAndRandomWhitespace" translate="no">multipleTrailingSlashesAndRandomWhitespace</a></b></span>()</li>
+<li class="fn" translate="no"><span class="name"><b><a href="struct.html#oneTrailingSlash" translate="no">oneTrailingSlash</a></b></span>()</li>
+</ul>
+</body>
+</html>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/trailing_backslashes/expected/html/struct.html b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/trailing_backslashes/expected/html/struct.html
new file mode 100644
index 000000000..0e5a0b45d
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/trailing_backslashes/expected/html/struct.html
@@ -0,0 +1,53 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+<!-- trailing_backslashes.cpp -->
+ <title>Struct Struct | TrailingBackslashes</title>
+</head>
+<body>
+<li>Struct</li>
+<div class="sidebar">
+<div class="toc">
+<h3 id="toc">Contents</h3>
+<ul>
+<li class="level1"><a href="#public-functions">Public Functions</a></li>
+<li class="level1"><a href="#details">Detailed Description</a></li>
+</ul>
+</div>
+<div class="sidebar-content" id="sidebar-content"></div></div>
+<h1 class="title" translate="no">Struct Struct</h1>
+<div class="table"><table class="alignedsummary" translate="no">
+<tr><td class="memItemLeft rightAlign topAlign"> Header:</td><td class="memItemRight bottomAlign"> <span class="preprocessor">#include &lt;Struct&gt;</span></td></tr>
+</table></div>
+<ul>
+<li><a href="struct-members.html">List of all members, including inherited members</a></li>
+</ul>
+<h2 id="public-functions">Public Functions</h2>
+<div class="table"><table class="alignedsummary" translate="no">
+<tr><td class="memItemLeft rightAlign topAlign"> void </td><td class="memItemRight bottomAlign"><b><a href="struct.html#MultipleTrailingSlashes" translate="no">MultipleTrailingSlashes</a></b>()</td></tr>
+<tr><td class="memItemLeft rightAlign topAlign"> void </td><td class="memItemRight bottomAlign"><b><a href="struct.html#multipleTrailingSlashesAndRandomWhitespace" translate="no">multipleTrailingSlashesAndRandomWhitespace</a></b>()</td></tr>
+<tr><td class="memItemLeft rightAlign topAlign"> void </td><td class="memItemRight bottomAlign"><b><a href="struct.html#oneTrailingSlash" translate="no">oneTrailingSlash</a></b>()</td></tr>
+</table></div>
+<!-- $$$Struct-description -->
+<div class="descr">
+<h2 id="details">Detailed Description</h2>
+</div>
+<!-- @@@Struct -->
+<div class="func">
+<h2>Member Function Documentation</h2>
+<!-- $$$MultipleTrailingSlashes[overload1]$$$MultipleTrailingSlashes -->
+<h3 class="fn" translate="no" id="MultipleTrailingSlashes"><span class="type">void</span> Struct::<span class="name">MultipleTrailingSlashes</span>()</h3>
+<p><b>See also </b><a href="struct.html#oneTrailingSlash" translate="no">one slash</a>.</p>
+<!-- @@@MultipleTrailingSlashes -->
+<!-- $$$multipleTrailingSlashesAndRandomWhitespace[overload1]$$$multipleTrailingSlashesAndRandomWhitespace -->
+<h3 class="fn" translate="no" id="multipleTrailingSlashesAndRandomWhitespace"><span class="type">void</span> Struct::<span class="name">multipleTrailingSlashesAndRandomWhitespace</span>()</h3>
+<p><b>See also </b><a href="struct.html#MultipleTrailingSlashes" translate="no">two slashes here</a>.</p>
+<!-- @@@multipleTrailingSlashesAndRandomWhitespace -->
+<!-- $$$oneTrailingSlash[overload1]$$$oneTrailingSlash -->
+<h3 class="fn" translate="no" id="oneTrailingSlash"><span class="type">void</span> Struct::<span class="name">oneTrailingSlash</span>()</h3>
+<p><b>See also </b><a href="struct.html#multipleTrailingSlashesAndRandomWhitespace" translate="no">two slashes again</a>.</p>
+<!-- @@@oneTrailingSlash -->
+</div>
+</body>
+</html>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/trailing_backslashes/expected/html/trailingbackslashes.index b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/trailing_backslashes/expected/html/trailingbackslashes.index
new file mode 100644
index 000000000..be2edeaf0
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/trailing_backslashes/expected/html/trailingbackslashes.index
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE QDOCINDEX>
+<INDEX url="" title="TrailingBackslashes Reference Documentation" version="" project="TrailingBackslashes">
+ <namespace name="" status="active" access="public" module="trailingbackslashes">
+ <struct name="Struct" href="struct.html" status="active" access="public" location="trailing_backslashes.h" documented="true" module="TrailingBackslashes">
+ <function name="MultipleTrailingSlashes" fullname="Struct::MultipleTrailingSlashes" href="struct.html#MultipleTrailingSlashes" status="active" access="public" location="trailing_backslashes.h" documented="true" meta="plain" type="void" signature="void MultipleTrailingSlashes()">
+ <keyword name="two-slashes-here" title="two slashes here"/>
+ </function>
+ <function name="multipleTrailingSlashesAndRandomWhitespace" fullname="Struct::multipleTrailingSlashesAndRandomWhitespace" href="struct.html#multipleTrailingSlashesAndRandomWhitespace" status="active" access="public" location="trailing_backslashes.h" documented="true" meta="plain" type="void" signature="void multipleTrailingSlashesAndRandomWhitespace()">
+ <keyword name="two-slashes-again" title="two slashes again"/>
+ </function>
+ <function name="oneTrailingSlash" fullname="Struct::oneTrailingSlash" href="struct.html#oneTrailingSlash" status="active" access="public" location="trailing_backslashes.h" documented="true" meta="plain" type="void" signature="void oneTrailingSlash()">
+ <keyword name="one-slash" title="one slash"/>
+ </function>
+ </struct>
+ <module name="TrailingBackslashes" href="trailingbackslashes-module.html" status="internal" seen="false" title=""/>
+ </namespace>
+</INDEX>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/trailing_backslashes/expected/webxml/struct.webxml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/trailing_backslashes/expected/webxml/struct.webxml
new file mode 100644
index 000000000..85a7a1f28
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/trailing_backslashes/expected/webxml/struct.webxml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<WebXML>
+ <document>
+ <struct name="Struct" href="struct.html" status="active" access="public" location="trailing_backslashes.h" documented="true" module="TrailingBackslashes">
+ <description/>
+ <function name="MultipleTrailingSlashes" fullname="Struct::MultipleTrailingSlashes" href="struct.html#MultipleTrailingSlashes" status="active" access="public" location="trailing_backslashes.h" documented="true" meta="plain" type="void" signature="void MultipleTrailingSlashes()">
+ <keyword name="two-slashes-here" title="two slashes here"/>
+ <description>
+ <see-also>
+ <link raw="one slash" href="struct.html#oneTrailingSlash" type="function">one slash</link>
+ </see-also>
+ </description>
+ </function>
+ <function name="multipleTrailingSlashesAndRandomWhitespace" fullname="Struct::multipleTrailingSlashesAndRandomWhitespace" href="struct.html#multipleTrailingSlashesAndRandomWhitespace" status="active" access="public" location="trailing_backslashes.h" documented="true" meta="plain" type="void" signature="void multipleTrailingSlashesAndRandomWhitespace()">
+ <keyword name="two-slashes-again" title="two slashes again"/>
+ <description>
+ <see-also>
+ <link raw="two slashes here" href="struct.html#MultipleTrailingSlashes" type="function">two slashes here</link>
+ </see-also>
+ </description>
+ </function>
+ <function name="oneTrailingSlash" fullname="Struct::oneTrailingSlash" href="struct.html#oneTrailingSlash" status="active" access="public" location="trailing_backslashes.h" documented="true" meta="plain" type="void" signature="void oneTrailingSlash()">
+ <keyword name="one-slash" title="one slash"/>
+ <description>
+ <see-also>
+ <link raw="two slashes again" href="struct.html#multipleTrailingSlashesAndRandomWhitespace" type="function">two slashes again</link>
+ </see-also>
+ </description>
+ </function>
+ </struct>
+ </document>
+</WebXML>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/trailing_backslashes/expected/webxml/trailingbackslashes.index b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/trailing_backslashes/expected/webxml/trailingbackslashes.index
new file mode 100644
index 000000000..be2edeaf0
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/trailing_backslashes/expected/webxml/trailingbackslashes.index
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE QDOCINDEX>
+<INDEX url="" title="TrailingBackslashes Reference Documentation" version="" project="TrailingBackslashes">
+ <namespace name="" status="active" access="public" module="trailingbackslashes">
+ <struct name="Struct" href="struct.html" status="active" access="public" location="trailing_backslashes.h" documented="true" module="TrailingBackslashes">
+ <function name="MultipleTrailingSlashes" fullname="Struct::MultipleTrailingSlashes" href="struct.html#MultipleTrailingSlashes" status="active" access="public" location="trailing_backslashes.h" documented="true" meta="plain" type="void" signature="void MultipleTrailingSlashes()">
+ <keyword name="two-slashes-here" title="two slashes here"/>
+ </function>
+ <function name="multipleTrailingSlashesAndRandomWhitespace" fullname="Struct::multipleTrailingSlashesAndRandomWhitespace" href="struct.html#multipleTrailingSlashesAndRandomWhitespace" status="active" access="public" location="trailing_backslashes.h" documented="true" meta="plain" type="void" signature="void multipleTrailingSlashesAndRandomWhitespace()">
+ <keyword name="two-slashes-again" title="two slashes again"/>
+ </function>
+ <function name="oneTrailingSlash" fullname="Struct::oneTrailingSlash" href="struct.html#oneTrailingSlash" status="active" access="public" location="trailing_backslashes.h" documented="true" meta="plain" type="void" signature="void oneTrailingSlash()">
+ <keyword name="one-slash" title="one slash"/>
+ </function>
+ </struct>
+ <module name="TrailingBackslashes" href="trailingbackslashes-module.html" status="internal" seen="false" title=""/>
+ </namespace>
+</INDEX>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/trailing_backslashes/src/trailing_backslashes.cpp b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/trailing_backslashes/src/trailing_backslashes.cpp
new file mode 100644
index 000000000..878425613
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/trailing_backslashes/src/trailing_backslashes.cpp
@@ -0,0 +1,32 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+/*!
+ \struct Struct
+*/
+
+/*!
+ \fn void Struct::oneTrailingSlash()
+ \keyword one \
+ slash
+
+ \sa {two slashes again}
+*/
+
+/*!
+ \fn void Struct::MultipleTrailingSlashes()
+ \keyword two \
+ slashes \
+ here
+
+ \sa {one slash}
+*/
+
+/*!
+ \fn void Struct::multipleTrailingSlashesAndRandomWhitespace()
+ \keyword two\
+ slashes \
+ again
+
+ \sa {two slashes here}
+*/
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/trailing_backslashes/src/trailing_backslashes.h b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/trailing_backslashes/src/trailing_backslashes.h
new file mode 100644
index 000000000..0fb059b97
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/trailing_backslashes/src/trailing_backslashes.h
@@ -0,0 +1,14 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#ifndef TRAILING_BACKSLASHES_H
+#define TRAILING_BACKSLASHES_H
+
+struct Struct
+{
+ void oneTrailingSlash(){};
+ void MultipleTrailingSlashes(){};
+ void multipleTrailingSlashesAndRandomWhitespace(){};
+};
+
+#endif // TRAILING_BACKSLASHES_H
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/trailing_backslashes/trailing_backslashes.qdocconf b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/trailing_backslashes/trailing_backslashes.qdocconf
new file mode 100644
index 000000000..3f8f4d011
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/trailing_backslashes/trailing_backslashes.qdocconf
@@ -0,0 +1,29 @@
+project = TrailingBackslashes
+
+headerdirs = ./src/
+sourcedirs = ./src/
+exampledirs = ./src/
+
+outputformats = WebXML HTML DocBook
+WebXML.quotinginformation = true
+WebXML.nosubdirs = true
+WebXML.outputsubdir = webxml
+
+HTML.nosubdirs = true
+HTML.outputsubdir = html
+
+DocBook.nosubdirs = true
+DocBook.outputsubdir = docbook
+
+sources.fileextensions = "*.qml *.cpp *.qdoc"
+headers.fileextensions = "*.h"
+
+# images
+imagedirs = ./src/images
+
+warninglimit.enabled = true
+
+# don't write host system-specific paths to index files
+locationinfo = false
+
+warninglimit = 1
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/usingdirective/expected/docbook/space.xml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/usingdirective/expected/docbook/space.xml
new file mode 100644
index 000000000..174b64092
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/usingdirective/expected/docbook/space.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<db:article xmlns:db="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" version="5.2" xml:lang="en">
+<db:info>
+<db:title>Space Namespace</db:title>
+<db:productname>UsingDirective</db:productname>
+<db:titleabbrev>UsingDirective Reference Documentation</db:titleabbrev>
+<db:abstract>
+<db:para>A namespace...in space.</db:para>
+</db:abstract>
+</db:info>
+<db:variablelist>
+<db:varlistentry>
+<db:term>Header</db:term>
+<db:listitem>
+<db:para>Space</db:para>
+</db:listitem>
+</db:varlistentry>
+</db:variablelist>
+<db:section xml:id="details">
+<db:title>Detailed Description</db:title>
+</db:section>
+<db:section xml:id="function-documentation">
+<db:title>Function Documentation</db:title>
+<db:section xml:id="spaceFun">
+<db:title>void spaceFun(Space::spacename <db:emphasis>space</db:emphasis>)</db:title>
+<db:para>A <db:code role="parameter">space</db:code> function.</db:para>
+</db:section>
+</db:section>
+</db:article>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/usingdirective/expected/html/space.html b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/usingdirective/expected/html/space.html
new file mode 100644
index 000000000..60e389382
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/usingdirective/expected/html/space.html
@@ -0,0 +1,43 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+<!-- space.cpp -->
+ <meta name="description" content="A namespace...in space.">
+ <title>Space Namespace | UsingDirective</title>
+</head>
+<body>
+<div class="sidebar">
+<div class="toc">
+<h3 id="toc">Contents</h3>
+<ul>
+<li class="level1"><a href="#functions">Functions</a></li>
+<li class="level1"><a href="#details">Detailed Description</a></li>
+</ul>
+</div>
+<div class="sidebar-content" id="sidebar-content"></div></div>
+<h1 class="title" translate="no">Space Namespace</h1>
+<!-- $$$Space-brief -->
+<p>A namespace...in space. <a href="#details">More...</a></p>
+<!-- @@@Space -->
+<div class="table"><table class="alignedsummary" translate="no">
+<tr><td class="memItemLeft rightAlign topAlign"> Header:</td><td class="memItemRight bottomAlign"> <span class="preprocessor">#include &lt;Space&gt;</span></td></tr>
+</table></div>
+<h2 id="functions">Functions</h2>
+<div class="table"><table class="alignedsummary" translate="no">
+<tr><td class="memItemLeft rightAlign topAlign"> void </td><td class="memItemRight bottomAlign"><b><a href="space.html#spaceFun" translate="no">spaceFun</a></b>(Space::spacename <i>space</i>)</td></tr>
+</table></div>
+<!-- $$$Space-description -->
+<div class="descr">
+<h2 id="details">Detailed Description</h2>
+</div>
+<!-- @@@Space -->
+<div class="func">
+<h2>Function Documentation</h2>
+<!-- $$$spaceFun[overload1]$$$spaceFunSpace::spacename -->
+<h3 class="fn" translate="no" id="spaceFun"><span class="type">void</span> <span class="name">spaceFun</span>(<span class="type">Space::spacename</span> <i>space</i>)</h3>
+<p>A <i translate="no">space</i> function.</p>
+<!-- @@@spaceFun -->
+</div>
+</body>
+</html>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/usingdirective/expected/html/usingdirective.index b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/usingdirective/expected/html/usingdirective.index
new file mode 100644
index 000000000..252512890
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/usingdirective/expected/html/usingdirective.index
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE QDOCINDEX>
+<INDEX url="" title="UsingDirective Reference Documentation" version="" project="UsingDirective">
+ <namespace name="" status="active" access="public" module="usingdirective">
+ <function name="spaceFun" href="space.html#spaceFun" status="active" access="public" location="space.h" documented="true" related="0" meta="plain" type="void" signature="void spaceFun(Space::spacename space)">
+ <parameter type="Space::spacename" name="space" default=""/>
+ </function>
+ <namespace name="Space" href="space.html" status="active" access="public" location="space.h" documented="true" module="UsingDirective" brief="A namespace...in space">
+ <function name="spaceFun" href="space.html#spaceFun" status="active" access="public" location="space.h" documented="true" related="0" meta="plain" type="void" signature="void spaceFun(Space::spacename space)">
+ <parameter type="Space::spacename" name="space" default=""/>
+ </function>
+ </namespace>
+ <module name="UsingDirective" href="usingdirective-module.html" status="internal" seen="false" title=""/>
+ </namespace>
+</INDEX>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/usingdirective/expected/webxml/space.webxml b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/usingdirective/expected/webxml/space.webxml
new file mode 100644
index 000000000..15102ba9e
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/usingdirective/expected/webxml/space.webxml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<WebXML>
+ <document>
+ <namespace name="Space" href="space.html" status="active" access="public" location="space.h" documented="true" module="UsingDirective" brief="A namespace...in space">
+ <description>
+ <brief>A namespace...in space.</brief>
+ </description>
+ <function name="spaceFun" href="space.html#spaceFun" status="active" access="public" location="space.h" documented="true" related="0" meta="plain" type="void" signature="void spaceFun(Space::spacename space)">
+ <parameter type="Space::spacename" name="space" default=""/>
+ <description>
+ <para>A <argument>space</argument> function.</para>
+ </description>
+ </function>
+ </namespace>
+ </document>
+</WebXML>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/usingdirective/expected/webxml/usingdirective.index b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/usingdirective/expected/webxml/usingdirective.index
new file mode 100644
index 000000000..252512890
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/usingdirective/expected/webxml/usingdirective.index
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE QDOCINDEX>
+<INDEX url="" title="UsingDirective Reference Documentation" version="" project="UsingDirective">
+ <namespace name="" status="active" access="public" module="usingdirective">
+ <function name="spaceFun" href="space.html#spaceFun" status="active" access="public" location="space.h" documented="true" related="0" meta="plain" type="void" signature="void spaceFun(Space::spacename space)">
+ <parameter type="Space::spacename" name="space" default=""/>
+ </function>
+ <namespace name="Space" href="space.html" status="active" access="public" location="space.h" documented="true" module="UsingDirective" brief="A namespace...in space">
+ <function name="spaceFun" href="space.html#spaceFun" status="active" access="public" location="space.h" documented="true" related="0" meta="plain" type="void" signature="void spaceFun(Space::spacename space)">
+ <parameter type="Space::spacename" name="space" default=""/>
+ </function>
+ </namespace>
+ <module name="UsingDirective" href="usingdirective-module.html" status="internal" seen="false" title=""/>
+ </namespace>
+</INDEX>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/usingdirective/src/UsingDirective b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/usingdirective/src/UsingDirective
new file mode 100644
index 000000000..422d01e91
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/usingdirective/src/UsingDirective
@@ -0,0 +1,2 @@
+#include "alias.h"
+#include "space.h"
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/usingdirective/src/alias.h b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/usingdirective/src/alias.h
new file mode 100644
index 000000000..5691035da
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/usingdirective/src/alias.h
@@ -0,0 +1,10 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#pragma once
+
+#include "space.h"
+
+namespace Alias {
+ using spacename = Space::spacename;
+}
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/usingdirective/src/space.cpp b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/usingdirective/src/space.cpp
new file mode 100644
index 000000000..d84156da0
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/usingdirective/src/space.cpp
@@ -0,0 +1,21 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "space.h"
+
+/*!
+ \namespace Space
+ \inmodule UsingDirective
+ \brief A namespace...in space.
+*/
+
+using namespace Alias;
+using namespace Space;
+
+/*!
+ \relates Space
+ A \a space function.
+*/
+void spaceFun(spacename space)
+{
+}
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/usingdirective/src/space.h b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/usingdirective/src/space.h
new file mode 100644
index 000000000..09472535c
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/usingdirective/src/space.h
@@ -0,0 +1,10 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#pragma once
+
+namespace Space {
+ typedef int spacename;
+}
+
+void spaceFun(Space::spacename space);
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/usingdirective/usingdirective.qdocconf b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/usingdirective/usingdirective.qdocconf
new file mode 100644
index 000000000..01456b049
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/usingdirective/usingdirective.qdocconf
@@ -0,0 +1,27 @@
+sources.fileextensions = "*.qml *.cpp *.qdoc"
+headers.fileextensions = "*.h"
+
+# images
+imagedirs = ./src/images
+
+# zero warning policy
+warninglimit = 0
+warninglimit.enabled = true
+
+# don't write host system-specific paths to index files
+locationinfo = false
+
+# by default, use -outputdir directly, no subdir.
+# outputsubdir '.' matches the root of expected_output/
+outputformats = HTML WebXML DocBook
+HTML.nosubdirs = true
+HTML.outputsubdir = html
+
+DocBook.nosubdirs = true
+DocBook.outputsubdir = docbook
+
+WebXML.nosubdirs = true
+WebXML.outputsubdir = webxml
+
+project = UsingDirective
+{includepaths,headerdirs,sourcedirs} = ./src/
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/tst_validateqdocoutputfiles.cpp b/src/qdoc/qdoc/tests/validateqdocoutputfiles/tst_validateqdocoutputfiles.cpp
new file mode 100644
index 000000000..a3c08ad7b
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/tst_validateqdocoutputfiles.cpp
@@ -0,0 +1,182 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#include <QtTest/QtTest>
+
+#include <utility>
+
+class tst_validateQdocOutputFiles : public QObject
+{
+ Q_OBJECT
+private:
+ void runQDocProcess(const QStringList &arguments);
+ std::optional<QByteArray> gitDiffDirectories(const QString &actualPath,
+ const QString &expectedPath);
+
+private slots:
+ void initTestCase();
+ void init();
+ void qdocProjects_data();
+ void qdocProjects();
+
+private:
+ const QString m_testDataDirectory = QFINDTESTDATA("testdata");
+ QString m_qdocBinary{};
+ QString m_extraParams{};
+ QScopedPointer<QTemporaryDir> m_outputDir{};
+};
+
+static constexpr QLatin1StringView ASAN_OPTIONS_ENVVAR{"ASAN_OPTIONS"};
+static inline bool regenerate{false};
+
+//! Update `README.md` if you change the name of this environment variable!
+static constexpr QLatin1StringView REGENERATE_ENVVAR{"QDOC_REGENERATE_TESTDATA"};
+static QProcessEnvironment s_environment {QProcessEnvironment::systemEnvironment()};
+
+void tst_validateQdocOutputFiles::initTestCase()
+{
+ if (s_environment.contains(REGENERATE_ENVVAR)) {
+ qInfo() << "Regenerating expected output for all tests.";
+ regenerate = true;
+ qInfo("Removing %s environment variable.", REGENERATE_ENVVAR.constData());
+ s_environment.remove(REGENERATE_ENVVAR);
+ }
+
+ // We must disable the use of sigaltstack for ASan to work properly with QDoc when
+ // linked against libclang, to avoid a crash in ASan. This is a known issue and workaround,
+ // see e.g. https://github.com/google/sanitizers/issues/849 and
+ // https://github.com/KDE/kdevelop/commit/e306f3e39aba37b606dadba195fa5b7b73816f8f.
+ // We do this for the process environment of the QDoc process only to avoid affecting
+ // other processes that might be started by the test runner in COIN.
+ const QString optionString = s_environment.contains(ASAN_OPTIONS_ENVVAR) ? ",use_sigaltstack=0" : "use_sigaltstack=0";
+ s_environment.insert(ASAN_OPTIONS_ENVVAR, s_environment.value(ASAN_OPTIONS_ENVVAR) + optionString);
+ qInfo() << "Disabling ASan's alternate signal stack by setting `ASAN_OPTIONS=use_sigaltstack=0`.";
+
+ // Build the path to the QDoc binary the same way moc tests do for moc.
+ const auto binpath = QLibraryInfo::path(QLibraryInfo::BinariesPath);
+ const auto extension = QSysInfo::productType() == "windows" ? ".exe" : "";
+ m_qdocBinary = binpath + QLatin1String("/qdoc") + extension;
+ QVERIFY(QFile::exists(m_qdocBinary));
+
+ // Resolve the path to the file containing extra parameters
+ m_extraParams = QFileInfo(QTest::currentAppName()).dir().filePath(DOCINCPATH);
+ if (!QFileInfo::exists(m_extraParams)) {
+ qWarning("Cannot locate %s", m_extraParams.toLocal8Bit().constData());
+ m_extraParams.clear();
+ } else {
+ m_extraParams.insert(0, '@');
+ }
+}
+
+void tst_validateQdocOutputFiles::init()
+{
+ m_outputDir.reset(new QTemporaryDir());
+ if (!m_outputDir->isValid()) {
+ const QString errorMessage =
+ "Couldn't create temporary directory: " + m_outputDir->errorString();
+ QFAIL(qPrintable(errorMessage));
+ }
+}
+
+void tst_validateQdocOutputFiles::runQDocProcess(const QStringList &arguments)
+{
+ QProcess qdocProcess;
+ qdocProcess.setProcessEnvironment(s_environment);
+ qdocProcess.setProgram(m_qdocBinary);
+ qdocProcess.setArguments(arguments);
+
+ auto failQDoc = [&](QProcess::ProcessError) {
+ qFatal("Running qdoc failed with exit code %i: %s",
+ qdocProcess.exitCode(), qUtf8Printable(qdocProcess.errorString()));
+ };
+ QObject::connect(&qdocProcess, &QProcess::errorOccurred, this, failQDoc);
+
+ qdocProcess.start();
+ qdocProcess.waitForFinished();
+ if (qdocProcess.exitCode() == 0)
+ return;
+
+ QString errors = qdocProcess.readAllStandardError();
+ if (!errors.isEmpty())
+ qInfo().nospace() << "Received errors:\n" << qUtf8Printable(errors);
+ if (!QTest::currentTestFailed())
+ failQDoc(QProcess::UnknownError);
+}
+
+std::optional<QByteArray>
+tst_validateQdocOutputFiles::gitDiffDirectories(const QString &actualPath, const QString &expectedPath)
+{
+ QProcess gitProcess;
+ gitProcess.setProgram("git");
+
+ const QStringList arguments{"diff", "--", actualPath, expectedPath};
+ gitProcess.setArguments(arguments);
+
+ auto failGit = [&](QProcess::ProcessError) {
+ qFatal("Running git failed with exit code %i: %s",
+ gitProcess.exitCode(), gitProcess.errorString().toLocal8Bit().constData());
+ };
+ QObject::connect(&gitProcess, &QProcess::errorOccurred, this, failGit);
+
+ gitProcess.start();
+ gitProcess.waitForFinished();
+
+ if (gitProcess.exitCode() == 0)
+ return {};
+
+ return gitProcess.readAllStandardOutput();
+}
+
+void tst_validateQdocOutputFiles::qdocProjects_data()
+{
+ using namespace Qt::StringLiterals;
+ QTest::addColumn<QString>("qdocconf");
+ QTest::addColumn<QString>("expectedPath");
+
+ QDirIterator qdocconfit(m_testDataDirectory, QStringList { u"*.qdocconf"_s },
+ QDir::Files | QDir::NoDotAndDotDot, QDirIterator::Subdirectories);
+ while (qdocconfit.hasNext()) {
+ const QFileInfo configFile = qdocconfit.nextFileInfo();
+ if (configFile.baseName() != configFile.dir().dirName())
+ continue;
+
+ const QString testName =
+ configFile.dir().dirName() + u'/' + configFile.fileName();
+
+ QTest::newRow(testName.toUtf8().constData())
+ << configFile.absoluteFilePath() << configFile.dir().absolutePath() + "/expected/";
+ }
+}
+
+void tst_validateQdocOutputFiles::qdocProjects()
+{
+ QFETCH(const QString, qdocconf);
+ QFETCH(const QString, expectedPath);
+
+ QString actualPath{m_outputDir->path()};
+ if (regenerate) {
+ actualPath = expectedPath;
+ QDir pathToRemove{expectedPath};
+ if (!pathToRemove.removeRecursively())
+ qCritical("Cannot remove expected output directory, aborting!");
+ }
+
+ const QStringList arguments{ "-outputdir", actualPath, m_extraParams, qdocconf };
+
+ runQDocProcess(arguments);
+
+ if (regenerate) {
+ const QString message = "Regenerated expected output files for" + qdocconf;
+ QSKIP(message.toLocal8Bit().constData());
+ }
+
+ std::optional<QByteArray> gitDiff = gitDiffDirectories(actualPath, expectedPath);
+ if (gitDiff.has_value()) {
+ qInfo() << qUtf8Printable(gitDiff.value());
+ QFAIL("Inspect the output for details.");
+ }
+ QVERIFY(true);
+}
+
+QTEST_MAIN(tst_validateQdocOutputFiles)
+#include "tst_validateqdocoutputfiles.moc"
diff --git a/src/qdoc/qdoccommandlineparser.h b/src/qdoc/qdoccommandlineparser.h
deleted file mode 100644
index 911f568c9..000000000
--- a/src/qdoc/qdoccommandlineparser.h
+++ /dev/null
@@ -1,53 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2019 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the tools applications of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#ifndef QDOCCOMMANDLINEPARSER_H
-#define QDOCCOMMANDLINEPARSER_H
-
-#include <QtCore/qcommandlineparser.h>
-
-QT_BEGIN_NAMESPACE
-
-struct QDocCommandLineParser : public QCommandLineParser
-{
- QDocCommandLineParser();
- void process(const QStringList &arguments);
-
- QCommandLineOption defineOption, dependsOption, highlightingOption;
- QCommandLineOption showInternalOption, redirectDocumentationToDevNullOption;
- QCommandLineOption noExamplesOption, indexDirOption, installDirOption;
- QCommandLineOption outputDirOption, outputFormatOption;
- QCommandLineOption noLinkErrorsOption, autoLinkErrorsOption, debugOption;
- QCommandLineOption prepareOption, generateOption, logProgressOption, singleExecOption;
- QCommandLineOption includePathOption, includePathSystemOption, frameworkOption;
- QCommandLineOption timestampsOption, useDocBookExtensions;
-};
-
-QT_END_NAMESPACE
-
-#endif // QDOCCOMMANDLINEPARSER_H
diff --git a/src/qdoc/qmlcodemarker.h b/src/qdoc/qmlcodemarker.h
deleted file mode 100644
index 6f6fc6990..000000000
--- a/src/qdoc/qmlcodemarker.h
+++ /dev/null
@@ -1,69 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the tools applications of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#ifndef QMLCODEMARKER_H
-#define QMLCODEMARKER_H
-
-#include "cppcodemarker.h"
-
-#ifndef QT_NO_DECLARATIVE
-# include <private/qqmljsastfwd_p.h>
-#endif
-
-QT_BEGIN_NAMESPACE
-
-class QmlCodeMarker : public CppCodeMarker
-{
-public:
- QmlCodeMarker() = default;
- ~QmlCodeMarker() override = default;
-
- bool recognizeCode(const QString &code) override;
- bool recognizeExtension(const QString &ext) override;
- bool recognizeLanguage(const QString &language) override;
- [[nodiscard]] Atom::AtomType atomType() const override;
- QString markedUpCode(const QString &code, const Node *relative,
- const Location &location) override;
-
- QString markedUpName(const Node *node) override;
- QString markedUpIncludes(const QStringList &includes) override;
- QString functionBeginRegExp(const QString &funcName) override;
- QString functionEndRegExp(const QString &funcName) override;
-
- /* Copied from src/declarative/qml/qdeclarativescriptparser.cpp */
-#ifndef QT_NO_DECLARATIVE
- QList<QQmlJS::SourceLocation> extractPragmas(QString &script);
-#endif
-
-private:
- QString addMarkUp(const QString &code, const Node *relative, const Location &location);
-};
-
-QT_END_NAMESPACE
-
-#endif
diff --git a/src/qdoc/qmlcodeparser.cpp b/src/qdoc/qmlcodeparser.cpp
deleted file mode 100644
index e89290bf4..000000000
--- a/src/qdoc/qmlcodeparser.cpp
+++ /dev/null
@@ -1,226 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the tools applications of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include "qmlcodeparser.h"
-
-#include "node.h"
-#include "qmlvisitor.h"
-
-#ifndef QT_NO_DECLARATIVE
-# include <private/qqmljsast_p.h>
-#endif
-#include <qdebug.h>
-
-QT_BEGIN_NAMESPACE
-
-/*!
- Constructs the QML code parser.
- */
-QmlCodeParser::QmlCodeParser()
-#ifndef QT_NO_DECLARATIVE
- : m_lexer(nullptr), m_parser(nullptr)
-#endif
-{
-}
-
-/*!
- Initializes the code parser base class.
- Also creates a lexer and parser from QQmlJS.
- */
-void QmlCodeParser::initializeParser()
-{
- CodeParser::initializeParser();
-
-#ifndef QT_NO_DECLARATIVE
- m_lexer = new QQmlJS::Lexer(&m_engine);
- m_parser = new QQmlJS::Parser(&m_engine);
-#endif
-}
-
-/*!
- Terminates the QML code parser. Deletes the lexer and parser
- created by the constructor.
- */
-void QmlCodeParser::terminateParser()
-{
-#ifndef QT_NO_DECLARATIVE
- delete m_lexer;
- delete m_parser;
-#endif
-}
-
-/*!
- Returns "QML".
- */
-QString QmlCodeParser::language()
-{
- return "QML";
-}
-
-/*!
- Returns a string list containing "*.qml". This is the only
- file type parsed by the QMLN parser.
- */
-QStringList QmlCodeParser::sourceFileNameFilter()
-{
- return QStringList() << "*.qml";
-}
-
-/*!
- Parses the source file at \a filePath and inserts the contents
- into the database. The \a location is used for error reporting.
-
- If it can't open the file at \a filePath, it reports an error
- and returns without doing anything.
- */
-void QmlCodeParser::parseSourceFile(const Location &location, const QString &filePath)
-{
- QFile in(filePath);
- m_currentFile = filePath;
- if (!in.open(QIODevice::ReadOnly)) {
- location.error(QStringLiteral("Cannot open QML file '%1'").arg(filePath));
- m_currentFile.clear();
- return;
- }
-
-#ifndef QT_NO_DECLARATIVE
- QString document = in.readAll();
- in.close();
-
- Location fileLocation(filePath);
-
- QString newCode = document;
- extractPragmas(newCode);
- m_lexer->setCode(newCode, 1);
-
- if (m_parser->parse()) {
- QQmlJS::AST::UiProgram *ast = m_parser->ast();
- QmlDocVisitor visitor(filePath, newCode, &m_engine, topicCommands() + commonMetaCommands(),
- topicCommands());
- QQmlJS::AST::Node::accept(ast, &visitor);
- if (visitor.hasError()) {
- qDebug().nospace() << qPrintable(filePath) << ": Could not analyze QML file. "
- << "The output is incomplete.";
- }
- }
- const auto &messages = m_parser->diagnosticMessages();
- for (const auto &msg : messages) {
- qDebug().nospace() << qPrintable(filePath) << ':'
- << msg.loc.startLine << ": QML syntax error at col "
- << msg.loc.startColumn
- << ": " << qPrintable(msg.message);
- }
- m_currentFile.clear();
-#else
- location.warning("QtDeclarative not installed; cannot parse QML or JS.");
-#endif
-}
-
-static QSet<QString> topicCommands_;
-/*!
- Returns the set of strings representing the topic commands.
- */
-const QSet<QString> &QmlCodeParser::topicCommands()
-{
- if (topicCommands_.isEmpty()) {
- topicCommands_ << COMMAND_VARIABLE << COMMAND_QMLCLASS << COMMAND_QMLTYPE
- << COMMAND_QMLPROPERTY << COMMAND_QMLPROPERTYGROUP // mws 13/03/2019
- << COMMAND_QMLATTACHEDPROPERTY << COMMAND_QMLSIGNAL
- << COMMAND_QMLATTACHEDSIGNAL << COMMAND_QMLMETHOD
- << COMMAND_QMLATTACHEDMETHOD << COMMAND_QMLBASICTYPE << COMMAND_JSTYPE
- << COMMAND_JSPROPERTY << COMMAND_JSPROPERTYGROUP // mws 13/03/2019
- << COMMAND_JSATTACHEDPROPERTY << COMMAND_JSSIGNAL << COMMAND_JSATTACHEDSIGNAL
- << COMMAND_JSMETHOD << COMMAND_JSATTACHEDMETHOD << COMMAND_JSBASICTYPE;
- }
- return topicCommands_;
-}
-
-#ifndef QT_NO_DECLARATIVE
-/*!
- Copy and paste from src/declarative/qml/qdeclarativescriptparser.cpp.
- This function blanks out the section of the \a str beginning at \a idx
- and running for \a n characters.
-*/
-static void replaceWithSpace(QString &str, int idx, int n)
-{
- QChar *data = str.data() + idx;
- const QChar space(QLatin1Char(' '));
- for (int ii = 0; ii < n; ++ii)
- *data++ = space;
-}
-
-/*!
- Copy & paste from src/declarative/qml/qdeclarativescriptparser.cpp,
- then modified to return no values.
-
- Searches for ".pragma <value>" declarations within \a script.
- Currently supported pragmas are: library
-*/
-void QmlCodeParser::extractPragmas(QString &script)
-{
- const QString pragma(QLatin1String("pragma"));
-
- QQmlJS::Lexer l(nullptr);
- l.setCode(script, 0);
-
- int token = l.lex();
-
- while (true) {
- if (token != QQmlJSGrammar::T_DOT)
- return;
-
- int startOffset = l.tokenOffset();
- int startLine = l.tokenStartLine();
-
- token = l.lex();
-
- if (token != QQmlJSGrammar::T_IDENTIFIER || l.tokenStartLine() != startLine
- || script.mid(l.tokenOffset(), l.tokenLength()) != pragma)
- return;
-
- token = l.lex();
-
- if (token != QQmlJSGrammar::T_IDENTIFIER || l.tokenStartLine() != startLine)
- return;
-
- QString pragmaValue = script.mid(l.tokenOffset(), l.tokenLength());
- int endOffset = l.tokenLength() + l.tokenOffset();
-
- token = l.lex();
- if (l.tokenStartLine() == startLine)
- return;
-
- if (pragmaValue == QLatin1String("library"))
- replaceWithSpace(script, startOffset, endOffset - startOffset);
- else
- return;
- }
-}
-#endif
-
-QT_END_NAMESPACE
diff --git a/src/qdoc/qmlcodeparser.h b/src/qdoc/qmlcodeparser.h
deleted file mode 100644
index b8b141681..000000000
--- a/src/qdoc/qmlcodeparser.h
+++ /dev/null
@@ -1,77 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the tools applications of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#ifndef QMLCODEPARSER_H
-#define QMLCODEPARSER_H
-
-#include "codeparser.h"
-
-#include <QtCore/qset.h>
-
-#ifndef QT_NO_DECLARATIVE
-# include <private/qqmljsengine_p.h>
-# include <private/qqmljslexer_p.h>
-# include <private/qqmljsparser_p.h>
-#endif
-
-QT_BEGIN_NAMESPACE
-
-class Node;
-class QString;
-
-class QmlCodeParser : public CodeParser
-{
-public:
- QmlCodeParser();
- ~QmlCodeParser() override = default;
-
- void initializeParser() override;
- void terminateParser() override;
- QString language() override;
- QStringList sourceFileNameFilter() override;
- void parseSourceFile(const Location &location, const QString &filePath) override;
-
-#ifndef QT_NO_DECLARATIVE
- /* Copied from src/declarative/qml/qdeclarativescriptparser.cpp */
- void extractPragmas(QString &script);
-#endif
-
-protected:
- const QSet<QString> &topicCommands();
-
-private:
-#ifndef QT_NO_DECLARATIVE
- QQmlJS::Engine m_engine {};
- QQmlJS::Lexer *m_lexer { nullptr };
- QQmlJS::Parser *m_parser { nullptr };
-#endif
-};
-
-QT_END_NAMESPACE
-
-#endif
diff --git a/src/qdoc/qmlpropertynode.cpp b/src/qdoc/qmlpropertynode.cpp
deleted file mode 100644
index 681b878b3..000000000
--- a/src/qdoc/qmlpropertynode.cpp
+++ /dev/null
@@ -1,158 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the tools applications of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include "qmlpropertynode.h"
-
-#include "classnode.h"
-#include "propertynode.h"
-
-#include <utility>
-#include "qdocdatabase.h"
-
-QT_BEGIN_NAMESPACE
-
-/*!
- Constructor for the QML property node.
- */
-QmlPropertyNode::QmlPropertyNode(Aggregate *parent, const QString &name, QString type,
- bool attached)
- : Node(parent->isJsType() ? JsProperty : QmlProperty, parent, name),
- m_type(std::move(type)),
- m_attached(attached)
-{
- if (m_type == "alias")
- m_isAlias = true;
- if (name.startsWith("__"))
- setStatus(Internal);
-}
-
-/*!
- Returns \c true if a QML property or attached property is
- not read-only. The algorithm for figuring this out is long
- amd tedious and almost certainly will break. It currently
- doesn't work for the qmlproperty:
-
- \code
- bool PropertyChanges::explicit,
- \endcode
-
- ...because the tokenizer gets confused on \e{explicit}.
- */
-bool QmlPropertyNode::isWritable()
-{
- if (readOnly_ != FlagValueDefault)
- return !fromFlagValue(readOnly_, false);
-
- QmlTypeNode *qcn = qmlTypeNode();
- if (qcn) {
- if (qcn->cppClassRequired()) {
- if (qcn->classNode()) {
- PropertyNode *pn = findCorrespondingCppProperty();
- if (pn)
- return pn->isWritable();
- else
- defLocation().warning(
- QStringLiteral(
- "No Q_PROPERTY for QML property %1::%2::%3 "
- "in C++ class documented as QML type: "
- "(property not found in the C++ class or its base classes)")
- .arg(logicalModuleName(), qmlTypeName(), name()));
- } else
- defLocation().warning(QStringLiteral("No Q_PROPERTY for QML property %1::%2::%3 "
- "in C++ class documented as QML type: "
- "(C++ class not specified or not found).")
- .arg(logicalModuleName(), qmlTypeName(), name()));
- }
- }
- return true;
-}
-
-/*!
- Returns \c true if this QML property is marked with \required or the
- corresponding C++ property uses the REQUIRED keyword.
-*/
-bool QmlPropertyNode::isRequired()
-{
- if (m_required != FlagValueDefault)
- return fromFlagValue(m_required, false);
-
- PropertyNode *pn = findCorrespondingCppProperty();
- return pn != nullptr && pn->isRequired();
-}
-
-/*!
- Returns a pointer this QML property's corresponding C++
- property, if it has one.
- */
-PropertyNode *QmlPropertyNode::findCorrespondingCppProperty()
-{
- PropertyNode *pn;
- Node *n = parent();
- while (n && !(n->isQmlType() || n->isJsType()))
- n = n->parent();
- if (n) {
- auto *qcn = static_cast<QmlTypeNode *>(n);
- ClassNode *cn = qcn->classNode();
- if (cn) {
- /*
- If there is a dot in the property name, first
- find the C++ property corresponding to the QML
- property group.
- */
- QStringList dotSplit = name().split(QChar('.'));
- pn = cn->findPropertyNode(dotSplit[0]);
- if (pn) {
- /*
- Now find the C++ property corresponding to
- the QML property in the QML property group,
- <group>.<property>.
- */
- if (dotSplit.size() > 1) {
- QStringList path(extractClassName(pn->qualifiedDataType()));
- Node *nn = QDocDatabase::qdocDB()->findClassNode(path);
- if (nn) {
- auto *cn = static_cast<ClassNode *>(nn);
- PropertyNode *pn2 = cn->findPropertyNode(dotSplit[1]);
- /*
- If found, return the C++ property
- corresponding to the QML property.
- Otherwise, return the C++ property
- corresponding to the QML property
- group.
- */
- return (pn2 ? pn2 : pn);
- }
- } else
- return pn;
- }
- }
- }
- return nullptr;
-}
-
-QT_END_NAMESPACE
diff --git a/src/qdoc/relatedclass.h b/src/qdoc/relatedclass.h
deleted file mode 100644
index b1c921d2a..000000000
--- a/src/qdoc/relatedclass.h
+++ /dev/null
@@ -1,59 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the tools applications of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#ifndef RELATEDCLASS_H
-#define RELATEDCLASS_H
-
-#include "access.h"
-
-#include <QtCore/qstring.h>
-#include <QtCore/qstringlist.h>
-
-#include <utility>
-
-QT_BEGIN_NAMESPACE
-
-class ClassNode;
-
-struct RelatedClass
-{
- RelatedClass() = default;
- // constructor for resolved base class
- RelatedClass(Access access, ClassNode *node) : m_access(access), m_node(node) {}
- // constructor for unresolved base class
- RelatedClass(Access access, QStringList path) : m_access(access), m_path(std::move(path)) { }
- [[nodiscard]] bool isPrivate() const;
-
- Access m_access {};
- ClassNode *m_node { nullptr };
- QStringList m_path {};
-};
-
-QT_END_NAMESPACE
-
-#endif // RELATEDCLASS_H
diff --git a/src/qdoc/sharedcommentnode.cpp b/src/qdoc/sharedcommentnode.cpp
deleted file mode 100644
index ad7ef4de3..000000000
--- a/src/qdoc/sharedcommentnode.cpp
+++ /dev/null
@@ -1,81 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the tools applications of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include "sharedcommentnode.h"
-
-#include "aggregate.h"
-#include "functionnode.h"
-#include "qmltypenode.h"
-
-QT_BEGIN_NAMESPACE
-
-SharedCommentNode::SharedCommentNode(QmlTypeNode *parent, int count, QString &group)
- : Node(Node::SharedComment, parent, group)
-{
- m_collective.reserve(count);
-}
-
-/*!
- Searches the shared comment node's member nodes for function
- nodes. Each function node's overload flag is set.
- */
-void SharedCommentNode::setOverloadFlags()
-{
- for (auto *node : m_collective) {
- if (node->isFunction())
- static_cast<FunctionNode *>(node)->setOverloadFlag();
- }
-}
-
-/*!
- Clone this node on the heap and make the clone a child of
- \a parent.
-
- Returns the pointer to the clone.
- */
-Node *SharedCommentNode::clone(Aggregate *parent)
-{
- auto *scn = new SharedCommentNode(*this); // shallow copy
- scn->setParent(nullptr);
- parent->addChild(scn);
-
- return scn;
-}
-
-/*!
- Sets the related nonmember flag in this node and in each
- node in the shared comment's collective to \a value.
- */
-void SharedCommentNode::setRelatedNonmember(bool value)
-{
- Node::setRelatedNonmember(value);
- for (auto *node : m_collective)
- node->setRelatedNonmember(value);
-}
-
-QT_END_NAMESPACE
diff --git a/src/qdoc/sharedcommentnode.h b/src/qdoc/sharedcommentnode.h
deleted file mode 100644
index 33a85b3c0..000000000
--- a/src/qdoc/sharedcommentnode.h
+++ /dev/null
@@ -1,77 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the tools applications of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#ifndef SHAREDCOMMENTNODE_H
-#define SHAREDCOMMENTNODE_H
-
-#include "node.h"
-
-#include <QtCore/qglobal.h>
-#include <QtCore/qlist.h>
-
-QT_BEGIN_NAMESPACE
-
-class Aggregate;
-class QmlTypeNode;
-
-class SharedCommentNode : public Node
-{
-public:
- explicit SharedCommentNode(Node *node) : Node(Node::SharedComment, node->parent(), QString())
- {
- m_collective.reserve(1);
- append(node);
- }
- SharedCommentNode(QmlTypeNode *parent, int count, QString &group);
- ~SharedCommentNode() override { m_collective.clear(); }
-
- [[nodiscard]] bool isPropertyGroup() const override
- {
- return !name().isEmpty() && !m_collective.isEmpty()
- && (m_collective.at(0)->isQmlProperty() || m_collective.at(0)->isJsProperty());
- }
- [[nodiscard]] qsizetype count() const { return m_collective.size(); }
- void append(Node *node)
- {
- m_collective.append(node);
- node->setSharedCommentNode(this);
- setGenus(node->genus());
- }
- void sort() { std::sort(m_collective.begin(), m_collective.end(), Node::nodeNameLessThan); }
- [[nodiscard]] const QList<Node *> &collective() const { return m_collective; }
- void setOverloadFlags();
- void setRelatedNonmember(bool value) override;
- Node *clone(Aggregate *parent) override;
-
-private:
- QList<Node *> m_collective {};
-};
-
-QT_END_NAMESPACE
-
-#endif // SHAREDCOMMENTNODE_H
diff --git a/src/qdoc/singleton.h b/src/qdoc/singleton.h
deleted file mode 100644
index ba6757652..000000000
--- a/src/qdoc/singleton.h
+++ /dev/null
@@ -1,57 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2020 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the tools applications of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-#ifndef SINGLETON_H
-#define SINGLETON_H
-
-QT_BEGIN_NAMESPACE
-
-/*!
- \class Singleton
- \internal
-
- Class template for singleton objects in QDoc.
- */
-template<typename T>
-class Singleton
-{
-public:
- Singleton(const Singleton &) = delete;
- Singleton &operator=(const Singleton &) = delete;
- static T &instance()
- {
- static T s_instance {};
- return s_instance;
- }
-
-protected:
- Singleton() = default;
-};
-
-QT_END_NAMESPACE
-
-#endif // SINGLETON_H
diff --git a/src/qdoc/tagfilewriter.h b/src/qdoc/tagfilewriter.h
deleted file mode 100644
index 2c6cc4b6a..000000000
--- a/src/qdoc/tagfilewriter.h
+++ /dev/null
@@ -1,58 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2020 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the tools applications of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#ifndef TAGFILEWRITER_H
-#define TAGFILEWRITER_H
-
-#include <QtCore/qxmlstream.h>
-
-QT_BEGIN_NAMESPACE
-
-class Aggregate;
-class Generator;
-class QDocDatabase;
-
-class TagFileWriter
-{
-public:
- TagFileWriter();
- ~TagFileWriter() = default;
-
- void generateTagFile(const QString &fileName, Generator *generator);
-
-private:
- void generateTagFileCompounds(QXmlStreamWriter &writer, const Aggregate *inner);
- void generateTagFileMembers(QXmlStreamWriter &writer, const Aggregate *inner);
-
- QDocDatabase *m_qdb { nullptr };
- Generator *m_generator { nullptr };
-};
-
-QT_END_NAMESPACE
-
-#endif // TAGFILEWRITER_H
diff --git a/src/qdoc/topic.h b/src/qdoc/topic.h
deleted file mode 100644
index a1db74ff3..000000000
--- a/src/qdoc/topic.h
+++ /dev/null
@@ -1,54 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2020 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the tools applications of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-#ifndef TOPIC_H
-#define TOPIC_H
-
-QT_BEGIN_NAMESPACE
-
-struct Topic
-{
-public:
- Topic() = default;
- Topic(QString &t, QString a) : m_topic(t), m_args(std::move(a)) { }
- ~Topic() = default;
-
- [[nodiscard]] bool isEmpty() const { return m_topic.isEmpty(); }
- void clear()
- {
- m_topic.clear();
- m_args.clear();
- }
-
- QString m_topic {};
- QString m_args {};
-};
-typedef QList<Topic> TopicList;
-
-QT_END_NAMESPACE
-
-#endif // TOPIC_H
diff --git a/src/qdoc/typedefnode.cpp b/src/qdoc/typedefnode.cpp
deleted file mode 100644
index 760379db5..000000000
--- a/src/qdoc/typedefnode.cpp
+++ /dev/null
@@ -1,80 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the tools applications of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include "typedefnode.h"
-
-#include "aggregate.h"
-
-QT_BEGIN_NAMESPACE
-
-/*!
- \class TypedefNode
- */
-
-/*!
- */
-void TypedefNode::setAssociatedEnum(const EnumNode *enume)
-{
- m_associatedEnum = enume;
-}
-
-/*!
- Clone this node on the heap and make the clone a child of
- \a parent.
-
- Returns the pointer to the clone.
- */
-Node *TypedefNode::clone(Aggregate *parent)
-{
- auto *tn = new TypedefNode(*this); // shallow copy
- tn->setParent(nullptr);
- parent->addChild(tn);
-
- return tn;
-}
-
-/*!
- \class TypeAliasNode
- */
-
-/*!
- Clone this node on the heap and make the clone a child of
- \a parent.
-
- Returns the pointer to the clone.
- */
-Node *TypeAliasNode::clone(Aggregate *parent)
-{
- auto *tan = new TypeAliasNode(*this); // shallow copy
- tan->setParent(nullptr);
- parent->addChild(tan);
-
- return tan;
-}
-
-QT_END_NAMESPACE
diff --git a/src/qdoc/typedefnode.h b/src/qdoc/typedefnode.h
deleted file mode 100644
index da14d7dec..000000000
--- a/src/qdoc/typedefnode.h
+++ /dev/null
@@ -1,79 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the tools applications of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#ifndef TYPEDEFNODE_H
-#define TYPEDEFNODE_H
-
-#include "enumnode.h"
-#include "node.h"
-
-#include <QtCore/qglobal.h>
-#include <QtCore/qstring.h>
-
-QT_BEGIN_NAMESPACE
-
-class Aggregate;
-
-class TypedefNode : public Node
-{
-public:
- TypedefNode(Aggregate *parent, const QString &name, NodeType type = Typedef)
- : Node(type, parent, name)
- {
- }
-
- bool hasAssociatedEnum() const { return m_associatedEnum != nullptr; }
- const EnumNode *associatedEnum() const { return m_associatedEnum; }
- Node *clone(Aggregate *parent) override;
-
-private:
- void setAssociatedEnum(const EnumNode *t);
-
- friend class EnumNode;
-
- const EnumNode *m_associatedEnum { nullptr };
-};
-
-class TypeAliasNode : public TypedefNode
-{
-public:
- TypeAliasNode(Aggregate *parent, const QString &name, const QString &aliasedType)
- : TypedefNode(parent, name, NodeType::TypeAlias), m_aliasedType(aliasedType)
- {
- }
-
- const QString &aliasedType() const { return m_aliasedType; }
- Node *clone(Aggregate *parent) override;
-
-private:
- QString m_aliasedType {};
-};
-
-QT_END_NAMESPACE
-
-#endif // TYPEDEFNODE_H
diff --git a/src/qdoc/usingclause.cpp b/src/qdoc/usingclause.cpp
deleted file mode 100644
index e4ce57f3d..000000000
--- a/src/qdoc/usingclause.cpp
+++ /dev/null
@@ -1,69 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the tools applications of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include "usingclause.h"
-
-QT_BEGIN_NAMESPACE
-
-/*!
- \struct UsingClause
- \brief This is supposed to describe a using clause, but I think it is not used.
-
- This struct is only used in ClassNode. It describes a \c using clause that
- was parsed. But now it looks like it is not actually used at all.
-
- Maybe we can get rid of it?
-*/
-
-/*! \fn UsingClause::UsingClause()
- The default constructor does nothing. It is only used for allocating empty
- instances of UsingClause in containers.
- */
-
-/*! \fn UsingClause::UsingClause(const QString &signature)
- We assume the node that the using clause refers to has not yet been
- created, so this constructor provides its \a signature, which is the
- qualified path name of whatever it is.
- */
-
-/*! \fn const QString &UsingClause::signature() const
- This function returns a const reference to the signature, which is the qualified
- path name of whatever the using clause refers to.
- */
-
-/*! \fn const Node *UsingClause::node()
- This function returns a pointer to the node which has been resolved by looking
- up the signature in the qdoc database. If it wasn't resolved, \c nullptr is returned.
- */
-
-/*! \fn void UsingClause::setNode(const Node *n)
- This function is called when the signature can be resolved. The node pointer
- is set to \a n.
- */
-
-QT_END_NAMESPACE
diff --git a/src/qdoc/usingclause.h b/src/qdoc/usingclause.h
deleted file mode 100644
index 3311aceac..000000000
--- a/src/qdoc/usingclause.h
+++ /dev/null
@@ -1,55 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the tools applications of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#ifndef USINGCLAUSE_H
-#define USINGCLAUSE_H
-
-#include <QtCore/qglobal.h>
-#include <QtCore/qstring.h>
-
-#include <utility>
-
-QT_BEGIN_NAMESPACE
-
-class Node;
-
-struct UsingClause
-{
- UsingClause() = default;
- explicit UsingClause(QString signature) : m_signature(std::move(signature)) { }
- [[nodiscard]] const QString &signature() const { return m_signature; }
- [[nodiscard]] const Node *node() const { return m_node; }
- void setNode(const Node *n) { m_node = n; }
-
- const Node *m_node { nullptr };
- QString m_signature {};
-};
-
-QT_END_NAMESPACE
-
-#endif // USINGCLAUSE_H
diff --git a/src/qdoc/utilities.h b/src/qdoc/utilities.h
deleted file mode 100644
index 488c960ad..000000000
--- a/src/qdoc/utilities.h
+++ /dev/null
@@ -1,52 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the tools applications of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#ifndef UTILITIES_H
-#define UTILITIES_H
-
-#include <QtCore/qstring.h>
-#include <QtCore/qloggingcategory.h>
-
-QT_BEGIN_NAMESPACE
-
-Q_DECLARE_LOGGING_CATEGORY(lcQdoc)
-Q_DECLARE_LOGGING_CATEGORY(lcQdocClang)
-
-namespace Utilities {
-void startDebugging(const QString &message);
-void stopDebugging(const QString &message);
-bool debugging();
-
-QString separator(qsizetype wordPosition, qsizetype numberOfWords);
-QString comma(qsizetype wordPosition, qsizetype numberOfWords);
-QStringList getInternalIncludePaths(const QString &compiler);
-}
-
-QT_END_NAMESPACE
-
-#endif // UTILITIES_H
diff --git a/src/qdoc/variablenode.cpp b/src/qdoc/variablenode.cpp
deleted file mode 100644
index 9fcdb6d26..000000000
--- a/src/qdoc/variablenode.cpp
+++ /dev/null
@@ -1,48 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2020 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the tools applications of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include "variablenode.h"
-
-QT_BEGIN_NAMESPACE
-
-/*!
- Clone this node on the heap and make the clone a child of
- \a parent.
-
- Returns a pointer to the clone.
- */
-Node *VariableNode::clone(Aggregate *parent)
-{
- auto *vn = new VariableNode(*this); // shallow copy
- vn->setParent(nullptr);
- parent->addChild(vn);
-
- return vn;
-}
-
-QT_END_NAMESPACE
diff --git a/src/qdoc/variablenode.h b/src/qdoc/variablenode.h
deleted file mode 100644
index 3a63af66c..000000000
--- a/src/qdoc/variablenode.h
+++ /dev/null
@@ -1,69 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2020 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the tools applications of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#ifndef VARIABLENODE_H
-#define VARIABLENODE_H
-
-#include "aggregate.h"
-#include "node.h"
-
-#include <QtCore/qglobal.h>
-#include <QtCore/qstring.h>
-
-QT_BEGIN_NAMESPACE
-
-class VariableNode : public Node
-{
-public:
- VariableNode(Aggregate *parent, const QString &name);
-
- void setLeftType(const QString &leftType) { m_leftType = leftType; }
- void setRightType(const QString &rightType) { m_rightType = rightType; }
- void setStatic(bool b) { m_static = b; }
-
- [[nodiscard]] const QString &leftType() const { return m_leftType; }
- [[nodiscard]] const QString &rightType() const { return m_rightType; }
- [[nodiscard]] QString dataType() const { return m_leftType + m_rightType; }
- [[nodiscard]] bool isStatic() const override { return m_static; }
- Node *clone(Aggregate *parent) override;
-
-private:
- QString m_leftType {};
- QString m_rightType {};
- bool m_static { false };
-};
-
-inline VariableNode::VariableNode(Aggregate *parent, const QString &name)
- : Node(Variable, parent, name)
-{
- setGenus(Node::CPP);
-}
-
-QT_END_NAMESPACE
-
-#endif // VARIABLENODE_H
diff --git a/src/qdoc/xmlgenerator.h b/src/qdoc/xmlgenerator.h
deleted file mode 100644
index c5dfdc33f..000000000
--- a/src/qdoc/xmlgenerator.h
+++ /dev/null
@@ -1,73 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2019 Thibaut Cuvelier
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the tools applications of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#ifndef XMLGENERATOR_H
-#define XMLGENERATOR_H
-
-#include "node.h"
-#include "generator.h"
-
-#include <QtCore/qmap.h>
-#include <QtCore/qstring.h>
-
-QT_BEGIN_NAMESPACE
-
-class XmlGenerator : public Generator
-{
-public:
- explicit XmlGenerator() = default;
-
-protected:
- QHash<QString, QString> refMap;
-
- static bool hasBrief(const Node *node);
- static bool isThreeColumnEnumValueTable(const Atom *atom);
- static int hOffset(const Node *node);
-
- static void rewritePropertyBrief(const Atom *atom, const Node *relative);
- static Node::NodeType typeFromString(const Atom *atom);
- static void setImageFileName(const Node *relative, const QString &fileName);
- static QPair<QString, int> getAtomListValue(const Atom *atom);
- static QPair<QString, QString> getTableWidthAttr(const Atom *atom);
-
- QString registerRef(const QString &ref);
- QString refForNode(const Node *node);
- QString linkForNode(const Node *node, const Node *relative);
- QString getLink(const Atom *atom, const Node *relative, const Node **node);
- QString getAutoLink(const Atom *atom, const Node *relative, const Node **node,
- Node::Genus = Node::DontCare);
-
- QPair<QString, QString> anchorForNode(const Node *node);
-
- static QString targetType(const Node *node);
-};
-
-QT_END_NAMESPACE
-
-#endif // XMLGENERATOR_H