File indexing completed on 2024-05-12 05:40:59
0001 /* 0002 SPDX-FileCopyrightText: 2015 Sergio Martins <smartins@kde.org> 0003 0004 SPDX-License-Identifier: LGPL-2.0-or-later 0005 */ 0006 0007 #include "copyable-polymorphic.h" 0008 #include "AccessSpecifierManager.h" 0009 #include "ClazyContext.h" 0010 #include "FixItUtils.h" 0011 #include "StringUtils.h" 0012 #include "Utils.h" 0013 0014 #include <clang/AST/DeclCXX.h> 0015 #include <clang/Basic/LLVM.h> 0016 #include <clang/Basic/Specifiers.h> 0017 #include <llvm/Support/Casting.h> 0018 0019 class ClazyContext; 0020 namespace clang 0021 { 0022 class Decl; 0023 } // namespace clang 0024 0025 using namespace clang; 0026 0027 /// Returns whether the class has non-private copy-ctor or copy-assign 0028 static bool hasPublicCopy(const CXXRecordDecl *record) 0029 { 0030 CXXConstructorDecl *copyCtor = Utils::copyCtor(record); 0031 const bool hasCallableCopyCtor = copyCtor && !copyCtor->isDeleted() && copyCtor->getAccess() == clang::AS_public; 0032 if (!hasCallableCopyCtor) { 0033 CXXMethodDecl *copyAssign = Utils::copyAssign(record); 0034 const bool hasCallableCopyAssign = copyAssign && !copyAssign->isDeleted() && copyAssign->getAccess() == clang::AS_public; 0035 if (!hasCallableCopyAssign) { 0036 return false; 0037 } 0038 } 0039 0040 return true; 0041 } 0042 0043 /// Checks if there's any base class with public copy 0044 static bool hasPublicCopyInAncestors(const CXXRecordDecl *record) 0045 { 0046 if (!record) { 0047 return false; 0048 } 0049 0050 for (auto base : record->bases()) { 0051 if (const Type *t = base.getType().getTypePtrOrNull()) { 0052 CXXRecordDecl *baseRecord = t->getAsCXXRecordDecl(); 0053 if (hasPublicCopy(baseRecord)) { 0054 return true; 0055 } 0056 if (hasPublicCopyInAncestors(t->getAsCXXRecordDecl())) { 0057 return true; 0058 } 0059 } 0060 } 0061 0062 return false; 0063 } 0064 0065 CopyablePolymorphic::CopyablePolymorphic(const std::string &name, ClazyContext *context) 0066 : CheckBase(name, context) 0067 { 0068 context->enableAccessSpecifierManager(); 0069 } 0070 0071 void CopyablePolymorphic::VisitDecl(clang::Decl *decl) 0072 { 0073 auto *record = dyn_cast<CXXRecordDecl>(decl); 0074 if (!record || !record->hasDefinition() || record->getDefinition() != record || !record->isPolymorphic()) { 0075 return; 0076 } 0077 0078 if (!hasPublicCopy(record)) { 0079 return; 0080 } 0081 0082 if (record->isEffectivelyFinal() && !hasPublicCopyInAncestors(record)) { 0083 // If the derived class is final, and all the base classes copy-ctors are protected or private then it's ok 0084 return; 0085 } 0086 0087 emitWarning(record->getBeginLoc(), "Polymorphic class " + record->getQualifiedNameAsString() + " is copyable. Potential slicing.", fixits(record)); 0088 } 0089 0090 std::vector<clang::FixItHint> CopyablePolymorphic::fixits(clang::CXXRecordDecl *record) 0091 { 0092 std::vector<FixItHint> result; 0093 if (!m_context->accessSpecifierManager) { 0094 return {}; 0095 } 0096 0097 const StringRef className = clazy::name(record); 0098 0099 // Insert Q_DISABLE_COPY(classname) in the private section if one exists, 0100 // otherwise at the end of the class declaration 0101 SourceLocation pos = m_context->accessSpecifierManager->firstLocationOfSection(clang::AccessSpecifier::AS_private, record); 0102 0103 if (pos.isValid()) { 0104 pos = Lexer::findLocationAfterToken(pos, clang::tok::colon, sm(), lo(), false); 0105 result.push_back(clazy::createInsertion(pos, std::string("\n\tQ_DISABLE_COPY(") + className.data() + std::string(")"))); 0106 } else { 0107 pos = record->getBraceRange().getEnd(); 0108 result.push_back(clazy::createInsertion(pos, std::string("\tQ_DISABLE_COPY(") + className.data() + std::string(")\n"))); 0109 } 0110 0111 // If the class has a default constructor, then we need to readd it, 0112 // as the disabled copy constructor removes it. 0113 // Add it in the public section if one exists, otherwise add a 0114 // public section at the top of the class declaration. 0115 if (record->hasDefaultConstructor()) { 0116 pos = m_context->accessSpecifierManager->firstLocationOfSection(clang::AccessSpecifier::AS_public, record); 0117 if (pos.isInvalid()) { 0118 pos = record->getBraceRange().getBegin().getLocWithOffset(1); 0119 result.push_back(clazy::createInsertion(pos, std::string("\npublic:\n\t") + className.data() + std::string("() = default;"))); 0120 } else { 0121 pos = Lexer::findLocationAfterToken(pos, clang::tok::colon, sm(), lo(), false); 0122 result.push_back(clazy::createInsertion(pos, std::string("\n\t") + className.data() + std::string("() = default;"))); 0123 } 0124 } 0125 0126 return result; 0127 }