41 ast_matchers::internal::Matcher<CXXRecordDecl> InheritsVirtualMethod =
42 hasAnyBase(hasType(cxxRecordDecl(has(cxxMethodDecl(isVirtual())))));
46 anyOf(has(cxxMethodDecl(isVirtual())), InheritsVirtualMethod),
48 unless(hasPublicVirtualOrProtectedNonVirtualDestructor()))
49 .bind(
"ProblematicClassOrStruct"),
55 const SourceManager &SM,
const LangOptions &LangOpts) {
56 if (Destructor.getLocation().isMacroID())
59 SourceLocation VirtualBeginLoc = Destructor.getBeginLoc();
60 SourceLocation VirtualBeginSpellingLoc =
61 SM.getSpellingLoc(Destructor.getBeginLoc());
62 SourceLocation VirtualEndLoc = VirtualBeginSpellingLoc.getLocWithOffset(
63 Lexer::MeasureTokenLength(VirtualBeginSpellingLoc, SM, LangOpts));
67 std::optional<Token> NextToken =
68 Lexer::findNextToken(VirtualEndLoc, SM, LangOpts);
71 SourceLocation StartOfNextToken = NextToken->getLocation();
73 return CharSourceRange::getCharRange(VirtualBeginLoc, StartOfNextToken);
78 for (DeclContext::specific_decl_iterator<AccessSpecDecl>
79 AS{StructOrClass.decls_begin()},
80 ASEnd{StructOrClass.decls_end()};
82 AccessSpecDecl *ASDecl = *AS;
83 if (ASDecl->getAccess() == AccessSpecifier::AS_public)
92 const SourceManager &SourceManager) {
93 std::string DestructorString;
95 bool AppendLineBreak =
false;
99 if (!AccessSpecDecl) {
100 if (StructOrClass.isClass()) {
101 Loc = StructOrClass.getEndLoc();
102 DestructorString =
"public:";
103 AppendLineBreak =
true;
105 Loc = StructOrClass.getBraceRange().getBegin().getLocWithOffset(1);
108 Loc = AccessSpecDecl->getEndLoc().getLocWithOffset(1);
111 DestructorString = (llvm::Twine(DestructorString) +
"\nvirtual ~" +
112 StructOrClass.getName().str() +
"() = default;" +
113 (AppendLineBreak ?
"\n" :
""))
116 return FixItHint::CreateInsertion(Loc, DestructorString);
127 const std::string &Keyword) {
128 size_t KeywordIndex = DestructorString.find(Keyword);
129 if (KeywordIndex != std::string::npos)
130 DestructorString.erase(KeywordIndex, Keyword.length());
131 return DestructorString;
135 const std::string &Visibility,
const CXXDestructorDecl &Destructor,
136 const SourceManager &SM,
const LangOptions &LangOpts) {
137 std::string DestructorString =
138 (llvm::Twine() + Visibility +
":\n" +
139 (Visibility ==
"public" && !Destructor.isVirtual() ?
"virtual " :
""))
143 if (Visibility ==
"protected" && Destructor.isVirtualAsWritten())
144 OriginalDestructor =
eraseKeyword(OriginalDestructor,
"virtual ");
147 (llvm::Twine(DestructorString) + OriginalDestructor +
148 (Destructor.isExplicitlyDefaulted() ?
";\n" :
"") +
"private:")
154 SourceLocation EndLocation;
155 if (Destructor.isExplicitlyDefaulted())
158 .getLocWithOffset(1);
160 EndLocation = Destructor.getEndLoc().getLocWithOffset(1);
162 auto OriginalDestructorRange =
163 CharSourceRange::getCharRange(Destructor.getBeginLoc(), EndLocation);
164 return FixItHint::CreateReplacement(OriginalDestructorRange,
169 const MatchFinder::MatchResult &Result) {
171 const auto *MatchedClassOrStruct =
172 Result.Nodes.getNodeAs<CXXRecordDecl>(
"ProblematicClassOrStruct");
174 const CXXDestructorDecl *Destructor = MatchedClassOrStruct->getDestructor();
178 if (Destructor->getAccess() == AccessSpecifier::AS_private) {
179 diag(MatchedClassOrStruct->getLocation(),
180 "destructor of %0 is private and prevents using the type")
181 << MatchedClassOrStruct;
182 diag(MatchedClassOrStruct->getLocation(),
183 "make it public and virtual", DiagnosticIDs::Note)
185 "public", *Destructor, *Result.SourceManager, getLangOpts());
186 diag(MatchedClassOrStruct->getLocation(),
187 "make it protected", DiagnosticIDs::Note)
189 "protected", *Destructor, *Result.SourceManager, getLangOpts());
195 bool ProtectedAndVirtual =
false;
198 if (MatchedClassOrStruct->hasUserDeclaredDestructor()) {
199 if (Destructor->getAccess() == AccessSpecifier::AS_public) {
200 Fix = FixItHint::CreateInsertion(Destructor->getLocation(),
"virtual ");
201 }
else if (Destructor->getAccess() == AccessSpecifier::AS_protected) {
202 ProtectedAndVirtual =
true;
203 if (
const auto MaybeRange =
205 Result.Context->getLangOpts()))
206 Fix = FixItHint::CreateRemoval(*MaybeRange);
210 *Result.SourceManager);
213 diag(MatchedClassOrStruct->getLocation(),
214 "destructor of %0 is %select{public and non-virtual|protected and "
216 << MatchedClassOrStruct << ProtectedAndVirtual;
217 diag(MatchedClassOrStruct->getLocation(),
218 "make it %select{public and virtual|protected and non-virtual}0",
220 << ProtectedAndVirtual <<
Fix;
static cl::opt< bool > Fix("fix", desc(R"(
Apply suggested fixes. Without -fix-errors
clang-tidy will bail out if any compilation
errors were found.
)"), cl::init(false), cl::cat(ClangTidyCategory))