23#include <QtCore/qdebug.h>
24#include <QtCore/qdir.h>
25#include <QtCore/qelapsedtimer.h>
26#include <QtCore/qfile.h>
27#include <QtCore/qregularexpression.h>
28#include <QtCore/qscopedvaluerollback.h>
29#include <QtCore/qtemporarydir.h>
30#include <QtCore/qtextstream.h>
31#include <QtCore/qvarlengtharray.h>
33#include <clang-c/Index.h>
35#include <clang/AST/Decl.h>
36#include <clang/AST/DeclFriend.h>
37#include <clang/AST/DeclTemplate.h>
38#include <clang/AST/Expr.h>
39#include <clang/AST/Type.h>
40#include <clang/AST/TypeLoc.h>
41#include <clang/Basic/SourceLocation.h>
42#include <clang/Frontend/ASTUnit.h>
43#include <clang/Lex/Lexer.h>
44#include <llvm/Support/Casting.h>
46#include "clang/AST/QualTypeNames.h"
54using namespace Qt::Literals::StringLiterals;
64 clang_disposeIndex(
index);
69 CXTranslationUnit
tu =
nullptr;
80 clang_disposeTranslationUnit(
tu);
88static CXTranslationUnit_Flags
flags_ =
static_cast<CXTranslationUnit_Flags>(0);
92#ifndef QT_NO_DEBUG_STREAM
96 QDebugStateSaver saver(debug);
99 const size_t size = v.size();
100 debug <<
"std::vector<>[" << size <<
"](";
101 for (size_t i = 0; i < size; ++i) {
113 if (!lcQdocClang().isDebugEnabled())
116 static const auto displayOptions = CXDiagnosticDisplayOptions::CXDiagnostic_DisplaySourceLocation
117 | CXDiagnosticDisplayOptions::CXDiagnostic_DisplayColumn
118 | CXDiagnosticDisplayOptions::CXDiagnostic_DisplayOption;
120 for (
unsigned i = 0, numDiagnostics = clang_getNumDiagnostics(translationUnit); i < numDiagnostics; ++i) {
121 auto diagnostic = clang_getDiagnostic(translationUnit, i);
122 auto formattedDiagnostic = clang_formatDiagnostic(diagnostic, displayOptions);
123 qCDebug(lcQdocClang) << clang_getCString(formattedDiagnostic);
124 clang_disposeString(formattedDiagnostic);
125 clang_disposeDiagnostic(diagnostic);
130
131
132
133
134
135
136
137
138
139
140
141
143 assert(clang_isDeclaration(clang_getCursorKind(cursor)));
145 return static_cast<
const clang::Decl*>(cursor.data[0]);
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
180 declaration_context.getPrintingPolicy()
185
186
187
188
190 if (!typeName.contains(
"(unnamed "_L1) && !typeName.contains(
"(anonymous "_L1)) {
195 static const QRegularExpression pattern(
196 R"(\((unnamed|anonymous) (struct|union|class) at [^)]+\))"
198 QString cleaned = typeName;
199 cleaned.replace(pattern,
"(\\1 \\2)"_L1);
204
205
206
207
208
209
210
211
212
214 QString default_value = QString::fromStdString(clang::Lexer::getSourceText(
215 clang::CharSourceRange::getTokenRange(expression->getSourceRange()),
216 declaration_context.getSourceManager(),
217 declaration_context.getLangOpts()
220 if (default_value.startsWith(
"="))
221 default_value.remove(0, 1);
223 default_value = default_value.trimmed();
225 return default_value.toStdString();
229
230
231
232
233
234
235
236
238#if LIBCLANG_VERSION_MAJOR >= 19
239 return (parameter && parameter->hasDefaultArgument()) ?
240 get_fully_qualified_type_name(parameter->getDefaultArgument().getArgument().getAsType(), parameter->getASTContext()) :
243 return (parameter && parameter->hasDefaultArgument()) ?
244 get_fully_qualified_type_name(parameter->getDefaultArgument(), parameter->getASTContext()) :
251
252
253
254
255
256
257
258
260#if LIBCLANG_VERSION_MAJOR >= 19
261 return (parameter && parameter->hasDefaultArgument()) ?
262 get_expression_as_string(parameter->getDefaultArgument().getSourceExpression(), parameter->getASTContext()) :
"";
264 return (parameter && parameter->hasDefaultArgument()) ?
265 get_expression_as_string(parameter->getDefaultArgument(), parameter->getASTContext()) :
"";
271
272
273
274
275
276
277
278
280 std::string default_value{};
282 if (parameter && parameter->hasDefaultArgument()) {
283 const clang::TemplateName template_name = parameter->getDefaultArgument().getArgument().getAsTemplate();
285 llvm::raw_string_ostream ss{default_value};
286 template_name.print(ss, parameter->getASTContext().getPrintingPolicy(),
clang::TemplateName::Qualified::AsWritten);
289 return default_value;
293
294
295
296
297
298
299
300
301
302
304 if (!parameter || !parameter->hasDefaultArg() || parameter->hasUnparsedDefaultArg())
307 return get_expression_as_string(
308 parameter->hasUninstantiatedDefaultArg() ? parameter->getUninstantiatedDefaultArg() : parameter->getDefaultArg(),
309 parameter->getASTContext()
314
315
316
317
318
319
320
322 if (!declaration)
return "";
324 if (
auto type_template_parameter = llvm::dyn_cast<clang::TemplateTypeParmDecl>(declaration))
325 return get_default_value_initializer_as_string(type_template_parameter);
327 if (
auto non_type_template_parameter = llvm::dyn_cast<clang::NonTypeTemplateParmDecl>(declaration))
328 return get_default_value_initializer_as_string(non_type_template_parameter);
330 if (
auto template_template_parameter = llvm::dyn_cast<clang::TemplateTemplateParmDecl>(declaration)) {
331 return get_default_value_initializer_as_string(template_template_parameter);
334 if (
auto function_parameter = llvm::dyn_cast<clang::ParmVarDecl>(declaration)) {
335 return get_default_value_initializer_as_string(function_parameter);
342
343
344
345
349 CXCursorVisitor visitor = [](CXCursor c, CXCursor,
350 CXClientData client_data) -> CXChildVisitResult {
351 return (*
static_cast<T *>(client_data))(c);
353 return clang_visitChildren(cursor, visitor, &lambda);
357
358
361 QString ret = QString::fromUtf8(clang_getCString(string));
362 clang_disposeString(string);
367
368
369
371 assert(template_declaration);
375 auto template_parameters = template_declaration->getTemplateParameters();
376 for (
auto template_parameter : template_parameters->asArray()) {
377 auto kind{RelaxedTemplateParameter::Kind::TypeTemplateParameter};
380 if (
auto non_type_template_parameter = llvm::dyn_cast<clang::NonTypeTemplateParmDecl>(template_parameter)) {
381 kind = RelaxedTemplateParameter::Kind::NonTypeTemplateParameter;
382 type = get_fully_qualified_type_name(non_type_template_parameter->getType(), non_type_template_parameter->getASTContext());
414 if (QString::fromStdString(type).startsWith(
"typename ")) type.erase(0, std::string(
"typename ").size());
417 auto template_template_parameter = llvm::dyn_cast<clang::TemplateTemplateParmDecl>(template_parameter);
418 if (template_template_parameter) kind = RelaxedTemplateParameter::Kind::TemplateTemplateParameter;
420 template_declaration_ir.parameters.push_back({
422 template_parameter->isTemplateParameterPack(),
425 template_parameter->getNameAsString(),
426 get_default_value_initializer_as_string(template_parameter)
428 (template_template_parameter ?
429 std::optional<TemplateDeclarationStorage>(TemplateDeclarationStorage{
430 get_template_declaration(template_template_parameter).parameters
435 if (
const clang::Expr* requires_clause = template_parameters->getRequiresClause()) {
436 template_declaration_ir.requires_clause =
437 QString::fromStdString(get_expression_as_string(
438 requires_clause, template_declaration->getASTContext())).simplified().toStdString();
441 return template_declaration_ir;
445
446
449 unsigned int line, column;
451 clang_getPresumedLocation(location, &file, &line, &column);
459
460
466 case CX_CXXProtected:
476
477
486 unsigned int offset1,
unsigned int offset2)
488 return QString::fromUtf8(cache.mid(offset1, offset2 - offset1));
491static QString
readFile(CXFile cxFile,
unsigned int offset1,
unsigned int offset2)
493 using FileCache = QList<FileCacheEntry>;
494 static FileCache cache;
496 CXString cxFileName = clang_getFileName(cxFile);
497 const QByteArray fileName = clang_getCString(cxFileName);
498 clang_disposeString(cxFileName);
500 for (
const auto &entry : std::as_const(cache)) {
501 if (fileName == entry.fileName)
502 return fromCache(entry.content, offset1, offset2);
505 QFile file(QString::fromUtf8(fileName));
506 if (file.open(QIODeviceBase::ReadOnly)) {
508 cache.prepend(entry);
509 while (cache.size() > 5)
511 return fromCache(entry.content, offset1, offset2);
518 auto start = clang_getRangeStart(range);
519 auto end = clang_getRangeEnd(range);
521 unsigned int offset1, offset2;
522 clang_getFileLocation(start, &file1,
nullptr,
nullptr, &offset1);
523 clang_getFileLocation(end, &file2,
nullptr,
nullptr, &offset2);
525 if (file1 != file2 || offset2 <= offset1)
528 return readFile(file1, offset1, offset2);
532
533
534
535
538 if (clang_getCursorKind(cursor) == CXCursor_ConversionFunction) {
542 auto conversion_declaration =
543 static_cast<
const clang::CXXConversionDecl*>(get_cursor_declaration(cursor));
545 return QLatin1String(
"operator ") + QString::fromStdString(get_fully_qualified_type_name(
546 conversion_declaration->getConversionType(),
547 conversion_declaration->getASTContext()
551 QString name = fromCXString(clang_getCursorSpelling(cursor));
554 auto ltLoc = name.indexOf(
'<');
555 if (ltLoc > 0 && !name.startsWith(
"operator<"))
556 name = name.left(ltLoc);
561
562
563
567 auto kind = clang_getCursorKind(cur);
568 while (!clang_isInvalid(kind) && kind != CXCursor_TranslationUnit) {
570 case CXCursor_Namespace:
571 case CXCursor_StructDecl:
572 case CXCursor_ClassDecl:
573 case CXCursor_UnionDecl:
574 case CXCursor_ClassTemplate:
576 path.prepend(fromCXString(clang_getCursorSpelling(cur)));
578 case CXCursor_FunctionDecl:
579 case CXCursor_FunctionTemplate:
580 case CXCursor_CXXMethod:
581 case CXCursor_Constructor:
582 case CXCursor_Destructor:
583 case CXCursor_ConversionFunction:
584 path = functionName(cur);
589 cur = clang_getCursorSemanticParent(cur);
590 kind = clang_getCursorKind(cur);
596
597
598
599
600
601
604 param_type = param_type.getNonReferenceType();
605 while (param_type->isPointerType())
606 param_type = param_type->getPointeeType();
607 param_type = param_type.getUnqualifiedType();
609 if (param_type->isBuiltinType())
612 if (
const auto *record_type = param_type->getAs<clang::RecordType>()) {
613 if (
const auto *record_decl = record_type->getDecl())
614 return QString::fromStdString(record_decl->getQualifiedNameAsString());
619 QString class_name = QString::fromStdString(param_type.getAsString());
620 class_name.remove(
"const "_L1).remove(
"volatile "_L1);
621 class_name.remove(
"class "_L1).remove(
"struct "_L1);
622 class_name = class_name.trimmed();
624 if (class_name.isEmpty() || class_name.contains(
'('_L1) || class_name.contains(
'['_L1))
629 if (
auto angle = class_name.indexOf(
'<'_L1); angle > 0)
630 class_name.truncate(angle);
636
637
638
641 auto kind = clang_getCursorKind(cur);
642 if (clang_isInvalid(kind))
644 if (kind == CXCursor_TranslationUnit)
652 case CXCursor_TemplateTypeParameter:
653 case CXCursor_NonTypeTemplateParameter:
654 case CXCursor_TemplateTemplateParameter:
664 auto parent =
static_cast<Aggregate *>(p);
667 if (clang_Cursor_isAnonymous(cur)) {
670 QLatin1String(
"anonymous"));
672 name = fromCXString(clang_getCursorSpelling(cur));
675 case CXCursor_Namespace:
677 case CXCursor_StructDecl:
678 case CXCursor_ClassDecl:
679 case CXCursor_UnionDecl:
680 case CXCursor_ClassTemplate:
682 case CXCursor_FunctionDecl:
683 case CXCursor_FunctionTemplate:
684 case CXCursor_CXXMethod:
685 case CXCursor_Constructor:
686 case CXCursor_Destructor:
687 case CXCursor_ConversionFunction: {
689 parent->findChildren(functionName(cur), candidates);
692 if (candidates.isEmpty() && cur_decl && cur_decl->getFriendObjectKind() !=
clang::Decl::FOK_None) {
694 lexical_parent && lexical_parent
->isAggregate() && lexical_parent != parent) {
695 static_cast<Aggregate *>(lexical_parent)->findChildren(functionName(cur), candidates);
702 if (candidates.isEmpty()) {
703 auto *func_decl = cur_decl ? cur_decl->getAsFunction() :
nullptr;
707 QString funcName = functionName(cur);
708 QSet<ClassNode *> searched_classes;
710 for (
unsigned i = 0; i < func_decl->getNumParams(); ++i) {
711 auto class_name = classNameFromParameterType(func_decl->getParamDecl(i)->getType());
715 auto *class_node = qdb->findClassNode(class_name->split(
"::"_L1));
716 if (!class_node || searched_classes.contains(class_node))
719 searched_classes.insert(class_node);
721 class_node->findChildren(funcName, class_candidates);
723 for (Node *candidate : class_candidates) {
724 if (!candidate->isFunction(Genus::CPP))
726 auto *fn =
static_cast<FunctionNode *>(candidate);
727 if (fn->isHiddenFriend())
728 candidates.append(candidate);
733 if (candidates.isEmpty())
736 CXType funcType = clang_getCursorType(cur);
737 auto numArg = clang_getNumArgTypes(funcType);
738 bool isVariadic = clang_isFunctionTypeVariadic(funcType);
739 QVarLengthArray<QString, 20> args;
742 if (kind == CXCursor_FunctionTemplate)
743 relaxed_template_declaration = get_template_declaration(
747 for (Node *candidate : std::as_const(candidates)) {
748 if (!candidate->isFunction(Genus::CPP))
751 auto fn =
static_cast<FunctionNode *>(candidate);
753 if (!fn->templateDecl() && relaxed_template_declaration)
756 if (fn->templateDecl() && !relaxed_template_declaration)
759 if (fn->templateDecl() && relaxed_template_declaration &&
760 !are_template_declarations_substitutable(*fn->templateDecl(), *relaxed_template_declaration))
763 const Parameters ¶meters = fn->parameters();
765 if (parameters.count() != numArg + isVariadic) {
767 if (numArg > 0 && parameters.isPrivateSignal() &&
768 (parameters.isEmpty() || !parameters.last().type().endsWith(
769 QLatin1String(
"QPrivateSignal")))) {
770 if (parameters.count() != --numArg + isVariadic)
777 if (fn->isConst() !=
bool(clang_CXXMethod_isConst(cur)))
780 if (isVariadic && parameters.last().type() != QLatin1String(
"..."))
783 if (fn->isRef() != (clang_Type_getCXXRefQualifier(funcType) == CXRefQualifier_LValue))
786 if (fn->isRefRef() != (clang_Type_getCXXRefQualifier(funcType) == CXRefQualifier_RValue))
789 auto function_declaration = get_cursor_declaration(cur)->getAsFunction();
791 bool different =
false;
792 for (
int i = 0; i < numArg; ++i) {
793 CXType argType = clang_getArgType(funcType, i);
795 if (args.size() <= i)
796 args.append(QString::fromStdString(get_fully_qualified_type_name(
797 function_declaration->getParamDecl(i)->getOriginalType(),
798 function_declaration->getASTContext()
801 QString recordedType = parameters.at(i).type();
802 QString typeSpelling = args.at(i);
804 different = recordedType != typeSpelling;
807 if (different && (argType.kind == CXType_Typedef || argType.kind == CXType_Elaborated)) {
808 QStringView canonicalType = parameters.at(i).canonicalType();
809 if (!canonicalType.isEmpty()) {
810 different = canonicalType !=
811 QString::fromStdString(get_fully_qualified_type_name(
812 function_declaration->getParamDecl(i)->getOriginalType().getCanonicalType(),
813 function_declaration->getASTContext()
828 case CXCursor_EnumDecl:
829 return parent->findNonfunctionChild(name, &
Node::isEnumType);
830 case CXCursor_FieldDecl:
831 case CXCursor_VarDecl:
833 case CXCursor_TypedefDecl:
842 CXCursor *overridden;
843 unsigned int numOverridden = 0;
844 clang_getOverriddenCursors(cursor, &overridden, &numOverridden);
845 for (uint i = 0; i < numOverridden; ++i) {
846 QString path = reconstructQualifiedPathForCursor(overridden[i]);
847 if (!path.isEmpty()) {
849 fn->setOverridesThis(path);
853 clang_disposeOverriddenCursors(overridden);
857
858
859
860
872 docSource = u"Destroys the instance of \\notranslate %1."_s.arg(className);
874 docSource += u" This destructor is virtual."_s;
876 docSource = u"Default-constructs an instance of \\notranslate %1."_s.arg(className);
878 docSource = u"Copy-constructs an instance of \\notranslate %1."_s.arg(className);
880 docSource = u"Move-constructs an instance of \\notranslate %1."_s.arg(className);
883 const QString other = (!params.isEmpty() && !params.at(0).name().isEmpty())
884 ? params.at(0).name()
886 docSource = fn->isCAssign()
887 ? u"Copy-assigns \\a %1 to this \\notranslate %2 instance."_s.arg(other, className)
888 : u"Move-assigns \\a %1 to this \\notranslate %2 instance."_s.arg(other, className);
891 if (docSource.isEmpty())
894 if (fn->isDeletedAsWritten())
895 docSource += u" This function is deleted."_s;
897 static const QSet<QString> noMetaCommands;
898 static const QSet<QString> noTopics;
910 internalFilePatterns_(internalFilePatterns)
912 std::transform(allHeaders.cbegin(), allHeaders.cend(),
std::inserter(allHeaders_, allHeaders_.begin()),
913 [](
const auto& header_file_path) ->
const QString& {
return header_file_path.filename; });
920 auto ret = visitChildrenLambda(cursor, [&](CXCursor cur) {
921 auto loc = clang_getCursorLocation(cur);
922 if (clang_Location_isFromMainFile(loc))
923 return visitSource(cur, loc);
926 clang_getFileLocation(loc, &file,
nullptr,
nullptr,
nullptr);
927 bool isInteresting =
false;
928 auto it = isInterestingCache_.find(file);
929 if (it != isInterestingCache_.end()) {
932 QFileInfo fi(fromCXString(clang_getFileName(file)));
934 isInteresting = allHeaders_.find(fi.fileName()) != allHeaders_.end();
935 isInterestingCache_[file] = isInteresting;
938 return visitHeader(cur, loc);
941 return CXChildVisit_Continue;
943 return ret ? CXChildVisit_Break : CXChildVisit_Continue;
947
948
949
950 CXChildVisitResult
visitFnArg(CXCursor cursor,
Node **fnNode,
bool &ignoreSignature)
952 auto ret = visitChildrenLambda(cursor, [&](CXCursor cur) {
953 auto loc = clang_getCursorLocation(cur);
954 if (clang_Location_isFromMainFile(loc))
955 return visitFnSignature(cur, loc, fnNode, ignoreSignature);
956 return CXChildVisit_Continue;
958 return ret ? CXChildVisit_Break : CXChildVisit_Continue;
964 bool detectQmlSingleton(CXCursor cursor);
966
967
968
971 unsigned int line {}, column {};
972 friend bool operator<(
const SimpleLoc &a,
const SimpleLoc &b)
974 return a.line != b.line ? a.line < b.line : a.column < b.column;
978
979
980
981
982 QMap<SimpleLoc, CXCursor> declMap_;
986 std::set<QString> allHeaders_;
987 QHash<CXFile,
bool> isInterestingCache_;
991
992
993 bool ignoredSymbol(
const QString &symbolName)
995 if (symbolName == QLatin1String(
"QPrivateSignal"))
998 if (symbolName.startsWith(
"_qt_property_"))
1001 if (symbolName.startsWith(
"<deduction guide"))
1006 CXChildVisitResult visitSource(CXCursor cursor, CXSourceLocation loc);
1007 CXChildVisitResult visitHeader(CXCursor cursor, CXSourceLocation loc);
1008 CXChildVisitResult visitFnSignature(CXCursor cursor, CXSourceLocation loc,
Node **fnNode,
1009 bool &ignoreSignature);
1010 void processFunction(
FunctionNode *fn, CXCursor cursor);
1011 bool parseProperty(
const QString &spelling,
const Location &loc);
1012 void readParameterNamesAndAttributes(
FunctionNode *fn, CXCursor cursor);
1013 Aggregate *getSemanticParent(CXCursor cursor);
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1030 bool hasSingletonMacro =
false;
1032 visitChildrenLambda(cursor, [&hasSingletonMacro](CXCursor child) -> CXChildVisitResult {
1034 if (clang_getCursorKind(child) == CXCursor_CallExpr) {
1035 CXSourceRange range = clang_getCursorExtent(child);
1036 QString sourceText = getSpelling(range);
1038 static const QRegularExpression qmlSingletonPattern(
1039 R"(Q_CLASSINFO\s*\(\s*["\']QML\.Singleton["\']\s*,\s*["\']true["\']\s*\))");
1040 if (qmlSingletonPattern.match(sourceText).hasMatch()) {
1041 hasSingletonMacro =
true;
1042 return CXChildVisit_Break;
1047 if (clang_getCursorKind(child) == CXCursor_EnumDecl) {
1048 QString spelling = fromCXString(clang_getCursorSpelling(child));
1049 if (spelling ==
"QmlIsSingleton"_L1) {
1050 hasSingletonMacro =
true;
1051 return CXChildVisit_Break;
1055 return CXChildVisit_Continue;
1058 return hasSingletonMacro;
1062
1063
1064
1065CXChildVisitResult
ClangVisitor::visitSource(CXCursor cursor, CXSourceLocation loc)
1067 auto kind = clang_getCursorKind(cursor);
1068 if (clang_isDeclaration(kind)) {
1070 clang_getPresumedLocation(loc,
nullptr, &l.line, &l.column);
1071 declMap_.insert(l, cursor);
1072 return CXChildVisit_Recurse;
1074 return CXChildVisit_Continue;
1078
1079
1080
1081
1084 CXCursor sp = clang_getCursorSemanticParent(cursor);
1085 CXCursor lp = clang_getCursorLexicalParent(cursor);
1086 if (!clang_equalCursors(sp, lp) && clang_isDeclaration(clang_getCursorKind(sp))) {
1089 return static_cast<Aggregate *>(spn);
1095CXChildVisitResult
ClangVisitor::visitFnSignature(CXCursor cursor, CXSourceLocation,
Node **fnNode,
1096 bool &ignoreSignature)
1098 switch (clang_getCursorKind(cursor)) {
1099 case CXCursor_Namespace:
1100 return CXChildVisit_Recurse;
1101 case CXCursor_FunctionDecl:
1102 case CXCursor_FunctionTemplate:
1103 case CXCursor_CXXMethod:
1104 case CXCursor_Constructor:
1105 case CXCursor_Destructor:
1106 case CXCursor_ConversionFunction: {
1107 ignoreSignature =
false;
1108 if (ignoredSymbol(functionName(cursor))) {
1110 ignoreSignature =
true;
1115 auto *fn =
static_cast<FunctionNode *>(*fnNode);
1116 readParameterNamesAndAttributes(fn, cursor);
1120 if (
const auto function_declaration = declaration->getAsFunction()) {
1121 auto declaredReturnType = function_declaration->getDeclaredReturnType();
1122 if (llvm::dyn_cast_if_present<
clang::AutoType>(declaredReturnType.getTypePtrOrNull()))
1123 fn->setDeclaredReturnType(QString::fromStdString(declaredReturnType.getAsString()));
1127 QString name = functionName(cursor);
1128 if (ignoredSymbol(name))
1129 return CXChildVisit_Continue;
1130 Aggregate *semanticParent = getSemanticParent(cursor);
1131 if (semanticParent && semanticParent
->isClass()) {
1133 processFunction(candidate, cursor);
1134 if (!candidate->isSpecialMemberFunction()) {
1136 return CXChildVisit_Continue;
1138 candidate->setImplicitlyGenerated(
true);
1148 return CXChildVisit_Continue;
1151CXChildVisitResult
ClangVisitor::visitHeader(CXCursor cursor, CXSourceLocation loc)
1153 auto kind = clang_getCursorKind(cursor);
1156 case CXCursor_TypeAliasTemplateDecl:
1157 case CXCursor_TypeAliasDecl: {
1158 const QString aliasName = fromCXString(clang_getCursorSpelling(cursor));
1159 QString aliasedType;
1161 const auto *templateDecl = (kind == CXCursor_TypeAliasTemplateDecl)
1165 if (kind == CXCursor_TypeAliasTemplateDecl) {
1167 if (
const auto *aliasTemplate = llvm::dyn_cast<clang::TypeAliasTemplateDecl>(templateDecl)) {
1168 if (
const auto *aliasDecl = aliasTemplate->getTemplatedDecl()) {
1169 clang::QualType underlyingType = aliasDecl->getUnderlyingType();
1170 aliasedType = QString::fromStdString(underlyingType.getAsString());
1175 const CXType aliasedCXType = clang_getTypedefDeclUnderlyingType(cursor);
1176 if (aliasedCXType.kind != CXType_Invalid) {
1177 aliasedType = fromCXString(clang_getTypeSpelling(aliasedCXType));
1181 if (!aliasedType.isEmpty()) {
1182 auto *ta =
new TypeAliasNode(parent_, aliasName, aliasedType);
1187 ta->setTemplateDecl(get_template_declaration(templateDecl));
1189 return CXChildVisit_Continue;
1191 case CXCursor_StructDecl:
1192 case CXCursor_UnionDecl:
1193 if (fromCXString(clang_getCursorSpelling(cursor)).isEmpty())
1194 return CXChildVisit_Continue;
1196 case CXCursor_ClassTemplate:
1198 case CXCursor_ClassDecl: {
1199 if (!clang_isCursorDefinition(cursor))
1200 return CXChildVisit_Continue;
1203 return CXChildVisit_Continue;
1205 QString className = cleanAnonymousTypeName(fromCXString(clang_getCursorSpelling(cursor)));
1207 Aggregate *semanticParent = getSemanticParent(cursor);
1208 if (semanticParent && semanticParent->findNonfunctionChild(className, &
Node::isClassNode)) {
1209 return CXChildVisit_Continue;
1212 CXCursorKind actualKind = (kind == CXCursor_ClassTemplate) ?
1213 clang_getTemplateCursorKind(cursor) : kind;
1216 if (actualKind == CXCursor_StructDecl)
1218 else if (actualKind == CXCursor_UnionDecl)
1221 auto *classe =
new ClassNode(type, semanticParent, className);
1225 classe->setLocation(location);
1227 if (!internalFilePatterns_.exactMatches.isEmpty() || !internalFilePatterns_.globPatterns.isEmpty()
1228 || !internalFilePatterns_.regexPatterns.isEmpty()) {
1229 if (
Config::matchesInternalFilePattern(location.filePath(), internalFilePatterns_))
1233 classe->setAnonymous(clang_Cursor_isAnonymous(cursor));
1235 if (detectQmlSingleton(cursor)) {
1236 classe->setQmlSingleton(
true);
1239 if (kind == CXCursor_ClassTemplate) {
1241 classe->setTemplateDecl(get_template_declaration(template_declaration));
1244 QScopedValueRollback<Aggregate *> setParent(parent_, classe);
1247 case CXCursor_CXXBaseSpecifier: {
1249 return CXChildVisit_Continue;
1251 auto type = clang_getCursorType(cursor);
1252 auto baseCursor = clang_getTypeDeclaration(type);
1254 auto classe =
static_cast<ClassNode *>(parent_);
1256 QString bcName = reconstructQualifiedPathForCursor(baseCursor);
1257 classe->addUnresolvedBaseClass(access,
1258 bcName.split(QLatin1String(
"::"), Qt::SkipEmptyParts));
1259 return CXChildVisit_Continue;
1261 auto baseClasse =
static_cast<ClassNode *>(baseNode);
1262 classe->addResolvedBaseClass(access, baseClasse);
1263 return CXChildVisit_Continue;
1265 case CXCursor_Namespace: {
1266 QString namespaceName = fromCXString(clang_getCursorDisplayName(cursor));
1276 QScopedValueRollback<Aggregate *> setParent(parent_, ns);
1279 case CXCursor_FunctionTemplate:
1281 case CXCursor_FunctionDecl:
1282 case CXCursor_CXXMethod:
1283 case CXCursor_Constructor:
1284 case CXCursor_Destructor:
1285 case CXCursor_ConversionFunction: {
1287 return CXChildVisit_Continue;
1288 QString name = functionName(cursor);
1289 if (ignoredSymbol(name))
1290 return CXChildVisit_Continue;
1293 return CXChildVisit_Continue;
1296 CXSourceRange range = clang_Cursor_getCommentRange(cursor);
1297 if (!clang_Range_isNull(range)) {
1298 QString comment = getSpelling(range);
1299 if (comment.startsWith(
"//!")) {
1300 qsizetype tag = comment.indexOf(QChar(
'['));
1302 qsizetype end = comment.indexOf(QChar(
']'), ++tag);
1304 fn->setTag(comment.mid(tag, end - tag));
1309 processFunction(fn, cursor);
1311 if (kind == CXCursor_FunctionTemplate) {
1313 fn->setTemplateDecl(get_template_declaration(template_declaration));
1316 if (!clang_Location_isInSystemHeader(loc))
1317 autoGenerateSmfDoc(fn, parent_->name());
1319 return CXChildVisit_Continue;
1321#if CINDEX_VERSION
>= 36
1322 case CXCursor_FriendDecl: {
1326 case CXCursor_EnumDecl: {
1327 auto *en =
static_cast<EnumNode *>(findNodeForCursor(qdb_, cursor));
1328 if (en && en->items().size())
1329 return CXChildVisit_Continue;
1331 QString enumTypeName = fromCXString(clang_getCursorSpelling(cursor));
1333 if (clang_Cursor_isAnonymous(cursor)) {
1334 enumTypeName =
"anonymous";
1340 Node *n = parent_->findNonfunctionChild(enumTypeName, &
Node::isEnumType);
1342 en =
static_cast<EnumNode *>(n);
1346 en =
new EnumNode(parent_, enumTypeName, clang_EnumDecl_isScoped(cursor));
1349 en->setAnonymous(clang_Cursor_isAnonymous(cursor));
1353 visitChildrenLambda(cursor, [&](CXCursor cur) {
1354 if (clang_getCursorKind(cur) != CXCursor_EnumConstantDecl)
1355 return CXChildVisit_Continue;
1358 visitChildrenLambda(cur, [&](CXCursor cur) {
1359 if (clang_isExpression(clang_getCursorKind(cur))) {
1360 value = getSpelling(clang_getCursorExtent(cur));
1361 return CXChildVisit_Break;
1363 return CXChildVisit_Continue;
1365 if (value.isEmpty()) {
1366 QLatin1String hex(
"0x");
1367 if (!en->items().isEmpty() && en->items().last().value().startsWith(hex)) {
1368 value = hex + QString::number(clang_getEnumConstantDeclValue(cur), 16);
1370 value = QString::number(clang_getEnumConstantDeclValue(cur));
1374 en->addItem(EnumItem(fromCXString(clang_getCursorSpelling(cur)), std::move(value)));
1375 return CXChildVisit_Continue;
1377 return CXChildVisit_Continue;
1379 case CXCursor_FieldDecl:
1380 case CXCursor_VarDecl: {
1382 return CXChildVisit_Continue;
1384 auto value_declaration =
1386 assert(value_declaration);
1389 auto var =
new VariableNode(parent_, fromCXString(clang_getCursorSpelling(cursor)));
1391 var->setAccess(access);
1393 var->setLeftType(cleanAnonymousTypeName(QString::fromStdString(get_fully_qualified_type_name(
1394 value_declaration->getType(),
1395 value_declaration->getASTContext()
1397 var->setStatic(kind == CXCursor_VarDecl && parent_
->isClassNode());
1399 return CXChildVisit_Continue;
1401 case CXCursor_TypedefDecl: {
1403 return CXChildVisit_Continue;
1404 auto *td =
new TypedefNode(parent_, fromCXString(clang_getCursorSpelling(cursor)));
1408 visitChildrenLambda(cursor, [&](CXCursor cur) {
1409 if (clang_getCursorKind(cur) != CXCursor_TemplateRef
1410 || fromCXString(clang_getCursorSpelling(cur)) != QLatin1String(
"QFlags"))
1411 return CXChildVisit_Continue;
1413 visitChildrenLambda(cursor, [&](CXCursor cur) {
1414 if (clang_getCursorKind(cur) != CXCursor_TypeRef)
1415 return CXChildVisit_Continue;
1418 if (en && en->isEnumType())
1419 static_cast<EnumNode *>(en)->setFlagsType(td);
1420 return CXChildVisit_Break;
1422 return CXChildVisit_Break;
1424 return CXChildVisit_Continue;
1430 parseProperty(getSpelling(clang_getCursorExtent(cursor)),
1433 return CXChildVisit_Continue;
1442 visitChildrenLambda(cursor, [&](CXCursor cur) {
1443 auto kind = clang_getCursorKind(cur);
1444 if (kind == CXCursor_AnnotateAttr) {
1445 QString annotation = fromCXString(clang_getCursorDisplayName(cur));
1446 if (annotation == QLatin1String(
"qt_slot")) {
1448 }
else if (annotation == QLatin1String(
"qt_signal")) {
1451 if (annotation == QLatin1String(
"qt_invokable"))
1453 }
else if (kind == CXCursor_CXXOverrideAttr) {
1455 }
else if (kind == CXCursor_ParmDecl) {
1457 return CXChildVisit_Break;
1459 if (QString name = fromCXString(clang_getCursorSpelling(cur)); !name.isEmpty())
1460 parameters
[i
].setName(name);
1463 Q_ASSERT(parameter_declaration);
1467 if (!default_value.empty())
1468 parameters
[i
].setDefaultValue(QString::fromStdString(default_value));
1472 return CXChildVisit_Continue;
1478 CXCursorKind kind = clang_getCursorKind(cursor);
1479 CXType funcType = clang_getCursorType(cursor);
1486 : clang_CXXMethod_isPureVirtual(cursor)
1503 const clang::FunctionDecl* function_declaration = declaration->getAsFunction();
1505 if (kind == CXCursor_Constructor
1507 || (kind == CXCursor_FunctionTemplate && fn->name() == parent_->name()))
1509 else if (kind == CXCursor_Destructor)
1511 else if (kind != CXCursor_ConversionFunction)
1512 fn->setReturnType(cleanAnonymousTypeName(QString::fromStdString(get_fully_qualified_type_name(
1513 function_declaration->getReturnType(),
1514 function_declaration->getASTContext()
1517 const clang::CXXConstructorDecl* constructor_declaration = llvm::dyn_cast<
const clang::CXXConstructorDecl>(function_declaration);
1522 const clang::CXXConversionDecl* conversion_declaration = llvm::dyn_cast<
const clang::CXXConversionDecl>(function_declaration);
1528 (constructor_declaration && constructor_declaration->isExplicit()) ||
1529 (conversion_declaration && conversion_declaration->isExplicit())
1532 const clang::CXXMethodDecl* method_declaration = llvm::dyn_cast<
const clang::CXXMethodDecl>(function_declaration);
1537 const clang::FunctionType* function_type = function_declaration->getFunctionType();
1538 const clang::FunctionProtoType* function_prototype =
static_cast<
const clang::FunctionProtoType*>(function_type);
1540 if (function_prototype) {
1541 clang::FunctionProtoType::ExceptionSpecInfo exception_specification = function_prototype->getExceptionSpecInfo();
1543 if (exception_specification.Type !=
clang::ExceptionSpecificationType::EST_None) {
1544 const std::string exception_specification_spelling =
1545 exception_specification.NoexceptExpr ? get_expression_as_string(
1546 exception_specification.NoexceptExpr,
1547 function_declaration->getASTContext()
1550 if (exception_specification_spelling !=
"false")
1551 fn->markNoexcept(QString::fromStdString(exception_specification_spelling));
1558#if LIBCLANG_VERSION_MAJOR >= 21
1559 if (
const auto trailing_requires = function_declaration->getTrailingRequiresClause();
1560 trailing_requires.ConstraintExpr) {
1561 QString requires_str = QString::fromStdString(
1562 get_expression_as_string(trailing_requires.ConstraintExpr,
1563 function_declaration->getASTContext()));
1564 fn->setTrailingRequiresClause(requires_str.simplified());
1567 if (
const clang::Expr *trailing_requires = function_declaration->getTrailingRequiresClause()) {
1568 QString requires_str = QString::fromStdString(
1569 get_expression_as_string(trailing_requires,
1570 function_declaration->getASTContext()));
1571 fn->setTrailingRequiresClause(requires_str.simplified());
1575 CXRefQualifierKind refQualKind = clang_Type_getCXXRefQualifier(funcType);
1576 if (refQualKind == CXRefQualifier_LValue)
1578 else if (refQualKind == CXRefQualifier_RValue)
1587 parameters
.reserve(function_declaration->getNumParams()
);
1589 for (clang::ParmVarDecl*
const parameter_declaration : function_declaration->parameters()) {
1590 clang::QualType parameter_type = parameter_declaration->getOriginalType();
1592 parameters.append(cleanAnonymousTypeName(QString::fromStdString(get_fully_qualified_type_name(
1594 parameter_declaration->getASTContext()
1597 if (!parameter_type.isCanonical())
1598 parameters.last().setCanonicalType(cleanAnonymousTypeName(QString::fromStdString(get_fully_qualified_type_name(
1599 parameter_type.getCanonicalType(),
1600 parameter_declaration->getASTContext()
1605 if (parameters
.last().type().endsWith(QLatin1String(
"QPrivateSignal"))) {
1611 if (clang_isFunctionTypeVariadic(funcType))
1612 parameters.append(QStringLiteral(
"..."));
1613 readParameterNamesAndAttributes(fn, cursor);
1615 if (declaration && declaration->getFriendObjectKind() !=
clang::Decl::FOK_None) {
1617 if (function_declaration && function_declaration->isThisDeclarationADefinition())
1624 if (!spelling.startsWith(QLatin1String(
"Q_PROPERTY"))
1625 && !spelling.startsWith(QLatin1String(
"QDOC_PROPERTY"))
1626 && !spelling.startsWith(QLatin1String(
"Q_OVERRIDE")))
1629 qsizetype lpIdx = spelling.indexOf(QChar(
'('));
1630 qsizetype rpIdx = spelling.lastIndexOf(QChar(
')'));
1631 if (lpIdx <= 0 || rpIdx <= lpIdx)
1634 QString signature = spelling.mid(lpIdx + 1, rpIdx - lpIdx - 1);
1635 signature = signature.simplified();
1636 QStringList parts = signature.split(QChar(
' '), Qt::SkipEmptyParts);
1638 static const QStringList attrs =
1639 QStringList() <<
"READ" <<
"MEMBER" <<
"WRITE"
1640 <<
"NOTIFY" <<
"CONSTANT" <<
"FINAL"
1641 <<
"REQUIRED" <<
"BINDABLE" <<
"DESIGNABLE"
1642 <<
"RESET" <<
"REVISION" <<
"SCRIPTABLE"
1643 <<
"STORED" <<
"USER";
1647 auto it =
std::find_if(parts.cbegin(), parts.cend(),
1648 [](
const QString &attr) ->
bool {
1649 return attrs.contains(attr);
1652 if (it == parts.cend() ||
std::distance(parts.cbegin(), it) < 2)
1655 QStringList typeParts;
1656 std::copy(parts.cbegin(), it,
std::back_inserter(typeParts));
1657 parts.erase(parts.cbegin(), it);
1658 QString name = typeParts.takeLast();
1661 while (!name.isEmpty() && name.front() == QChar(
'*')) {
1662 typeParts.last().push_back(name.front());
1667 if (parts.size() < 2 || name.isEmpty())
1672 property->setLocation(loc);
1673 property->setDataType(typeParts.join(QChar(
' ')));
1676 while (i < parts.size()) {
1677 const QString &key = parts.at(i++);
1679 if (key ==
"CONSTANT") {
1680 property->setConstant();
1681 }
else if (key ==
"REQUIRED") {
1682 property->setRequired();
1684 if (i < parts.size()) {
1685 QString value = parts.at(i++);
1686 if (key ==
"READ") {
1688 }
else if (key ==
"WRITE") {
1690 property->setWritable(
true);
1691 }
else if (key ==
"MEMBER") {
1692 property->setWritable(
true);
1693 }
else if (key ==
"STORED") {
1694 property->setStored(value.toLower() ==
"true");
1695 }
else if (key ==
"BINDABLE") {
1698 }
else if (key ==
"RESET") {
1700 }
else if (key ==
"NOTIFY") {
1709
1710
1711
1712
1713
1717 clang_getPresumedLocation(loc,
nullptr, &docloc.line, &docloc.column);
1718 auto decl_it = declMap_.upperBound(docloc);
1719 if (decl_it == declMap_.end())
1722 unsigned int declLine = decl_it.key().line;
1723 unsigned int nextCommentLine;
1724 clang_getPresumedLocation(nextCommentLoc,
nullptr, &nextCommentLine,
nullptr);
1725 if (nextCommentLine < declLine)
1729 if (decl_it != declMap_.begin()) {
1730 CXSourceLocation prevDeclEnd = clang_getRangeEnd(clang_getCursorExtent(*(
std::prev(decl_it))));
1731 unsigned int prevDeclLine;
1732 clang_getPresumedLocation(prevDeclEnd,
nullptr, &prevDeclLine,
nullptr);
1733 if (prevDeclLine >= docloc.line) {
1736 auto parent = clang_getCursorLexicalParent(*decl_it);
1737 if (!clang_equalCursors(parent, *(
std::prev(decl_it))))
1741 auto *node = findNodeForCursor(qdb_, *decl_it);
1743 if (node && node->isFunction(Genus::CPP))
1744 readParameterNamesAndAttributes(
static_cast<
FunctionNode *>(node), *decl_it);
1751 const std::vector<QByteArray>& include_paths,
1752 const QList<QByteArray>& defines,
1759 m_allHeaders = config.getHeaderFiles();
1760 m_internalFilePatterns = config.getInternalFilePatternsCompiled();
1768 "-fms-compatibility-version=19",
1772 "-DQT_DISABLE_DEPRECATED_UP_TO=0",
1773 "-DQT_ANNOTATE_CLASS(type,...)=static_assert(sizeof(#__VA_ARGS__),#type);",
1774 "-DQT_ANNOTATE_CLASS2(type,a1,a2)=static_assert(sizeof(#a1,#a2),#type);",
1775 "-DQT_ANNOTATE_FUNCTION(a)=__attribute__((annotate(#a)))",
1776 "-DQT_ANNOTATE_ACCESS_SPECIFIER(a)=__attribute__((annotate(#a)))",
1777 "-Wno-constant-logical-operand",
1778 "-Wno-macro-redefined",
1779 "-Wno-nullability-completeness",
1780 "-fvisibility=default",
1786
1787
1788
1795 for (
const auto &p : std::as_const(defines))
1796 args.push_back(p.constData());
1801 QList<QByteArray> result;
1802 for (
const auto& [header_path, _] : allHeaders) {
1803 const QByteArray path =
"-I" + header_path.toLatin1();
1804 const QByteArray parent =
1805 "-I" + QDir::cleanPath(header_path + QLatin1String(
"/../")).toLatin1();
1812
1813
1814
1816 const std::vector<QByteArray>& include_paths,
1817 const std::set<Config::HeaderFilePath>& all_headers,
1818 std::vector<
const char*>& args
1820 if (include_paths.empty()) {
1822
1823
1824
1825
1826 qCWarning(lcQdoc) <<
"No include paths passed to qdoc; guessing reasonable include paths";
1828 QString basicIncludeDir = QDir::cleanPath(QString(Config::installDir +
"/../include"));
1829 args.emplace_back(QByteArray(
"-I" + basicIncludeDir.toLatin1()).constData());
1831 auto include_paths_from_headers = includePathsFromHeaders(all_headers);
1832 args.insert(args.end(), include_paths_from_headers.begin(), include_paths_from_headers.end());
1834 std::copy(include_paths.begin(), include_paths.end(),
std::back_inserter(args));
1839
1840
1841
1842
1845 QString module_header,
1846 const std::set<Config::HeaderFilePath>& all_headers,
1847 const std::vector<QByteArray>& include_paths,
1848 const QList<QByteArray>& defines,
1849 const InclusionPolicy& policy
1851 static std::vector<
const char*> arguments{};
1853 if (module_header.isEmpty())
return std::nullopt;
1855 getDefaultArgs(defines, arguments);
1856 getMoreArgs(include_paths, all_headers, arguments);
1858 flags_ =
static_cast<CXTranslationUnit_Flags>(CXTranslationUnit_Incomplete
1859 | CXTranslationUnit_SkipFunctionBodies
1860 | CXTranslationUnit_KeepGoing);
1864 QTemporaryDir pch_directory{QDir::tempPath() + QLatin1String(
"/qdoc_pch")};
1865 if (!pch_directory.isValid())
return std::nullopt;
1867 const QByteArray module = module_header.toUtf8();
1870 qCDebug(lcQdoc) <<
"Build and visit PCH for" << module_header;
1873 struct FindPredicate
1875 enum SearchType { Any, Module };
1876 QByteArray &candidate_;
1877 const QByteArray &module_;
1879 FindPredicate(QByteArray &candidate,
const QByteArray &module,
1880 SearchType type = Any)
1881 : candidate_(candidate), module_(module), type_(type)
1885 bool operator()(
const QByteArray &p)
const
1887 if (type_ != Any && !p.endsWith(module_))
1889 candidate_ = p +
"/";
1890 candidate_.append(module_);
1891 if (p.startsWith(
"-I"))
1892 candidate_ = candidate_.mid(2);
1893 return QFile::exists(QString::fromUtf8(candidate_));
1898 QByteArray candidate;
1899 auto it =
std::find_if(include_paths.begin(), include_paths.end(),
1900 FindPredicate(candidate, module, FindPredicate::Module));
1901 if (it == include_paths.end())
1902 it =
std::find_if(include_paths.begin(), include_paths.end(),
1903 FindPredicate(candidate, module, FindPredicate::Any));
1904 if (it != include_paths.end())
1905 header =
std::move(candidate);
1907 if (header.isEmpty()) {
1908 qWarning() <<
"(qdoc) Could not find the module header in include paths for module"
1909 << module <<
" (include paths: " << include_paths <<
")";
1910 qWarning() <<
" Artificial module header built from header dirs in qdocconf "
1913 arguments.push_back(
"-xc++");
1917 QString tmpHeader = pch_directory.path() +
"/" + module;
1918 if (QFile tmpHeaderFile(tmpHeader); tmpHeaderFile.open(QIODevice::Text | QIODevice::WriteOnly)) {
1919 QTextStream out(&tmpHeaderFile);
1920 if (header.isEmpty()) {
1921 for (
const auto& [header_path, header_name] : all_headers) {
1922 bool shouldInclude = !header_name.startsWith(
"moc_"_L1);
1925 if (header_name.endsWith(
"_p.h"_L1))
1926 shouldInclude = shouldInclude && policy.showInternal;
1928 if (shouldInclude) {
1929 out <<
"#include \"" << header_path <<
"/" << header_name <<
"\"\n";
1933 QFileInfo headerFile(header);
1934 if (!headerFile.exists()) {
1935 qWarning() <<
"Could not find module header file" << header;
1936 return std::nullopt;
1939 out <<
"#include \"" << header <<
"\"\n";
1941 if (policy.showInternal) {
1942 for (
const auto& [header_path, header_name] : all_headers) {
1943 bool shouldInclude = !header_name.startsWith(
"moc_"_L1);
1944 if (header_name.endsWith(
"_p.h"_L1) && shouldInclude)
1945 out <<
"#include \"" << header_path <<
"/" << header_name <<
"\"\n";
1952 clang_parseTranslationUnit2(index, tmpHeader.toLatin1().data(), arguments.data(),
1953 static_cast<
int>(arguments.size()),
nullptr, 0,
1954 flags_ | CXTranslationUnit_ForSerialization, &tu
.tu);
1955 qCDebug(lcQdoc) <<
__FUNCTION__ <<
"clang_parseTranslationUnit2(" << tmpHeader << arguments
1956 <<
") returns" << err;
1961 qCCritical(lcQdoc) <<
"Could not create PCH file for " << module_header;
1962 return std::nullopt;
1965 QByteArray pch_name = pch_directory.path().toUtf8() +
"/" + module +
".pch";
1966 auto error = clang_saveTranslationUnit(tu, pch_name.constData(),
1967 clang_defaultSaveOptions(tu));
1969 qCCritical(lcQdoc) <<
"Could not save PCH file for" << module_header;
1970 return std::nullopt;
1975 CXCursor cur = clang_getTranslationUnitCursor(tu);
1976 auto &config = Config::instance();
1977 ClangVisitor visitor(qdb, all_headers, config.getInternalFilePatternsCompiled());
1979 qCDebug(lcQdoc) <<
"PCH built and visited for" << module_header;
1981 return std::make_optional(
PCHFile{
std::move(pch_directory),
std::move(pch_name)});
1986 if (t.count(QChar(
'.')) > 1)
1987 t.truncate(t.lastIndexOf(QChar(
'.')));
1992
1993
1994
1995
1996
1997
1998
1999
2002 flags_ =
static_cast<CXTranslationUnit_Flags>(CXTranslationUnit_Incomplete
2003 | CXTranslationUnit_SkipFunctionBodies
2004 | CXTranslationUnit_KeepGoing);
2008 getDefaultArgs(m_defines, m_args);
2009 if (m_pch && !filePath.endsWith(
".mm")
2010 && !std::holds_alternative<CppHeaderSourceFile>(tag_source_file(filePath).second)) {
2011 m_args.push_back(
"-w");
2012 m_args.push_back(
"-include-pch");
2013 m_args.push_back((*m_pch).get().name.constData());
2015 getMoreArgs(m_includePaths, m_allHeaders, m_args);
2019 clang_parseTranslationUnit2(index, filePath.toLocal8Bit(), m_args.data(),
2020 static_cast<
int>(m_args.size()),
nullptr, 0,
flags_, &tu
.tu);
2021 qCDebug(lcQdoc) <<
__FUNCTION__ <<
"clang_parseTranslationUnit2(" << filePath << m_args
2022 <<
") returns" << err;
2026 qWarning() <<
"(qdoc) Could not parse source file" << filePath <<
" error code:" << err;
2030 ParsedCppFileIR parse_result{};
2032 CXCursor tuCur = clang_getTranslationUnitCursor(tu);
2033 ClangVisitor visitor(m_qdb, m_allHeaders, m_internalFilePatterns);
2037 unsigned int numTokens = 0;
2038 const QSet<QString> &commands = CppCodeParser::topic_commands + CppCodeParser::meta_commands;
2039 clang_tokenize(tu, clang_getCursorExtent(tuCur), &tokens, &numTokens);
2041 for (
unsigned int i = 0; i < numTokens; ++i) {
2042 if (clang_getTokenKind(tokens[i]) != CXToken_Comment)
2044 QString comment = fromCXString(clang_getTokenSpelling(tu, tokens[i]));
2045 if (!comment.startsWith(
"/*!"))
2048 auto commentLoc = clang_getTokenLocation(tu, tokens[i]);
2051 Doc::trimCStyleComment(loc, comment);
2054 Doc doc(loc, end_loc, comment, commands, CppCodeParser::topic_commands);
2060 if (i + 1 < numTokens) {
2062 CXSourceLocation nextCommentLoc = commentLoc;
2063 while (i + 2 < numTokens && clang_getTokenKind(tokens[i + 1]) != CXToken_Comment)
2065 nextCommentLoc = clang_getTokenLocation(tu, tokens[i + 1]);
2072 bool future =
false;
2074 QString sinceVersion = doc.metaCommandArgs(
COMMAND_SINCE).at(0).first;
2075 if (getUnpatchedVersion(
std::move(sinceVersion)) >
2076 getUnpatchedVersion(Config::instance().get(
CONFIG_VERSION).asString()))
2081 QStringLiteral(
"Cannot tie this documentation to anything"),
2082 QStringLiteral(
"qdoc found a /*! ... */ comment, but there was no "
2083 "topic command (e.g., '\\%1', '\\%2') in the "
2084 "comment and no function definition following "
2092 CXCursor cur = clang_getCursor(tu, commentLoc);
2094 CXCursorKind kind = clang_getCursorKind(cur);
2095 if (clang_isTranslationUnit(kind) || clang_isInvalid(kind))
2097 if (kind == CXCursor_Namespace) {
2098 parse_result.untied.back().context << fromCXString(clang_getCursorSpelling(cur));
2100 cur = clang_getCursorLexicalParent(cur);
2105 clang_disposeTokens(tu, tokens, numTokens);
2106 m_namespaceScope.clear();
2109 return parse_result;
2113
2114
2115
2116
2117
2118
2119
2120
2121
2122
2123
2125 const QString &idTag, QStringList context)
2127 Node *fnNode =
nullptr;
2129
2130
2131
2132
2133
2134 if (!idTag.isEmpty()) {
2135 fnNode = m_qdb->findFunctionNodeForTag(idTag);
2138 QStringLiteral(
"tag \\fn [%1] not used in any include file in current module").arg(idTag));
2141
2142
2143
2144
2145 auto *fn =
static_cast<FunctionNode *>(fnNode);
2146 QStringList leftParenSplit = fnSignature.mid(fnSignature.indexOf(fn->name())).split(
'(');
2147 if (leftParenSplit.size() > 1) {
2148 QStringList rightParenSplit = leftParenSplit[1].split(
')');
2149 if (!rightParenSplit.empty()) {
2150 QString params = rightParenSplit[0];
2151 if (!params.isEmpty()) {
2152 QStringList commaSplit = params.split(
',');
2154 if (parameters
.count() == commaSplit.size()) {
2155 for (
int i = 0; i < parameters
.count(); ++i) {
2156 QStringList blankSplit = commaSplit[i].split(
' ', Qt::SkipEmptyParts);
2157 if (blankSplit.size() > 1) {
2158 QString pName = blankSplit.last();
2160 auto it =
std::find_if(
std::begin(pName),
std::end(pName),
2161 [](
const QChar &c) {
return c.isLetter(); });
2162 parameters
[i
].setName(
2163 pName.remove(0,
std::distance(
std::begin(pName), it)));
2173 auto flags =
static_cast<CXTranslationUnit_Flags>(CXTranslationUnit_Incomplete
2174 | CXTranslationUnit_SkipFunctionBodies
2175 | CXTranslationUnit_KeepGoing);
2179 getDefaultArgs(m_defines, m_args);
2182 m_args.push_back(
"-w");
2183 m_args.push_back(
"-include-pch");
2184 m_args.push_back((*m_pch).get().name.constData());
2189 for (
const auto &ns : std::as_const(context))
2190 s_fn.prepend(
"namespace " + ns.toUtf8() +
" {");
2191 s_fn += fnSignature.toUtf8();
2192 if (!s_fn.endsWith(
";"))
2194 s_fn.append(context.size(),
'}');
2197 CXUnsavedFile unsavedFile { dummyFileName, s_fn.constData(),
2198 static_cast<
unsigned long>(s_fn.size()) };
2199 CXErrorCode err = clang_parseTranslationUnit2(index, dummyFileName, m_args.data(),
2200 int(m_args.size()), &unsavedFile, 1, flags, &tu
.tu);
2201 qCDebug(lcQdoc) <<
__FUNCTION__ <<
"clang_parseTranslationUnit2(" << dummyFileName << m_args
2202 <<
") returns" << err;
2205 location.error(QStringLiteral(
"clang could not parse \\fn %1").arg(fnSignature));
2209
2210
2211
2212
2213
2214 CXCursor cur = clang_getTranslationUnitCursor(tu);
2215 auto &config = Config::instance();
2216 ClangVisitor visitor(m_qdb, m_allHeaders, config.getInternalFilePatternsCompiled());
2217 bool ignoreSignature =
false;
2221 unsigned diagnosticCount = clang_getNumDiagnostics(tu);
2222 const auto &config = Config::instance();
2223 if (diagnosticCount > 0 && (!config.preparing() || config.singleExec())) {
2224 return FnMatchError{ fnSignature, location };
static const clang::Decl * get_cursor_declaration(CXCursor cursor)
Returns the underlying Decl that cursor represents.
static QString reconstructQualifiedPathForCursor(CXCursor cur)
Reconstruct the qualified path name of a function that is being overridden.
static std::optional< QString > classNameFromParameterType(clang::QualType param_type)
QString functionName(CXCursor cursor)
Returns the function name from a given cursor representing a function declaration.
static std::string get_default_value_initializer_as_string(const clang::TemplateTemplateParmDecl *parameter)
static QString fromCXString(CXString &&string)
convert a CXString to a QString, and dispose the CXString
static QDebug operator<<(QDebug debug, const std::vector< T > &v)
static QString getSpelling(CXSourceRange range)
static void setOverridesForFunction(FunctionNode *fn, CXCursor cursor)
static QList< QByteArray > includePathsFromHeaders(const std::set< Config::HeaderFilePath > &allHeaders)
static const auto kClangDontDisplayDiagnostics
void getMoreArgs(const std::vector< QByteArray > &include_paths, const std::set< Config::HeaderFilePath > &all_headers, std::vector< const char * > &args)
Load the include paths into moreArgs.
static std::string get_default_value_initializer_as_string(const clang::ParmVarDecl *parameter)
void getDefaultArgs(const QList< QByteArray > &defines, std::vector< const char * > &args)
Load the default arguments and the defines into args.
static std::string get_expression_as_string(const clang::Expr *expression, const clang::ASTContext &declaration_context)
bool visitChildrenLambda(CXCursor cursor, T &&lambda)
Call clang_visitChildren on the given cursor with the lambda as a callback T can be any functor that ...
static std::string get_default_value_initializer_as_string(const clang::NamedDecl *declaration)
static RelaxedTemplateDeclaration get_template_declaration(const clang::TemplateDecl *template_declaration)
static std::string get_default_value_initializer_as_string(const clang::NonTypeTemplateParmDecl *parameter)
static QString fromCache(const QByteArray &cache, unsigned int offset1, unsigned int offset2)
static float getUnpatchedVersion(QString t)
static Location fromCXSourceLocation(CXSourceLocation location)
convert a CXSourceLocation to a qdoc Location
static QString cleanAnonymousTypeName(const QString &typeName)
static Access fromCX_CXXAccessSpecifier(CX_CXXAccessSpecifier spec)
convert a CX_CXXAccessSpecifier to Node::Access
static std::string get_default_value_initializer_as_string(const clang::TemplateTypeParmDecl *parameter)
constexpr const char fnDummyFileName[]
static CXTranslationUnit_Flags flags_
static Node * findNodeForCursor(QDocDatabase *qdb, CXCursor cur)
Find the node from the QDocDatabase qdb that corresponds to the declaration represented by the cursor...
static void autoGenerateSmfDoc(FunctionNode *fn, const QString &className)
static std::string get_fully_qualified_type_name(clang::QualType type, const clang::ASTContext &declaration_context)
Returns a string representing the name of type as if it was referred to at the end of the translation...
static void printDiagnostics(const CXTranslationUnit &translationUnit)
static const char * defaultArgs_[]
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, const InclusionPolicy &policy)
Building the PCH must be possible when there are no .cpp files, so it is moved here to its own member...
static QString readFile(CXFile cxFile, unsigned int offset1, unsigned int offset2)
void addChild(Node *child)
Adds the child to this node's child list and sets the child's parent pointer to this Aggregate.
ParsedCppFileIR parse_cpp_file(const QString &filePath)
Get ready to parse the C++ cpp file identified by filePath and add its parsed contents to the databas...
ClangCodeParser(QDocDatabase *qdb, Config &, const std::vector< QByteArray > &include_paths, const QList< QByteArray > &defines, std::optional< std::reference_wrapper< const PCHFile > > pch)
Node * nodeForCommentAtLocation(CXSourceLocation loc, CXSourceLocation nextCommentLoc)
Given a comment at location loc, return a Node for this comment nextCommentLoc is the location of the...
CXChildVisitResult visitChildren(CXCursor cursor)
ClangVisitor(QDocDatabase *qdb, const std::set< Config::HeaderFilePath > &allHeaders, const Config::InternalFilePatterns &internalFilePatterns)
CXChildVisitResult visitFnArg(CXCursor cursor, Node **fnNode, bool &ignoreSignature)
The ClassNode represents a C++ class.
static bool isWorthWarningAbout(const Doc &doc)
Test for whether a doc comment warrants warnings.
The Config class contains the configuration variables for controlling how qdoc produces documentation...
const Location & location() const
Returns the starting location of a qdoc comment.
void markAutoGenerated()
Marks this documentation as auto-generated by QDoc.
TopicList topicsUsed() const
Returns a reference to the list of topic commands used in the current qdoc comment.
This node is used to represent any kind of function being documented.
void markDeletedAsWritten()
void setVirtualness(Virtualness virtualness)
bool isNonvirtual() const
void setInvokable(bool b)
bool isSpecialMemberFunction() const
bool isDeletedAsWritten() const
void markExplicitlyDefaulted()
void setMetaness(Metaness metaness)
bool isExplicitlyDefaulted() const
Parameters & parameters()
void setHiddenFriend(bool b)
The Location class provides a way to mark a location in a file.
This class represents a C++ namespace.
This class describes one instance of using the Q_PROPERTY macro.
This class provides exclusive access to the qdoc database, which consists of a forrest of trees and a...
NamespaceNode * primaryTreeRoot()
Returns a pointer to the root node of the primary tree.
Status
Specifies the status of the QQmlIncubator.
#define CONFIG_DOCUMENTATIONINHEADERS
bool hasTooManyTopics(const Doc &doc)
Checks if there are too many topic commands in doc.
This namespace holds QDoc-internal utility methods.
std::string getFullyQualifiedName(QualType QT, const ASTContext &Ctx, const PrintingPolicy &Policy, bool WithGlobalNsPrefix=false)
QList< Node * > NodeVector
Returns the spelling in the file for a source range.
std::variant< Node *, FnMatchError > operator()(const Location &location, const QString &fnSignature, const QString &idTag, QStringList context)
Use clang to parse the function signature from a function command.
Encapsulates information about.
The Node class is the base class for all the nodes in QDoc's parse tree.
void setAccess(Access t)
Sets the node's access type to t.
bool isNamespace() const
Returns true if the node type is Namespace.
bool isTypedef() const
Returns true if the node type is Typedef.
bool isVariable() const
Returns true if the node type is Variable.
void setLocation(const Location &t)
Sets the node's declaration location, its definition location, or both, depending on the suffix of th...
virtual bool isAggregate() const
Returns true if this node is an aggregate, which means it inherits Aggregate and can therefore have c...
virtual void setRelatedNonmember(bool b)
Sets a flag in the node indicating whether this node is a related nonmember of something.
const Location & location() const
If this node's definition location is empty, this function returns this node's declaration location.
bool isFunction(Genus g=Genus::DontCare) const
Returns true if this is a FunctionNode and its Genus is set to g.
void setDoc(const Doc &doc, bool replace=false)
Sets this Node's Doc to doc.
bool isClass() const
Returns true if the node type is Class.
bool hasDoc() const
Returns true if this node is documented, or it represents a documented node read from the index ('had...
virtual bool isClassNode() const
Returns true if this is an instance of ClassNode.
A class for parsing and managing a function parameter list.
Parameter & operator[](int index)
operator CXTranslationUnit()