22#include <QtCore/qdebug.h>
23#include <QtCore/qdir.h>
24#include <QtCore/qelapsedtimer.h>
25#include <QtCore/qfile.h>
26#include <QtCore/qregularexpression.h>
27#include <QtCore/qscopedvaluerollback.h>
28#include <QtCore/qtemporarydir.h>
29#include <QtCore/qtextstream.h>
30#include <QtCore/qvarlengtharray.h>
32#include <clang-c/Index.h>
34#include <clang/AST/Decl.h>
35#include <clang/AST/DeclFriend.h>
36#include <clang/AST/DeclTemplate.h>
37#include <clang/AST/Expr.h>
38#include <clang/AST/Type.h>
39#include <clang/AST/TypeLoc.h>
40#include <clang/Basic/SourceLocation.h>
41#include <clang/Frontend/ASTUnit.h>
42#include <clang/Lex/Lexer.h>
43#include <llvm/Support/Casting.h>
45#include "clang/AST/QualTypeNames.h"
52using namespace Qt::Literals::StringLiterals;
62 clang_disposeIndex(
index);
67 CXTranslationUnit
tu =
nullptr;
78 clang_disposeTranslationUnit(
tu);
86static CXTranslationUnit_Flags
flags_ =
static_cast<CXTranslationUnit_Flags>(0);
90#ifndef QT_NO_DEBUG_STREAM
94 QDebugStateSaver saver(debug);
97 const size_t size = v.size();
98 debug <<
"std::vector<>[" << size <<
"](";
99 for (size_t i = 0; i < size; ++i) {
111 if (!lcQdocClang().isDebugEnabled())
114 static const auto displayOptions = CXDiagnosticDisplayOptions::CXDiagnostic_DisplaySourceLocation
115 | CXDiagnosticDisplayOptions::CXDiagnostic_DisplayColumn
116 | CXDiagnosticDisplayOptions::CXDiagnostic_DisplayOption;
118 for (
unsigned i = 0, numDiagnostics = clang_getNumDiagnostics(translationUnit); i < numDiagnostics; ++i) {
119 auto diagnostic = clang_getDiagnostic(translationUnit, i);
120 auto formattedDiagnostic = clang_formatDiagnostic(diagnostic, displayOptions);
121 qCDebug(lcQdocClang) << clang_getCString(formattedDiagnostic);
122 clang_disposeString(formattedDiagnostic);
123 clang_disposeDiagnostic(diagnostic);
128
129
130
131
132
133
134
135
136
137
138
139
141 assert(clang_isDeclaration(clang_getCursorKind(cursor)));
143 return static_cast<
const clang::Decl*>(cursor.data[0]);
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
173
178 declaration_context.getPrintingPolicy()
183
184
185
186
188 if (!typeName.contains(
"(unnamed "_L1) && !typeName.contains(
"(anonymous "_L1)) {
193 static const QRegularExpression pattern(
194 R"(\((unnamed|anonymous) (struct|union|class) at [^)]+\))"
196 QString cleaned = typeName;
197 cleaned.replace(pattern,
"(\\1 \\2)"_L1);
202
203
204
205
206
207
208
209
210
212 QString default_value = QString::fromStdString(clang::Lexer::getSourceText(
213 clang::CharSourceRange::getTokenRange(expression->getSourceRange()),
214 declaration_context.getSourceManager(),
215 declaration_context.getLangOpts()
218 if (default_value.startsWith(
"="))
219 default_value.remove(0, 1);
221 default_value = default_value.trimmed();
223 return default_value.toStdString();
227
228
229
230
231
232
233
234
236#if LIBCLANG_VERSION_MAJOR >= 19
237 return (parameter && parameter->hasDefaultArgument()) ?
238 get_fully_qualified_type_name(parameter->getDefaultArgument().getArgument().getAsType(), parameter->getASTContext()) :
241 return (parameter && parameter->hasDefaultArgument()) ?
242 get_fully_qualified_type_name(parameter->getDefaultArgument(), parameter->getASTContext()) :
249
250
251
252
253
254
255
256
258#if LIBCLANG_VERSION_MAJOR >= 19
259 return (parameter && parameter->hasDefaultArgument()) ?
260 get_expression_as_string(parameter->getDefaultArgument().getSourceExpression(), parameter->getASTContext()) :
"";
262 return (parameter && parameter->hasDefaultArgument()) ?
263 get_expression_as_string(parameter->getDefaultArgument(), parameter->getASTContext()) :
"";
269
270
271
272
273
274
275
276
278 std::string default_value{};
280 if (parameter && parameter->hasDefaultArgument()) {
281 const clang::TemplateName template_name = parameter->getDefaultArgument().getArgument().getAsTemplate();
283 llvm::raw_string_ostream ss{default_value};
284 template_name.print(ss, parameter->getASTContext().getPrintingPolicy(),
clang::TemplateName::Qualified::AsWritten);
287 return default_value;
291
292
293
294
295
296
297
298
299
300
302 if (!parameter || !parameter->hasDefaultArg() || parameter->hasUnparsedDefaultArg())
305 return get_expression_as_string(
306 parameter->hasUninstantiatedDefaultArg() ? parameter->getUninstantiatedDefaultArg() : parameter->getDefaultArg(),
307 parameter->getASTContext()
312
313
314
315
316
317
318
320 if (!declaration)
return "";
322 if (
auto type_template_parameter = llvm::dyn_cast<clang::TemplateTypeParmDecl>(declaration))
323 return get_default_value_initializer_as_string(type_template_parameter);
325 if (
auto non_type_template_parameter = llvm::dyn_cast<clang::NonTypeTemplateParmDecl>(declaration))
326 return get_default_value_initializer_as_string(non_type_template_parameter);
328 if (
auto template_template_parameter = llvm::dyn_cast<clang::TemplateTemplateParmDecl>(declaration)) {
329 return get_default_value_initializer_as_string(template_template_parameter);
332 if (
auto function_parameter = llvm::dyn_cast<clang::ParmVarDecl>(declaration)) {
333 return get_default_value_initializer_as_string(function_parameter);
340
341
342
343
347 CXCursorVisitor visitor = [](CXCursor c, CXCursor,
348 CXClientData client_data) -> CXChildVisitResult {
349 return (*
static_cast<T *>(client_data))(c);
351 return clang_visitChildren(cursor, visitor, &lambda);
355
356
359 QString ret = QString::fromUtf8(clang_getCString(string));
360 clang_disposeString(string);
365
366
367
369 assert(template_declaration);
373 auto template_parameters = template_declaration->getTemplateParameters();
374 for (
auto template_parameter : template_parameters->asArray()) {
375 auto kind{RelaxedTemplateParameter::Kind::TypeTemplateParameter};
378 if (
auto non_type_template_parameter = llvm::dyn_cast<clang::NonTypeTemplateParmDecl>(template_parameter)) {
379 kind = RelaxedTemplateParameter::Kind::NonTypeTemplateParameter;
380 type = get_fully_qualified_type_name(non_type_template_parameter->getType(), non_type_template_parameter->getASTContext());
412 if (QString::fromStdString(type).startsWith(
"typename ")) type.erase(0, std::string(
"typename ").size());
415 auto template_template_parameter = llvm::dyn_cast<clang::TemplateTemplateParmDecl>(template_parameter);
416 if (template_template_parameter) kind = RelaxedTemplateParameter::Kind::TemplateTemplateParameter;
418 template_declaration_ir.parameters.push_back({
420 template_parameter->isTemplateParameterPack(),
423 template_parameter->getNameAsString(),
424 get_default_value_initializer_as_string(template_parameter)
426 (template_template_parameter ?
427 std::optional<TemplateDeclarationStorage>(TemplateDeclarationStorage{
428 get_template_declaration(template_template_parameter).parameters
433 return template_declaration_ir;
437
438
441 unsigned int line, column;
443 clang_getPresumedLocation(location, &file, &line, &column);
451
452
458 case CX_CXXProtected:
468
469
478 unsigned int offset1,
unsigned int offset2)
480 return QString::fromUtf8(cache.mid(offset1, offset2 - offset1));
483static QString
readFile(CXFile cxFile,
unsigned int offset1,
unsigned int offset2)
485 using FileCache = QList<FileCacheEntry>;
486 static FileCache cache;
488 CXString cxFileName = clang_getFileName(cxFile);
489 const QByteArray fileName = clang_getCString(cxFileName);
490 clang_disposeString(cxFileName);
492 for (
const auto &entry : std::as_const(cache)) {
493 if (fileName == entry.fileName)
494 return fromCache(entry.content, offset1, offset2);
497 QFile file(QString::fromUtf8(fileName));
498 if (file.open(QIODeviceBase::ReadOnly)) {
500 cache.prepend(entry);
501 while (cache.size() > 5)
503 return fromCache(entry.content, offset1, offset2);
510 auto start = clang_getRangeStart(range);
511 auto end = clang_getRangeEnd(range);
513 unsigned int offset1, offset2;
514 clang_getFileLocation(start, &file1,
nullptr,
nullptr, &offset1);
515 clang_getFileLocation(end, &file2,
nullptr,
nullptr, &offset2);
517 if (file1 != file2 || offset2 <= offset1)
520 return readFile(file1, offset1, offset2);
524
525
526
527
530 if (clang_getCursorKind(cursor) == CXCursor_ConversionFunction) {
534 auto conversion_declaration =
537 return QLatin1String(
"operator ") + QString::fromStdString(get_fully_qualified_type_name(
538 conversion_declaration->getConversionType(),
539 conversion_declaration->getASTContext()
543 QString name = fromCXString(clang_getCursorSpelling(cursor));
546 auto ltLoc = name.indexOf(
'<');
547 if (ltLoc > 0 && !name.startsWith(
"operator<"))
548 name = name.left(ltLoc);
553
554
555
559 auto kind = clang_getCursorKind(cur);
560 while (!clang_isInvalid(kind) && kind != CXCursor_TranslationUnit) {
562 case CXCursor_Namespace:
563 case CXCursor_StructDecl:
564 case CXCursor_ClassDecl:
565 case CXCursor_UnionDecl:
566 case CXCursor_ClassTemplate:
568 path.prepend(fromCXString(clang_getCursorSpelling(cur)));
570 case CXCursor_FunctionDecl:
571 case CXCursor_FunctionTemplate:
572 case CXCursor_CXXMethod:
573 case CXCursor_Constructor:
574 case CXCursor_Destructor:
575 case CXCursor_ConversionFunction:
576 path = functionName(cur);
581 cur = clang_getCursorSemanticParent(cur);
582 kind = clang_getCursorKind(cur);
588
589
590
593 auto kind = clang_getCursorKind(cur);
594 if (clang_isInvalid(kind))
596 if (kind == CXCursor_TranslationUnit)
604 case CXCursor_TemplateTypeParameter:
605 case CXCursor_NonTypeTemplateParameter:
606 case CXCursor_TemplateTemplateParameter:
616 auto parent =
static_cast<
Aggregate *>(p);
619 if (clang_Cursor_isAnonymous(cur)) {
622 QLatin1String(
"anonymous"));
624 name = fromCXString(clang_getCursorSpelling(cur));
627 case CXCursor_Namespace:
629 case CXCursor_StructDecl:
630 case CXCursor_ClassDecl:
631 case CXCursor_UnionDecl:
632 case CXCursor_ClassTemplate:
634 case CXCursor_FunctionDecl:
635 case CXCursor_FunctionTemplate:
636 case CXCursor_CXXMethod:
637 case CXCursor_Constructor:
638 case CXCursor_Destructor:
639 case CXCursor_ConversionFunction: {
641 parent->findChildren(functionName(cur), candidates);
645 lexical_parent && lexical_parent
->isAggregate() && lexical_parent != parent) {
646 static_cast<Aggregate *>(lexical_parent)->findChildren(functionName(cur), candidates);
650 if (candidates.isEmpty())
653 CXType funcType = clang_getCursorType(cur);
654 auto numArg = clang_getNumArgTypes(funcType);
655 bool isVariadic = clang_isFunctionTypeVariadic(funcType);
656 QVarLengthArray<QString, 20> args;
659 if (kind == CXCursor_FunctionTemplate)
660 relaxed_template_declaration = get_template_declaration(
664 for (Node *candidate : std::as_const(candidates)) {
665 if (!candidate->isFunction(Genus::CPP))
668 auto fn =
static_cast<FunctionNode *>(candidate);
670 if (!fn->templateDecl() && relaxed_template_declaration)
673 if (fn->templateDecl() && !relaxed_template_declaration)
676 if (fn->templateDecl() && relaxed_template_declaration &&
677 !are_template_declarations_substitutable(*fn->templateDecl(), *relaxed_template_declaration))
680 const Parameters ¶meters = fn->parameters();
682 if (parameters.count() != numArg + isVariadic) {
684 if (numArg > 0 && parameters.isPrivateSignal() &&
685 (parameters.isEmpty() || !parameters.last().type().endsWith(
686 QLatin1String(
"QPrivateSignal")))) {
687 if (parameters.count() != --numArg + isVariadic)
694 if (fn->isConst() !=
bool(clang_CXXMethod_isConst(cur)))
697 if (isVariadic && parameters.last().type() != QLatin1String(
"..."))
700 if (fn->isRef() != (clang_Type_getCXXRefQualifier(funcType) == CXRefQualifier_LValue))
703 if (fn->isRefRef() != (clang_Type_getCXXRefQualifier(funcType) == CXRefQualifier_RValue))
706 auto function_declaration = get_cursor_declaration(cur)->getAsFunction();
708 bool different =
false;
709 for (
int i = 0; i < numArg; ++i) {
710 CXType argType = clang_getArgType(funcType, i);
712 if (args.size() <= i)
713 args.append(QString::fromStdString(get_fully_qualified_type_name(
714 function_declaration->getParamDecl(i)->getOriginalType(),
715 function_declaration->getASTContext()
718 QString recordedType = parameters.at(i).type();
719 QString typeSpelling = args.at(i);
721 different = recordedType != typeSpelling;
724 if (different && (argType.kind == CXType_Typedef || argType.kind == CXType_Elaborated)) {
725 QStringView canonicalType = parameters.at(i).canonicalType();
726 if (!canonicalType.isEmpty()) {
727 different = canonicalType !=
728 QString::fromStdString(get_fully_qualified_type_name(
729 function_declaration->getParamDecl(i)->getOriginalType().getCanonicalType(),
730 function_declaration->getASTContext()
745 case CXCursor_EnumDecl:
746 return parent->findNonfunctionChild(name, &
Node::isEnumType);
747 case CXCursor_FieldDecl:
748 case CXCursor_VarDecl:
750 case CXCursor_TypedefDecl:
759 CXCursor *overridden;
760 unsigned int numOverridden = 0;
761 clang_getOverriddenCursors(cursor, &overridden, &numOverridden);
762 for (uint i = 0; i < numOverridden; ++i) {
763 QString path = reconstructQualifiedPathForCursor(overridden[i]);
764 if (!path.isEmpty()) {
766 fn->setOverridesThis(path);
770 clang_disposeOverriddenCursors(overridden);
775
776
777
778
779
780
781QString normalizePath(
const QString &path)
783 return QDir::cleanPath(QDir::fromNativeSeparators(path));
787
788
789
790
791
792
793QString canonicalOrEmpty(
const QString &path)
795 const QString canonical = QFileInfo(path).canonicalFilePath();
796 return canonical.isEmpty() ? QString() : normalizePath(canonical);
806 internalFilePatterns_(internalFilePatterns)
808 for (
const auto &header_file_path : allHeaders) {
809 const QString fullPath = QDir(header_file_path.path).filePath(header_file_path.filename);
810 const QString canonical = canonicalOrEmpty(fullPath);
812 if (!canonical.isEmpty())
813 allHeaders_.insert(canonical);
815 allHeaders_.insert(normalizePath(fullPath));
823 auto ret = visitChildrenLambda(cursor, [&](CXCursor cur) {
824 auto loc = clang_getCursorLocation(cur);
825 if (clang_Location_isFromMainFile(loc))
826 return visitSource(cur, loc);
829 clang_getFileLocation(loc, &file,
nullptr,
nullptr,
nullptr);
830 bool isInteresting =
false;
831 auto it = isInterestingCache_.find(file);
832 if (it != isInterestingCache_.end()) {
835 const QString clangFileName = fromCXString(clang_getFileName(file));
836 const QString canonical = canonicalOrEmpty(clangFileName);
838 if (!canonical.isEmpty())
839 isInteresting = allHeaders_.find(canonical) != allHeaders_.end();
841 if (!isInteresting) {
842 const QString normalized = normalizePath(clangFileName);
843 isInteresting = allHeaders_.find(normalized) != allHeaders_.end();
846 isInterestingCache_[file] = isInteresting;
849 return visitHeader(cur, loc);
852 return CXChildVisit_Continue;
854 return ret ? CXChildVisit_Break : CXChildVisit_Continue;
858
859
860
861 CXChildVisitResult
visitFnArg(CXCursor cursor,
Node **fnNode,
bool &ignoreSignature)
863 auto ret = visitChildrenLambda(cursor, [&](CXCursor cur) {
864 auto loc = clang_getCursorLocation(cur);
865 if (clang_Location_isFromMainFile(loc))
866 return visitFnSignature(cur, loc, fnNode, ignoreSignature);
867 return CXChildVisit_Continue;
869 return ret ? CXChildVisit_Break : CXChildVisit_Continue;
875 bool detectQmlSingleton(CXCursor cursor);
877
878
879
882 unsigned int line {}, column {};
883 friend bool operator<(
const SimpleLoc &a,
const SimpleLoc &b)
885 return a.line != b.line ? a.line < b.line : a.column < b.column;
889
890
891
892
893 QMap<SimpleLoc, CXCursor> declMap_;
897 std::set<QString> allHeaders_;
898 QHash<CXFile,
bool> isInterestingCache_;
902
903
904 bool ignoredSymbol(
const QString &symbolName)
906 if (symbolName == QLatin1String(
"QPrivateSignal"))
909 if (symbolName.startsWith(
"_qt_property_"))
912 if (symbolName.startsWith(
"<deduction guide"))
917 CXChildVisitResult visitSource(CXCursor cursor, CXSourceLocation loc);
918 CXChildVisitResult visitHeader(CXCursor cursor, CXSourceLocation loc);
919 CXChildVisitResult visitFnSignature(CXCursor cursor, CXSourceLocation loc,
Node **fnNode,
920 bool &ignoreSignature);
921 void processFunction(
FunctionNode *fn, CXCursor cursor);
922 bool parseProperty(
const QString &spelling,
const Location &loc);
923 void readParameterNamesAndAttributes(
FunctionNode *fn, CXCursor cursor);
924 Aggregate *getSemanticParent(CXCursor cursor);
928
929
930
931
932
933
934
935
936
937
938
941 bool hasSingletonMacro =
false;
943 visitChildrenLambda(cursor, [&hasSingletonMacro](CXCursor child) -> CXChildVisitResult {
945 if (clang_getCursorKind(child) == CXCursor_CallExpr) {
946 CXSourceRange range = clang_getCursorExtent(child);
947 QString sourceText = getSpelling(range);
949 static const QRegularExpression qmlSingletonPattern(
950 R"(Q_CLASSINFO\s*\(\s*["\']QML\.Singleton["\']\s*,\s*["\']true["\']\s*\))");
951 if (qmlSingletonPattern.match(sourceText).hasMatch()) {
952 hasSingletonMacro =
true;
953 return CXChildVisit_Break;
958 if (clang_getCursorKind(child) == CXCursor_EnumDecl) {
959 QString spelling = fromCXString(clang_getCursorSpelling(child));
960 if (spelling ==
"QmlIsSingleton"_L1) {
961 hasSingletonMacro =
true;
962 return CXChildVisit_Break;
966 return CXChildVisit_Continue;
969 return hasSingletonMacro;
973
974
975
976CXChildVisitResult
ClangVisitor::visitSource(CXCursor cursor, CXSourceLocation loc)
978 auto kind = clang_getCursorKind(cursor);
979 if (clang_isDeclaration(kind)) {
981 clang_getPresumedLocation(loc,
nullptr, &l.line, &l.column);
982 declMap_.insert(l, cursor);
983 return CXChildVisit_Recurse;
985 return CXChildVisit_Continue;
989
990
991
992
995 CXCursor sp = clang_getCursorSemanticParent(cursor);
996 CXCursor lp = clang_getCursorLexicalParent(cursor);
997 if (!clang_equalCursors(sp, lp) && clang_isDeclaration(clang_getCursorKind(sp))) {
1006CXChildVisitResult
ClangVisitor::visitFnSignature(CXCursor cursor, CXSourceLocation,
Node **fnNode,
1007 bool &ignoreSignature)
1009 switch (clang_getCursorKind(cursor)) {
1010 case CXCursor_Namespace:
1011 return CXChildVisit_Recurse;
1012 case CXCursor_FunctionDecl:
1013 case CXCursor_FunctionTemplate:
1014 case CXCursor_CXXMethod:
1015 case CXCursor_Constructor:
1016 case CXCursor_Destructor:
1017 case CXCursor_ConversionFunction: {
1018 ignoreSignature =
false;
1019 if (ignoredSymbol(functionName(cursor))) {
1021 ignoreSignature =
true;
1027 readParameterNamesAndAttributes(fn, cursor);
1031 if (
const auto function_declaration = declaration->getAsFunction()) {
1032 auto declaredReturnType = function_declaration->getDeclaredReturnType();
1033 if (llvm::dyn_cast_if_present<clang::AutoType>(declaredReturnType.getTypePtrOrNull()))
1034 fn->setDeclaredReturnType(QString::fromStdString(declaredReturnType.getAsString()));
1038 QString name = functionName(cursor);
1039 if (ignoredSymbol(name))
1040 return CXChildVisit_Continue;
1041 Aggregate *semanticParent = getSemanticParent(cursor);
1042 if (semanticParent && semanticParent
->isClass()) {
1044 processFunction(candidate, cursor);
1045 if (!candidate->isSpecialMemberFunction()) {
1047 return CXChildVisit_Continue;
1049 candidate->setDefault(
true);
1059 return CXChildVisit_Continue;
1062CXChildVisitResult
ClangVisitor::visitHeader(CXCursor cursor, CXSourceLocation loc)
1064 auto kind = clang_getCursorKind(cursor);
1067 case CXCursor_TypeAliasTemplateDecl:
1068 case CXCursor_TypeAliasDecl: {
1069 const QString aliasName = fromCXString(clang_getCursorSpelling(cursor));
1070 QString aliasedType;
1072 const auto *templateDecl = (kind == CXCursor_TypeAliasTemplateDecl)
1076 if (kind == CXCursor_TypeAliasTemplateDecl) {
1078 if (
const auto *aliasTemplate = llvm::dyn_cast<clang::TypeAliasTemplateDecl>(templateDecl)) {
1079 if (
const auto *aliasDecl = aliasTemplate->getTemplatedDecl()) {
1080 clang::QualType underlyingType = aliasDecl->getUnderlyingType();
1081 aliasedType = QString::fromStdString(underlyingType.getAsString());
1086 const CXType aliasedCXType = clang_getTypedefDeclUnderlyingType(cursor);
1087 if (aliasedCXType.kind != CXType_Invalid) {
1088 aliasedType = fromCXString(clang_getTypeSpelling(aliasedCXType));
1092 if (!aliasedType.isEmpty()) {
1093 auto *ta =
new TypeAliasNode(parent_, aliasName, aliasedType);
1098 ta->setTemplateDecl(get_template_declaration(templateDecl));
1100 return CXChildVisit_Continue;
1102 case CXCursor_StructDecl:
1103 case CXCursor_UnionDecl:
1104 if (fromCXString(clang_getCursorSpelling(cursor)).isEmpty())
1105 return CXChildVisit_Continue;
1107 case CXCursor_ClassTemplate:
1109 case CXCursor_ClassDecl: {
1110 if (!clang_isCursorDefinition(cursor))
1111 return CXChildVisit_Continue;
1114 return CXChildVisit_Continue;
1116 QString className = cleanAnonymousTypeName(fromCXString(clang_getCursorSpelling(cursor)));
1118 Aggregate *semanticParent = getSemanticParent(cursor);
1119 if (semanticParent && semanticParent->findNonfunctionChild(className, &
Node::isClassNode)) {
1120 return CXChildVisit_Continue;
1123 CXCursorKind actualKind = (kind == CXCursor_ClassTemplate) ?
1124 clang_getTemplateCursorKind(cursor) : kind;
1127 if (actualKind == CXCursor_StructDecl)
1129 else if (actualKind == CXCursor_UnionDecl)
1132 auto *classe =
new ClassNode(type, semanticParent, className);
1136 classe->setLocation(location);
1138 if (!internalFilePatterns_.exactMatches.isEmpty() || !internalFilePatterns_.globPatterns.isEmpty()
1139 || !internalFilePatterns_.regexPatterns.isEmpty()) {
1140 if (
Config::matchesInternalFilePattern(location.filePath(), internalFilePatterns_))
1144 classe->setAnonymous(clang_Cursor_isAnonymous(cursor));
1146 if (detectQmlSingleton(cursor)) {
1147 classe->setQmlSingleton(
true);
1150 if (kind == CXCursor_ClassTemplate) {
1152 classe->setTemplateDecl(get_template_declaration(template_declaration));
1155 QScopedValueRollback<Aggregate *> setParent(parent_, classe);
1158 case CXCursor_CXXBaseSpecifier: {
1160 return CXChildVisit_Continue;
1162 auto type = clang_getCursorType(cursor);
1163 auto baseCursor = clang_getTypeDeclaration(type);
1165 auto classe =
static_cast<
ClassNode *>(parent_);
1167 QString bcName = reconstructQualifiedPathForCursor(baseCursor);
1168 classe->addUnresolvedBaseClass(access,
1169 bcName.split(QLatin1String(
"::"), Qt::SkipEmptyParts));
1170 return CXChildVisit_Continue;
1172 auto baseClasse =
static_cast<
ClassNode *>(baseNode);
1174 return CXChildVisit_Continue;
1176 case CXCursor_Namespace: {
1177 QString namespaceName = fromCXString(clang_getCursorDisplayName(cursor));
1187 QScopedValueRollback<Aggregate *> setParent(parent_, ns);
1190 case CXCursor_FunctionTemplate:
1192 case CXCursor_FunctionDecl:
1193 case CXCursor_CXXMethod:
1194 case CXCursor_Constructor:
1195 case CXCursor_Destructor:
1196 case CXCursor_ConversionFunction: {
1198 return CXChildVisit_Continue;
1199 QString name = functionName(cursor);
1200 if (ignoredSymbol(name))
1201 return CXChildVisit_Continue;
1204 return CXChildVisit_Continue;
1207 CXSourceRange range = clang_Cursor_getCommentRange(cursor);
1208 if (!clang_Range_isNull(range)) {
1209 QString comment = getSpelling(range);
1210 if (comment.startsWith(
"//!")) {
1211 qsizetype tag = comment.indexOf(QChar(
'['));
1213 qsizetype end = comment.indexOf(QChar(
']'), ++tag);
1215 fn->setTag(comment.mid(tag, end - tag));
1220 processFunction(fn, cursor);
1222 if (kind == CXCursor_FunctionTemplate) {
1224 fn->setTemplateDecl(get_template_declaration(template_declaration));
1227 return CXChildVisit_Continue;
1229#if CINDEX_VERSION
>= 36
1230 case CXCursor_FriendDecl: {
1234 case CXCursor_EnumDecl: {
1236 if (en && en->items().size())
1237 return CXChildVisit_Continue;
1239 QString enumTypeName = fromCXString(clang_getCursorSpelling(cursor));
1241 if (clang_Cursor_isAnonymous(cursor)) {
1242 enumTypeName =
"anonymous";
1248 Node *n = parent_->findNonfunctionChild(enumTypeName, &
Node::isEnumType);
1254 en =
new EnumNode(parent_, enumTypeName, clang_EnumDecl_isScoped(cursor));
1261 visitChildrenLambda(cursor, [&](CXCursor cur) {
1262 if (clang_getCursorKind(cur) != CXCursor_EnumConstantDecl)
1263 return CXChildVisit_Continue;
1266 visitChildrenLambda(cur, [&](CXCursor cur) {
1267 if (clang_isExpression(clang_getCursorKind(cur))) {
1268 value = getSpelling(clang_getCursorExtent(cur));
1269 return CXChildVisit_Break;
1271 return CXChildVisit_Continue;
1273 if (value.isEmpty()) {
1274 QLatin1String hex(
"0x");
1275 if (!en->items().isEmpty() && en->items().last().value().startsWith(hex)) {
1276 value = hex + QString::number(clang_getEnumConstantDeclValue(cur), 16);
1278 value = QString::number(clang_getEnumConstantDeclValue(cur));
1282 en->addItem(EnumItem(fromCXString(clang_getCursorSpelling(cur)), std::move(value)));
1283 return CXChildVisit_Continue;
1285 return CXChildVisit_Continue;
1287 case CXCursor_FieldDecl:
1288 case CXCursor_VarDecl: {
1290 return CXChildVisit_Continue;
1292 auto value_declaration =
1294 assert(value_declaration);
1297 auto var =
new VariableNode(parent_, fromCXString(clang_getCursorSpelling(cursor)));
1299 var->setAccess(access);
1301 var->setLeftType(cleanAnonymousTypeName(QString::fromStdString(get_fully_qualified_type_name(
1302 value_declaration->getType(),
1303 value_declaration->getASTContext()
1305 var->setStatic(kind == CXCursor_VarDecl && parent_
->isClassNode());
1307 return CXChildVisit_Continue;
1309 case CXCursor_TypedefDecl: {
1311 return CXChildVisit_Continue;
1312 auto *td =
new TypedefNode(parent_, fromCXString(clang_getCursorSpelling(cursor)));
1316 visitChildrenLambda(cursor, [&](CXCursor cur) {
1317 if (clang_getCursorKind(cur) != CXCursor_TemplateRef
1318 || fromCXString(clang_getCursorSpelling(cur)) != QLatin1String(
"QFlags"))
1319 return CXChildVisit_Continue;
1321 visitChildrenLambda(cursor, [&](CXCursor cur) {
1322 if (clang_getCursorKind(cur) != CXCursor_TypeRef)
1323 return CXChildVisit_Continue;
1328 return CXChildVisit_Break;
1330 return CXChildVisit_Break;
1332 return CXChildVisit_Continue;
1338 parseProperty(getSpelling(clang_getCursorExtent(cursor)),
1339 fromCXSourceLocation(loc));
1341 return CXChildVisit_Continue;
1350 visitChildrenLambda(cursor, [&](CXCursor cur) {
1351 auto kind = clang_getCursorKind(cur);
1352 if (kind == CXCursor_AnnotateAttr) {
1353 QString annotation = fromCXString(clang_getCursorDisplayName(cur));
1354 if (annotation == QLatin1String(
"qt_slot")) {
1356 }
else if (annotation == QLatin1String(
"qt_signal")) {
1359 if (annotation == QLatin1String(
"qt_invokable"))
1361 }
else if (kind == CXCursor_CXXOverrideAttr) {
1363 }
else if (kind == CXCursor_ParmDecl) {
1365 return CXChildVisit_Break;
1367 if (QString name = fromCXString(clang_getCursorSpelling(cur)); !name.isEmpty())
1368 parameters
[i
].setName(name);
1371 Q_ASSERT(parameter_declaration);
1375 if (!default_value.empty())
1376 parameters[i].setDefaultValue(QString::fromStdString(default_value));
1380 return CXChildVisit_Continue;
1386 CXCursorKind kind = clang_getCursorKind(cursor);
1387 CXType funcType = clang_getCursorType(cursor);
1394 : clang_CXXMethod_isPureVirtual(cursor)
1411 const clang::FunctionDecl* function_declaration = declaration->getAsFunction();
1413 if (kind == CXCursor_Constructor
1415 || (kind == CXCursor_FunctionTemplate && fn->name() == parent_->name()))
1417 else if (kind == CXCursor_Destructor)
1420 fn->setReturnType(cleanAnonymousTypeName(QString::fromStdString(get_fully_qualified_type_name(
1421 function_declaration->getReturnType(),
1422 function_declaration->getASTContext()
1425 const clang::CXXConstructorDecl* constructor_declaration = llvm::dyn_cast<
const clang::CXXConstructorDecl>(function_declaration);
1430 const clang::CXXConversionDecl* conversion_declaration = llvm::dyn_cast<
const clang::CXXConversionDecl>(function_declaration);
1434 (constructor_declaration && constructor_declaration->isExplicit()) ||
1435 (conversion_declaration && conversion_declaration->isExplicit())
1438 const clang::CXXMethodDecl* method_declaration = llvm::dyn_cast<
const clang::CXXMethodDecl>(function_declaration);
1443 const clang::FunctionType* function_type = function_declaration->getFunctionType();
1444 const clang::FunctionProtoType* function_prototype =
static_cast<
const clang::FunctionProtoType*>(function_type);
1446 if (function_prototype) {
1447 clang::FunctionProtoType::ExceptionSpecInfo exception_specification = function_prototype->getExceptionSpecInfo();
1449 if (exception_specification.Type !=
clang::ExceptionSpecificationType::EST_None) {
1450 const std::string exception_specification_spelling =
1451 exception_specification.NoexceptExpr ? get_expression_as_string(
1452 exception_specification.NoexceptExpr,
1453 function_declaration->getASTContext()
1456 if (exception_specification_spelling !=
"false")
1457 fn->markNoexcept(QString::fromStdString(exception_specification_spelling));
1461 CXRefQualifierKind refQualKind = clang_Type_getCXXRefQualifier(funcType);
1462 if (refQualKind == CXRefQualifier_LValue)
1464 else if (refQualKind == CXRefQualifier_RValue)
1473 parameters
.reserve(function_declaration->getNumParams()
);
1475 for (clang::ParmVarDecl*
const parameter_declaration : function_declaration->parameters()) {
1476 clang::QualType parameter_type = parameter_declaration->getOriginalType();
1478 parameters.append(cleanAnonymousTypeName(QString::fromStdString(get_fully_qualified_type_name(
1480 parameter_declaration->getASTContext()
1483 if (!parameter_type.isCanonical())
1484 parameters.last().setCanonicalType(cleanAnonymousTypeName(QString::fromStdString(get_fully_qualified_type_name(
1485 parameter_type.getCanonicalType(),
1486 parameter_declaration->getASTContext()
1491 if (parameters
.last().type().endsWith(QLatin1String(
"QPrivateSignal"))) {
1497 if (clang_isFunctionTypeVariadic(funcType))
1498 parameters.append(QStringLiteral(
"..."));
1499 readParameterNamesAndAttributes(fn, cursor);
1501 if (declaration->getFriendObjectKind() !=
clang::Decl::FOK_None)
1507 if (!spelling.startsWith(QLatin1String(
"Q_PROPERTY"))
1508 && !spelling.startsWith(QLatin1String(
"QDOC_PROPERTY"))
1509 && !spelling.startsWith(QLatin1String(
"Q_OVERRIDE")))
1512 qsizetype lpIdx = spelling.indexOf(QChar(
'('));
1513 qsizetype rpIdx = spelling.lastIndexOf(QChar(
')'));
1514 if (lpIdx <= 0 || rpIdx <= lpIdx)
1517 QString signature = spelling.mid(lpIdx + 1, rpIdx - lpIdx - 1);
1518 signature = signature.simplified();
1519 QStringList parts = signature.split(QChar(
' '), Qt::SkipEmptyParts);
1521 static const QStringList attrs =
1522 QStringList() <<
"READ" <<
"MEMBER" <<
"WRITE"
1523 <<
"NOTIFY" <<
"CONSTANT" <<
"FINAL"
1524 <<
"REQUIRED" <<
"BINDABLE" <<
"DESIGNABLE"
1525 <<
"RESET" <<
"REVISION" <<
"SCRIPTABLE"
1526 <<
"STORED" <<
"USER";
1530 auto it =
std::find_if(parts.cbegin(), parts.cend(),
1531 [](
const QString &attr) ->
bool {
1532 return attrs.contains(attr);
1535 if (it == parts.cend() ||
std::distance(parts.cbegin(), it) < 2)
1538 QStringList typeParts;
1539 std::copy(parts.cbegin(), it,
std::back_inserter(typeParts));
1540 parts.erase(parts.cbegin(), it);
1541 QString name = typeParts.takeLast();
1544 while (!name.isEmpty() && name.front() == QChar(
'*')) {
1545 typeParts.last().push_back(name.front());
1550 if (parts.size() < 2 || name.isEmpty())
1555 property->setLocation(loc);
1556 property->setDataType(typeParts.join(QChar(
' ')));
1559 while (i < parts.size()) {
1560 const QString &key = parts.at(i++);
1562 if (key ==
"CONSTANT") {
1563 property->setConstant();
1564 }
else if (key ==
"REQUIRED") {
1565 property->setRequired();
1567 if (i < parts.size()) {
1568 QString value = parts.at(i++);
1569 if (key ==
"READ") {
1571 }
else if (key ==
"WRITE") {
1573 property->setWritable(
true);
1574 }
else if (key ==
"MEMBER") {
1575 property->setWritable(
true);
1576 }
else if (key ==
"STORED") {
1577 property->setStored(value.toLower() ==
"true");
1578 }
else if (key ==
"BINDABLE") {
1581 }
else if (key ==
"RESET") {
1583 }
else if (key ==
"NOTIFY") {
1592
1593
1594
1595
1596
1600 clang_getPresumedLocation(loc,
nullptr, &docloc.line, &docloc.column);
1601 auto decl_it = declMap_.upperBound(docloc);
1602 if (decl_it == declMap_.end())
1605 unsigned int declLine = decl_it.key().line;
1606 unsigned int nextCommentLine;
1607 clang_getPresumedLocation(nextCommentLoc,
nullptr, &nextCommentLine,
nullptr);
1608 if (nextCommentLine < declLine)
1612 if (decl_it != declMap_.begin()) {
1613 CXSourceLocation prevDeclEnd = clang_getRangeEnd(clang_getCursorExtent(*(
std::prev(decl_it))));
1614 unsigned int prevDeclLine;
1615 clang_getPresumedLocation(prevDeclEnd,
nullptr, &prevDeclLine,
nullptr);
1616 if (prevDeclLine >= docloc.line) {
1619 auto parent = clang_getCursorLexicalParent(*decl_it);
1620 if (!clang_equalCursors(parent, *(
std::prev(decl_it))))
1624 auto *node = findNodeForCursor(qdb_, *decl_it);
1626 if (node && node->isFunction(Genus::CPP))
1627 readParameterNamesAndAttributes(
static_cast<
FunctionNode *>(node), *decl_it);
1634 const std::vector<QByteArray>& include_paths,
1635 const QList<QByteArray>& defines,
1642 m_allHeaders = config.getHeaderFiles();
1643 m_internalFilePatterns = config.getInternalFilePatternsCompiled();
1648
1649
1650
1651
1652
1653#if LIBCLANG_VERSION_MAJOR == 15
1661 "-fms-compatibility-version=19",
1665 "-DQT_DISABLE_DEPRECATED_UP_TO=0",
1666 "-DQT_ANNOTATE_CLASS(type,...)=static_assert(sizeof(#__VA_ARGS__),#type);",
1667 "-DQT_ANNOTATE_CLASS2(type,a1,a2)=static_assert(sizeof(#a1,#a2),#type);",
1668 "-DQT_ANNOTATE_FUNCTION(a)=__attribute__((annotate(#a)))",
1669 "-DQT_ANNOTATE_ACCESS_SPECIFIER(a)=__attribute__((annotate(#a)))",
1670 "-Wno-constant-logical-operand",
1671 "-Wno-macro-redefined",
1672 "-Wno-nullability-completeness",
1673 "-fvisibility=default",
1679
1680
1681
1688 for (
const auto &p : std::as_const(defines))
1689 args.push_back(p.constData());
1694 QList<QByteArray> result;
1695 for (
const auto& [header_path, _] : allHeaders) {
1696 const QByteArray path =
"-I" + header_path.toLatin1();
1697 const QByteArray parent =
1698 "-I" + QDir::cleanPath(header_path + QLatin1String(
"/../")).toLatin1();
1705
1706
1707
1709 const std::vector<QByteArray>& include_paths,
1710 const std::set<Config::HeaderFilePath>& all_headers,
1711 std::vector<
const char*>& args
1713 if (include_paths.empty()) {
1715
1716
1717
1718
1719 qCWarning(lcQdoc) <<
"No include paths passed to qdoc; guessing reasonable include paths";
1721 QString basicIncludeDir = QDir::cleanPath(QString(Config::installDir +
"/../include"));
1722 args.emplace_back(QByteArray(
"-I" + basicIncludeDir.toLatin1()).constData());
1724 auto include_paths_from_headers = includePathsFromHeaders(all_headers);
1725 args.insert(args.end(), include_paths_from_headers.begin(), include_paths_from_headers.end());
1727 std::copy(include_paths.begin(), include_paths.end(),
std::back_inserter(args));
1732
1733
1734
1735
1738 QString module_header,
1739 const std::set<Config::HeaderFilePath>& all_headers,
1740 const std::vector<QByteArray>& include_paths,
1741 const QList<QByteArray>& defines,
1742 const InclusionPolicy& policy
1744 static std::vector<
const char*> arguments{};
1746 if (module_header.isEmpty())
return std::nullopt;
1748 getDefaultArgs(defines, arguments);
1749 getMoreArgs(include_paths, all_headers, arguments);
1751 flags_ =
static_cast<CXTranslationUnit_Flags>(CXTranslationUnit_Incomplete
1752 | CXTranslationUnit_SkipFunctionBodies
1753 | CXTranslationUnit_KeepGoing);
1757 QTemporaryDir pch_directory{QDir::tempPath() + QLatin1String(
"/qdoc_pch")};
1758 if (!pch_directory.isValid())
return std::nullopt;
1760 const QByteArray module = module_header.toUtf8();
1763 qCDebug(lcQdoc) <<
"Build and visit PCH for" << module_header;
1766 struct FindPredicate
1768 enum SearchType { Any, Module };
1769 QByteArray &candidate_;
1770 const QByteArray &module_;
1772 FindPredicate(QByteArray &candidate,
const QByteArray &module,
1773 SearchType type = Any)
1774 : candidate_(candidate), module_(module), type_(type)
1778 bool operator()(
const QByteArray &p)
const
1780 if (type_ != Any && !p.endsWith(module_))
1782 candidate_ = p +
"/";
1783 candidate_.append(module_);
1784 if (p.startsWith(
"-I"))
1785 candidate_ = candidate_.mid(2);
1786 return QFile::exists(QString::fromUtf8(candidate_));
1791 QByteArray candidate;
1792 auto it =
std::find_if(include_paths.begin(), include_paths.end(),
1793 FindPredicate(candidate, module, FindPredicate::Module));
1794 if (it == include_paths.end())
1795 it =
std::find_if(include_paths.begin(), include_paths.end(),
1796 FindPredicate(candidate, module, FindPredicate::Any));
1797 if (it != include_paths.end())
1798 header =
std::move(candidate);
1800 if (header.isEmpty()) {
1801 qWarning() <<
"(qdoc) Could not find the module header in include paths for module"
1802 << module <<
" (include paths: " << include_paths <<
")";
1803 qWarning() <<
" Artificial module header built from header dirs in qdocconf "
1806 arguments.push_back(
"-xc++");
1810 QString tmpHeader = pch_directory.path() +
"/" + module;
1811 if (QFile tmpHeaderFile(tmpHeader); tmpHeaderFile.open(QIODevice::Text | QIODevice::WriteOnly)) {
1812 QTextStream out(&tmpHeaderFile);
1813 if (header.isEmpty()) {
1814 for (
const auto& [header_path, header_name] : all_headers) {
1815 bool shouldInclude = !header_name.startsWith(
"moc_"_L1);
1818 if (header_name.endsWith(
"_p.h"_L1))
1819 shouldInclude = shouldInclude && policy.showInternal;
1821 if (shouldInclude) {
1822 out <<
"#include \"" << header_path <<
"/" << header_name <<
"\"\n";
1826 QFileInfo headerFile(header);
1827 if (!headerFile.exists()) {
1828 qWarning() <<
"Could not find module header file" << header;
1829 return std::nullopt;
1832 out <<
"#include \"" << header <<
"\"\n";
1834 if (policy.showInternal) {
1835 for (
const auto& [header_path, header_name] : all_headers) {
1836 bool shouldInclude = !header_name.startsWith(
"moc_"_L1);
1837 if (header_name.endsWith(
"_p.h"_L1) && shouldInclude)
1838 out <<
"#include \"" << header_path <<
"/" << header_name <<
"\"\n";
1845 clang_parseTranslationUnit2(index, tmpHeader.toLatin1().data(), arguments.data(),
1846 static_cast<
int>(arguments.size()),
nullptr, 0,
1847 flags_ | CXTranslationUnit_ForSerialization, &tu
.tu);
1848 qCDebug(lcQdoc) <<
__FUNCTION__ <<
"clang_parseTranslationUnit2(" << tmpHeader << arguments
1849 <<
") returns" << err;
1854 qCCritical(lcQdoc) <<
"Could not create PCH file for " << module_header;
1855 return std::nullopt;
1858 QByteArray pch_name = pch_directory.path().toUtf8() +
"/" + module +
".pch";
1859 auto error = clang_saveTranslationUnit(tu, pch_name.constData(),
1860 clang_defaultSaveOptions(tu));
1862 qCCritical(lcQdoc) <<
"Could not save PCH file for" << module_header;
1863 return std::nullopt;
1868 CXCursor cur = clang_getTranslationUnitCursor(tu);
1869 auto &config = Config::instance();
1870 ClangVisitor visitor(qdb, all_headers, config.getInternalFilePatternsCompiled());
1872 qCDebug(lcQdoc) <<
"PCH built and visited for" << module_header;
1874 return std::make_optional(
PCHFile{
std::move(pch_directory),
std::move(pch_name)});
1879 if (t.count(QChar(
'.')) > 1)
1880 t.truncate(t.lastIndexOf(QChar(
'.')));
1885
1886
1887
1888
1889
1890
1891
1892
1895 flags_ =
static_cast<CXTranslationUnit_Flags>(CXTranslationUnit_Incomplete
1896 | CXTranslationUnit_SkipFunctionBodies
1897 | CXTranslationUnit_KeepGoing);
1901 getDefaultArgs(m_defines, m_args);
1902 if (m_pch && !filePath.endsWith(
".mm")
1903 && !std::holds_alternative<CppHeaderSourceFile>(tag_source_file(filePath).second)) {
1904 m_args.push_back(
"-w");
1905 m_args.push_back(
"-include-pch");
1906 m_args.push_back((*m_pch).get().name.constData());
1908 getMoreArgs(m_includePaths, m_allHeaders, m_args);
1912 clang_parseTranslationUnit2(index, filePath.toLocal8Bit(), m_args.data(),
1913 static_cast<
int>(m_args.size()),
nullptr, 0, flags_, &tu.tu);
1914 qCDebug(lcQdoc) <<
__FUNCTION__ <<
"clang_parseTranslationUnit2(" << filePath << m_args
1915 <<
") returns" << err;
1919 qWarning() <<
"(qdoc) Could not parse source file" << filePath <<
" error code:" << err;
1923 ParsedCppFileIR parse_result{};
1925 CXCursor tuCur = clang_getTranslationUnitCursor(tu);
1926 ClangVisitor visitor(m_qdb, m_allHeaders, m_internalFilePatterns);
1930 unsigned int numTokens = 0;
1931 const QSet<QString> &commands = CppCodeParser::topic_commands + CppCodeParser::meta_commands;
1932 clang_tokenize(tu, clang_getCursorExtent(tuCur), &tokens, &numTokens);
1934 for (
unsigned int i = 0; i < numTokens; ++i) {
1935 if (clang_getTokenKind(tokens[i]) != CXToken_Comment)
1937 QString comment = fromCXString(clang_getTokenSpelling(tu, tokens[i]));
1938 if (!comment.startsWith(
"/*!"))
1941 auto commentLoc = clang_getTokenLocation(tu, tokens[i]);
1944 Doc::trimCStyleComment(loc, comment);
1947 Doc doc(loc, end_loc, comment, commands, CppCodeParser::topic_commands);
1953 if (i + 1 < numTokens) {
1955 CXSourceLocation nextCommentLoc = commentLoc;
1956 while (i + 2 < numTokens && clang_getTokenKind(tokens[i + 1]) != CXToken_Comment)
1958 nextCommentLoc = clang_getTokenLocation(tu, tokens[i + 1]);
1965 bool future =
false;
1967 QString sinceVersion = doc.metaCommandArgs(
COMMAND_SINCE).at(0).first;
1968 if (getUnpatchedVersion(std::move(sinceVersion)) >
1969 getUnpatchedVersion(Config::instance().get(
CONFIG_VERSION).asString()))
1974 QStringLiteral(
"Cannot tie this documentation to anything"),
1975 QStringLiteral(
"qdoc found a /*! ... */ comment, but there was no "
1976 "topic command (e.g., '\\%1', '\\%2') in the "
1977 "comment and no function definition following "
1985 CXCursor cur = clang_getCursor(tu, commentLoc);
1987 CXCursorKind kind = clang_getCursorKind(cur);
1988 if (clang_isTranslationUnit(kind) || clang_isInvalid(kind))
1990 if (kind == CXCursor_Namespace) {
1991 parse_result.untied.back().context << fromCXString(clang_getCursorSpelling(cur));
1993 cur = clang_getCursorLexicalParent(cur);
1998 clang_disposeTokens(tu, tokens, numTokens);
1999 m_namespaceScope.clear();
2002 return parse_result;
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2018 const QString &idTag, QStringList context)
2020 Node *fnNode =
nullptr;
2022
2023
2024
2025
2026
2027 if (!idTag.isEmpty()) {
2028 fnNode = m_qdb->findFunctionNodeForTag(idTag);
2031 QStringLiteral(
"tag \\fn [%1] not used in any include file in current module").arg(idTag));
2034
2035
2036
2037
2039 QStringList leftParenSplit = fnSignature.mid(fnSignature.indexOf(fn->name())).split(
'(');
2040 if (leftParenSplit.size() > 1) {
2041 QStringList rightParenSplit = leftParenSplit[1].split(
')');
2042 if (!rightParenSplit.empty()) {
2043 QString params = rightParenSplit[0];
2044 if (!params.isEmpty()) {
2045 QStringList commaSplit = params.split(
',');
2047 if (parameters
.count() == commaSplit.size()) {
2048 for (
int i = 0; i < parameters
.count(); ++i) {
2049 QStringList blankSplit = commaSplit[i].split(
' ', Qt::SkipEmptyParts);
2050 if (blankSplit.size() > 1) {
2051 QString pName = blankSplit.last();
2053 auto it =
std::find_if(
std::begin(pName),
std::end(pName),
2054 [](
const QChar &c) {
return c.isLetter(); });
2055 parameters
[i
].setName(
2056 pName.remove(0,
std::distance(
std::begin(pName), it)));
2066 auto flags =
static_cast<CXTranslationUnit_Flags>(CXTranslationUnit_Incomplete
2067 | CXTranslationUnit_SkipFunctionBodies
2068 | CXTranslationUnit_KeepGoing);
2072 getDefaultArgs(m_defines, m_args);
2075 m_args.push_back(
"-w");
2076 m_args.push_back(
"-include-pch");
2077 m_args.push_back((*m_pch).get().name.constData());
2082 for (
const auto &ns : std::as_const(context))
2083 s_fn.prepend(
"namespace " + ns.toUtf8() +
" {");
2084 s_fn += fnSignature.toUtf8();
2085 if (!s_fn.endsWith(
";"))
2087 s_fn.append(context.size(),
'}');
2090 CXUnsavedFile unsavedFile { dummyFileName, s_fn.constData(),
2091 static_cast<
unsigned long>(s_fn.size()) };
2092 CXErrorCode err = clang_parseTranslationUnit2(index, dummyFileName, m_args.data(),
2093 int(m_args.size()), &unsavedFile, 1, flags, &tu.tu);
2094 qCDebug(lcQdoc) <<
__FUNCTION__ <<
"clang_parseTranslationUnit2(" << dummyFileName << m_args
2095 <<
") returns" << err;
2098 location.error(QStringLiteral(
"clang could not parse \\fn %1").arg(fnSignature));
2102
2103
2104
2105
2106
2107 CXCursor cur = clang_getTranslationUnitCursor(tu);
2108 auto &config = Config::instance();
2109 ClangVisitor visitor(m_qdb, m_allHeaders, config.getInternalFilePatternsCompiled());
2110 bool ignoreSignature =
false;
2114 unsigned diagnosticCount = clang_getNumDiagnostics(tu);
2115 const auto &config = Config::instance();
2116 if (diagnosticCount > 0 && (!config.preparing() || config.singleExec())) {
2117 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)
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.
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()