File indexing completed on 2024-05-12 05:41:02

0001 /*
0002     SPDX-FileCopyrightText: 2020 The Qt Company Ltd.
0003     SPDX-FileCopyrightText: 2020 Lucie Gerard <lucie.gerard@qt.io>
0004 
0005     SPDX-License-Identifier: LGPL-2.0-or-later
0006 */
0007 
0008 #include "qt6-fwd-fixes.h"
0009 #include "ClazyContext.h"
0010 #include "FixItUtils.h"
0011 #include "StringUtils.h"
0012 #include "Utils.h"
0013 
0014 #include <clang/AST/Decl.h>
0015 #include <clang/AST/DeclCXX.h>
0016 #include <clang/AST/Expr.h>
0017 #include <clang/AST/ExprCXX.h>
0018 #include <clang/AST/Stmt.h>
0019 #include <clang/AST/Type.h>
0020 #include <clang/Basic/Diagnostic.h>
0021 #include <clang/Basic/LLVM.h>
0022 #include <clang/Basic/SourceLocation.h>
0023 #include <clang/Lex/Lexer.h>
0024 #include <llvm/ADT/ArrayRef.h>
0025 #include <llvm/ADT/StringRef.h>
0026 #include <llvm/Support/Casting.h>
0027 
0028 #include <clang/Basic/Specifiers.h>
0029 
0030 using namespace clang;
0031 
0032 Qt6FwdFixes::Qt6FwdFixes(const std::string &name, ClazyContext *context)
0033     : CheckBase(name, context, Option_CanIgnoreIncludes)
0034 {
0035     enablePreProcessorCallbacks();
0036     context->enablePreprocessorVisitor();
0037 }
0038 
0039 static std::set<std::string> interestingFwdDecl = {
0040     "QCache", "QHash",   "QMap",        "QMultiHash",     "QMultiMap", "QPair",    "QQueue",       "QSet",        "QStack",       "QVarLengthArray",
0041     "QList",  "QVector", "QStringList", "QByteArrayList", "QMetaType", "QVariant", "QVariantList", "QVariantMap", "QVariantHash", "QVariantPair"};
0042 
0043 SourceLocation locForNextSemiColon(SourceLocation loc, const clang::SourceManager &sm, const clang::LangOptions &lo)
0044 {
0045     std::pair<FileID, unsigned> locInfo = sm.getDecomposedLoc(loc);
0046     bool InvalidTemp = false;
0047     StringRef File = sm.getBufferData(locInfo.first, &InvalidTemp);
0048     if (InvalidTemp) {
0049         return {};
0050     }
0051 
0052     const char *TokenBegin = File.data() + locInfo.second;
0053     Lexer lexer(sm.getLocForStartOfFile(locInfo.first), lo, File.begin(), TokenBegin, File.end());
0054 
0055     Token Tok;
0056     lexer.LexFromRawLexer(Tok);
0057 
0058     SourceLocation TokenLoc = Tok.getLocation();
0059 
0060     // Calculate how much chars needs to be skipped until the ';'
0061     // plus white spaces and \n or \r  after
0062     unsigned NumCharsUntilSemiColon = 0;
0063     unsigned NumWhitespaceChars = 0;
0064     const char *TokenEnd = sm.getCharacterData(TokenLoc) + Tok.getLength();
0065     unsigned char C = *TokenEnd;
0066 
0067     while (C != ';') {
0068         C = *(++TokenEnd);
0069         NumCharsUntilSemiColon++;
0070     }
0071     C = *(++TokenEnd);
0072     while (isHorizontalWhitespace(C)) {
0073         C = *(++TokenEnd);
0074         NumWhitespaceChars++;
0075     }
0076     // Skip \r, \n, \r\n, or \n\r
0077     if (C == '\n' || C == '\r') {
0078         char PrevC = C;
0079         C = *(++TokenEnd);
0080         NumWhitespaceChars++;
0081         if ((C == '\n' || C == '\r') && C != PrevC) {
0082             NumWhitespaceChars++;
0083         }
0084     }
0085     return loc.getLocWithOffset(Tok.getLength() + NumCharsUntilSemiColon + NumWhitespaceChars + 1);
0086 }
0087 
0088 void Qt6FwdFixes::VisitDecl(clang::Decl *decl)
0089 {
0090     auto *recDecl = dyn_cast<CXXRecordDecl>(decl);
0091     if (!recDecl) {
0092         return;
0093     }
0094     auto *parent = recDecl->getParent();
0095     std::string parentType = parent->getDeclKindName();
0096     if (parentType != "TranslationUnit") {
0097         return;
0098     }
0099     if (recDecl->hasDefinition()) {
0100         return;
0101     }
0102     if (interestingFwdDecl.find(recDecl->getNameAsString()) == interestingFwdDecl.end()) {
0103         return;
0104     }
0105 
0106     const std::string currentFile = m_sm.getFilename(decl->getLocation()).str();
0107     if (m_currentFile != currentFile) {
0108         m_currentFile = currentFile;
0109         m_including_qcontainerfwd = false;
0110         if (m_qcontainerfwd_included_in_files.find(currentFile) != m_qcontainerfwd_included_in_files.end()) {
0111             m_including_qcontainerfwd = true;
0112         }
0113     }
0114 
0115     SourceLocation endLoc = locForNextSemiColon(recDecl->getBeginLoc(), m_sm, lo());
0116 
0117     SourceLocation beginLoc;
0118     auto *tempclass = recDecl->getDescribedClassTemplate();
0119     if (tempclass) {
0120         beginLoc = tempclass->getBeginLoc();
0121     } else {
0122         beginLoc = recDecl->getBeginLoc();
0123     }
0124 
0125     std::vector<FixItHint> fixits;
0126     std::string message;
0127     auto warningLocation = beginLoc;
0128     SourceRange fixitRange = SourceRange(beginLoc, endLoc);
0129 
0130     std::string replacement;
0131     CharSourceRange controledFixitRange = CharSourceRange(fixitRange, false);
0132     if (!m_including_qcontainerfwd) {
0133         replacement += "#include <QtCore/qcontainerfwd.h>\n";
0134         fixits.push_back(FixItHint::CreateReplacement(controledFixitRange, replacement));
0135     } else {
0136         fixits.push_back(FixItHint::CreateRemoval(controledFixitRange));
0137     }
0138 
0139     message += "Using forward declaration of ";
0140     message += recDecl->getNameAsString();
0141     message += ".";
0142     if (m_including_qcontainerfwd) {
0143         message += " (already)";
0144     }
0145     message += " Including <QtCore/qcontainerfwd.h> instead.";
0146 
0147     emitWarning(warningLocation, message, fixits);
0148     m_including_qcontainerfwd = true;
0149     return;
0150 }
0151 
0152 void Qt6FwdFixes::VisitInclusionDirective(clang::SourceLocation HashLoc,
0153                                           const clang::Token & /*IncludeTok*/,
0154                                           clang::StringRef FileName,
0155                                           bool /*IsAngled*/,
0156                                           clang::CharSourceRange /*FilenameRange*/,
0157                                           clazy::OptionalFileEntryRef /*File*/,
0158                                           clang::StringRef /*SearchPath*/,
0159                                           clang::StringRef /*RelativePath*/,
0160                                           const clang::Module * /*Imported*/,
0161                                           clang::SrcMgr::CharacteristicKind /*FileType*/)
0162 {
0163     auto current_file = m_sm.getFilename(HashLoc);
0164     if (FileName.str() == "QtCore/qcontainerfwd.h") {
0165         m_qcontainerfwd_included_in_files.insert(current_file);
0166         return;
0167     }
0168 }