22#include <QtCore/qdebug.h>
23#include <QtCore/qelapsedtimer.h>
24#include <QtCore/qfile.h>
25#include <QtCore/qregularexpression.h>
26#include <QtCore/qscopedvaluerollback.h>
27#include <QtCore/qtemporarydir.h>
28#include <QtCore/qtextstream.h>
29#include <QtCore/qvarlengtharray.h>
31#include <clang-c/Index.h>
33#include <clang/AST/Decl.h>
34#include <clang/AST/DeclFriend.h>
35#include <clang/AST/DeclTemplate.h>
36#include <clang/AST/Expr.h>
37#include <clang/AST/Type.h>
38#include <clang/AST/TypeLoc.h>
39#include <clang/Basic/SourceLocation.h>
40#include <clang/Frontend/ASTUnit.h>
41#include <clang/Lex/Lexer.h>
42#include <llvm/Support/Casting.h>
44#include "clang/AST/QualTypeNames.h"
51using namespace Qt::Literals::StringLiterals;
61 clang_disposeIndex(
index);
66 CXTranslationUnit
tu =
nullptr;
77 clang_disposeTranslationUnit(
tu);
85static CXTranslationUnit_Flags
flags_ =
static_cast<CXTranslationUnit_Flags>(0);
89#ifndef QT_NO_DEBUG_STREAM
93 QDebugStateSaver saver(debug);
96 const size_t size = v.size();
97 debug <<
"std::vector<>[" << size <<
"](";
98 for (size_t i = 0; i < size; ++i) {
110 if (!lcQdocClang().isDebugEnabled())
113 static const auto displayOptions = CXDiagnosticDisplayOptions::CXDiagnostic_DisplaySourceLocation
114 | CXDiagnosticDisplayOptions::CXDiagnostic_DisplayColumn
115 | CXDiagnosticDisplayOptions::CXDiagnostic_DisplayOption;
117 for (
unsigned i = 0, numDiagnostics = clang_getNumDiagnostics(translationUnit); i < numDiagnostics; ++i) {
118 auto diagnostic = clang_getDiagnostic(translationUnit, i);
119 auto formattedDiagnostic = clang_formatDiagnostic(diagnostic, displayOptions);
120 qCDebug(lcQdocClang) << clang_getCString(formattedDiagnostic);
121 clang_disposeString(formattedDiagnostic);
122 clang_disposeDiagnostic(diagnostic);
127
128
129
130
131
132
133
134
135
136
137
138
140 assert(clang_isDeclaration(clang_getCursorKind(cursor)));
142 return static_cast<
const clang::Decl*>(cursor.data[0]);
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
177 declaration_context.getPrintingPolicy()
182
183
184
185
187 if (!typeName.contains(
"(unnamed "_L1) && !typeName.contains(
"(anonymous "_L1)) {
192 static const QRegularExpression pattern(
193 R"(\((unnamed|anonymous) (struct|union|class) at [^)]+\))"
195 QString cleaned = typeName;
196 cleaned.replace(pattern,
"(\\1 \\2)"_L1);
201
202
203
204
205
206
207
208
209
211 QString default_value = QString::fromStdString(clang::Lexer::getSourceText(
212 clang::CharSourceRange::getTokenRange(expression->getSourceRange()),
213 declaration_context.getSourceManager(),
214 declaration_context.getLangOpts()
217 if (default_value.startsWith(
"="))
218 default_value.remove(0, 1);
220 default_value = default_value.trimmed();
222 return default_value.toStdString();
226
227
228
229
230
231
232
233
235#if LIBCLANG_VERSION_MAJOR >= 19
236 return (parameter && parameter->hasDefaultArgument()) ?
237 get_fully_qualified_type_name(parameter->getDefaultArgument().getArgument().getAsType(), parameter->getASTContext()) :
240 return (parameter && parameter->hasDefaultArgument()) ?
241 get_fully_qualified_type_name(parameter->getDefaultArgument(), parameter->getASTContext()) :
248
249
250
251
252
253
254
255
257#if LIBCLANG_VERSION_MAJOR >= 19
258 return (parameter && parameter->hasDefaultArgument()) ?
259 get_expression_as_string(parameter->getDefaultArgument().getSourceExpression(), parameter->getASTContext()) :
"";
261 return (parameter && parameter->hasDefaultArgument()) ?
262 get_expression_as_string(parameter->getDefaultArgument(), parameter->getASTContext()) :
"";
268
269
270
271
272
273
274
275
277 std::string default_value{};
279 if (parameter && parameter->hasDefaultArgument()) {
280 const clang::TemplateName template_name = parameter->getDefaultArgument().getArgument().getAsTemplate();
282 llvm::raw_string_ostream ss{default_value};
283 template_name.print(ss, parameter->getASTContext().getPrintingPolicy(),
clang::TemplateName::Qualified::AsWritten);
286 return default_value;
290
291
292
293
294
295
296
297
298
299
301 if (!parameter || !parameter->hasDefaultArg() || parameter->hasUnparsedDefaultArg())
304 return get_expression_as_string(
305 parameter->hasUninstantiatedDefaultArg() ? parameter->getUninstantiatedDefaultArg() : parameter->getDefaultArg(),
306 parameter->getASTContext()
311
312
313
314
315
316
317
319 if (!declaration)
return "";
321 if (
auto type_template_parameter = llvm::dyn_cast<clang::TemplateTypeParmDecl>(declaration))
322 return get_default_value_initializer_as_string(type_template_parameter);
324 if (
auto non_type_template_parameter = llvm::dyn_cast<clang::NonTypeTemplateParmDecl>(declaration))
325 return get_default_value_initializer_as_string(non_type_template_parameter);
327 if (
auto template_template_parameter = llvm::dyn_cast<clang::TemplateTemplateParmDecl>(declaration)) {
328 return get_default_value_initializer_as_string(template_template_parameter);
331 if (
auto function_parameter = llvm::dyn_cast<clang::ParmVarDecl>(declaration)) {
332 return get_default_value_initializer_as_string(function_parameter);
339
340
341
342
346 CXCursorVisitor visitor = [](CXCursor c, CXCursor,
347 CXClientData client_data) -> CXChildVisitResult {
348 return (*
static_cast<T *>(client_data))(c);
350 return clang_visitChildren(cursor, visitor, &lambda);
354
355
358 QString ret = QString::fromUtf8(clang_getCString(string));
359 clang_disposeString(string);
364
365
366
368 assert(template_declaration);
372 auto template_parameters = template_declaration->getTemplateParameters();
373 for (
auto template_parameter : template_parameters->asArray()) {
374 auto kind{RelaxedTemplateParameter::Kind::TypeTemplateParameter};
377 if (
auto non_type_template_parameter = llvm::dyn_cast<clang::NonTypeTemplateParmDecl>(template_parameter)) {
378 kind = RelaxedTemplateParameter::Kind::NonTypeTemplateParameter;
379 type = get_fully_qualified_type_name(non_type_template_parameter->getType(), non_type_template_parameter->getASTContext());
411 if (QString::fromStdString(type).startsWith(
"typename ")) type.erase(0, std::string(
"typename ").size());
414 auto template_template_parameter = llvm::dyn_cast<clang::TemplateTemplateParmDecl>(template_parameter);
415 if (template_template_parameter) kind = RelaxedTemplateParameter::Kind::TemplateTemplateParameter;
417 template_declaration_ir.parameters.push_back({
419 template_parameter->isTemplateParameterPack(),
422 template_parameter->getNameAsString(),
423 get_default_value_initializer_as_string(template_parameter)
425 (template_template_parameter ?
426 std::optional<TemplateDeclarationStorage>(TemplateDeclarationStorage{
427 get_template_declaration(template_template_parameter).parameters
432 return template_declaration_ir;
436
437
440 unsigned int line, column;
442 clang_getPresumedLocation(location, &file, &line, &column);
450
451
457 case CX_CXXProtected:
467
468
477 unsigned int offset1,
unsigned int offset2)
479 return QString::fromUtf8(cache.mid(offset1, offset2 - offset1));
482static QString
readFile(CXFile cxFile,
unsigned int offset1,
unsigned int offset2)
484 using FileCache = QList<FileCacheEntry>;
485 static FileCache cache;
487 CXString cxFileName = clang_getFileName(cxFile);
488 const QByteArray fileName = clang_getCString(cxFileName);
489 clang_disposeString(cxFileName);
491 for (
const auto &entry : std::as_const(cache)) {
492 if (fileName == entry.fileName)
493 return fromCache(entry.content, offset1, offset2);
496 QFile file(QString::fromUtf8(fileName));
497 if (file.open(QIODeviceBase::ReadOnly)) {
499 cache.prepend(entry);
500 while (cache.size() > 5)
502 return fromCache(entry.content, offset1, offset2);
509 auto start = clang_getRangeStart(range);
510 auto end = clang_getRangeEnd(range);
512 unsigned int offset1, offset2;
513 clang_getFileLocation(start, &file1,
nullptr,
nullptr, &offset1);
514 clang_getFileLocation(end, &file2,
nullptr,
nullptr, &offset2);
516 if (file1 != file2 || offset2 <= offset1)
519 return readFile(file1, offset1, offset2);
523
524
525
526
529 if (clang_getCursorKind(cursor) == CXCursor_ConversionFunction) {
533 auto conversion_declaration =
536 return QLatin1String(
"operator ") + QString::fromStdString(get_fully_qualified_type_name(
537 conversion_declaration->getConversionType(),
538 conversion_declaration->getASTContext()
542 QString name = fromCXString(clang_getCursorSpelling(cursor));
545 auto ltLoc = name.indexOf(
'<');
546 if (ltLoc > 0 && !name.startsWith(
"operator<"))
547 name = name.left(ltLoc);
552
553
554
558 auto kind = clang_getCursorKind(cur);
559 while (!clang_isInvalid(kind) && kind != CXCursor_TranslationUnit) {
561 case CXCursor_Namespace:
562 case CXCursor_StructDecl:
563 case CXCursor_ClassDecl:
564 case CXCursor_UnionDecl:
565 case CXCursor_ClassTemplate:
567 path.prepend(fromCXString(clang_getCursorSpelling(cur)));
569 case CXCursor_FunctionDecl:
570 case CXCursor_FunctionTemplate:
571 case CXCursor_CXXMethod:
572 case CXCursor_Constructor:
573 case CXCursor_Destructor:
574 case CXCursor_ConversionFunction:
575 path = functionName(cur);
580 cur = clang_getCursorSemanticParent(cur);
581 kind = clang_getCursorKind(cur);
587
588
589
592 auto kind = clang_getCursorKind(cur);
593 if (clang_isInvalid(kind))
595 if (kind == CXCursor_TranslationUnit)
603 case CXCursor_TemplateTypeParameter:
604 case CXCursor_NonTypeTemplateParameter:
605 case CXCursor_TemplateTemplateParameter:
615 auto parent =
static_cast<
Aggregate *>(p);
618 if (clang_Cursor_isAnonymous(cur)) {
621 QLatin1String(
"anonymous"));
623 name = fromCXString(clang_getCursorSpelling(cur));
626 case CXCursor_Namespace:
628 case CXCursor_StructDecl:
629 case CXCursor_ClassDecl:
630 case CXCursor_UnionDecl:
631 case CXCursor_ClassTemplate:
633 case CXCursor_FunctionDecl:
634 case CXCursor_FunctionTemplate:
635 case CXCursor_CXXMethod:
636 case CXCursor_Constructor:
637 case CXCursor_Destructor:
638 case CXCursor_ConversionFunction: {
640 parent->findChildren(functionName(cur), candidates);
644 lexical_parent && lexical_parent
->isAggregate() && lexical_parent != parent) {
645 static_cast<Aggregate *>(lexical_parent)->findChildren(functionName(cur), candidates);
649 if (candidates.isEmpty())
652 CXType funcType = clang_getCursorType(cur);
653 auto numArg = clang_getNumArgTypes(funcType);
654 bool isVariadic = clang_isFunctionTypeVariadic(funcType);
655 QVarLengthArray<QString, 20> args;
658 if (kind == CXCursor_FunctionTemplate)
659 relaxed_template_declaration = get_template_declaration(
663 for (Node *candidate : std::as_const(candidates)) {
664 if (!candidate->isFunction(Genus::CPP))
667 auto fn =
static_cast<FunctionNode *>(candidate);
669 if (!fn->templateDecl() && relaxed_template_declaration)
672 if (fn->templateDecl() && !relaxed_template_declaration)
675 if (fn->templateDecl() && relaxed_template_declaration &&
676 !are_template_declarations_substitutable(*fn->templateDecl(), *relaxed_template_declaration))
679 const Parameters ¶meters = fn->parameters();
681 if (parameters.count() != numArg + isVariadic) {
683 if (numArg > 0 && parameters.isPrivateSignal() &&
684 (parameters.isEmpty() || !parameters.last().type().endsWith(
685 QLatin1String(
"QPrivateSignal")))) {
686 if (parameters.count() != --numArg + isVariadic)
693 if (fn->isConst() !=
bool(clang_CXXMethod_isConst(cur)))
696 if (isVariadic && parameters.last().type() != QLatin1String(
"..."))
699 if (fn->isRef() != (clang_Type_getCXXRefQualifier(funcType) == CXRefQualifier_LValue))
702 if (fn->isRefRef() != (clang_Type_getCXXRefQualifier(funcType) == CXRefQualifier_RValue))
705 auto function_declaration = get_cursor_declaration(cur)->getAsFunction();
707 bool different =
false;
708 for (
int i = 0; i < numArg; ++i) {
709 CXType argType = clang_getArgType(funcType, i);
711 if (args.size() <= i)
712 args.append(QString::fromStdString(get_fully_qualified_type_name(
713 function_declaration->getParamDecl(i)->getOriginalType(),
714 function_declaration->getASTContext()
717 QString recordedType = parameters.at(i).type();
718 QString typeSpelling = args.at(i);
720 different = recordedType != typeSpelling;
723 if (different && (argType.kind == CXType_Typedef || argType.kind == CXType_Elaborated)) {
724 QStringView canonicalType = parameters.at(i).canonicalType();
725 if (!canonicalType.isEmpty()) {
726 different = canonicalType !=
727 QString::fromStdString(get_fully_qualified_type_name(
728 function_declaration->getParamDecl(i)->getOriginalType().getCanonicalType(),
729 function_declaration->getASTContext()
744 case CXCursor_EnumDecl:
745 return parent->findNonfunctionChild(name, &
Node::isEnumType);
746 case CXCursor_FieldDecl:
747 case CXCursor_VarDecl:
749 case CXCursor_TypedefDecl:
758 CXCursor *overridden;
759 unsigned int numOverridden = 0;
760 clang_getOverriddenCursors(cursor, &overridden, &numOverridden);
761 for (uint i = 0; i < numOverridden; ++i) {
762 QString path = reconstructQualifiedPathForCursor(overridden[i]);
763 if (!path.isEmpty()) {
765 fn->setOverridesThis(path);
769 clang_disposeOverriddenCursors(overridden);
778 std::transform(allHeaders.cbegin(), allHeaders.cend(), std::inserter(allHeaders_, allHeaders_.begin()),
779 [](
const auto& header_file_path) ->
const QString& {
return header_file_path.filename; });
786 auto ret = visitChildrenLambda(cursor, [&](CXCursor cur) {
787 auto loc = clang_getCursorLocation(cur);
788 if (clang_Location_isFromMainFile(loc))
789 return visitSource(cur, loc);
792 clang_getFileLocation(loc, &file,
nullptr,
nullptr,
nullptr);
793 bool isInteresting =
false;
794 auto it = isInterestingCache_.find(file);
795 if (it != isInterestingCache_.end()) {
798 QFileInfo fi(fromCXString(clang_getFileName(file)));
800 isInteresting = allHeaders_.find(fi.fileName()) != allHeaders_.end();
801 isInterestingCache_[file] = isInteresting;
804 return visitHeader(cur, loc);
807 return CXChildVisit_Continue;
809 return ret ? CXChildVisit_Break : CXChildVisit_Continue;
813
814
815
816 CXChildVisitResult
visitFnArg(CXCursor cursor,
Node **fnNode,
bool &ignoreSignature)
818 auto ret = visitChildrenLambda(cursor, [&](CXCursor cur) {
819 auto loc = clang_getCursorLocation(cur);
820 if (clang_Location_isFromMainFile(loc))
821 return visitFnSignature(cur, loc, fnNode, ignoreSignature);
822 return CXChildVisit_Continue;
824 return ret ? CXChildVisit_Break : CXChildVisit_Continue;
830 bool detectQmlSingleton(CXCursor cursor);
832
833
834
837 unsigned int line {}, column {};
838 friend bool operator<(
const SimpleLoc &a,
const SimpleLoc &b)
840 return a.line != b.line ? a.line < b.line : a.column < b.column;
844
845
846
847
848 QMap<SimpleLoc, CXCursor> declMap_;
852 std::set<QString> allHeaders_;
853 QHash<CXFile,
bool> isInterestingCache_;
856
857
858 bool ignoredSymbol(
const QString &symbolName)
860 if (symbolName == QLatin1String(
"QPrivateSignal"))
863 if (symbolName.startsWith(
"_qt_property_"))
866 if (symbolName.startsWith(
"<deduction guide"))
871 CXChildVisitResult visitSource(CXCursor cursor, CXSourceLocation loc);
872 CXChildVisitResult visitHeader(CXCursor cursor, CXSourceLocation loc);
873 CXChildVisitResult visitFnSignature(CXCursor cursor, CXSourceLocation loc,
Node **fnNode,
874 bool &ignoreSignature);
875 void processFunction(
FunctionNode *fn, CXCursor cursor);
876 bool parseProperty(
const QString &spelling,
const Location &loc);
877 void readParameterNamesAndAttributes(
FunctionNode *fn, CXCursor cursor);
878 Aggregate *getSemanticParent(CXCursor cursor);
882
883
884
885
886
887
888
889
890
891
892
895 bool hasSingletonMacro =
false;
897 visitChildrenLambda(cursor, [&hasSingletonMacro](CXCursor child) -> CXChildVisitResult {
899 if (clang_getCursorKind(child) == CXCursor_CallExpr) {
900 CXSourceRange range = clang_getCursorExtent(child);
901 QString sourceText = getSpelling(range);
903 static const QRegularExpression qmlSingletonPattern(
904 R"(Q_CLASSINFO\s*\(\s*["\']QML\.Singleton["\']\s*,\s*["\']true["\']\s*\))");
905 if (qmlSingletonPattern.match(sourceText).hasMatch()) {
906 hasSingletonMacro =
true;
907 return CXChildVisit_Break;
912 if (clang_getCursorKind(child) == CXCursor_EnumDecl) {
913 QString spelling = fromCXString(clang_getCursorSpelling(child));
914 if (spelling ==
"QmlIsSingleton"_L1) {
915 hasSingletonMacro =
true;
916 return CXChildVisit_Break;
920 return CXChildVisit_Continue;
923 return hasSingletonMacro;
927
928
929
930CXChildVisitResult
ClangVisitor::visitSource(CXCursor cursor, CXSourceLocation loc)
932 auto kind = clang_getCursorKind(cursor);
933 if (clang_isDeclaration(kind)) {
935 clang_getPresumedLocation(loc,
nullptr, &l.line, &l.column);
936 declMap_.insert(l, cursor);
937 return CXChildVisit_Recurse;
939 return CXChildVisit_Continue;
943
944
945
946
949 CXCursor sp = clang_getCursorSemanticParent(cursor);
950 CXCursor lp = clang_getCursorLexicalParent(cursor);
951 if (!clang_equalCursors(sp, lp) && clang_isDeclaration(clang_getCursorKind(sp))) {
960CXChildVisitResult
ClangVisitor::visitFnSignature(CXCursor cursor, CXSourceLocation,
Node **fnNode,
961 bool &ignoreSignature)
963 switch (clang_getCursorKind(cursor)) {
964 case CXCursor_Namespace:
965 return CXChildVisit_Recurse;
966 case CXCursor_FunctionDecl:
967 case CXCursor_FunctionTemplate:
968 case CXCursor_CXXMethod:
969 case CXCursor_Constructor:
970 case CXCursor_Destructor:
971 case CXCursor_ConversionFunction: {
972 ignoreSignature =
false;
973 if (ignoredSymbol(functionName(cursor))) {
975 ignoreSignature =
true;
981 readParameterNamesAndAttributes(fn, cursor);
985 if (
const auto function_declaration = declaration->getAsFunction()) {
986 auto declaredReturnType = function_declaration->getDeclaredReturnType();
987 if (llvm::dyn_cast_if_present<clang::AutoType>(declaredReturnType.getTypePtrOrNull()))
988 fn->setDeclaredReturnType(QString::fromStdString(declaredReturnType.getAsString()));
992 QString name = functionName(cursor);
993 if (ignoredSymbol(name))
994 return CXChildVisit_Continue;
995 Aggregate *semanticParent = getSemanticParent(cursor);
996 if (semanticParent && semanticParent
->isClass()) {
998 processFunction(candidate, cursor);
999 if (!candidate->isSpecialMemberFunction()) {
1001 return CXChildVisit_Continue;
1003 candidate->setDefault(
true);
1013 return CXChildVisit_Continue;
1016CXChildVisitResult
ClangVisitor::visitHeader(CXCursor cursor, CXSourceLocation loc)
1018 auto kind = clang_getCursorKind(cursor);
1021 case CXCursor_TypeAliasTemplateDecl:
1022 case CXCursor_TypeAliasDecl: {
1023 const QString aliasName = fromCXString(clang_getCursorSpelling(cursor));
1024 QString aliasedType;
1026 const auto *templateDecl = (kind == CXCursor_TypeAliasTemplateDecl)
1030 if (kind == CXCursor_TypeAliasTemplateDecl) {
1032 if (
const auto *aliasTemplate = llvm::dyn_cast<clang::TypeAliasTemplateDecl>(templateDecl)) {
1033 if (
const auto *aliasDecl = aliasTemplate->getTemplatedDecl()) {
1034 clang::QualType underlyingType = aliasDecl->getUnderlyingType();
1035 aliasedType = QString::fromStdString(underlyingType.getAsString());
1040 const CXType aliasedCXType = clang_getTypedefDeclUnderlyingType(cursor);
1041 if (aliasedCXType.kind != CXType_Invalid) {
1042 aliasedType = fromCXString(clang_getTypeSpelling(aliasedCXType));
1046 if (!aliasedType.isEmpty()) {
1047 auto *ta =
new TypeAliasNode(parent_, aliasName, aliasedType);
1052 ta->setTemplateDecl(get_template_declaration(templateDecl));
1054 return CXChildVisit_Continue;
1056 case CXCursor_StructDecl:
1057 case CXCursor_UnionDecl:
1058 if (fromCXString(clang_getCursorSpelling(cursor)).isEmpty())
1059 return CXChildVisit_Continue;
1061 case CXCursor_ClassTemplate:
1063 case CXCursor_ClassDecl: {
1064 if (!clang_isCursorDefinition(cursor))
1065 return CXChildVisit_Continue;
1068 return CXChildVisit_Continue;
1070 QString className = cleanAnonymousTypeName(fromCXString(clang_getCursorSpelling(cursor)));
1072 Aggregate *semanticParent = getSemanticParent(cursor);
1073 if (semanticParent && semanticParent->findNonfunctionChild(className, &
Node::isClassNode)) {
1074 return CXChildVisit_Continue;
1077 CXCursorKind actualKind = (kind == CXCursor_ClassTemplate) ?
1078 clang_getTemplateCursorKind(cursor) : kind;
1081 if (actualKind == CXCursor_StructDecl)
1083 else if (actualKind == CXCursor_UnionDecl)
1086 auto *classe =
new ClassNode(type, semanticParent, className);
1089 classe->setAnonymous(clang_Cursor_isAnonymous(cursor));
1091 if (detectQmlSingleton(cursor)) {
1092 classe->setQmlSingleton(
true);
1095 if (kind == CXCursor_ClassTemplate) {
1097 classe->setTemplateDecl(get_template_declaration(template_declaration));
1100 QScopedValueRollback<Aggregate *> setParent(parent_, classe);
1103 case CXCursor_CXXBaseSpecifier: {
1105 return CXChildVisit_Continue;
1107 auto type = clang_getCursorType(cursor);
1108 auto baseCursor = clang_getTypeDeclaration(type);
1110 auto classe =
static_cast<
ClassNode *>(parent_);
1112 QString bcName = reconstructQualifiedPathForCursor(baseCursor);
1113 classe->addUnresolvedBaseClass(access,
1114 bcName.split(QLatin1String(
"::"), Qt::SkipEmptyParts));
1115 return CXChildVisit_Continue;
1117 auto baseClasse =
static_cast<
ClassNode *>(baseNode);
1119 return CXChildVisit_Continue;
1121 case CXCursor_Namespace: {
1122 QString namespaceName = fromCXString(clang_getCursorDisplayName(cursor));
1132 QScopedValueRollback<Aggregate *> setParent(parent_, ns);
1135 case CXCursor_FunctionTemplate:
1137 case CXCursor_FunctionDecl:
1138 case CXCursor_CXXMethod:
1139 case CXCursor_Constructor:
1140 case CXCursor_Destructor:
1141 case CXCursor_ConversionFunction: {
1143 return CXChildVisit_Continue;
1144 QString name = functionName(cursor);
1145 if (ignoredSymbol(name))
1146 return CXChildVisit_Continue;
1149 return CXChildVisit_Continue;
1152 CXSourceRange range = clang_Cursor_getCommentRange(cursor);
1153 if (!clang_Range_isNull(range)) {
1154 QString comment = getSpelling(range);
1155 if (comment.startsWith(
"//!")) {
1156 qsizetype tag = comment.indexOf(QChar(
'['));
1158 qsizetype end = comment.indexOf(QChar(
']'), ++tag);
1160 fn->setTag(comment.mid(tag, end - tag));
1165 processFunction(fn, cursor);
1167 if (kind == CXCursor_FunctionTemplate) {
1169 fn->setTemplateDecl(get_template_declaration(template_declaration));
1172 return CXChildVisit_Continue;
1174#if CINDEX_VERSION
>= 36
1175 case CXCursor_FriendDecl: {
1179 case CXCursor_EnumDecl: {
1181 if (en && en->items().size())
1182 return CXChildVisit_Continue;
1184 QString enumTypeName = fromCXString(clang_getCursorSpelling(cursor));
1186 if (clang_Cursor_isAnonymous(cursor)) {
1187 enumTypeName =
"anonymous";
1193 Node *n = parent_->findNonfunctionChild(enumTypeName, &
Node::isEnumType);
1199 en =
new EnumNode(parent_, enumTypeName, clang_EnumDecl_isScoped(cursor));
1206 visitChildrenLambda(cursor, [&](CXCursor cur) {
1207 if (clang_getCursorKind(cur) != CXCursor_EnumConstantDecl)
1208 return CXChildVisit_Continue;
1211 visitChildrenLambda(cur, [&](CXCursor cur) {
1212 if (clang_isExpression(clang_getCursorKind(cur))) {
1213 value = getSpelling(clang_getCursorExtent(cur));
1214 return CXChildVisit_Break;
1216 return CXChildVisit_Continue;
1218 if (value.isEmpty()) {
1219 QLatin1String hex(
"0x");
1220 if (!en->items().isEmpty() && en->items().last().value().startsWith(hex)) {
1221 value = hex + QString::number(clang_getEnumConstantDeclValue(cur), 16);
1223 value = QString::number(clang_getEnumConstantDeclValue(cur));
1227 en->addItem(EnumItem(fromCXString(clang_getCursorSpelling(cur)), std::move(value)));
1228 return CXChildVisit_Continue;
1230 return CXChildVisit_Continue;
1232 case CXCursor_FieldDecl:
1233 case CXCursor_VarDecl: {
1235 return CXChildVisit_Continue;
1237 auto value_declaration =
1239 assert(value_declaration);
1242 auto var =
new VariableNode(parent_, fromCXString(clang_getCursorSpelling(cursor)));
1244 var->setAccess(access);
1246 var->setLeftType(cleanAnonymousTypeName(QString::fromStdString(get_fully_qualified_type_name(
1247 value_declaration->getType(),
1248 value_declaration->getASTContext()
1250 var->setStatic(kind == CXCursor_VarDecl && parent_
->isClassNode());
1252 return CXChildVisit_Continue;
1254 case CXCursor_TypedefDecl: {
1256 return CXChildVisit_Continue;
1257 auto *td =
new TypedefNode(parent_, fromCXString(clang_getCursorSpelling(cursor)));
1261 visitChildrenLambda(cursor, [&](CXCursor cur) {
1262 if (clang_getCursorKind(cur) != CXCursor_TemplateRef
1263 || fromCXString(clang_getCursorSpelling(cur)) != QLatin1String(
"QFlags"))
1264 return CXChildVisit_Continue;
1266 visitChildrenLambda(cursor, [&](CXCursor cur) {
1267 if (clang_getCursorKind(cur) != CXCursor_TypeRef)
1268 return CXChildVisit_Continue;
1273 return CXChildVisit_Break;
1275 return CXChildVisit_Break;
1277 return CXChildVisit_Continue;
1283 parseProperty(getSpelling(clang_getCursorExtent(cursor)),
1284 fromCXSourceLocation(loc));
1286 return CXChildVisit_Continue;
1295 visitChildrenLambda(cursor, [&](CXCursor cur) {
1296 auto kind = clang_getCursorKind(cur);
1297 if (kind == CXCursor_AnnotateAttr) {
1298 QString annotation = fromCXString(clang_getCursorDisplayName(cur));
1299 if (annotation == QLatin1String(
"qt_slot")) {
1301 }
else if (annotation == QLatin1String(
"qt_signal")) {
1304 if (annotation == QLatin1String(
"qt_invokable"))
1306 }
else if (kind == CXCursor_CXXOverrideAttr) {
1308 }
else if (kind == CXCursor_ParmDecl) {
1310 return CXChildVisit_Break;
1312 if (QString name = fromCXString(clang_getCursorSpelling(cur)); !name.isEmpty())
1313 parameters
[i
].setName(name);
1316 Q_ASSERT(parameter_declaration);
1320 if (!default_value.empty())
1321 parameters[i].setDefaultValue(QString::fromStdString(default_value));
1325 return CXChildVisit_Continue;
1331 CXCursorKind kind = clang_getCursorKind(cursor);
1332 CXType funcType = clang_getCursorType(cursor);
1339 : clang_CXXMethod_isPureVirtual(cursor)
1356 const clang::FunctionDecl* function_declaration = declaration->getAsFunction();
1358 if (kind == CXCursor_Constructor
1360 || (kind == CXCursor_FunctionTemplate && fn->name() == parent_->name()))
1362 else if (kind == CXCursor_Destructor)
1365 fn->setReturnType(cleanAnonymousTypeName(QString::fromStdString(get_fully_qualified_type_name(
1366 function_declaration->getReturnType(),
1367 function_declaration->getASTContext()
1370 const clang::CXXConstructorDecl* constructor_declaration = llvm::dyn_cast<
const clang::CXXConstructorDecl>(function_declaration);
1375 const clang::CXXConversionDecl* conversion_declaration = llvm::dyn_cast<
const clang::CXXConversionDecl>(function_declaration);
1379 (constructor_declaration && constructor_declaration->isExplicit()) ||
1380 (conversion_declaration && conversion_declaration->isExplicit())
1383 const clang::CXXMethodDecl* method_declaration = llvm::dyn_cast<
const clang::CXXMethodDecl>(function_declaration);
1388 const clang::FunctionType* function_type = function_declaration->getFunctionType();
1389 const clang::FunctionProtoType* function_prototype =
static_cast<
const clang::FunctionProtoType*>(function_type);
1391 if (function_prototype) {
1392 clang::FunctionProtoType::ExceptionSpecInfo exception_specification = function_prototype->getExceptionSpecInfo();
1394 if (exception_specification.Type !=
clang::ExceptionSpecificationType::EST_None) {
1395 const std::string exception_specification_spelling =
1396 exception_specification.NoexceptExpr ? get_expression_as_string(
1397 exception_specification.NoexceptExpr,
1398 function_declaration->getASTContext()
1401 if (exception_specification_spelling !=
"false")
1402 fn->markNoexcept(QString::fromStdString(exception_specification_spelling));
1406 CXRefQualifierKind refQualKind = clang_Type_getCXXRefQualifier(funcType);
1407 if (refQualKind == CXRefQualifier_LValue)
1409 else if (refQualKind == CXRefQualifier_RValue)
1418 parameters
.reserve(function_declaration->getNumParams()
);
1420 for (clang::ParmVarDecl*
const parameter_declaration : function_declaration->parameters()) {
1421 clang::QualType parameter_type = parameter_declaration->getOriginalType();
1423 parameters.append(cleanAnonymousTypeName(QString::fromStdString(get_fully_qualified_type_name(
1425 parameter_declaration->getASTContext()
1428 if (!parameter_type.isCanonical())
1429 parameters.last().setCanonicalType(cleanAnonymousTypeName(QString::fromStdString(get_fully_qualified_type_name(
1430 parameter_type.getCanonicalType(),
1431 parameter_declaration->getASTContext()
1436 if (parameters
.last().type().endsWith(QLatin1String(
"QPrivateSignal"))) {
1442 if (clang_isFunctionTypeVariadic(funcType))
1443 parameters.append(QStringLiteral(
"..."));
1444 readParameterNamesAndAttributes(fn, cursor);
1446 if (declaration->getFriendObjectKind() !=
clang::Decl::FOK_None)
1452 if (!spelling.startsWith(QLatin1String(
"Q_PROPERTY"))
1453 && !spelling.startsWith(QLatin1String(
"QDOC_PROPERTY"))
1454 && !spelling.startsWith(QLatin1String(
"Q_OVERRIDE")))
1457 qsizetype lpIdx = spelling.indexOf(QChar(
'('));
1458 qsizetype rpIdx = spelling.lastIndexOf(QChar(
')'));
1459 if (lpIdx <= 0 || rpIdx <= lpIdx)
1462 QString signature = spelling.mid(lpIdx + 1, rpIdx - lpIdx - 1);
1463 signature = signature.simplified();
1464 QStringList parts = signature.split(QChar(
' '), Qt::SkipEmptyParts);
1466 static const QStringList attrs =
1467 QStringList() <<
"READ" <<
"MEMBER" <<
"WRITE"
1468 <<
"NOTIFY" <<
"CONSTANT" <<
"FINAL"
1469 <<
"REQUIRED" <<
"BINDABLE" <<
"DESIGNABLE"
1470 <<
"RESET" <<
"REVISION" <<
"SCRIPTABLE"
1471 <<
"STORED" <<
"USER";
1475 auto it =
std::find_if(parts.cbegin(), parts.cend(),
1476 [](
const QString &attr) ->
bool {
1477 return attrs.contains(attr);
1480 if (it == parts.cend() ||
std::distance(parts.cbegin(), it) < 2)
1483 QStringList typeParts;
1484 std::copy(parts.cbegin(), it,
std::back_inserter(typeParts));
1485 parts.erase(parts.cbegin(), it);
1486 QString name = typeParts.takeLast();
1489 while (!name.isEmpty() && name.front() == QChar(
'*')) {
1490 typeParts.last().push_back(name.front());
1495 if (parts.size() < 2 || name.isEmpty())
1500 property->setLocation(loc);
1501 property->setDataType(typeParts.join(QChar(
' ')));
1504 while (i < parts.size()) {
1505 const QString &key = parts.at(i++);
1507 if (key ==
"CONSTANT") {
1508 property->setConstant();
1509 }
else if (key ==
"REQUIRED") {
1510 property->setRequired();
1512 if (i < parts.size()) {
1513 QString value = parts.at(i++);
1514 if (key ==
"READ") {
1516 }
else if (key ==
"WRITE") {
1518 property->setWritable(
true);
1519 }
else if (key ==
"MEMBER") {
1520 property->setWritable(
true);
1521 }
else if (key ==
"STORED") {
1522 property->setStored(value.toLower() ==
"true");
1523 }
else if (key ==
"BINDABLE") {
1526 }
else if (key ==
"RESET") {
1528 }
else if (key ==
"NOTIFY") {
1537
1538
1539
1540
1541
1545 clang_getPresumedLocation(loc,
nullptr, &docloc.line, &docloc.column);
1546 auto decl_it = declMap_.upperBound(docloc);
1547 if (decl_it == declMap_.end())
1550 unsigned int declLine = decl_it.key().line;
1551 unsigned int nextCommentLine;
1552 clang_getPresumedLocation(nextCommentLoc,
nullptr, &nextCommentLine,
nullptr);
1553 if (nextCommentLine < declLine)
1557 if (decl_it != declMap_.begin()) {
1558 CXSourceLocation prevDeclEnd = clang_getRangeEnd(clang_getCursorExtent(*(
std::prev(decl_it))));
1559 unsigned int prevDeclLine;
1560 clang_getPresumedLocation(prevDeclEnd,
nullptr, &prevDeclLine,
nullptr);
1561 if (prevDeclLine >= docloc.line) {
1564 auto parent = clang_getCursorLexicalParent(*decl_it);
1565 if (!clang_equalCursors(parent, *(
std::prev(decl_it))))
1569 auto *node = findNodeForCursor(qdb_, *decl_it);
1571 if (node && node->isFunction(Genus::CPP))
1572 readParameterNamesAndAttributes(
static_cast<
FunctionNode *>(node), *decl_it);
1579 const std::vector<QByteArray>& include_paths,
1580 const QList<QByteArray>& defines,
1587 m_allHeaders = config.getHeaderFiles();
1592
1593
1594
1595
1596
1597#if LIBCLANG_VERSION_MAJOR == 15
1605 "-fms-compatibility-version=19",
1609 "-DQT_DISABLE_DEPRECATED_UP_TO=0",
1610 "-DQT_ANNOTATE_CLASS(type,...)=static_assert(sizeof(#__VA_ARGS__),#type);",
1611 "-DQT_ANNOTATE_CLASS2(type,a1,a2)=static_assert(sizeof(#a1,#a2),#type);",
1612 "-DQT_ANNOTATE_FUNCTION(a)=__attribute__((annotate(#a)))",
1613 "-DQT_ANNOTATE_ACCESS_SPECIFIER(a)=__attribute__((annotate(#a)))",
1614 "-Wno-constant-logical-operand",
1615 "-Wno-macro-redefined",
1616 "-Wno-nullability-completeness",
1617 "-fvisibility=default",
1623
1624
1625
1632 for (
const auto &p : std::as_const(defines))
1633 args.push_back(p.constData());
1638 QList<QByteArray> result;
1639 for (
const auto& [header_path, _] : allHeaders) {
1640 const QByteArray path =
"-I" + header_path.toLatin1();
1641 const QByteArray parent =
1642 "-I" + QDir::cleanPath(header_path + QLatin1String(
"/../")).toLatin1();
1649
1650
1651
1653 const std::vector<QByteArray>& include_paths,
1654 const std::set<Config::HeaderFilePath>& all_headers,
1655 std::vector<
const char*>& args
1657 if (include_paths.empty()) {
1659
1660
1661
1662
1663 qCWarning(lcQdoc) <<
"No include paths passed to qdoc; guessing reasonable include paths";
1665 QString basicIncludeDir = QDir::cleanPath(QString(Config::installDir +
"/../include"));
1666 args.emplace_back(QByteArray(
"-I" + basicIncludeDir.toLatin1()).constData());
1668 auto include_paths_from_headers = includePathsFromHeaders(all_headers);
1669 args.insert(args.end(), include_paths_from_headers.begin(), include_paths_from_headers.end());
1671 std::copy(include_paths.begin(), include_paths.end(),
std::back_inserter(args));
1676
1677
1678
1679
1682 QString module_header,
1683 const std::set<Config::HeaderFilePath>& all_headers,
1684 const std::vector<QByteArray>& include_paths,
1685 const QList<QByteArray>& defines,
1686 const InclusionPolicy& policy
1688 static std::vector<
const char*> arguments{};
1690 if (module_header.isEmpty())
return std::nullopt;
1692 getDefaultArgs(defines, arguments);
1693 getMoreArgs(include_paths, all_headers, arguments);
1695 flags_ =
static_cast<CXTranslationUnit_Flags>(CXTranslationUnit_Incomplete
1696 | CXTranslationUnit_SkipFunctionBodies
1697 | CXTranslationUnit_KeepGoing);
1701 QTemporaryDir pch_directory{QDir::tempPath() + QLatin1String(
"/qdoc_pch")};
1702 if (!pch_directory.isValid())
return std::nullopt;
1704 const QByteArray module = module_header.toUtf8();
1707 qCDebug(lcQdoc) <<
"Build and visit PCH for" << module_header;
1710 struct FindPredicate
1712 enum SearchType { Any, Module };
1713 QByteArray &candidate_;
1714 const QByteArray &module_;
1716 FindPredicate(QByteArray &candidate,
const QByteArray &module,
1717 SearchType type = Any)
1718 : candidate_(candidate), module_(module), type_(type)
1722 bool operator()(
const QByteArray &p)
const
1724 if (type_ != Any && !p.endsWith(module_))
1726 candidate_ = p +
"/";
1727 candidate_.append(module_);
1728 if (p.startsWith(
"-I"))
1729 candidate_ = candidate_.mid(2);
1730 return QFile::exists(QString::fromUtf8(candidate_));
1735 QByteArray candidate;
1736 auto it =
std::find_if(include_paths.begin(), include_paths.end(),
1737 FindPredicate(candidate, module, FindPredicate::Module));
1738 if (it == include_paths.end())
1739 it =
std::find_if(include_paths.begin(), include_paths.end(),
1740 FindPredicate(candidate, module, FindPredicate::Any));
1741 if (it != include_paths.end())
1742 header =
std::move(candidate);
1744 if (header.isEmpty()) {
1745 qWarning() <<
"(qdoc) Could not find the module header in include paths for module"
1746 << module <<
" (include paths: " << include_paths <<
")";
1747 qWarning() <<
" Artificial module header built from header dirs in qdocconf "
1750 arguments.push_back(
"-xc++");
1754 QString tmpHeader = pch_directory.path() +
"/" + module;
1755 if (QFile tmpHeaderFile(tmpHeader); tmpHeaderFile.open(QIODevice::Text | QIODevice::WriteOnly)) {
1756 QTextStream out(&tmpHeaderFile);
1757 if (header.isEmpty()) {
1758 for (
const auto& [header_path, header_name] : all_headers) {
1759 bool shouldInclude = !header_name.startsWith(
"moc_"_L1);
1762 if (header_name.endsWith(
"_p.h"_L1))
1763 shouldInclude = shouldInclude && policy.showInternal;
1765 if (shouldInclude) {
1766 out <<
"#include \"" << header_path <<
"/" << header_name <<
"\"\n";
1770 QFileInfo headerFile(header);
1771 if (!headerFile.exists()) {
1772 qWarning() <<
"Could not find module header file" << header;
1773 return std::nullopt;
1776 out <<
"#include \"" << header <<
"\"\n";
1778 if (policy.showInternal) {
1779 for (
const auto& [header_path, header_name] : all_headers) {
1780 bool shouldInclude = !header_name.startsWith(
"moc_"_L1);
1781 if (header_name.endsWith(
"_p.h"_L1) && shouldInclude)
1782 out <<
"#include \"" << header_path <<
"/" << header_name <<
"\"\n";
1789 clang_parseTranslationUnit2(index, tmpHeader.toLatin1().data(), arguments.data(),
1790 static_cast<
int>(arguments.size()),
nullptr, 0,
1791 flags_ | CXTranslationUnit_ForSerialization, &tu
.tu);
1792 qCDebug(lcQdoc) <<
__FUNCTION__ <<
"clang_parseTranslationUnit2(" << tmpHeader << arguments
1793 <<
") returns" << err;
1798 qCCritical(lcQdoc) <<
"Could not create PCH file for " << module_header;
1799 return std::nullopt;
1802 QByteArray pch_name = pch_directory.path().toUtf8() +
"/" + module +
".pch";
1803 auto error = clang_saveTranslationUnit(tu, pch_name.constData(),
1804 clang_defaultSaveOptions(tu));
1806 qCCritical(lcQdoc) <<
"Could not save PCH file for" << module_header;
1807 return std::nullopt;
1812 CXCursor cur = clang_getTranslationUnitCursor(tu);
1815 qCDebug(lcQdoc) <<
"PCH built and visited for" << module_header;
1817 return std::make_optional(
PCHFile{
std::move(pch_directory),
std::move(pch_name)});
1822 if (t.count(QChar(
'.')) > 1)
1823 t.truncate(t.lastIndexOf(QChar(
'.')));
1828
1829
1830
1831
1832
1833
1834
1835
1838 flags_ =
static_cast<CXTranslationUnit_Flags>(CXTranslationUnit_Incomplete
1839 | CXTranslationUnit_SkipFunctionBodies
1840 | CXTranslationUnit_KeepGoing);
1844 getDefaultArgs(m_defines, m_args);
1845 if (m_pch && !filePath.endsWith(
".mm")
1846 && !std::holds_alternative<CppHeaderSourceFile>(tag_source_file(filePath).second)) {
1847 m_args.push_back(
"-w");
1848 m_args.push_back(
"-include-pch");
1849 m_args.push_back((*m_pch).get().name.constData());
1851 getMoreArgs(m_includePaths, m_allHeaders, m_args);
1855 clang_parseTranslationUnit2(index, filePath.toLocal8Bit(), m_args.data(),
1856 static_cast<
int>(m_args.size()),
nullptr, 0, flags_, &tu.tu);
1857 qCDebug(lcQdoc) <<
__FUNCTION__ <<
"clang_parseTranslationUnit2(" << filePath << m_args
1858 <<
") returns" << err;
1862 qWarning() <<
"(qdoc) Could not parse source file" << filePath <<
" error code:" << err;
1866 ParsedCppFileIR parse_result{};
1868 CXCursor tuCur = clang_getTranslationUnitCursor(tu);
1873 unsigned int numTokens = 0;
1874 const QSet<QString> &commands = CppCodeParser::topic_commands + CppCodeParser::meta_commands;
1875 clang_tokenize(tu, clang_getCursorExtent(tuCur), &tokens, &numTokens);
1877 for (
unsigned int i = 0; i < numTokens; ++i) {
1878 if (clang_getTokenKind(tokens[i]) != CXToken_Comment)
1880 QString comment = fromCXString(clang_getTokenSpelling(tu, tokens[i]));
1881 if (!comment.startsWith(
"/*!"))
1884 auto commentLoc = clang_getTokenLocation(tu, tokens[i]);
1887 Doc::trimCStyleComment(loc, comment);
1890 Doc doc(loc, end_loc, comment, commands, CppCodeParser::topic_commands);
1896 if (i + 1 < numTokens) {
1898 CXSourceLocation nextCommentLoc = commentLoc;
1899 while (i + 2 < numTokens && clang_getTokenKind(tokens[i + 1]) != CXToken_Comment)
1901 nextCommentLoc = clang_getTokenLocation(tu, tokens[i + 1]);
1908 bool future =
false;
1910 QString sinceVersion = doc.metaCommandArgs(
COMMAND_SINCE).at(0).first;
1911 if (getUnpatchedVersion(std::move(sinceVersion)) >
1912 getUnpatchedVersion(Config::instance().get(
CONFIG_VERSION).asString()))
1917 QStringLiteral(
"Cannot tie this documentation to anything"),
1918 QStringLiteral(
"qdoc found a /*! ... */ comment, but there was no "
1919 "topic command (e.g., '\\%1', '\\%2') in the "
1920 "comment and no function definition following "
1928 CXCursor cur = clang_getCursor(tu, commentLoc);
1930 CXCursorKind kind = clang_getCursorKind(cur);
1931 if (clang_isTranslationUnit(kind) || clang_isInvalid(kind))
1933 if (kind == CXCursor_Namespace) {
1934 parse_result.untied.back().context << fromCXString(clang_getCursorSpelling(cur));
1936 cur = clang_getCursorLexicalParent(cur);
1941 clang_disposeTokens(tu, tokens, numTokens);
1942 m_namespaceScope.clear();
1945 return parse_result;
1949
1950
1951
1952
1953
1954
1955
1956
1957
1958
1959
1961 const QString &idTag, QStringList context)
1963 Node *fnNode =
nullptr;
1965
1966
1967
1968
1969
1970 if (!idTag.isEmpty()) {
1971 fnNode = m_qdb->findFunctionNodeForTag(idTag);
1974 QStringLiteral(
"tag \\fn [%1] not used in any include file in current module").arg(idTag));
1977
1978
1979
1980
1982 QStringList leftParenSplit = fnSignature.mid(fnSignature.indexOf(fn->name())).split(
'(');
1983 if (leftParenSplit.size() > 1) {
1984 QStringList rightParenSplit = leftParenSplit[1].split(
')');
1985 if (!rightParenSplit.empty()) {
1986 QString params = rightParenSplit[0];
1987 if (!params.isEmpty()) {
1988 QStringList commaSplit = params.split(
',');
1990 if (parameters
.count() == commaSplit.size()) {
1991 for (
int i = 0; i < parameters
.count(); ++i) {
1992 QStringList blankSplit = commaSplit[i].split(
' ', Qt::SkipEmptyParts);
1993 if (blankSplit.size() > 1) {
1994 QString pName = blankSplit.last();
1996 auto it =
std::find_if(
std::begin(pName),
std::end(pName),
1997 [](
const QChar &c) {
return c.isLetter(); });
1998 parameters
[i
].setName(
1999 pName.remove(0,
std::distance(
std::begin(pName), it)));
2009 auto flags =
static_cast<CXTranslationUnit_Flags>(CXTranslationUnit_Incomplete
2010 | CXTranslationUnit_SkipFunctionBodies
2011 | CXTranslationUnit_KeepGoing);
2015 getDefaultArgs(m_defines, m_args);
2018 m_args.push_back(
"-w");
2019 m_args.push_back(
"-include-pch");
2020 m_args.push_back((*m_pch).get().name.constData());
2025 for (
const auto &ns : std::as_const(context))
2026 s_fn.prepend(
"namespace " + ns.toUtf8() +
" {");
2027 s_fn += fnSignature.toUtf8();
2028 if (!s_fn.endsWith(
";"))
2030 s_fn.append(context.size(),
'}');
2033 CXUnsavedFile unsavedFile { dummyFileName, s_fn.constData(),
2034 static_cast<
unsigned long>(s_fn.size()) };
2035 CXErrorCode err = clang_parseTranslationUnit2(index, dummyFileName, m_args.data(),
2036 int(m_args.size()), &unsavedFile, 1, flags, &tu.tu);
2037 qCDebug(lcQdoc) <<
__FUNCTION__ <<
"clang_parseTranslationUnit2(" << dummyFileName << m_args
2038 <<
") returns" << err;
2041 location.error(QStringLiteral(
"clang could not parse \\fn %1").arg(fnSignature));
2045
2046
2047
2048
2049
2050 CXCursor cur = clang_getTranslationUnitCursor(tu);
2052 bool ignoreSignature =
false;
2056 unsigned diagnosticCount = clang_getNumDiagnostics(tu);
2057 const auto &config = Config::instance();
2058 if (diagnosticCount > 0 && (!config.preparing() || config.singleExec())) {
2059 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.
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 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)
ClangVisitor(QDocDatabase *qdb, const std::set< Config::HeaderFilePath > &allHeaders)
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)
CXChildVisitResult visitFnArg(CXCursor cursor, Node **fnNode, bool &ignoreSignature)
The ClassNode represents a C++ class.
void addResolvedBaseClass(Access access, ClassNode *node)
Adds the base class node to this class's list of base classes.
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.
TopicList topicsUsed() const
Returns a reference to the list of topic commands used in the current qdoc comment.
void setFlagsType(TypedefNode *typedefNode)
void setAnonymous(bool anonymous)
This node is used to represent any kind of function being documented.
void setVirtualness(Virtualness virtualness)
bool isNonvirtual() const
void setInvokable(bool b)
void setMetaness(Metaness metaness)
Parameters & parameters()
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.
#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 isEnumType() const
Returns true if the node type is Enum.
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.
bool isFunction(Genus g=Genus::DontCare) const
Returns true if this is a FunctionNode and its Genus is set to g.
bool isClass() const
Returns true if the node type is Class.
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()