Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
clangcodeparser.cpp
Go to the documentation of this file.
1// Copyright (C) 2021 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
3
6
7#include "access.h"
8#include "classnode.h"
9#include "config.h"
10#include "enumnode.h"
11#include "functionnode.h"
12#include "namespacenode.h"
13#include "propertynode.h"
14#include "qdocdatabase.h"
15#include "typedefnode.h"
16#include "variablenode.h"
18#include "utilities.h"
19
20#include <QtCore/qdebug.h>
21#include <QtCore/qelapsedtimer.h>
22#include <QtCore/qfile.h>
23#include <QtCore/qscopedvaluerollback.h>
24#include <QtCore/qtemporarydir.h>
25#include <QtCore/qtextstream.h>
26#include <QtCore/qvarlengtharray.h>
27
28#include <clang-c/Index.h>
29
30#include <clang/AST/Decl.h>
31#include <clang/AST/DeclFriend.h>
32#include <clang/AST/DeclTemplate.h>
33#include <clang/AST/Expr.h>
34#include <clang/AST/Type.h>
35#include <clang/AST/TypeLoc.h>
36#include <clang/Basic/SourceLocation.h>
37#include <clang/Frontend/ASTUnit.h>
38#include <clang/Lex/Lexer.h>
39#include <llvm/Support/Casting.h>
40
41#include "clang/AST/QualTypeNames.h"
43
44#include <cstdio>
45
46QT_BEGIN_NAMESPACE
47
48struct CompilationIndex {
49 CXIndex index = nullptr;
50
51 operator CXIndex() {
52 return index;
53 }
54
55 ~CompilationIndex() {
56 clang_disposeIndex(index);
57 }
58};
59
61 CXTranslationUnit tu = nullptr;
62
63 operator CXTranslationUnit() {
64 return tu;
65 }
66
67 operator bool() {
68 return tu;
69 }
70
72 clang_disposeTranslationUnit(tu);
73 }
74};
75
76// We're printing diagnostics in ClangCodeParser::printDiagnostics,
77// so avoid clang itself printing them.
78static const auto kClangDontDisplayDiagnostics = 0;
79
80static CXTranslationUnit_Flags flags_ = static_cast<CXTranslationUnit_Flags>(0);
81
82constexpr const char fnDummyFileName[] = "/fn_dummyfile.cpp";
83
84#ifndef QT_NO_DEBUG_STREAM
85template<class T>
86static QDebug operator<<(QDebug debug, const std::vector<T> &v)
87{
88 QDebugStateSaver saver(debug);
89 debug.noquote();
90 debug.nospace();
91 const size_t size = v.size();
92 debug << "std::vector<>[" << size << "](";
93 for (size_t i = 0; i < size; ++i) {
94 if (i)
95 debug << ", ";
96 debug << v[i];
97 }
98 debug << ')';
99 return debug;
100}
101#endif // !QT_NO_DEBUG_STREAM
102
103static void printDiagnostics(const CXTranslationUnit &translationUnit)
104{
105 if (!lcQdocClang().isDebugEnabled())
106 return;
107
108 static const auto displayOptions = CXDiagnosticDisplayOptions::CXDiagnostic_DisplaySourceLocation
109 | CXDiagnosticDisplayOptions::CXDiagnostic_DisplayColumn
110 | CXDiagnosticDisplayOptions::CXDiagnostic_DisplayOption;
111
112 for (unsigned i = 0, numDiagnostics = clang_getNumDiagnostics(translationUnit); i < numDiagnostics; ++i) {
113 auto diagnostic = clang_getDiagnostic(translationUnit, i);
114 auto formattedDiagnostic = clang_formatDiagnostic(diagnostic, displayOptions);
115 qCDebug(lcQdocClang) << clang_getCString(formattedDiagnostic);
116 clang_disposeString(formattedDiagnostic);
117 clang_disposeDiagnostic(diagnostic);
118 }
119}
120
121/*!
122 * Returns the underlying Decl that \a cursor represents.
123 *
124 * This can be used to drop back down from a LibClang's CXCursor to
125 * the underlying C++ AST that Clang provides.
126 *
127 * It should be used when LibClang does not expose certain
128 * functionalities that are available in the C++ AST.
129 *
130 * The CXCursor should represent a declaration. Usages of this
131 * function on CXCursors that do not represent a declaration may
132 * produce undefined results.
133 */
134static const clang::Decl* get_cursor_declaration(CXCursor cursor) {
135 assert(clang_isDeclaration(clang_getCursorKind(cursor)));
136
137 return static_cast<const clang::Decl*>(cursor.data[0]);
138}
139
140
141/*!
142 * Returns a string representing the name of \a type as if it was
143 * referred to at the end of the translation unit that it was parsed
144 * from.
145 *
146 * For example, given the following code:
147 *
148 * \code
149 * namespace foo {
150 * template<typename T>
151 * struct Bar {
152 * using Baz = const T&;
153 *
154 * void bam(Baz);
155 * };
156 * }
157 * \endcode
158 *
159 * Given a parsed translation unit and an AST node, say \e {decl},
160 * representing the parameter declaration of the first argument of \c {bam},
161 * calling \c{get_fully_qualified_name(decl->getType(), * decl->getASTContext())}
162 * would result in the string \c {foo::Bar<T>::Baz}.
163 *
164 * This should generally be used every time the stringified
165 * representation of a type is acquired as part of parsing with Clang,
166 * so as to ensure a consistent behavior and output.
167 */
168static std::string get_fully_qualified_type_name(clang::QualType type, const clang::ASTContext& declaration_context) {
170 type,
171 declaration_context,
172 declaration_context.getPrintingPolicy()
173 );
174}
175
176/*
177 * Retrieves expression as written in the original source code.
178 *
179 * declaration_context should be the ASTContext of the declaration
180 * from which the expression was extracted from.
181 *
182 * If the expression contains a leading equal sign it will be removed.
183 *
184 * Leading and trailing spaces will be similarly removed from the expression.
185 */
186static std::string get_expression_as_string(const clang::Expr* expression, const clang::ASTContext& declaration_context) {
187 QString default_value = QString::fromStdString(clang::Lexer::getSourceText(
188 clang::CharSourceRange::getTokenRange(expression->getSourceRange()),
189 declaration_context.getSourceManager(),
190 declaration_context.getLangOpts()
191 ).str());
192
193 if (default_value.startsWith("="))
194 default_value.remove(0, 1);
195
196 default_value = default_value.trimmed();
197
198 return default_value.toStdString();
199}
200
201/*
202 * Retrieves the default value of the passed in type template parameter as a string.
203 *
204 * The default value of a type template parameter is always a type,
205 * and its stringified representation will be return as the fully
206 * qualified version of the type.
207 *
208 * If the parameter has no default value the empty string will be returned.
209 */
210static std::string get_default_value_initializer_as_string(const clang::TemplateTypeParmDecl* parameter) {
211#if LIBCLANG_VERSION_MAJOR >= 19
212 return (parameter && parameter->hasDefaultArgument()) ?
213 get_fully_qualified_type_name(parameter->getDefaultArgument().getArgument().getAsType(), parameter->getASTContext()) :
214 "";
215#else
216 return (parameter && parameter->hasDefaultArgument()) ?
217 get_fully_qualified_type_name(parameter->getDefaultArgument(), parameter->getASTContext()) :
218 "";
219#endif
220
221}
222
223/*
224 * Retrieves the default value of the passed in non-type template parameter as a string.
225 *
226 * The default value of a non-type template parameter is an expression
227 * and its stringified representation will be return as it was written
228 * in the original code.
229 *
230 * If the parameter as no default value the empty string will be returned.
231 */
232static std::string get_default_value_initializer_as_string(const clang::NonTypeTemplateParmDecl* parameter) {
233#if LIBCLANG_VERSION_MAJOR >= 19
234 return (parameter && parameter->hasDefaultArgument()) ?
235 get_expression_as_string(parameter->getDefaultArgument().getSourceExpression(), parameter->getASTContext()) : "";
236#else
237 return (parameter && parameter->hasDefaultArgument()) ?
238 get_expression_as_string(parameter->getDefaultArgument(), parameter->getASTContext()) : "";
239#endif
240
241}
242
243/*
244 * Retrieves the default value of the passed in template template parameter as a string.
245 *
246 * The default value of a template template parameter is a template
247 * name and its stringified representation will be returned as a fully
248 * qualified version of that name.
249 *
250 * If the parameter as no default value the empty string will be returned.
251 */
252static std::string get_default_value_initializer_as_string(const clang::TemplateTemplateParmDecl* parameter) {
253 std::string default_value{};
254
255 if (parameter && parameter->hasDefaultArgument()) {
256 const clang::TemplateName template_name = parameter->getDefaultArgument().getArgument().getAsTemplate();
257
258 llvm::raw_string_ostream ss{default_value};
259 template_name.print(ss, parameter->getASTContext().getPrintingPolicy(), clang::TemplateName::Qualified::AsWritten);
260 }
261
262 return default_value;
263}
264
265/*
266 * Retrieves the default value of the passed in function parameter as
267 * a string.
268 *
269 * The default value of a function parameter is an expression and its
270 * stringified representation will be returned as it was written in
271 * the original code.
272 *
273 * If the parameter as no default value or Clang was not able to yet
274 * parse it at this time the empty string will be returned.
275 */
276static std::string get_default_value_initializer_as_string(const clang::ParmVarDecl* parameter) {
277 if (!parameter || !parameter->hasDefaultArg() || parameter->hasUnparsedDefaultArg())
278 return "";
279
280 return get_expression_as_string(
281 parameter->hasUninstantiatedDefaultArg() ? parameter->getUninstantiatedDefaultArg() : parameter->getDefaultArg(),
282 parameter->getASTContext()
283 );
284}
285
286/*
287 * Retrieves the default value of the passed in declaration, based on
288 * its concrete type, as a string.
289 *
290 * If the declaration is a nullptr or the concrete type of the
291 * declaration is not a supported one, the returned string will be the
292 * empty string.
293 */
294static std::string get_default_value_initializer_as_string(const clang::NamedDecl* declaration) {
295 if (!declaration) return "";
296
297 if (auto type_template_parameter = llvm::dyn_cast<clang::TemplateTypeParmDecl>(declaration))
298 return get_default_value_initializer_as_string(type_template_parameter);
299
300 if (auto non_type_template_parameter = llvm::dyn_cast<clang::NonTypeTemplateParmDecl>(declaration))
301 return get_default_value_initializer_as_string(non_type_template_parameter);
302
303 if (auto template_template_parameter = llvm::dyn_cast<clang::TemplateTemplateParmDecl>(declaration)) {
304 return get_default_value_initializer_as_string(template_template_parameter);
305 }
306
307 if (auto function_parameter = llvm::dyn_cast<clang::ParmVarDecl>(declaration)) {
308 return get_default_value_initializer_as_string(function_parameter);
309 }
310
311 return "";
312}
313
314/*!
315 Call clang_visitChildren on the given cursor with the lambda as a callback
316 T can be any functor that is callable with a CXCursor parameter and returns a CXChildVisitResult
317 (in other word compatible with function<CXChildVisitResult(CXCursor)>
318 */
319template<typename T>
320bool visitChildrenLambda(CXCursor cursor, T &&lambda)
321{
322 CXCursorVisitor visitor = [](CXCursor c, CXCursor,
323 CXClientData client_data) -> CXChildVisitResult {
324 return (*static_cast<T *>(client_data))(c);
325 };
326 return clang_visitChildren(cursor, visitor, &lambda);
327}
328
329/*!
330 convert a CXString to a QString, and dispose the CXString
331 */
332static QString fromCXString(CXString &&string)
333{
334 QString ret = QString::fromUtf8(clang_getCString(string));
335 clang_disposeString(string);
336 return ret;
337}
338
339/*
340 * Returns an intermediate representation that models the the given
341 * template declaration.
342 */
343static RelaxedTemplateDeclaration get_template_declaration(const clang::TemplateDecl* template_declaration) {
344 assert(template_declaration);
345
346 RelaxedTemplateDeclaration template_declaration_ir{};
347
348 auto template_parameters = template_declaration->getTemplateParameters();
349 for (auto template_parameter : template_parameters->asArray()) {
350 auto kind{RelaxedTemplateParameter::Kind::TypeTemplateParameter};
351 std::string type{};
352
353 if (auto non_type_template_parameter = llvm::dyn_cast<clang::NonTypeTemplateParmDecl>(template_parameter)) {
354 kind = RelaxedTemplateParameter::Kind::NonTypeTemplateParameter;
355 type = get_fully_qualified_type_name(non_type_template_parameter->getType(), non_type_template_parameter->getASTContext());
356
357 // REMARK: QDoc uses this information to match a user
358 // provided documentation (for example from an "\fn"
359 // command) with a `Node` that was extracted from the
360 // code-base.
361 //
362 // Due to how QDoc obtains an AST for documentation that
363 // is provided by the user, there might be a mismatch in
364 // the type of certain non type template parameters.
365 //
366 // QDoc generally builds a fake out-of-line definition for
367 // a callable provided through an "\fn" command, when it
368 // needs to match it.
369 // In that context, certain type names may be dependent
370 // names, while they may not be when the element they
371 // represent is extracted from the code-base.
372 //
373 // This in turn makes their stringified representation
374 // different in the two contextes, as a dependent name may
375 // require the "typename" keyword to precede it.
376 //
377 // Since QDoc uses a very simplified model, and it
378 // generally doesn't need care about the exact name
379 // resolution rules for C++, since it passes by
380 // Clang-validated data, we remove the "typename" keyword
381 // if it prefixes the type representation, so that it
382 // doesn't impact the matching procedure..
383
384 // KLUDGE: Waiting for C++20 to avoid the conversion.
385 // Doesn't really impact performance in a
386 // meaningful way so it can be kept while waiting.
387 if (QString::fromStdString(type).startsWith("typename ")) type.erase(0, std::string("typename ").size());
388 }
389
390 auto template_template_parameter = llvm::dyn_cast<clang::TemplateTemplateParmDecl>(template_parameter);
391 if (template_template_parameter) kind = RelaxedTemplateParameter::Kind::TemplateTemplateParameter;
392
393 template_declaration_ir.parameters.push_back({
394 kind,
395 template_parameter->isTemplateParameterPack(),
396 {
397 type,
398 template_parameter->getNameAsString(),
399 get_default_value_initializer_as_string(template_parameter)
400 },
401 (template_template_parameter ?
402 std::optional<TemplateDeclarationStorage>(TemplateDeclarationStorage{
403 get_template_declaration(template_template_parameter).parameters
404 }) : std::nullopt)
405 });
406 }
407
408 return template_declaration_ir;
409}
410
411/*!
412 convert a CXSourceLocation to a qdoc Location
413 */
414static Location fromCXSourceLocation(CXSourceLocation location)
415{
416 unsigned int line, column;
417 CXString file;
418 clang_getPresumedLocation(location, &file, &line, &column);
419 Location l(fromCXString(std::move(file)));
420 l.setColumnNo(column);
421 l.setLineNo(line);
422 return l;
423}
424
425/*!
426 convert a CX_CXXAccessSpecifier to Node::Access
427 */
428static Access fromCX_CXXAccessSpecifier(CX_CXXAccessSpecifier spec)
429{
430 switch (spec) {
431 case CX_CXXPrivate:
432 return Access::Private;
433 case CX_CXXProtected:
434 return Access::Protected;
435 case CX_CXXPublic:
436 return Access::Public;
437 default:
438 return Access::Public;
439 }
440}
441
442/*!
443 Returns the spelling in the file for a source range
444 */
445
451
452static inline QString fromCache(const QByteArray &cache,
453 unsigned int offset1, unsigned int offset2)
454{
455 return QString::fromUtf8(cache.mid(offset1, offset2 - offset1));
456}
457
458static QString readFile(CXFile cxFile, unsigned int offset1, unsigned int offset2)
459{
460 using FileCache = QList<FileCacheEntry>;
461 static FileCache cache;
462
463 CXString cxFileName = clang_getFileName(cxFile);
464 const QByteArray fileName = clang_getCString(cxFileName);
465 clang_disposeString(cxFileName);
466
467 for (const auto &entry : std::as_const(cache)) {
468 if (fileName == entry.fileName)
469 return fromCache(entry.content, offset1, offset2);
470 }
471
472 QFile file(QString::fromUtf8(fileName));
473 if (file.open(QIODeviceBase::ReadOnly)) { // binary to match clang offsets
474 FileCacheEntry entry{fileName, file.readAll()};
475 cache.prepend(entry);
476 while (cache.size() > 5)
477 cache.removeLast();
478 return fromCache(entry.content, offset1, offset2);
479 }
480 return {};
481}
482
483static QString getSpelling(CXSourceRange range)
484{
485 auto start = clang_getRangeStart(range);
486 auto end = clang_getRangeEnd(range);
487 CXFile file1, file2;
488 unsigned int offset1, offset2;
489 clang_getFileLocation(start, &file1, nullptr, nullptr, &offset1);
490 clang_getFileLocation(end, &file2, nullptr, nullptr, &offset2);
491
492 if (file1 != file2 || offset2 <= offset1)
493 return QString();
494
495 return readFile(file1, offset1, offset2);
496}
497
498/*!
499 Returns the function name from a given cursor representing a
500 function declaration. This is usually clang_getCursorSpelling, but
501 not for the conversion function in which case it is a bit more complicated
502 */
503QString functionName(CXCursor cursor)
504{
505 if (clang_getCursorKind(cursor) == CXCursor_ConversionFunction) {
506 // For a CXCursor_ConversionFunction we don't want the spelling which would be something
507 // like "operator type-parameter-0-0" or "operator unsigned int". we want the actual name as
508 // spelled;
509 auto conversion_declaration =
510 static_cast<const clang::CXXConversionDecl*>(get_cursor_declaration(cursor));
511
512 return QLatin1String("operator ") + QString::fromStdString(get_fully_qualified_type_name(
513 conversion_declaration->getConversionType(),
514 conversion_declaration->getASTContext()
515 ));
516 }
517
518 QString name = fromCXString(clang_getCursorSpelling(cursor));
519
520 // Remove template stuff from constructor and destructor but not from operator<
521 auto ltLoc = name.indexOf('<');
522 if (ltLoc > 0 && !name.startsWith("operator<"))
523 name = name.left(ltLoc);
524 return name;
525}
526
527/*!
528 Reconstruct the qualified path name of a function that is
529 being overridden.
530 */
531static QString reconstructQualifiedPathForCursor(CXCursor cur)
532{
533 QString path;
534 auto kind = clang_getCursorKind(cur);
535 while (!clang_isInvalid(kind) && kind != CXCursor_TranslationUnit) {
536 switch (kind) {
537 case CXCursor_Namespace:
538 case CXCursor_StructDecl:
539 case CXCursor_ClassDecl:
540 case CXCursor_UnionDecl:
541 case CXCursor_ClassTemplate:
542 path.prepend("::");
543 path.prepend(fromCXString(clang_getCursorSpelling(cur)));
544 break;
545 case CXCursor_FunctionDecl:
546 case CXCursor_FunctionTemplate:
547 case CXCursor_CXXMethod:
548 case CXCursor_Constructor:
549 case CXCursor_Destructor:
550 case CXCursor_ConversionFunction:
551 path = functionName(cur);
552 break;
553 default:
554 break;
555 }
556 cur = clang_getCursorSemanticParent(cur);
557 kind = clang_getCursorKind(cur);
558 }
559 return path;
560}
561
562/*!
563 Find the node from the QDocDatabase \a qdb that corresponds to the declaration
564 represented by the cursor \a cur, if it exists.
565 */
566static Node *findNodeForCursor(QDocDatabase *qdb, CXCursor cur)
567{
568 auto kind = clang_getCursorKind(cur);
569 if (clang_isInvalid(kind))
570 return nullptr;
571 if (kind == CXCursor_TranslationUnit)
572 return qdb->primaryTreeRoot();
573
574 Node *p = findNodeForCursor(qdb, clang_getCursorSemanticParent(cur));
575 // Special case; if the cursor represents a template type|non-type|template parameter
576 // and its semantic parent is a function, return a pointer to the function node.
577 if (p && p->isFunction(Node::CPP)) {
578 switch (kind) {
579 case CXCursor_TemplateTypeParameter:
580 case CXCursor_NonTypeTemplateParameter:
581 case CXCursor_TemplateTemplateParameter:
582 return p;
583 default:
584 break;
585 }
586 }
587
588 // ...otherwise, the semantic parent must be an Aggregate node.
589 if (!p || !p->isAggregate())
590 return nullptr;
591 auto parent = static_cast<Aggregate *>(p);
592
593 QString name = fromCXString(clang_getCursorSpelling(cur));
594 switch (kind) {
595 case CXCursor_Namespace:
596 return parent->findNonfunctionChild(name, &Node::isNamespace);
597 case CXCursor_StructDecl:
598 case CXCursor_ClassDecl:
599 case CXCursor_UnionDecl:
600 case CXCursor_ClassTemplate:
601 return parent->findNonfunctionChild(name, &Node::isClassNode);
602 case CXCursor_FunctionDecl:
603 case CXCursor_FunctionTemplate:
604 case CXCursor_CXXMethod:
605 case CXCursor_Constructor:
606 case CXCursor_Destructor:
607 case CXCursor_ConversionFunction: {
608 NodeVector candidates;
609 parent->findChildren(functionName(cur), candidates);
610 // Hidden friend functions are recorded under their lexical parent in the database
611 if (candidates.isEmpty() && get_cursor_declaration(cur)->getFriendObjectKind() != clang::Decl::FOK_None) {
612 if (auto *lexical_parent = findNodeForCursor(qdb, clang_getCursorLexicalParent(cur));
613 lexical_parent && lexical_parent->isAggregate() && lexical_parent != parent) {
614 static_cast<Aggregate *>(lexical_parent)->findChildren(functionName(cur), candidates);
615 }
616 }
617
618 if (candidates.isEmpty())
619 return nullptr;
620
621 CXType funcType = clang_getCursorType(cur);
622 auto numArg = clang_getNumArgTypes(funcType);
623 bool isVariadic = clang_isFunctionTypeVariadic(funcType);
624 QVarLengthArray<QString, 20> args;
625
626 std::optional<RelaxedTemplateDeclaration> relaxed_template_declaration{std::nullopt};
627 if (kind == CXCursor_FunctionTemplate)
628 relaxed_template_declaration = get_template_declaration(
629 get_cursor_declaration(cur)->getAsFunction()->getDescribedFunctionTemplate()
630 );
631
632 for (Node *candidate : std::as_const(candidates)) {
633 if (!candidate->isFunction(Node::CPP))
634 continue;
635
636 auto fn = static_cast<FunctionNode *>(candidate);
637
638 if (!fn->templateDecl() && relaxed_template_declaration)
639 continue;
640
641 if (fn->templateDecl() && !relaxed_template_declaration)
642 continue;
643
644 if (fn->templateDecl() && relaxed_template_declaration &&
645 !are_template_declarations_substitutable(*fn->templateDecl(), *relaxed_template_declaration))
646 continue;
647
648 const Parameters &parameters = fn->parameters();
649
650 if (parameters.count() != numArg + isVariadic)
651 continue;
652
653 if (fn->isConst() != bool(clang_CXXMethod_isConst(cur)))
654 continue;
655
656 if (isVariadic && parameters.last().type() != QLatin1String("..."))
657 continue;
658
659 if (fn->isRef() != (clang_Type_getCXXRefQualifier(funcType) == CXRefQualifier_LValue))
660 continue;
661
662 if (fn->isRefRef() != (clang_Type_getCXXRefQualifier(funcType) == CXRefQualifier_RValue))
663 continue;
664
665 auto function_declaration = get_cursor_declaration(cur)->getAsFunction();
666
667 bool different = false;
668 for (int i = 0; i < numArg; ++i) {
669 CXType argType = clang_getArgType(funcType, i);
670
671 if (args.size() <= i)
672 args.append(QString::fromStdString(get_fully_qualified_type_name(
673 function_declaration->getParamDecl(i)->getOriginalType(),
674 function_declaration->getASTContext()
675 )));
676
677 QString recordedType = parameters.at(i).type();
678 QString typeSpelling = args.at(i);
679
680 different = recordedType != typeSpelling;
681
682 // Retry with a canonical type spelling
683 if (different && (argType.kind == CXType_Typedef || argType.kind == CXType_Elaborated)) {
684 QStringView canonicalType = parameters.at(i).canonicalType();
685 if (!canonicalType.isEmpty()) {
686 different = canonicalType !=
687 QString::fromStdString(get_fully_qualified_type_name(
688 function_declaration->getParamDecl(i)->getOriginalType().getCanonicalType(),
689 function_declaration->getASTContext()
690 ));
691 }
692 }
693
694 if (different) {
695 break;
696 }
697 }
698
699 if (!different)
700 return fn;
701 }
702 return nullptr;
703 }
704 case CXCursor_EnumDecl:
705 return parent->findNonfunctionChild(name, &Node::isEnumType);
706 case CXCursor_FieldDecl:
707 case CXCursor_VarDecl:
708 return parent->findNonfunctionChild(name, &Node::isVariable);
709 case CXCursor_TypedefDecl:
710 return parent->findNonfunctionChild(name, &Node::isTypedef);
711 default:
712 return nullptr;
713 }
714}
715
716static void setOverridesForFunction(FunctionNode *fn, CXCursor cursor)
717{
718 CXCursor *overridden;
719 unsigned int numOverridden = 0;
720 clang_getOverriddenCursors(cursor, &overridden, &numOverridden);
721 for (uint i = 0; i < numOverridden; ++i) {
722 QString path = reconstructQualifiedPathForCursor(overridden[i]);
723 if (!path.isEmpty()) {
724 fn->setOverride(true);
725 fn->setOverridesThis(path);
726 break;
727 }
728 }
729 clang_disposeOverriddenCursors(overridden);
730}
731
733{
734public:
735 ClangVisitor(QDocDatabase *qdb, const std::set<Config::HeaderFilePath> &allHeaders)
736 : qdb_(qdb), parent_(qdb->primaryTreeRoot())
737 {
738 std::transform(allHeaders.cbegin(), allHeaders.cend(), std::inserter(allHeaders_, allHeaders_.begin()),
739 [](const auto& header_file_path) { return header_file_path.filename; });
740 }
741
742 QDocDatabase *qdocDB() { return qdb_; }
743
744 CXChildVisitResult visitChildren(CXCursor cursor)
745 {
746 auto ret = visitChildrenLambda(cursor, [&](CXCursor cur) {
747 auto loc = clang_getCursorLocation(cur);
748 if (clang_Location_isFromMainFile(loc))
749 return visitSource(cur, loc);
750
751 CXFile file;
752 clang_getFileLocation(loc, &file, nullptr, nullptr, nullptr);
753 bool isInteresting = false;
754 auto it = isInterestingCache_.find(file);
755 if (it != isInterestingCache_.end()) {
756 isInteresting = *it;
757 } else {
758 QFileInfo fi(fromCXString(clang_getFileName(file)));
759 // Match by file name in case of PCH/installed headers
760 isInteresting = allHeaders_.find(fi.fileName()) != allHeaders_.end();
761 isInterestingCache_[file] = isInteresting;
762 }
763 if (isInteresting) {
764 return visitHeader(cur, loc);
765 }
766
767 return CXChildVisit_Continue;
768 });
769 return ret ? CXChildVisit_Break : CXChildVisit_Continue;
770 }
771
772 /*
773 Not sure about all the possibilities, when the cursor
774 location is not in the main file.
775 */
776 CXChildVisitResult visitFnArg(CXCursor cursor, Node **fnNode, bool &ignoreSignature)
777 {
778 auto ret = visitChildrenLambda(cursor, [&](CXCursor cur) {
779 auto loc = clang_getCursorLocation(cur);
780 if (clang_Location_isFromMainFile(loc))
781 return visitFnSignature(cur, loc, fnNode, ignoreSignature);
782 return CXChildVisit_Continue;
783 });
784 return ret ? CXChildVisit_Break : CXChildVisit_Continue;
785 }
786
787 Node *nodeForCommentAtLocation(CXSourceLocation loc, CXSourceLocation nextCommentLoc);
788
789private:
790 /*!
791 SimpleLoc represents a simple location in the main source file,
792 which can be used as a key in a QMap.
793 */
794 struct SimpleLoc
795 {
796 unsigned int line {}, column {};
797 friend bool operator<(const SimpleLoc &a, const SimpleLoc &b)
798 {
799 return a.line != b.line ? a.line < b.line : a.column < b.column;
800 }
801 };
802 /*!
803 \variable ClangVisitor::declMap_
804 Map of all the declarations in the source file so we can match them
805 with a documentation comment.
806 */
807 QMap<SimpleLoc, CXCursor> declMap_;
808
809 QDocDatabase *qdb_;
810 Aggregate *parent_;
811 std::set<QString> allHeaders_;
812 QHash<CXFile, bool> isInterestingCache_; // doing a canonicalFilePath is slow, so keep a cache.
813
814 /*!
815 Returns true if the symbol should be ignored for the documentation.
816 */
817 bool ignoredSymbol(const QString &symbolName)
818 {
819 if (symbolName == QLatin1String("QPrivateSignal"))
820 return true;
821 // Ignore functions generated by property macros
822 if (symbolName.startsWith("_qt_property_"))
823 return true;
824 // Ignore template argument deduction guides
825 if (symbolName.startsWith("<deduction guide"))
826 return true;
827 return false;
828 }
829
830 CXChildVisitResult visitSource(CXCursor cursor, CXSourceLocation loc);
831 CXChildVisitResult visitHeader(CXCursor cursor, CXSourceLocation loc);
832 CXChildVisitResult visitFnSignature(CXCursor cursor, CXSourceLocation loc, Node **fnNode,
833 bool &ignoreSignature);
834 void processFunction(FunctionNode *fn, CXCursor cursor);
835 bool parseProperty(const QString &spelling, const Location &loc);
836 void readParameterNamesAndAttributes(FunctionNode *fn, CXCursor cursor);
837 Aggregate *getSemanticParent(CXCursor cursor);
838};
839
840/*!
841 Visits a cursor in the .cpp file.
842 This fills the declMap_
843 */
844CXChildVisitResult ClangVisitor::visitSource(CXCursor cursor, CXSourceLocation loc)
845{
846 auto kind = clang_getCursorKind(cursor);
847 if (clang_isDeclaration(kind)) {
848 SimpleLoc l;
849 clang_getPresumedLocation(loc, nullptr, &l.line, &l.column);
850 declMap_.insert(l, cursor);
851 return CXChildVisit_Recurse;
852 }
853 return CXChildVisit_Continue;
854}
855
856/*!
857 If the semantic and lexical parent cursors of \a cursor are
858 not the same, find the Aggregate node for the semantic parent
859 cursor and return it. Otherwise return the current parent.
860 */
861Aggregate *ClangVisitor::getSemanticParent(CXCursor cursor)
862{
863 CXCursor sp = clang_getCursorSemanticParent(cursor);
864 CXCursor lp = clang_getCursorLexicalParent(cursor);
865 if (!clang_equalCursors(sp, lp) && clang_isDeclaration(clang_getCursorKind(sp))) {
866 Node *spn = findNodeForCursor(qdb_, sp);
867 if (spn && spn->isAggregate()) {
868 return static_cast<Aggregate *>(spn);
869 }
870 }
871 return parent_;
872}
873
874CXChildVisitResult ClangVisitor::visitFnSignature(CXCursor cursor, CXSourceLocation, Node **fnNode,
875 bool &ignoreSignature)
876{
877 switch (clang_getCursorKind(cursor)) {
878 case CXCursor_Namespace:
879 return CXChildVisit_Recurse;
880 case CXCursor_FunctionDecl:
881 case CXCursor_FunctionTemplate:
882 case CXCursor_CXXMethod:
883 case CXCursor_Constructor:
884 case CXCursor_Destructor:
885 case CXCursor_ConversionFunction: {
886 ignoreSignature = false;
887 if (ignoredSymbol(functionName(cursor))) {
888 *fnNode = nullptr;
889 ignoreSignature = true;
890 } else {
891 *fnNode = findNodeForCursor(qdb_, cursor);
892 if (*fnNode) {
893 if ((*fnNode)->isFunction(Node::CPP)) {
894 auto *fn = static_cast<FunctionNode *>(*fnNode);
895 readParameterNamesAndAttributes(fn, cursor);
896
897 const clang::Decl* declaration = get_cursor_declaration(cursor);
898 assert(declaration);
899 if (const auto function_declaration = declaration->getAsFunction()) {
900 auto declaredReturnType = function_declaration->getDeclaredReturnType();
901 if (llvm::dyn_cast_if_present<clang::AutoType>(declaredReturnType.getTypePtrOrNull()))
902 fn->setDeclaredReturnType(QString::fromStdString(declaredReturnType.getAsString()));
903 }
904 }
905 } else { // Possibly an implicitly generated special member
906 QString name = functionName(cursor);
907 if (ignoredSymbol(name))
908 return CXChildVisit_Continue;
909 Aggregate *semanticParent = getSemanticParent(cursor);
910 if (semanticParent && semanticParent->isClass()) {
911 auto *candidate = new FunctionNode(nullptr, name);
912 processFunction(candidate, cursor);
913 if (!candidate->isSpecialMemberFunction()) {
914 delete candidate;
915 return CXChildVisit_Continue;
916 }
917 candidate->setDefault(true);
918 semanticParent->addChild(*fnNode = candidate);
919 }
920 }
921 }
922 break;
923 }
924 default:
925 break;
926 }
927 return CXChildVisit_Continue;
928}
929
930CXChildVisitResult ClangVisitor::visitHeader(CXCursor cursor, CXSourceLocation loc)
931{
932 auto kind = clang_getCursorKind(cursor);
933
934 switch (kind) {
935 case CXCursor_TypeAliasTemplateDecl:
936 case CXCursor_TypeAliasDecl: {
937 QString aliasDecl = getSpelling(clang_getCursorExtent(cursor)).simplified();
938 QStringList typeAlias = aliasDecl.split(QLatin1Char('='));
939 if (typeAlias.size() == 2) {
940 typeAlias[0] = typeAlias[0].trimmed();
941 const QLatin1String usingString("using ");
942 qsizetype usingPos = typeAlias[0].indexOf(usingString);
943 if (usingPos != -1) {
944 typeAlias[0].remove(0, usingPos + usingString.size());
945 typeAlias[0] = typeAlias[0].split(QLatin1Char(' ')).first();
946 typeAlias[1] = typeAlias[1].trimmed();
947 auto *ta = new TypeAliasNode(parent_, typeAlias[0], typeAlias[1]);
948 ta->setAccess(fromCX_CXXAccessSpecifier(clang_getCXXAccessSpecifier(cursor)));
949 ta->setLocation(fromCXSourceLocation(clang_getCursorLocation(cursor)));
950
951 if (kind == CXCursor_TypeAliasTemplateDecl) {
952 auto template_decl = llvm::dyn_cast<clang::TemplateDecl>(get_cursor_declaration(cursor));
953 ta->setTemplateDecl(get_template_declaration(template_decl));
954 }
955 }
956 }
957 return CXChildVisit_Continue;
958 }
959 case CXCursor_StructDecl:
960 case CXCursor_UnionDecl:
961 if (fromCXString(clang_getCursorSpelling(cursor)).isEmpty()) // anonymous struct or union
962 return CXChildVisit_Continue;
963 Q_FALLTHROUGH();
964 case CXCursor_ClassTemplate:
965 Q_FALLTHROUGH();
966 case CXCursor_ClassDecl: {
967 if (!clang_isCursorDefinition(cursor))
968 return CXChildVisit_Continue;
969
970 if (findNodeForCursor(qdb_, cursor)) // Was already parsed, probably in another TU
971 return CXChildVisit_Continue;
972
973 QString className = fromCXString(clang_getCursorSpelling(cursor));
974
975 Aggregate *semanticParent = getSemanticParent(cursor);
976 if (semanticParent && semanticParent->findNonfunctionChild(className, &Node::isClassNode)) {
977 return CXChildVisit_Continue;
978 }
979
980 CXCursorKind actualKind = (kind == CXCursor_ClassTemplate) ?
981 clang_getTemplateCursorKind(cursor) : kind;
982
983 Node::NodeType type = Node::Class;
984 if (actualKind == CXCursor_StructDecl)
985 type = Node::Struct;
986 else if (actualKind == CXCursor_UnionDecl)
987 type = Node::Union;
988
989 auto *classe = new ClassNode(type, semanticParent, className);
990 classe->setAccess(fromCX_CXXAccessSpecifier(clang_getCXXAccessSpecifier(cursor)));
991 classe->setLocation(fromCXSourceLocation(clang_getCursorLocation(cursor)));
992
993 if (kind == CXCursor_ClassTemplate) {
994 auto template_declaration = llvm::dyn_cast<clang::TemplateDecl>(get_cursor_declaration(cursor));
995 classe->setTemplateDecl(get_template_declaration(template_declaration));
996 }
997
998 QScopedValueRollback<Aggregate *> setParent(parent_, classe);
999 return visitChildren(cursor);
1000 }
1001 case CXCursor_CXXBaseSpecifier: {
1002 if (!parent_->isClassNode())
1003 return CXChildVisit_Continue;
1004 auto access = fromCX_CXXAccessSpecifier(clang_getCXXAccessSpecifier(cursor));
1005 auto type = clang_getCursorType(cursor);
1006 auto baseCursor = clang_getTypeDeclaration(type);
1007 auto baseNode = findNodeForCursor(qdb_, baseCursor);
1008 auto classe = static_cast<ClassNode *>(parent_);
1009 if (baseNode == nullptr || !baseNode->isClassNode()) {
1010 QString bcName = reconstructQualifiedPathForCursor(baseCursor);
1011 classe->addUnresolvedBaseClass(access,
1012 bcName.split(QLatin1String("::"), Qt::SkipEmptyParts));
1013 return CXChildVisit_Continue;
1014 }
1015 auto baseClasse = static_cast<ClassNode *>(baseNode);
1016 classe->addResolvedBaseClass(access, baseClasse);
1017 return CXChildVisit_Continue;
1018 }
1019 case CXCursor_Namespace: {
1020 QString namespaceName = fromCXString(clang_getCursorDisplayName(cursor));
1021 NamespaceNode *ns = nullptr;
1022 if (parent_)
1023 ns = static_cast<NamespaceNode *>(
1024 parent_->findNonfunctionChild(namespaceName, &Node::isNamespace));
1025 if (!ns) {
1026 ns = new NamespaceNode(parent_, namespaceName);
1027 ns->setAccess(Access::Public);
1028 ns->setLocation(fromCXSourceLocation(clang_getCursorLocation(cursor)));
1029 }
1030 QScopedValueRollback<Aggregate *> setParent(parent_, ns);
1031 return visitChildren(cursor);
1032 }
1033 case CXCursor_FunctionTemplate:
1034 Q_FALLTHROUGH();
1035 case CXCursor_FunctionDecl:
1036 case CXCursor_CXXMethod:
1037 case CXCursor_Constructor:
1038 case CXCursor_Destructor:
1039 case CXCursor_ConversionFunction: {
1040 if (findNodeForCursor(qdb_, cursor)) // Was already parsed, probably in another TU
1041 return CXChildVisit_Continue;
1042 QString name = functionName(cursor);
1043 if (ignoredSymbol(name))
1044 return CXChildVisit_Continue;
1045 // constexpr constructors generate also a global instance; ignore
1046 if (kind == CXCursor_Constructor && parent_ == qdb_->primaryTreeRoot())
1047 return CXChildVisit_Continue;
1048
1049 auto *fn = new FunctionNode(parent_, name);
1050 CXSourceRange range = clang_Cursor_getCommentRange(cursor);
1051 if (!clang_Range_isNull(range)) {
1052 QString comment = getSpelling(range);
1053 if (comment.startsWith("//!")) {
1054 qsizetype tag = comment.indexOf(QChar('['));
1055 if (tag > 0) {
1056 qsizetype end = comment.indexOf(QChar(']'), ++tag);
1057 if (end > 0)
1058 fn->setTag(comment.mid(tag, end - tag));
1059 }
1060 }
1061 }
1062
1063 processFunction(fn, cursor);
1064
1065 if (kind == CXCursor_FunctionTemplate) {
1066 auto template_declaration = get_cursor_declaration(cursor)->getAsFunction()->getDescribedFunctionTemplate();
1067 fn->setTemplateDecl(get_template_declaration(template_declaration));
1068 }
1069
1070 return CXChildVisit_Continue;
1071 }
1072#if CINDEX_VERSION >= 36
1073 case CXCursor_FriendDecl: {
1074 return visitChildren(cursor);
1075 }
1076#endif
1077 case CXCursor_EnumDecl: {
1078 auto *en = static_cast<EnumNode *>(findNodeForCursor(qdb_, cursor));
1079 if (en && en->items().size())
1080 return CXChildVisit_Continue; // Was already parsed, probably in another TU
1081
1082 QString enumTypeName = fromCXString(clang_getCursorSpelling(cursor));
1083
1084 if (clang_Cursor_isAnonymous(cursor)) {
1085 enumTypeName = "anonymous";
1086 if (parent_ && (parent_->isClassNode() || parent_->isNamespace())) {
1087 Node *n = parent_->findNonfunctionChild(enumTypeName, &Node::isEnumType);
1088 if (n)
1089 en = static_cast<EnumNode *>(n);
1090 }
1091 }
1092 if (!en) {
1093 en = new EnumNode(parent_, enumTypeName, clang_EnumDecl_isScoped(cursor));
1094 en->setAccess(fromCX_CXXAccessSpecifier(clang_getCXXAccessSpecifier(cursor)));
1095 en->setLocation(fromCXSourceLocation(clang_getCursorLocation(cursor)));
1096 }
1097
1098 // Enum values
1099 visitChildrenLambda(cursor, [&](CXCursor cur) {
1100 if (clang_getCursorKind(cur) != CXCursor_EnumConstantDecl)
1101 return CXChildVisit_Continue;
1102
1103 QString value;
1104 visitChildrenLambda(cur, [&](CXCursor cur) {
1105 if (clang_isExpression(clang_getCursorKind(cur))) {
1106 value = getSpelling(clang_getCursorExtent(cur));
1107 return CXChildVisit_Break;
1108 }
1109 return CXChildVisit_Continue;
1110 });
1111 if (value.isEmpty()) {
1112 QLatin1String hex("0x");
1113 if (!en->items().isEmpty() && en->items().last().value().startsWith(hex)) {
1114 value = hex + QString::number(clang_getEnumConstantDeclValue(cur), 16);
1115 } else {
1116 value = QString::number(clang_getEnumConstantDeclValue(cur));
1117 }
1118 }
1119
1120 en->addItem(EnumItem(fromCXString(clang_getCursorSpelling(cur)), value));
1121 return CXChildVisit_Continue;
1122 });
1123 return CXChildVisit_Continue;
1124 }
1125 case CXCursor_FieldDecl:
1126 case CXCursor_VarDecl: {
1127 if (findNodeForCursor(qdb_, cursor)) // Was already parsed, probably in another TU
1128 return CXChildVisit_Continue;
1129
1130 auto value_declaration =
1131 llvm::dyn_cast<clang::ValueDecl>(get_cursor_declaration(cursor));
1132 assert(value_declaration);
1133
1134 auto access = fromCX_CXXAccessSpecifier(clang_getCXXAccessSpecifier(cursor));
1135 auto var = new VariableNode(parent_, fromCXString(clang_getCursorSpelling(cursor)));
1136
1137 var->setAccess(access);
1138 var->setLocation(fromCXSourceLocation(clang_getCursorLocation(cursor)));
1139 var->setLeftType(QString::fromStdString(get_fully_qualified_type_name(
1140 value_declaration->getType(),
1141 value_declaration->getASTContext()
1142 )));
1143 var->setStatic(kind == CXCursor_VarDecl && parent_->isClassNode());
1144
1145 return CXChildVisit_Continue;
1146 }
1147 case CXCursor_TypedefDecl: {
1148 if (findNodeForCursor(qdb_, cursor)) // Was already parsed, probably in another TU
1149 return CXChildVisit_Continue;
1150 auto *td = new TypedefNode(parent_, fromCXString(clang_getCursorSpelling(cursor)));
1151 td->setAccess(fromCX_CXXAccessSpecifier(clang_getCXXAccessSpecifier(cursor)));
1152 td->setLocation(fromCXSourceLocation(clang_getCursorLocation(cursor)));
1153 // Search to see if this is a Q_DECLARE_FLAGS (if the type is QFlags<ENUM>)
1154 visitChildrenLambda(cursor, [&](CXCursor cur) {
1155 if (clang_getCursorKind(cur) != CXCursor_TemplateRef
1156 || fromCXString(clang_getCursorSpelling(cur)) != QLatin1String("QFlags"))
1157 return CXChildVisit_Continue;
1158 // Found QFlags<XXX>
1159 visitChildrenLambda(cursor, [&](CXCursor cur) {
1160 if (clang_getCursorKind(cur) != CXCursor_TypeRef)
1161 return CXChildVisit_Continue;
1162 auto *en =
1163 findNodeForCursor(qdb_, clang_getTypeDeclaration(clang_getCursorType(cur)));
1164 if (en && en->isEnumType())
1165 static_cast<EnumNode *>(en)->setFlagsType(td);
1166 return CXChildVisit_Break;
1167 });
1168 return CXChildVisit_Break;
1169 });
1170 return CXChildVisit_Continue;
1171 }
1172 default:
1173 if (clang_isDeclaration(kind) && parent_->isClassNode()) {
1174 // may be a property macro or a static_assert
1175 // which is not exposed from the clang API
1176 parseProperty(getSpelling(clang_getCursorExtent(cursor)),
1177 fromCXSourceLocation(loc));
1178 }
1179 return CXChildVisit_Continue;
1180 }
1181}
1182
1183void ClangVisitor::readParameterNamesAndAttributes(FunctionNode *fn, CXCursor cursor)
1184{
1185 Parameters &parameters = fn->parameters();
1186 // Visit the parameters and attributes
1187 int i = 0;
1188 visitChildrenLambda(cursor, [&](CXCursor cur) {
1189 auto kind = clang_getCursorKind(cur);
1190 if (kind == CXCursor_AnnotateAttr) {
1191 QString annotation = fromCXString(clang_getCursorDisplayName(cur));
1192 if (annotation == QLatin1String("qt_slot")) {
1194 } else if (annotation == QLatin1String("qt_signal")) {
1196 }
1197 if (annotation == QLatin1String("qt_invokable"))
1198 fn->setInvokable(true);
1199 } else if (kind == CXCursor_CXXOverrideAttr) {
1200 fn->setOverride(true);
1201 } else if (kind == CXCursor_ParmDecl) {
1202 if (i >= parameters.count())
1203 return CXChildVisit_Break; // Attributes comes before parameters so we can break.
1204
1205 if (QString name = fromCXString(clang_getCursorSpelling(cur)); !name.isEmpty())
1206 parameters[i].setName(name);
1207
1208 const clang::ParmVarDecl* parameter_declaration = llvm::dyn_cast<const clang::ParmVarDecl>(get_cursor_declaration(cur));
1209 Q_ASSERT(parameter_declaration);
1210
1211 std::string default_value = get_default_value_initializer_as_string(parameter_declaration);
1212
1213 if (!default_value.empty())
1214 parameters[i].setDefaultValue(QString::fromStdString(default_value));
1215
1216 ++i;
1217 }
1218 return CXChildVisit_Continue;
1219 });
1220}
1221
1222void ClangVisitor::processFunction(FunctionNode *fn, CXCursor cursor)
1223{
1224 CXCursorKind kind = clang_getCursorKind(cursor);
1225 CXType funcType = clang_getCursorType(cursor);
1226 fn->setAccess(fromCX_CXXAccessSpecifier(clang_getCXXAccessSpecifier(cursor)));
1227 fn->setLocation(fromCXSourceLocation(clang_getCursorLocation(cursor)));
1228 fn->setStatic(clang_CXXMethod_isStatic(cursor));
1229 fn->setConst(clang_CXXMethod_isConst(cursor));
1230 fn->setVirtualness(!clang_CXXMethod_isVirtual(cursor)
1232 : clang_CXXMethod_isPureVirtual(cursor)
1235
1236 // REMARK: We assume that the following operations and casts are
1237 // generally safe.
1238 // Callers of those methods will generally check at the LibClang
1239 // level the kind of cursor we are dealing with and will pass on
1240 // only valid cursors that are of a function kind and that are at
1241 // least a declaration.
1242 //
1243 // Failure to do so implies a bug in the call chain and should be
1244 // dealt with as such.
1245 const clang::Decl* declaration = get_cursor_declaration(cursor);
1246
1247 assert(declaration);
1248
1249 const clang::FunctionDecl* function_declaration = declaration->getAsFunction();
1250
1251 if (kind == CXCursor_Constructor
1252 // a constructor template is classified as CXCursor_FunctionTemplate
1253 || (kind == CXCursor_FunctionTemplate && fn->name() == parent_->name()))
1255 else if (kind == CXCursor_Destructor)
1257 else
1258 fn->setReturnType(QString::fromStdString(get_fully_qualified_type_name(
1259 function_declaration->getReturnType(),
1260 function_declaration->getASTContext()
1261 )));
1262
1263 const clang::CXXConstructorDecl* constructor_declaration = llvm::dyn_cast<const clang::CXXConstructorDecl>(function_declaration);
1264
1265 if (constructor_declaration && constructor_declaration->isCopyConstructor()) fn->setMetaness(FunctionNode::CCtor);
1266 else if (constructor_declaration && constructor_declaration->isMoveConstructor()) fn->setMetaness(FunctionNode::MCtor);
1267
1268 const clang::CXXConversionDecl* conversion_declaration = llvm::dyn_cast<const clang::CXXConversionDecl>(function_declaration);
1269
1270 if (function_declaration->isConstexpr()) fn->markConstexpr();
1271 if (
1272 (constructor_declaration && constructor_declaration->isExplicit()) ||
1273 (conversion_declaration && conversion_declaration->isExplicit())
1274 ) fn->markExplicit();
1275
1276 const clang::CXXMethodDecl* method_declaration = llvm::dyn_cast<const clang::CXXMethodDecl>(function_declaration);
1277
1278 if (method_declaration && method_declaration->isCopyAssignmentOperator()) fn->setMetaness(FunctionNode::CAssign);
1279 else if (method_declaration && method_declaration->isMoveAssignmentOperator()) fn->setMetaness(FunctionNode::MAssign);
1280
1281 const clang::FunctionType* function_type = function_declaration->getFunctionType();
1282 const clang::FunctionProtoType* function_prototype = static_cast<const clang::FunctionProtoType*>(function_type);
1283
1284 if (function_prototype) {
1285 clang::FunctionProtoType::ExceptionSpecInfo exception_specification = function_prototype->getExceptionSpecInfo();
1286
1287 if (exception_specification.Type != clang::ExceptionSpecificationType::EST_None) {
1288 const std::string exception_specification_spelling =
1289 exception_specification.NoexceptExpr ? get_expression_as_string(
1290 exception_specification.NoexceptExpr,
1291 function_declaration->getASTContext()
1292 ) : "";
1293
1294 if (exception_specification_spelling != "false")
1295 fn->markNoexcept(QString::fromStdString(exception_specification_spelling));
1296 }
1297 }
1298
1299 CXRefQualifierKind refQualKind = clang_Type_getCXXRefQualifier(funcType);
1300 if (refQualKind == CXRefQualifier_LValue)
1301 fn->setRef(true);
1302 else if (refQualKind == CXRefQualifier_RValue)
1303 fn->setRefRef(true);
1304 // For virtual functions, determine what it overrides
1305 // (except for destructor for which we do not want to classify as overridden)
1306 if (!fn->isNonvirtual() && kind != CXCursor_Destructor)
1308
1309 Parameters &parameters = fn->parameters();
1310 parameters.clear();
1311 parameters.reserve(function_declaration->getNumParams());
1312
1313 for (clang::ParmVarDecl* const parameter_declaration : function_declaration->parameters()) {
1314 clang::QualType parameter_type = parameter_declaration->getOriginalType();
1315
1316 parameters.append(QString::fromStdString(get_fully_qualified_type_name(
1317 parameter_type,
1318 parameter_declaration->getASTContext()
1319 )));
1320
1321 if (!parameter_type.isCanonical())
1322 parameters.last().setCanonicalType(QString::fromStdString(get_fully_qualified_type_name(
1323 parameter_type.getCanonicalType(),
1324 parameter_declaration->getASTContext()
1325 )));
1326 }
1327
1328 if (parameters.count() > 0) {
1329 if (parameters.last().type().endsWith(QLatin1String("QPrivateSignal"))) {
1330 parameters.pop_back(); // remove the QPrivateSignal argument
1331 parameters.setPrivateSignal();
1332 }
1333 }
1334
1335 if (clang_isFunctionTypeVariadic(funcType))
1336 parameters.append(QStringLiteral("..."));
1337 readParameterNamesAndAttributes(fn, cursor);
1338
1339 if (declaration->getFriendObjectKind() != clang::Decl::FOK_None)
1340 fn->setRelatedNonmember(true);
1341}
1342
1343bool ClangVisitor::parseProperty(const QString &spelling, const Location &loc)
1344{
1345 if (!spelling.startsWith(QLatin1String("Q_PROPERTY"))
1346 && !spelling.startsWith(QLatin1String("QDOC_PROPERTY"))
1347 && !spelling.startsWith(QLatin1String("Q_OVERRIDE")))
1348 return false;
1349
1350 qsizetype lpIdx = spelling.indexOf(QChar('('));
1351 qsizetype rpIdx = spelling.lastIndexOf(QChar(')'));
1352 if (lpIdx <= 0 || rpIdx <= lpIdx)
1353 return false;
1354
1355 QString signature = spelling.mid(lpIdx + 1, rpIdx - lpIdx - 1);
1356 signature = signature.simplified();
1357 QStringList parts = signature.split(QChar(' '), Qt::SkipEmptyParts);
1358
1359 static const QStringList attrs =
1360 QStringList() << "READ" << "MEMBER" << "WRITE"
1361 << "NOTIFY" << "CONSTANT" << "FINAL"
1362 << "REQUIRED" << "BINDABLE" << "DESIGNABLE"
1363 << "RESET" << "REVISION" << "SCRIPTABLE"
1364 << "STORED" << "USER";
1365
1366 // Find the location of the first attribute. All preceding parts
1367 // represent the property type + name.
1368 auto it = std::find_if(parts.cbegin(), parts.cend(),
1369 [](const QString &attr) -> bool {
1370 return attrs.contains(attr);
1371 });
1372
1373 if (it == parts.cend() || std::distance(parts.cbegin(), it) < 2)
1374 return false;
1375
1376 QStringList typeParts;
1377 std::copy(parts.cbegin(), it, std::back_inserter(typeParts));
1378 parts.erase(parts.cbegin(), it);
1379 QString name = typeParts.takeLast();
1380
1381 // Move the pointer operator(s) from name to type
1382 while (!name.isEmpty() && name.front() == QChar('*')) {
1383 typeParts.last().push_back(name.front());
1384 name.removeFirst();
1385 }
1386
1387 // Need at least READ or MEMBER + getter/member name
1388 if (parts.size() < 2 || name.isEmpty())
1389 return false;
1390
1391 auto *property = new PropertyNode(parent_, name);
1392 property->setAccess(Access::Public);
1393 property->setLocation(loc);
1394 property->setDataType(typeParts.join(QChar(' ')));
1395
1396 int i = 0;
1397 while (i < parts.size()) {
1398 const QString &key = parts.at(i++);
1399 // Keywords with no associated values
1400 if (key == "CONSTANT") {
1401 property->setConstant();
1402 } else if (key == "REQUIRED") {
1403 property->setRequired();
1404 }
1405 if (i < parts.size()) {
1406 QString value = parts.at(i++);
1407 if (key == "READ") {
1408 qdb_->addPropertyFunction(property, value, PropertyNode::FunctionRole::Getter);
1409 } else if (key == "WRITE") {
1410 qdb_->addPropertyFunction(property, value, PropertyNode::FunctionRole::Setter);
1411 property->setWritable(true);
1412 } else if (key == "MEMBER") {
1413 property->setWritable(true);
1414 } else if (key == "STORED") {
1415 property->setStored(value.toLower() == "true");
1416 } else if (key == "BINDABLE") {
1417 property->setPropertyType(PropertyNode::PropertyType::BindableProperty);
1418 qdb_->addPropertyFunction(property, value, PropertyNode::FunctionRole::Bindable);
1419 } else if (key == "RESET") {
1420 qdb_->addPropertyFunction(property, value, PropertyNode::FunctionRole::Resetter);
1421 } else if (key == "NOTIFY") {
1422 qdb_->addPropertyFunction(property, value, PropertyNode::FunctionRole::Notifier);
1423 }
1424 }
1425 }
1426 return true;
1427}
1428
1429/*!
1430 Given a comment at location \a loc, return a Node for this comment
1431 \a nextCommentLoc is the location of the next comment so the declaration
1432 must be inbetween.
1433 Returns nullptr if no suitable declaration was found between the two comments.
1434 */
1435Node *ClangVisitor::nodeForCommentAtLocation(CXSourceLocation loc, CXSourceLocation nextCommentLoc)
1436{
1437 ClangVisitor::SimpleLoc docloc;
1438 clang_getPresumedLocation(loc, nullptr, &docloc.line, &docloc.column);
1439 auto decl_it = declMap_.upperBound(docloc);
1440 if (decl_it == declMap_.end())
1441 return nullptr;
1442
1443 unsigned int declLine = decl_it.key().line;
1444 unsigned int nextCommentLine;
1445 clang_getPresumedLocation(nextCommentLoc, nullptr, &nextCommentLine, nullptr);
1446 if (nextCommentLine < declLine)
1447 return nullptr; // there is another comment before the declaration, ignore it.
1448
1449 // make sure the previous decl was finished.
1450 if (decl_it != declMap_.begin()) {
1451 CXSourceLocation prevDeclEnd = clang_getRangeEnd(clang_getCursorExtent(*(std::prev(decl_it))));
1452 unsigned int prevDeclLine;
1453 clang_getPresumedLocation(prevDeclEnd, nullptr, &prevDeclLine, nullptr);
1454 if (prevDeclLine >= docloc.line) {
1455 // The previous declaration was still going. This is only valid if the previous
1456 // declaration is a parent of the next declaration.
1457 auto parent = clang_getCursorLexicalParent(*decl_it);
1458 if (!clang_equalCursors(parent, *(std::prev(decl_it))))
1459 return nullptr;
1460 }
1461 }
1462 auto *node = findNodeForCursor(qdb_, *decl_it);
1463 // borrow the parameter name from the definition
1464 if (node && node->isFunction(Node::CPP))
1465 readParameterNamesAndAttributes(static_cast<FunctionNode *>(node), *decl_it);
1466 return node;
1467}
1468
1470 QDocDatabase* qdb,
1471 Config& config,
1472 const std::vector<QByteArray>& include_paths,
1473 const QList<QByteArray>& defines,
1474 std::optional<std::reference_wrapper<const PCHFile>> pch
1475) : m_qdb{qdb},
1478 m_pch{pch}
1479{
1480 m_allHeaders = config.getHeaderFiles();
1481}
1482
1483static const char *defaultArgs_[] = {
1484/*
1485 https://bugreports.qt.io/browse/QTBUG-94365
1486 An unidentified bug in Clang 15.x causes parsing failures due to errors in
1487 the AST. This replicates only with C++20 support enabled - avoid the issue
1488 by using C++17 with Clang 15.
1489 */
1490#if LIBCLANG_VERSION_MAJOR == 15
1491 "-std=c++17",
1492#else
1493 "-std=c++20",
1494#endif
1495#ifndef Q_OS_WIN
1496 "-fPIC",
1497#else
1498 "-fms-compatibility-version=19",
1499#endif
1500 "-DQ_QDOC",
1501 "-DQ_CLANG_QDOC",
1502 "-DQT_DISABLE_DEPRECATED_UP_TO=0",
1503 "-DQT_ANNOTATE_CLASS(type,...)=static_assert(sizeof(#__VA_ARGS__),#type);",
1504 "-DQT_ANNOTATE_CLASS2(type,a1,a2)=static_assert(sizeof(#a1,#a2),#type);",
1505 "-DQT_ANNOTATE_FUNCTION(a)=__attribute__((annotate(#a)))",
1506 "-DQT_ANNOTATE_ACCESS_SPECIFIER(a)=__attribute__((annotate(#a)))",
1507 "-Wno-constant-logical-operand",
1508 "-Wno-macro-redefined",
1509 "-Wno-nullability-completeness",
1510 "-fvisibility=default",
1511 "-ferror-limit=0",
1512 ("-I" CLANG_RESOURCE_DIR),
1513 "-xc++"
1514};
1515
1516/*!
1517 Load the default arguments and the defines into \a args.
1518 Clear \a args first.
1519 */
1520void getDefaultArgs(const QList<QByteArray>& defines, std::vector<const char*>& args)
1521{
1522 args.clear();
1523 args.insert(args.begin(), std::begin(defaultArgs_), std::end(defaultArgs_));
1524
1525 // Add the defines from the qdocconf file.
1526 for (const auto &p : std::as_const(defines))
1527 args.push_back(p.constData());
1528}
1529
1530static QList<QByteArray> includePathsFromHeaders(const std::set<Config::HeaderFilePath> &allHeaders)
1531{
1532 QList<QByteArray> result;
1533 for (const auto& [header_path, _] : allHeaders) {
1534 const QByteArray path = "-I" + header_path.toLatin1();
1535 const QByteArray parent =
1536 "-I" + QDir::cleanPath(header_path + QLatin1String("/../")).toLatin1();
1537 }
1538
1539 return result;
1540}
1541
1542/*!
1543 Load the include paths into \a moreArgs. If no include paths
1544 were provided, try to guess reasonable include paths.
1545 */
1547 const std::vector<QByteArray>& include_paths,
1548 const std::set<Config::HeaderFilePath>& all_headers,
1549 std::vector<const char*>& args
1550) {
1551 if (include_paths.empty()) {
1552 /*
1553 The include paths provided are inadequate. Make a list
1554 of reasonable places to look for include files and use
1555 that list instead.
1556 */
1557 qCWarning(lcQdoc) << "No include paths passed to qdoc; guessing reasonable include paths";
1558
1559 QString basicIncludeDir = QDir::cleanPath(QString(Config::installDir + "/../include"));
1560 args.emplace_back(QByteArray("-I" + basicIncludeDir.toLatin1()).constData());
1561
1562 auto include_paths_from_headers = includePathsFromHeaders(all_headers);
1563 args.insert(args.end(), include_paths_from_headers.begin(), include_paths_from_headers.end());
1564 } else {
1565 std::copy(include_paths.begin(), include_paths.end(), std::back_inserter(args));
1566 }
1567}
1568
1569/*!
1570 Building the PCH must be possible when there are no .cpp
1571 files, so it is moved here to its own member function, and
1572 it is called after the list of header files is complete.
1573 */
1575 QDocDatabase* qdb,
1576 QString module_header,
1577 const std::set<Config::HeaderFilePath>& all_headers,
1578 const std::vector<QByteArray>& include_paths,
1579 const QList<QByteArray>& defines
1580) {
1581 static std::vector<const char*> arguments{};
1582
1583 if (module_header.isEmpty()) return std::nullopt;
1584
1585 getDefaultArgs(defines, arguments);
1586 getMoreArgs(include_paths, all_headers, arguments);
1587
1588 flags_ = static_cast<CXTranslationUnit_Flags>(CXTranslationUnit_Incomplete
1589 | CXTranslationUnit_SkipFunctionBodies
1590 | CXTranslationUnit_KeepGoing);
1591
1592 CompilationIndex index{ clang_createIndex(1, kClangDontDisplayDiagnostics) };
1593
1594 QTemporaryDir pch_directory{QDir::tempPath() + QLatin1String("/qdoc_pch")};
1595 if (!pch_directory.isValid()) return std::nullopt;
1596
1597 const QByteArray module = module_header.toUtf8();
1598 QByteArray header;
1599
1600 qCDebug(lcQdoc) << "Build and visit PCH for" << module_header;
1601 // A predicate for std::find_if() to locate a path to the module's header
1602 // (e.g. QtGui/QtGui) to be used as pre-compiled header
1603 struct FindPredicate
1604 {
1605 enum SearchType { Any, Module };
1606 QByteArray &candidate_;
1607 const QByteArray &module_;
1608 SearchType type_;
1609 FindPredicate(QByteArray &candidate, const QByteArray &module,
1610 SearchType type = Any)
1611 : candidate_(candidate), module_(module), type_(type)
1612 {
1613 }
1614
1615 bool operator()(const QByteArray &p) const
1616 {
1617 if (type_ != Any && !p.endsWith(module_))
1618 return false;
1619 candidate_ = p + "/";
1620 candidate_.append(module_);
1621 if (p.startsWith("-I"))
1622 candidate_ = candidate_.mid(2);
1623 return QFile::exists(QString::fromUtf8(candidate_));
1624 }
1625 };
1626
1627 // First, search for an include path that contains the module name, then any path
1628 QByteArray candidate;
1629 auto it = std::find_if(include_paths.begin(), include_paths.end(),
1630 FindPredicate(candidate, module, FindPredicate::Module));
1631 if (it == include_paths.end())
1632 it = std::find_if(include_paths.begin(), include_paths.end(),
1633 FindPredicate(candidate, module, FindPredicate::Any));
1634 if (it != include_paths.end())
1635 header = candidate;
1636
1637 if (header.isEmpty()) {
1638 qWarning() << "(qdoc) Could not find the module header in include paths for module"
1639 << module << " (include paths: " << include_paths << ")";
1640 qWarning() << " Artificial module header built from header dirs in qdocconf "
1641 "file";
1642 }
1643 arguments.push_back("-xc++");
1644
1645 TranslationUnit tu;
1646
1647 QString tmpHeader = pch_directory.path() + "/" + module;
1648 if (QFile tmpHeaderFile(tmpHeader); tmpHeaderFile.open(QIODevice::Text | QIODevice::WriteOnly)) {
1649 QTextStream out(&tmpHeaderFile);
1650 if (header.isEmpty()) {
1651 for (const auto& [header_path, header_name] : all_headers) {
1652 if (!header_name.endsWith(QLatin1String("_p.h"))
1653 && !header_name.startsWith(QLatin1String("moc_"))) {
1654 QString line = QLatin1String("#include \"") + header_path
1655 + QLatin1String("/") + header_name + QLatin1String("\"");
1656 out << line << "\n";
1657
1658 }
1659 }
1660 } else {
1661 QFileInfo headerFile(header);
1662 if (!headerFile.exists()) {
1663 qWarning() << "Could not find module header file" << header;
1664 return std::nullopt;
1665 }
1666 out << QLatin1String("#include \"") + header + QLatin1String("\"");
1667 }
1668 }
1669
1670 CXErrorCode err =
1671 clang_parseTranslationUnit2(index, tmpHeader.toLatin1().data(), arguments.data(),
1672 static_cast<int>(arguments.size()), nullptr, 0,
1673 flags_ | CXTranslationUnit_ForSerialization, &tu.tu);
1674 qCDebug(lcQdoc) << __FUNCTION__ << "clang_parseTranslationUnit2(" << tmpHeader << arguments
1675 << ") returns" << err;
1676
1678
1679 if (err || !tu) {
1680 qCCritical(lcQdoc) << "Could not create PCH file for " << module_header;
1681 return std::nullopt;
1682 }
1683
1684 QByteArray pch_name = pch_directory.path().toUtf8() + "/" + module + ".pch";
1685 auto error = clang_saveTranslationUnit(tu, pch_name.constData(),
1686 clang_defaultSaveOptions(tu));
1687 if (error) {
1688 qCCritical(lcQdoc) << "Could not save PCH file for" << module_header;
1689 return std::nullopt;
1690 }
1691
1692 // Visit the header now, as token from pre-compiled header won't be visited
1693 // later
1694 CXCursor cur = clang_getTranslationUnitCursor(tu);
1695 ClangVisitor visitor(qdb, all_headers);
1696 visitor.visitChildren(cur);
1697 qCDebug(lcQdoc) << "PCH built and visited for" << module_header;
1698
1699 return std::make_optional(PCHFile{std::move(pch_directory), pch_name});
1700}
1701
1702static float getUnpatchedVersion(QString t)
1703{
1704 if (t.count(QChar('.')) > 1)
1705 t.truncate(t.lastIndexOf(QChar('.')));
1706 return t.toFloat();
1707}
1708
1709/*!
1710 Get ready to parse the C++ cpp file identified by \a filePath
1711 and add its parsed contents to the database. \a location is
1712 used for reporting errors.
1713
1714 If parsing C++ header file as source, do not use the precompiled
1715 header as the source file itself is likely already included in the
1716 PCH and therefore interferes visiting the TU's children.
1717 */
1718ParsedCppFileIR ClangCodeParser::parse_cpp_file(const QString &filePath)
1719{
1720 flags_ = static_cast<CXTranslationUnit_Flags>(CXTranslationUnit_Incomplete
1721 | CXTranslationUnit_SkipFunctionBodies
1722 | CXTranslationUnit_KeepGoing);
1723
1724 CompilationIndex index{ clang_createIndex(1, kClangDontDisplayDiagnostics) };
1725
1726 getDefaultArgs(m_defines, m_args);
1727 if (m_pch && !filePath.endsWith(".mm")
1728 && !std::holds_alternative<CppHeaderSourceFile>(tag_source_file(filePath).second)) {
1729 m_args.push_back("-w");
1730 m_args.push_back("-include-pch");
1731 m_args.push_back((*m_pch).get().name.constData());
1732 }
1733 getMoreArgs(m_includePaths, m_allHeaders, m_args);
1734
1735 TranslationUnit tu;
1736 CXErrorCode err =
1737 clang_parseTranslationUnit2(index, filePath.toLocal8Bit(), m_args.data(),
1738 static_cast<int>(m_args.size()), nullptr, 0, flags_, &tu.tu);
1739 qCDebug(lcQdoc) << __FUNCTION__ << "clang_parseTranslationUnit2(" << filePath << m_args
1740 << ") returns" << err;
1742
1743 if (err || !tu) {
1744 qWarning() << "(qdoc) Could not parse source file" << filePath << " error code:" << err;
1745 return {};
1746 }
1747
1748 ParsedCppFileIR parse_result{};
1749
1750 CXCursor tuCur = clang_getTranslationUnitCursor(tu);
1751 ClangVisitor visitor(m_qdb, m_allHeaders);
1752 visitor.visitChildren(tuCur);
1753
1754 CXToken *tokens;
1755 unsigned int numTokens = 0;
1756 const QSet<QString> &commands = CppCodeParser::topic_commands + CppCodeParser::meta_commands;
1757 clang_tokenize(tu, clang_getCursorExtent(tuCur), &tokens, &numTokens);
1758
1759 for (unsigned int i = 0; i < numTokens; ++i) {
1760 if (clang_getTokenKind(tokens[i]) != CXToken_Comment)
1761 continue;
1762 QString comment = fromCXString(clang_getTokenSpelling(tu, tokens[i]));
1763 if (!comment.startsWith("/*!"))
1764 continue;
1765
1766 auto commentLoc = clang_getTokenLocation(tu, tokens[i]);
1767 auto loc = fromCXSourceLocation(commentLoc);
1768 auto end_loc = fromCXSourceLocation(clang_getRangeEnd(clang_getTokenExtent(tu, tokens[i])));
1769 Doc::trimCStyleComment(loc, comment);
1770
1771 // Doc constructor parses the comment.
1772 Doc doc(loc, end_loc, comment, commands, CppCodeParser::topic_commands);
1773 if (hasTooManyTopics(doc))
1774 continue;
1775
1776 if (doc.topicsUsed().isEmpty()) {
1777 Node *n = nullptr;
1778 if (i + 1 < numTokens) {
1779 // Try to find the next declaration.
1780 CXSourceLocation nextCommentLoc = commentLoc;
1781 while (i + 2 < numTokens && clang_getTokenKind(tokens[i + 1]) != CXToken_Comment)
1782 ++i; // already skip all the tokens that are not comments
1783 nextCommentLoc = clang_getTokenLocation(tu, tokens[i + 1]);
1784 n = visitor.nodeForCommentAtLocation(commentLoc, nextCommentLoc);
1785 }
1786
1787 if (n) {
1788 parse_result.tied.emplace_back(TiedDocumentation{doc, n});
1789 } else if (CodeParser::isWorthWarningAbout(doc)) {
1790 bool future = false;
1791 if (doc.metaCommandsUsed().contains(COMMAND_SINCE)) {
1792 QString sinceVersion = doc.metaCommandArgs(COMMAND_SINCE).at(0).first;
1793 if (getUnpatchedVersion(sinceVersion) >
1794 getUnpatchedVersion(Config::instance().get(CONFIG_VERSION).asString()))
1795 future = true;
1796 }
1797 if (!future) {
1798 doc.location().warning(
1799 QStringLiteral("Cannot tie this documentation to anything"),
1800 QStringLiteral("qdoc found a /*! ... */ comment, but there was no "
1801 "topic command (e.g., '\\%1', '\\%2') in the "
1802 "comment and no function definition following "
1803 "the comment.")
1804 .arg(COMMAND_FN, COMMAND_PAGE));
1805 }
1806 }
1807 } else {
1808 parse_result.untied.emplace_back(UntiedDocumentation{doc, QStringList()});
1809
1810 CXCursor cur = clang_getCursor(tu, commentLoc);
1811 while (true) {
1812 CXCursorKind kind = clang_getCursorKind(cur);
1813 if (clang_isTranslationUnit(kind) || clang_isInvalid(kind))
1814 break;
1815 if (kind == CXCursor_Namespace) {
1816 parse_result.untied.back().context << fromCXString(clang_getCursorSpelling(cur));
1817 }
1818 cur = clang_getCursorLexicalParent(cur);
1819 }
1820 }
1821 }
1822
1823 clang_disposeTokens(tu, tokens, numTokens);
1824 m_namespaceScope.clear();
1825 s_fn.clear();
1826
1827 return parse_result;
1828}
1829
1830/*!
1831 Use clang to parse the function signature from a function
1832 command. \a location is used for reporting errors. \a fnSignature
1833 is the string to parse. It is always a function decl.
1834 \a idTag is the optional bracketed argument passed to \\fn, or
1835 an empty string.
1836 \a context is a string list representing the scope (namespaces)
1837 under which the function is declared.
1838
1839 Returns a variant that's either a Node instance tied to the
1840 function declaration, or a parsing failure for later processing.
1841 */
1842std::variant<Node*, FnMatchError> FnCommandParser::operator()(const Location &location, const QString &fnSignature,
1843 const QString &idTag, QStringList context)
1844{
1845 Node *fnNode = nullptr;
1846 /*
1847 If the \fn command begins with a tag, then don't try to
1848 parse the \fn command with clang. Use the tag to search
1849 for the correct function node. It is an error if it can
1850 not be found. Return 0 in that case.
1851 */
1852 if (!idTag.isEmpty()) {
1853 fnNode = m_qdb->findFunctionNodeForTag(idTag);
1854 if (!fnNode) {
1855 location.error(
1856 QStringLiteral("tag \\fn [%1] not used in any include file in current module").arg(idTag));
1857 } else {
1858 /*
1859 The function node was found. Use the formal
1860 parameter names from the \fn command, because
1861 they will be the names used in the documentation.
1862 */
1863 auto *fn = static_cast<FunctionNode *>(fnNode);
1864 QStringList leftParenSplit = fnSignature.mid(fnSignature.indexOf(fn->name())).split('(');
1865 if (leftParenSplit.size() > 1) {
1866 QStringList rightParenSplit = leftParenSplit[1].split(')');
1867 if (!rightParenSplit.empty()) {
1868 QString params = rightParenSplit[0];
1869 if (!params.isEmpty()) {
1870 QStringList commaSplit = params.split(',');
1871 Parameters &parameters = fn->parameters();
1872 if (parameters.count() == commaSplit.size()) {
1873 for (int i = 0; i < parameters.count(); ++i) {
1874 QStringList blankSplit = commaSplit[i].split(' ', Qt::SkipEmptyParts);
1875 if (blankSplit.size() > 1) {
1876 QString pName = blankSplit.last();
1877 // Remove any non-letters from the start of parameter name
1878 auto it = std::find_if(std::begin(pName), std::end(pName),
1879 [](const QChar &c) { return c.isLetter(); });
1880 parameters[i].setName(
1881 pName.remove(0, std::distance(std::begin(pName), it)));
1882 }
1883 }
1884 }
1885 }
1886 }
1887 }
1888 }
1889 return fnNode;
1890 }
1891 auto flags = static_cast<CXTranslationUnit_Flags>(CXTranslationUnit_Incomplete
1892 | CXTranslationUnit_SkipFunctionBodies
1893 | CXTranslationUnit_KeepGoing);
1894
1895 CompilationIndex index{ clang_createIndex(1, kClangDontDisplayDiagnostics) };
1896
1897 getDefaultArgs(m_defines, m_args);
1898
1899 if (m_pch) {
1900 m_args.push_back("-w");
1901 m_args.push_back("-include-pch");
1902 m_args.push_back((*m_pch).get().name.constData());
1903 }
1904
1905 TranslationUnit tu;
1906 QByteArray s_fn{};
1907 for (const auto &ns : std::as_const(context))
1908 s_fn.prepend("namespace " + ns.toUtf8() + " {");
1909 s_fn += fnSignature.toUtf8();
1910 if (!s_fn.endsWith(";"))
1911 s_fn += "{ }";
1912 s_fn.append(context.size(), '}');
1913
1914 const char *dummyFileName = fnDummyFileName;
1915 CXUnsavedFile unsavedFile { dummyFileName, s_fn.constData(),
1916 static_cast<unsigned long>(s_fn.size()) };
1917 CXErrorCode err = clang_parseTranslationUnit2(index, dummyFileName, m_args.data(),
1918 int(m_args.size()), &unsavedFile, 1, flags, &tu.tu);
1919 qCDebug(lcQdoc) << __FUNCTION__ << "clang_parseTranslationUnit2(" << dummyFileName << m_args
1920 << ") returns" << err;
1922 if (err || !tu) {
1923 location.error(QStringLiteral("clang could not parse \\fn %1").arg(fnSignature));
1924 return fnNode;
1925 } else {
1926 /*
1927 Always visit the tu if one is constructed, because
1928 it might be possible to find the correct node, even
1929 if clang detected diagnostics. Only bother to report
1930 the diagnostics if they stop us finding the node.
1931 */
1932 CXCursor cur = clang_getTranslationUnitCursor(tu);
1933 ClangVisitor visitor(m_qdb, m_allHeaders);
1934 bool ignoreSignature = false;
1935 visitor.visitFnArg(cur, &fnNode, ignoreSignature);
1936
1937 if (!fnNode) {
1938 unsigned diagnosticCount = clang_getNumDiagnostics(tu);
1939 const auto &config = Config::instance();
1940 if (diagnosticCount > 0 && (!config.preparing() || config.singleExec())) {
1941 return FnMatchError{ fnSignature, location };
1942 }
1943 }
1944 }
1945 return fnNode;
1946}
1947
1948QT_END_NAMESPACE
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)
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)
Building the PCH must be possible when there are no .cpp files, so it is moved here to its own member...
static Location fromCXSourceLocation(CXSourceLocation location)
convert a CXSourceLocation to a qdoc Location
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_[]
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)
QDocDatabase * qdocDB()
CXChildVisitResult visitFnArg(CXCursor cursor, Node **fnNode, bool &ignoreSignature)
The ClassNode represents a C++ class.
Definition classnode.h:21
void addResolvedBaseClass(Access access, ClassNode *node)
Adds the base class node to this class's list of base classes.
Definition classnode.cpp:27
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...
Definition config.h:84
Definition doc.h:31
const Location & location() const
Returns the starting location of a qdoc comment.
Definition doc.cpp:90
TopicList topicsUsed() const
Returns a reference to the list of topic commands used in the current qdoc comment.
Definition doc.cpp:238
void setFlagsType(TypedefNode *typedefNode)
Definition enumnode.cpp:76
This node is used to represent any kind of function being documented.
void setConst(bool b)
void setStatic(bool b)
void setVirtualness(Virtualness virtualness)
bool isNonvirtual() const
void setInvokable(bool b)
void setRef(bool b)
void setOverride(bool b)
void setRefRef(bool b)
void markConstexpr()
void markExplicit()
void setMetaness(Metaness metaness)
Parameters & parameters()
The Location class provides a way to mark a location in a file.
Definition location.h:15
void setColumnNo(int no)
Definition location.h:36
void setLineNo(int no)
Definition location.h:35
This class represents a C++ namespace.
void setAccess(Access t)
Sets the node's access type to t.
Definition node.h:203
NodeType
An unsigned char value that identifies an object as a particular subclass of Node.
Definition node.h:54
@ Struct
Definition node.h:58
@ Union
Definition node.h:59
@ Class
Definition node.h:57
bool isNamespace() const
Returns true if the node type is Namespace.
Definition node.h:143
bool isTypedef() const
Returns true if the node type is Typedef.
Definition node.h:160
bool isFunction(Genus g=DontCare) const
Returns true if this is a FunctionNode and its Genus is set to g.
Definition node.h:135
bool isEnumType() const
Returns true if the node type is Enum.
Definition node.h:132
bool isVariable() const
Returns true if the node type is Variable.
Definition node.h:165
void setLocation(const Location &t)
Sets the node's declaration location, its definition location, or both, depending on the suffix of th...
Definition node.cpp:886
virtual bool isAggregate() const
Returns true if this node is an aggregate, which means it inherits Aggregate and can therefore have c...
Definition node.h:170
virtual void setRelatedNonmember(bool b)
Sets a flag in the node indicating whether this node is a related nonmember of something.
Definition node.h:218
@ CPP
Definition node.h:83
bool isClass() const
Returns true if the node type is Class.
Definition node.h:129
LinkType
An unsigned char value that probably should be moved out of the Node base class.
Definition node.h:112
virtual bool isClassNode() const
Returns true if this is an instance of ClassNode.
Definition node.h:177
A class for parsing and managing a function parameter list.
Definition parameters.h:57
Parameter & operator[](int index)
Definition parameters.h:77
void pop_back()
Definition parameters.h:81
void reserve(int count)
Definition parameters.h:73
void clear()
Definition parameters.h:62
Parameter & last()
Definition parameters.h:75
int count() const
Definition parameters.h:72
void setPrivateSignal()
Definition parameters.h:82
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 COMMAND_SINCE
Definition codeparser.h:73
#define COMMAND_FN
Definition codeparser.h:26
#define COMMAND_PAGE
Definition codeparser.h:44
#define CONFIG_VERSION
Definition config.h:394
bool hasTooManyTopics(const Doc &doc)
Checks if there are too many topic commands in doc.
std::string getFullyQualifiedName(QualType QT, const ASTContext &Ctx, const PrintingPolicy &Policy, bool WithGlobalNsPrefix=false)
QList< Node * > NodeVector
Definition node.h:43
#define assert
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.
Definition parsererror.h:13
CXTranslationUnit tu