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 }