File indexing completed on 2024-05-12 05:40:59

0001 /*
0002     SPDX-FileCopyrightText: 2016 Sergio Martins <smartins@kde.org>
0003 
0004     SPDX-License-Identifier: LGPL-2.0-or-later
0005 */
0006 
0007 #include "ctor-missing-parent-argument.h"
0008 #include "QtUtils.h"
0009 #include "StringUtils.h"
0010 #include "TypeUtils.h"
0011 
0012 #include <clang/AST/DeclBase.h>
0013 #include <clang/AST/DeclCXX.h>
0014 #include <clang/Basic/LLVM.h>
0015 #include <clang/Basic/SourceManager.h>
0016 #include <llvm/ADT/StringRef.h>
0017 #include <llvm/Support/Casting.h>
0018 
0019 class ClazyContext;
0020 
0021 using namespace clang;
0022 
0023 CtorMissingParentArgument::CtorMissingParentArgument(const std::string &name, ClazyContext *context)
0024     : CheckBase(name, context)
0025 {
0026 }
0027 
0028 static std::string expectedParentTypeFor(CXXRecordDecl *decl)
0029 {
0030     if (clazy::derivesFrom(decl, "QWidget")) {
0031         return "QWidget";
0032     }
0033     if (clazy::derivesFrom(decl, "QQuickItem")) {
0034         return "QQuickItem";
0035     } else if (clazy::derivesFrom(decl, "Qt3DCore::QEntity")) {
0036         return "Qt3DCore::QNode";
0037     }
0038 
0039     return "QObject";
0040 }
0041 
0042 void CtorMissingParentArgument::VisitDecl(Decl *decl)
0043 {
0044     auto *record = dyn_cast<CXXRecordDecl>(decl);
0045     bool ok = false;
0046 
0047     if (!clazy::isQObject(record)) {
0048         return;
0049     }
0050 
0051     if (record->hasInheritedConstructor()) {
0052         // When doing using QObject::QObject you inherit the ctors from QObject, so don't warn.
0053         // Would be nicer to check if the using directives really refer to QObject::QObject and not to
0054         // Some other non-object class, but I can't find a way to get to ConstructorUsingShadowDecl from the CxxRecordDecl
0055         // so we might miss some true-positives
0056         return;
0057     }
0058 
0059     const bool hasCtors = record->ctor_begin() != record->ctor_end();
0060     if (!hasCtors) {
0061         return;
0062     }
0063 
0064     const std::string parentType = expectedParentTypeFor(record);
0065     int numCtors = 0;
0066     const bool hasQObjectParam = clazy::recordHasCtorWithParam(record, parentType, /*by-ref*/ ok, /*by-ref*/ numCtors);
0067     if (!ok) {
0068         return;
0069     }
0070 
0071     if (numCtors > 0 && !hasQObjectParam) {
0072         clang::CXXRecordDecl *baseClass = clazy::getQObjectBaseClass(record);
0073         const bool baseHasQObjectParam = clazy::recordHasCtorWithParam(baseClass, parentType, /*by-ref*/ ok, /*by-ref*/ numCtors);
0074         if (ok && !baseHasQObjectParam && sm().isInSystemHeader(baseClass->getBeginLoc())) {
0075             // If the base class ctors don't accept QObject, and it's declared in a system header don't warn
0076             return;
0077         }
0078 
0079         if (clazy::name(baseClass) == "QCoreApplication") {
0080             return;
0081         }
0082 
0083         emitWarning(decl, record->getQualifiedNameAsString() + std::string(" should take ") + parentType + std::string(" parent argument in CTOR"));
0084     }
0085 }